package main import ( "encoding/json" "errors" "fmt" "io" "log" "net/http" "strings" ) type DispatchFunction func(*User, []string, io.ReadCloser) (interface{}, error) type apiHandler struct { PE *PictureExplorer Authenticate func(*http.Request) *User routes map[string](map[string]struct { AuthFunction DispatchFunction }) } func NewAPIHandler(pe *PictureExplorer, authenticate func(*http.Request) *User) *apiHandler { return &apiHandler{ pe, authenticate, map[string](map[string]struct { AuthFunction DispatchFunction }){ "images": ApiImagesRouting(pe), "next": ApiNextImagesRouting(pe), "version": ApiVersionRouting, }, } } func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { user := a.Authenticate(r) log.Printf("Handling %s API request from %s: %s %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, user, r.UserAgent()) // Extract URL arguments var sURL = strings.Split(r.URL.Path[1:], "/") if sURL[len(sURL)-1] == "" && len(sURL) > 0 { // Remove trailing / sURL = sURL[:len(sURL)-1] } w.Header().Set("Content-Type", "application/json") var ret interface{} var err error = nil // Refuse too large requests if r.ContentLength < 0 || r.ContentLength > 10485760 || ((r.Method == "DELETE" || r.Method == "GET" || r.Method == "HEAD") && r.ContentLength > 0) { http.Error(w, "{errmsg:\"Request too large or request size unknown\"}", http.StatusRequestEntityTooLarge) return } // Route request if len(sURL) == 0 { err = errors.New(fmt.Sprintf("No action provided")) } else if h, ok := a.routes[sURL[0]]; ok { if f, ok := h[r.Method]; ok { if f.AuthFunction(user, sURL[1:]) { ret, err = f.DispatchFunction(user, sURL[1:], r.Body) } else { w.Header().Set("WWW-Authenticate", "Basic realm=\"YouP0m\"") http.Error(w, "{errmsg:\"You are not allowed to do this request.\"}", http.StatusUnauthorized) return } } else { err = errors.New(fmt.Sprintf("Invalid action (%s) provided for %s.", r.Method, sURL[0])) } } // Format response resStatus := http.StatusOK if err != nil { ret = map[string]string{"errmsg": err.Error()} resStatus = http.StatusBadRequest } if ret == nil { ret = map[string]string{"errmsg": "Page not found"} resStatus = http.StatusNotFound } if j, err := json.Marshal(ret); err != nil { http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusInternalServerError) } else { w.WriteHeader(resStatus) w.Write(j) } }