diff --git a/echo.go b/echo.go index 9b403185..fe0cb8b4 100644 --- a/echo.go +++ b/echo.go @@ -14,8 +14,6 @@ import ( "encoding/xml" - "github.com/valyala/fasthttp" - "github.com/labstack/echo/engine" "github.com/labstack/gommon/log" ) @@ -473,25 +471,6 @@ func (binder) Bind(i interface{}, c Context) (err error) { return } -// WrapStandardHandler wraps `http.Handler` into `echo.Handler`. -func WrapStandardHandler(h http.Handler) Handler { - return HandlerFunc(func(c Context) error { - w := c.Response().Object().(http.ResponseWriter) - r := c.Request().Object().(*http.Request) - h.ServeHTTP(w, r) - return nil - }) -} - -// WrapFastHTTPHandler wraps `fasthttp.RequestHandler` into `echo.Handler`. -func WrapFastHTTPHandler(h fasthttp.RequestHandler) Handler { - return HandlerFunc(func(c Context) error { - ctx := c.Request().Object().(*fasthttp.RequestCtx) - h(ctx) - return nil - }) -} - func handlerName(h Handler) string { t := reflect.ValueOf(h).Type() if t.Kind() == reflect.Func { diff --git a/engine/fasthttp/server.go b/engine/fasthttp/server.go index d2f27141..b165724e 100644 --- a/engine/fasthttp/server.go +++ b/engine/fasthttp/server.go @@ -5,6 +5,7 @@ package fasthttp import ( "sync" + "github.com/labstack/echo" "github.com/labstack/echo/engine" "github.com/labstack/gommon/log" "github.com/valyala/fasthttp" @@ -121,3 +122,12 @@ func (s *Server) Start() { s.logger.Fatal(fasthttp.ListenAndServe(addr, handler)) } } + +// WrapHandler wraps `fasthttp.RequestHandler` into `echo.Handler`. +func WrapHandler(h fasthttp.RequestHandler) echo.Handler { + return echo.HandlerFunc(func(c echo.Context) error { + ctx := c.Request().Object().(*fasthttp.RequestCtx) + h(ctx) + return nil + }) +} diff --git a/engine/standard/server.go b/engine/standard/server.go index 55729ca9..0e5033f4 100644 --- a/engine/standard/server.go +++ b/engine/standard/server.go @@ -4,6 +4,7 @@ import ( "net/http" "sync" + "github.com/labstack/echo" "github.com/labstack/echo/engine" "github.com/labstack/gommon/log" ) @@ -116,3 +117,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.pool.response.Put(res) s.pool.header.Put(resHdr) } + +// WrapHandler wraps `http.Handler` into `echo.Handler`. +func WrapHandler(h http.Handler) echo.Handler { + return echo.HandlerFunc(func(c echo.Context) error { + w := c.Response().Object().(http.ResponseWriter) + r := c.Request().Object().(*http.Request) + h.ServeHTTP(w, r) + return nil + }) +} diff --git a/test/header.go b/test/header.go new file mode 100644 index 00000000..688e11ef --- /dev/null +++ b/test/header.go @@ -0,0 +1,33 @@ +package test + +import "net/http" + +type ( + Header struct { + header http.Header + } +) + +func (h *Header) Add(key, val string) { + h.header.Add(key, val) +} + +func (h *Header) Del(key string) { + h.header.Del(key) +} + +func (h *Header) Get(key string) string { + return h.header.Get(key) +} + +func (h *Header) Set(key, val string) { + h.header.Set(key, val) +} + +func (h *Header) Object() interface{} { + return h.header +} + +func (h *Header) reset(hdr http.Header) { + h.header = hdr +} diff --git a/test/http.go b/test/http.go deleted file mode 100644 index 2053abfc..00000000 --- a/test/http.go +++ /dev/null @@ -1,32 +0,0 @@ -package test - -import ( - "bytes" - "io" - "net/http" - "net/http/httptest" - - "github.com/labstack/echo/engine" - "github.com/labstack/echo/engine/standard" - "github.com/labstack/gommon/log" -) - -type ( - ResponseRecorder struct { - engine.Response - Body *bytes.Buffer - } -) - -func NewRequest(method, url string, body io.Reader) engine.Request { - r, _ := http.NewRequest(method, url, body) - return standard.NewRequest(r) -} - -func NewResponseRecorder() *ResponseRecorder { - r := httptest.NewRecorder() - return &ResponseRecorder{ - Response: standard.NewResponse(r, log.New("test")), - Body: r.Body, - } -} diff --git a/test/request.go b/test/request.go new file mode 100644 index 00000000..e50204d9 --- /dev/null +++ b/test/request.go @@ -0,0 +1,90 @@ +package test + +import ( + "io" + "net/http" + + "github.com/labstack/echo/engine" +) + +type ( + Request struct { + request *http.Request + url engine.URL + header engine.Header + } +) + +func NewRequest(method, url string, body io.Reader) engine.Request { + r, _ := http.NewRequest(method, url, body) + return &Request{ + request: r, + url: &URL{url: r.URL}, + header: &Header{r.Header}, + } +} + +func (r *Request) TLS() bool { + return r.request.TLS != nil +} + +func (r *Request) Scheme() string { + if r.TLS() { + return "https" + } + return "http" +} + +func (r *Request) Host() string { + return r.request.Host +} + +func (r *Request) URL() engine.URL { + return r.url +} + +func (r *Request) Header() engine.Header { + return r.header +} + +// func Proto() string { +// return r.request.Proto() +// } +// +// func ProtoMajor() int { +// return r.request.ProtoMajor() +// } +// +// func ProtoMinor() int { +// return r.request.ProtoMinor() +// } + +func (r *Request) RemoteAddress() string { + return r.request.RemoteAddr +} + +func (r *Request) Method() string { + return r.request.Method +} + +func (r *Request) URI() string { + return r.request.RequestURI +} + +func (r *Request) Body() io.ReadCloser { + return r.request.Body +} + +func (r *Request) FormValue(name string) string { + return r.request.FormValue(name) +} + +func (r *Request) Object() interface{} { + return r.request +} + +func (r *Request) reset(req *http.Request, h engine.Header, u engine.URL) { + r.request = req + r.header = h + r.url = u +} diff --git a/test/response.go b/test/response.go new file mode 100644 index 00000000..460252d5 --- /dev/null +++ b/test/response.go @@ -0,0 +1,94 @@ +package test + +import ( + "bytes" + "io" + "net/http" + "net/http/httptest" + + "github.com/labstack/echo/engine" + "github.com/labstack/gommon/log" +) + +type ( + Response struct { + response http.ResponseWriter + header engine.Header + status int + size int64 + committed bool + writer io.Writer + logger *log.Logger + } + + ResponseRecorder struct { + engine.Response + Body *bytes.Buffer + } +) + +func NewResponseRecorder() *ResponseRecorder { + rec := httptest.NewRecorder() + return &ResponseRecorder{ + Response: &Response{ + response: rec, + header: &Header{rec.Header()}, + writer: rec, + logger: log.New("test"), + }, + Body: rec.Body, + } +} + +func (r *Response) Header() engine.Header { + return r.header +} + +func (r *Response) WriteHeader(code int) { + if r.committed { + r.logger.Warn("response already committed") + return + } + r.status = code + r.response.WriteHeader(code) + r.committed = true +} + +func (r *Response) Write(b []byte) (n int, err error) { + n, err = r.writer.Write(b) + r.size += int64(n) + return +} + +func (r *Response) Status() int { + return r.status +} + +func (r *Response) Size() int64 { + return r.size +} + +func (r *Response) Committed() bool { + return r.committed +} + +func (r *Response) SetWriter(w io.Writer) { + r.writer = w +} + +func (r *Response) Writer() io.Writer { + return r.writer +} + +func (r *Response) Object() interface{} { + return r.response +} + +func (r *Response) reset(w http.ResponseWriter, h engine.Header) { + r.response = w + r.header = h + r.status = http.StatusOK + r.size = 0 + r.committed = false + r.writer = w +} diff --git a/test/server.go b/test/server.go new file mode 100644 index 00000000..66261968 --- /dev/null +++ b/test/server.go @@ -0,0 +1,118 @@ +package test + +import ( + "net/http" + "sync" + + "github.com/labstack/echo/engine" + "github.com/labstack/gommon/log" +) + +type ( + Server struct { + *http.Server + config *engine.Config + handler engine.HandlerFunc + pool *Pool + logger *log.Logger + } + + Pool struct { + request sync.Pool + response sync.Pool + header sync.Pool + url sync.Pool + } +) + +func New(addr string) *Server { + c := &engine.Config{Address: addr} + return NewConfig(c) +} + +func NewTLS(addr, certfile, keyfile string) *Server { + c := &engine.Config{ + Address: addr, + TLSCertfile: certfile, + TLSKeyfile: keyfile, + } + return NewConfig(c) +} + +func NewConfig(c *engine.Config) (s *Server) { + s = &Server{ + Server: new(http.Server), + config: c, + pool: &Pool{ + request: sync.Pool{ + New: func() interface{} { + return &Request{} + }, + }, + response: sync.Pool{ + New: func() interface{} { + return &Response{logger: s.logger} + }, + }, + header: sync.Pool{ + New: func() interface{} { + return &Header{} + }, + }, + url: sync.Pool{ + New: func() interface{} { + return &URL{} + }, + }, + }, + handler: func(req engine.Request, res engine.Response) { + s.logger.Fatal("handler not set") + }, + logger: log.New("echo"), + } + return +} + +func (s *Server) SetHandler(h engine.HandlerFunc) { + s.handler = h +} + +func (s *Server) SetLogger(l *log.Logger) { + s.logger = l +} + +func (s *Server) Start() { + s.Addr = s.config.Address + s.Handler = s + certfile := s.config.TLSCertfile + keyfile := s.config.TLSKeyfile + if certfile != "" && keyfile != "" { + s.logger.Fatal(s.ListenAndServeTLS(certfile, keyfile)) + } else { + s.logger.Fatal(s.ListenAndServe()) + } +} + +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Request + req := s.pool.request.Get().(*Request) + reqHdr := s.pool.header.Get().(*Header) + reqURL := s.pool.url.Get().(*URL) + reqHdr.reset(r.Header) + reqURL.reset(r.URL) + req.reset(r, reqHdr, reqURL) + + // Response + res := s.pool.response.Get().(*Response) + resHdr := s.pool.header.Get().(*Header) + resHdr.reset(w.Header()) + res.reset(w, resHdr) + + s.handler(req, res) + + s.pool.request.Put(req) + s.pool.header.Put(reqHdr) + s.pool.url.Put(reqURL) + s.pool.response.Put(res) + s.pool.header.Put(resHdr) +} diff --git a/test/url.go b/test/url.go new file mode 100644 index 00000000..8f64813f --- /dev/null +++ b/test/url.go @@ -0,0 +1,37 @@ +package test + +import "net/url" + +type ( + URL struct { + url *url.URL + query url.Values + } +) + +func (u *URL) URL() *url.URL { + return u.url +} + +func (u *URL) SetPath(path string) { + u.url.Path = path +} + +func (u *URL) Path() string { + return u.url.Path +} + +func (u *URL) QueryValue(name string) string { + if u.query == nil { + u.query = u.url.Query() + } + return u.query.Get(name) +} + +func (u *URL) Object() interface{} { + return u.url +} + +func (u *URL) reset(url *url.URL) { + u.url = url +}