func (h *APIHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { startTime := time.Now() var ( ctx context.Context cancel context.CancelFunc ) if h.timeout > 0 { ctx, cancel = context.WithTimeout(context.Background(), h.timeout) defer cancel() } else { ctx = context.Background() } ctx = cache.NewContext(ctx) if h.callbacks.OnInitCtx != nil { ctx = h.callbacks.OnInitCtx(ctx, req) } if h.callbacks.OnStartServing != nil { h.callbacks.OnStartServing(ctx, req) } defer func() { if h.callbacks.OnEndServing != nil { h.callbacks.OnEndServing(ctx, req, startTime) } }() if req.Method != "POST" && req.Method != "GET" { if h.callbacks.OnError != nil { err := &gorpc.CallHandlerError{ Type: gorpc.ErrorInvalidMethod, Err: errors.New("Invalid method"), } h.callbacks.OnError(ctx, w, req, nil, err) } h.writeError(ctx, w, "", http.StatusMethodNotAllowed) return } var resp httpSessionResponse handler, params, err := h.parseRequest(ctx, req) if err != nil { if h.callbacks.OnError != nil { h.callbacks.OnError(ctx, w, req, resp, err) } h.writeError(ctx, w, err.Error(), http.StatusBadRequest) return } if handler == nil { if h.callbacks.On404 != nil { h.callbacks.On404(ctx, req) } h.writeError(ctx, w, "", http.StatusNotFound) return } done := make(chan bool, 1) var cacheEntry *cache.CacheEntry go func() { defer func() { if r := recover(); r != nil { trace := make([]byte, 16*1024) n := runtime.Stack(trace, false) trace = trace[:n] if h.callbacks.OnPanic != nil { h.callbacks.OnPanic(ctx, w, r, trace, req) } err = &gorpc.CallHandlerError{ Type: gorpc.ErrorPanic, Err: fmt.Errorf("%#v\n\n%s", r, trace), } } done <- true }() cacheEntry, err = h.callHandlerWithCache(ctx, &resp, req, handler, params) }() // Wait handler or ctx timeout select { case <-ctx.Done(): case <-done: } if ctx.Err() == context.DeadlineExceeded { h.writeTimeoutError(ctx, req, w) return } if err != nil { if err.Type != gorpc.ErrorPanic && h.callbacks.OnError != nil { h.callbacks.OnError(ctx, w, req, nil, err) } switch err.Type { case gorpc.ErrorInParameters: h.writeError(ctx, w, err.UserMessage(), http.StatusBadRequest) default: h.writeInternalError(ctx, w, err.Error()) } return } h.writeResponse(ctx, cacheEntry, &resp, w, req, startTime) }
func (h *APIHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { startTime := time.Now() // TODO: create context with reasonable timeout ctx := context.Background() if h.callbacks.OnInitCtx != nil { ctx = h.callbacks.OnInitCtx(ctx, req) } ctx = cache.NewContext(ctx) if h.callbacks.OnStartServing != nil { h.callbacks.OnStartServing(ctx, req) } defer func() { if h.callbacks.OnEndServing != nil { h.callbacks.OnEndServing(ctx, req, startTime) } if r := recover(); r != nil { trace := make([]byte, 16*1024) n := runtime.Stack(trace, false) trace = trace[:n] if h.callbacks.OnPanic != nil { h.callbacks.OnPanic(ctx, w, r, trace, req) } h.writeError(ctx, w, "", http.StatusInternalServerError) } }() if req.Method != "POST" && req.Method != "GET" { if h.callbacks.OnError != nil { err := &gorpc.CallHandlerError{ Type: gorpc.ErrorInvalidMethod, Err: errors.New("Invalid method"), } h.callbacks.OnError(ctx, w, req, nil, err) } h.writeError(ctx, w, "", http.StatusMethodNotAllowed) return } var resp httpSessionResponse handler, params, err := h.parseRequest(ctx, req) if err != nil { if h.callbacks.OnError != nil { h.callbacks.OnError(ctx, w, req, resp, err) } h.writeError(ctx, w, err.Error(), http.StatusBadRequest) return } if handler == nil { if h.callbacks.On404 != nil { h.callbacks.On404(ctx, req) } h.writeError(ctx, w, "", http.StatusNotFound) return } cacheEntry, err := h.callHandlerWithCache(ctx, &resp, req, handler, params) if err != nil { if h.callbacks.OnError != nil { h.callbacks.OnError(ctx, w, req, nil, err) } switch err.Type { case gorpc.ErrorInParameters: h.writeError(ctx, w, err.UserMessage(), http.StatusBadRequest) default: h.writeError(ctx, w, "", http.StatusInternalServerError) } return } h.writeResponse(ctx, cacheEntry, &resp, w, req, startTime) }