func (h *HttpRouter) HasHandler(path, method string) bool {
	route, ok := h.mux[path]
	if !ok {
		return false
	}
	return route.HasHandler(method)
}
HasHandler returns true if the router has a handle to
handle a request to the path and method
package rpc
import (
	"context"
	"fmt"
	"net/http"
	"runtime/debug"
	"strconv"
	"github.com/oasislabs/oasis-gateway/errors"
	"github.com/oasislabs/oasis-gateway/log"
	"github.com/oasislabs/oasis-gateway/rw"
	"github.com/oasislabs/oasis-gateway/stats"
	stderr "github.com/pkg/errors"
	"github.com/rs/cors"
)
const HttpHeaderTraceID = "X-OASIS-TRACE-ID"
type HttpPreProcessor interface {
	ServeHTTP(w http.ResponseWriter, req *http.Request) (bool, *http.Request)
}
type HttpMiddleware interface {
	ServeHTTP(req *http.Request) (interface{}, error)
}
type HttpMiddlewareFunc func(req *http.Request) (interface{}, error)
func (f HttpMiddlewareFunc) ServeHTTP(req *http.Request) (interface{}, error) {
	return f(req)
}
type HttpError struct {
	Cause *errors.Error
	StatusCode int
}
func (e HttpError) Log(fields log.Fields) {
	fields.Add("status_code", e.StatusCode)
	if e.Cause != nil {
		e.Cause.Log(fields)
	}
}
func (e HttpError) Error() string {
	return fmt.Sprintf("%s with status code %d", e.Cause.Error(), e.StatusCode)
}
func MakeHttpError(ctx context.Context, error errors.Error, statusCode int) *HttpError {
	return &HttpError{
		Cause:      &error,
		StatusCode: statusCode,
	}
}
func HttpBadRequest(ctx context.Context, error errors.Error) *HttpError {
	return MakeHttpError(ctx, error, http.StatusBadRequest)
}
func HttpForbidden(ctx context.Context, error errors.Error) *HttpError {
	return MakeHttpError(ctx, error, http.StatusForbidden)
}
func HttpNotFound(ctx context.Context, error errors.Error) *HttpError {
	return MakeHttpError(ctx, error, http.StatusNotFound)
}
func HttpMethodNotAllowed(ctx context.Context, error errors.Error) *HttpError {
	return MakeHttpError(ctx, error, http.StatusMethodNotAllowed)
}
func HttpTooManyRequests(ctx context.Context, error errors.Error) *HttpError {
	return MakeHttpError(ctx, error, http.StatusTooManyRequests)
}
func HttpNotImplemented(ctx context.Context, error errors.Error) *HttpError {
	return MakeHttpError(ctx, error, http.StatusNotImplemented)
}
func HttpInternalServerError(ctx context.Context, error errors.Error) *HttpError {
	return MakeHttpError(ctx, error, http.StatusInternalServerError)
}
type MethodHandlers map[string]HttpMiddleware
func (h MethodHandlers) Add(method string, middleware HttpMiddleware) {
	h[method] = middleware
}
type RouteCounters map[string]*stats.CounterGroup
type RouteLatencies map[string]*stats.IntWindow
type HttpRoute struct {
	logger        log.Logger
	handlers      map[string]HttpMiddleware
	preProcessors []HttpPreProcessor
	encoder       Encoder
	tracker       *stats.MethodTracker
}
type HttpRouteProps struct {
	Logger        log.Logger
	Encoder       Encoder
	Handlers      MethodHandlers
	PreProcessors []HttpPreProcessor
}
func NewHttpRoute(props HttpRouteProps) *HttpRoute {
	methods := make([]string, 0, len(props.Handlers))
	for method := range props.Handlers {
		methods = append(methods, method)
	}
	return &HttpRoute{
		logger:        props.Logger,
		handlers:      props.Handlers,
		preProcessors: props.PreProcessors,
		tracker: stats.NewMethodTrackerWithResult(&stats.MethodTrackerProps{
			Methods:    methods,
			Results:    []string{"200", "204", "400", "401", "403", "405", "409", "500", "error", "preprocessor"},
			WindowSize: 64,
		}),
		encoder: props.Encoder,
	}
}
func (h *HttpRoute) Stats() stats.Metrics {
	return h.tracker.Stats()
}
func (h *HttpRoute) HasHandler(method string) bool {
	_, ok := h.handlers[method]
	return ok
}
func (h *HttpRoute) reportSuccess(
	res http.ResponseWriter,
	req *http.Request,
	body interface{},
) (int, error) {
	path := req.URL.EscapedPath()
	method := req.Method
	res.Header().Add(HttpHeaderTraceID, strconv.FormatInt(log.GetTraceID(req.Context()), 10))
	if body == nil {
		res.WriteHeader(http.StatusNoContent)
		h.logger.Info(req.Context(), "", log.MapFields{
			"path":        path,
			"method":      method,
			"call_type":   "HttpRequestHandleSuccess",
			"status_code": http.StatusNoContent,
		})
		return http.StatusNoContent, nil
	}
	if err := h.encoder.Encode(res, body); err != nil {
		res.WriteHeader(http.StatusInternalServerError)
		h.logger.Warn(req.Context(), "failed to encode response to response writer", log.MapFields{
			"path":        path,
			"method":      method,
			"call_type":   "HttpRequestHandleFailure",
			"status_code": http.StatusInternalServerError,
			"err":         err,
		})
		return 0, err
	}
	h.logger.Info(req.Context(), "", log.MapFields{
		"path":        path,
		"method":      method,
		"call_type":   "HttpRequestHandleSuccess",
		"status_code": http.StatusOK,
	})
	return http.StatusOK, nil
}
func (h *HttpRoute) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	_, _ = h.tracker.InstrumentResult(req.Method, func() *stats.TrackResult {
		var ok bool
		for _, preProcessor := range h.preProcessors {
			ok, req = preProcessor.ServeHTTP(res, req)
			if !ok {
				return &stats.TrackResult{
					Value: nil,
					Error: nil,
					Type:  "preprocessor",
				}
			}
		}
		status, err := h.serveHTTP(res, req)
		t := fmt.Sprintf("%d", status)
		if err != nil {
			t = "error"
		}
		return &stats.TrackResult{
			Value: nil,
			Error: nil,
			Type:  t,
		}
	})
}
func (h *HttpRoute) serveHTTP(res http.ResponseWriter, req *http.Request) (int, error) {
	handler, ok := h.handlers[req.Method]
	if !ok {
		return h.reportError(res, req, &HttpError{StatusCode: http.StatusMethodNotAllowed})
	}
	v, err := handler.ServeHTTP(req)
	if err != nil {
		return h.reportAnyError(res, req, err)
	}
	return h.reportSuccess(res, req, v)
}
func (h *HttpRoute) reportAnyError(res http.ResponseWriter, req *http.Request, err error) (int, error) {
	switch err := err.(type) {
	case HttpError:
		return h.reportError(res, req, &err)
	case *HttpError:
		return h.reportError(res, req, err)
	case errors.Error:
		return h.reportError(res, req, mapHttpError(err))
	default:
		return h.reportError(res, req, HttpInternalServerError(
			req.Context(), errors.New(errors.ErrInternalError, err)))
	}
}
func (h *HttpRoute) reportError(res http.ResponseWriter, req *http.Request, err *HttpError) (int, error) {
	path := req.URL.EscapedPath()
	method := req.Method
	res.Header().Add(HttpHeaderTraceID, strconv.FormatInt(log.GetTraceID(req.Context()), 10))
	res.WriteHeader(err.StatusCode)
	if err.Cause != nil {
		if eerr := h.encoder.Encode(res, Error{
			ErrorCode:   err.Cause.ErrorCode().Code(),
			Description: err.Cause.ErrorCode().Desc(),
		}); eerr != nil {
			h.logger.Debug(req.Context(), "failed to encode error response to response writer", log.MapFields{
				"path":      path,
				"method":    method,
				"call_type": "HttpRequestHandleFailure",
			}, err)
			return 0, err
		}
	}
	h.logger.Info(req.Context(), "", log.MapFields{
		"path":      path,
		"method":    method,
		"call_type": "HttpRequestHandleFailure",
	}, err)
	return err.StatusCode, nil
}
type HttpRouter struct {
	encoder Encoder
	mux     map[string]*HttpRoute
	logger  log.Logger
}
func (h *HttpRouter) HasRoute(path string) bool {
	_, ok := h.mux[path]
	return ok
}