package main import ( "encoding/base64" "encoding/json" "errors" "fmt" "io" "log" "net/http" "github.com/julienschmidt/httprouter" ) var router = httprouter.New() func Router() *httprouter.Router { return router } type HTTPResponse interface { WriteResponse(http.ResponseWriter) } type APIResponse struct { response interface{} } func (r APIResponse) WriteResponse(w http.ResponseWriter) { if str, found := r.response.(string); found { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) io.WriteString(w, str) } else if bts, found := r.response.([]byte); found { w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", "attachment") w.Header().Set("Content-Transfer-Encoding", "binary") w.WriteHeader(http.StatusOK) w.Write(bts) } else if j, err := json.Marshal(r.response); err != nil { w.Header().Set("Content-Type", "application/json") http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", err.Error()), http.StatusInternalServerError) } else { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(j) } } type APIErrorResponse struct { status int err error } func (r APIErrorResponse) WriteResponse(w http.ResponseWriter) { if r.status == 0 { r.status = http.StatusBadRequest } w.Header().Set("Content-Type", "application/json") http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", r.err.Error()), r.status) } type DispatchFunction func(httprouter.Params, []byte) HTTPResponse func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, []byte), access ...func(*User, *http.Request) *APIErrorResponse) func(http.ResponseWriter, *http.Request, httprouter.Params) { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { if addr := r.Header.Get("X-Forwarded-For"); addr != "" { r.RemoteAddr = addr } log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent()) // Read Authorization header var user *User = nil if cookie, err := r.Cookie("auth"); err == nil { if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil { w.Header().Set("Content-Type", "application/json") http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err.Error()), http.StatusNotAcceptable) return } else if session, err := getSession(sessionid); err != nil { w.Header().Set("Content-Type", "application/json") http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err.Error()), http.StatusUnauthorized) return } else if session.IdUser == nil { user = nil } else if std, err := getUser(int(*session.IdUser)); err != nil { w.Header().Set("Content-Type", "application/json") http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err.Error()), http.StatusUnauthorized) return } else { user = &std } } // Check access limitation for _, a := range access { if err := a(user, r); err != nil { w.Header().Set("Content-Type", "application/json") http.Error(w, fmt.Sprintf(`{"errmsg":%q}`, err.err.Error()), http.StatusForbidden) return } } // Read the body if r.ContentLength < 0 || r.ContentLength > 6553600 { w.Header().Set("Content-Type", "application/json") http.Error(w, "{errmsg:\"Request too large or request size unknown\"}", http.StatusRequestEntityTooLarge) return } var body []byte if r.ContentLength > 0 { tmp := make([]byte, 1024) for { n, err := r.Body.Read(tmp) for j := 0; j < n; j++ { body = append(body, tmp[j]) } if err != nil || n <= 0 { break } } } f(w, r, ps, body) } } func formatResponseHandler(f func(*http.Request, httprouter.Params, []byte) HTTPResponse) func(http.ResponseWriter, *http.Request, httprouter.Params, []byte) { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, body []byte) { f(r, ps, body).WriteResponse(w) } } func apiRawHandler(f func(http.ResponseWriter, httprouter.Params, []byte) HTTPResponse, access ...func(*User, *http.Request) *APIErrorResponse) func(http.ResponseWriter, *http.Request, httprouter.Params) { return rawHandler(func (w http.ResponseWriter, r *http.Request, ps httprouter.Params, b []byte) { formatResponseHandler(func (_ *http.Request, ps httprouter.Params, b []byte) HTTPResponse { return f(w, ps, b) })(w, r, ps, b) }, access...) } func apiHandler(f DispatchFunction, access ...func(*User, *http.Request) *APIErrorResponse) func(http.ResponseWriter, *http.Request, httprouter.Params) { return rawHandler(formatResponseHandler(func (_ *http.Request, ps httprouter.Params, b []byte) HTTPResponse { return f(ps, b) }), access...) } func formatApiResponse(i interface{}, err error) HTTPResponse { if err != nil { return APIErrorResponse{ status: http.StatusBadRequest, err: err, } } else { return APIResponse{i} } } func apiAuthHandler(f func(*User, httprouter.Params, []byte) HTTPResponse, access ...func(*User, *http.Request) *APIErrorResponse) func(http.ResponseWriter, *http.Request, httprouter.Params) { return rawHandler(formatResponseHandler(func (r *http.Request, ps httprouter.Params, b []byte) HTTPResponse { if cookie, err := r.Cookie("auth"); err != nil { return f(nil, ps, b) } else if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil { return APIErrorResponse{ status: http.StatusBadRequest, err: err, } } else if session, err := getSession(sessionid); err != nil { return APIErrorResponse{ status: http.StatusBadRequest, err: err, } } else if session.IdUser == nil { return f(nil, ps, b) } else if std, err := getUser(int(*session.IdUser)); err != nil { return APIErrorResponse{ status: http.StatusInternalServerError, err: err, } } else { return f(&std, ps, b) } }), access...) } func loggedUser(u *User, r *http.Request) *APIErrorResponse { if u != nil { return nil } else { ret := &APIErrorResponse{ status: http.StatusForbidden, err: errors.New("Permission Denied"), } return ret } } func adminRestricted(u *User, r *http.Request) *APIErrorResponse { if u != nil && u.IsAdmin { return nil } else { ret := &APIErrorResponse{ status: http.StatusForbidden, err: errors.New("Permission Denied"), } return ret } }