예제 #1
0
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)
}
예제 #2
0
파일: http_json.go 프로젝트: wtertius/gorpc
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)
}