Skip to content

Commit

Permalink
Merge pull request #145 from taik0/http_server
Browse files Browse the repository at this point in the history
Decoupled http server
  • Loading branch information
kpacha authored Aug 30, 2018
2 parents 1e232ac + 016b0ce commit f003ffd
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 39 deletions.
30 changes: 11 additions & 19 deletions router/gin/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package gin

import (
"context"
"fmt"
"net/http"

"github.com/gin-gonic/gin"
Expand All @@ -14,13 +13,17 @@ import (
"github.com/devopsfaith/krakend/router"
)

// RunServerFunc is a func that will run the http Server with the given params.
type RunServerFunc func(context.Context, config.ServiceConfig, http.Handler) error

// Config is the struct that collects the parts the router should be builded from
type Config struct {
Engine *gin.Engine
Middlewares []gin.HandlerFunc
HandlerFactory HandlerFactory
ProxyFactory proxy.Factory
Logger logging.Logger
RunServer RunServerFunc
}

// DefaultFactory returns a gin router factory with the injected proxy factory and logger.
Expand All @@ -33,6 +36,7 @@ func DefaultFactory(proxyFactory proxy.Factory, logger logging.Logger) router.Fa
HandlerFactory: EndpointHandler,
ProxyFactory: proxyFactory,
Logger: logger,
RunServer: router.RunServer,
},
)
}
Expand All @@ -53,12 +57,13 @@ func (rf factory) New() router.Router {

// NewWithContext implements the factory interface
func (rf factory) NewWithContext(ctx context.Context) router.Router {
return ginRouter{rf.cfg, ctx}
return ginRouter{rf.cfg, ctx, rf.cfg.RunServer}
}

type ginRouter struct {
cfg Config
ctx context.Context
cfg Config
ctx context.Context
RunServer RunServerFunc
}

// Run implements the router interface
Expand Down Expand Up @@ -87,23 +92,10 @@ func (r ginRouter) Run(cfg config.ServiceConfig) {
c.Header(router.CompleteResponseHeaderName, router.HeaderIncompleteResponseValue)
})

s := &http.Server{
Addr: fmt.Sprintf(":%d", cfg.Port),
Handler: r.cfg.Engine,
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
ReadHeaderTimeout: cfg.ReadHeaderTimeout,
IdleTimeout: cfg.IdleTimeout,
}

go func() {
r.cfg.Logger.Critical(s.ListenAndServe())
}()

<-r.ctx.Done()
if err := s.Shutdown(context.Background()); err != nil {
if err := r.RunServer(r.ctx, cfg, r.cfg.Engine); err != nil {
r.cfg.Logger.Error(err.Error())
}

r.cfg.Logger.Info("Router execution ended")
}

Expand Down
36 changes: 36 additions & 0 deletions router/gin/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ package gin
import (
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"testing"
"time"

"github.com/devopsfaith/krakend/config"
"github.com/devopsfaith/krakend/logging"
"github.com/devopsfaith/krakend/proxy"
"github.com/devopsfaith/krakend/router"
"github.com/gin-gonic/gin"
)

func TestDefaultFactory_ok(t *testing.T) {
Expand Down Expand Up @@ -221,6 +224,39 @@ func TestDefaultFactory_proxyFactoryCrash(t *testing.T) {
}
}

func TestRunServer_ko(t *testing.T) {
buff := new(bytes.Buffer)
logger, err := logging.NewLogger("ERROR", buff, "")
if err != nil {
t.Error("building the logger:", err.Error())
return
}

errorMsg := "runServer error"
runServerFunc := func(_ context.Context, _ config.ServiceConfig, _ http.Handler) error {
return errors.New(errorMsg)
}

pf := noopProxyFactory(map[string]interface{}{"supu": "tupu"})
r := NewFactory(
Config{
Engine: gin.Default(),
Middlewares: []gin.HandlerFunc{},
HandlerFactory: EndpointHandler,
ProxyFactory: pf,
Logger: logger,
RunServer: runServerFunc,
},
).New()

serviceCfg := config.ServiceConfig{}
r.Run(serviceCfg)
re := regexp.MustCompile(errorMsg)
if !re.MatchString(string(buff.Bytes())) {
t.Errorf("the logger doesn't contain the expected msg: %s", buff.Bytes())
}
}

func checkResponseIs404(t *testing.T, req *http.Request) {
expectedBody := "404 page not found"
resp, err := http.DefaultClient.Do(req)
Expand Down
1 change: 1 addition & 0 deletions router/gorilla/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func DefaultConfig(pf proxy.Factory, logger logging.Logger) mux.Config {
ProxyFactory: pf,
Logger: logger,
DebugPattern: "/__debug/{params}",
RunServer: router.RunServer,
}
}

Expand Down
32 changes: 12 additions & 20 deletions router/mux/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package mux

import (
"context"
"fmt"
"net/http"

"github.com/devopsfaith/krakend/config"
Expand All @@ -15,6 +14,9 @@ import (
// DefaultDebugPattern is the default pattern used to define the debug endpoint
const DefaultDebugPattern = "/__debug/"

// RunServerFunc is a func that will run the http Server with the given params.
type RunServerFunc func(context.Context, config.ServiceConfig, http.Handler) error

// Config is the struct that collects the parts the router should be builded from
type Config struct {
Engine Engine
Expand All @@ -23,6 +25,7 @@ type Config struct {
ProxyFactory proxy.Factory
Logger logging.Logger
DebugPattern string
RunServer RunServerFunc
}

// HandlerMiddleware is the interface for the decorators over the http.Handler
Expand All @@ -40,6 +43,7 @@ func DefaultFactory(pf proxy.Factory, logger logging.Logger) router.Factory {
ProxyFactory: pf,
Logger: logger,
DebugPattern: DefaultDebugPattern,
RunServer: router.RunServer,
},
}
}
Expand All @@ -58,17 +62,18 @@ type factory struct {

// New implements the factory interface
func (rf factory) New() router.Router {
return httpRouter{rf.cfg, context.Background()}
return rf.NewWithContext(context.Background())
}

// NewWithContext implements the factory interface
func (rf factory) NewWithContext(ctx context.Context) router.Router {
return httpRouter{rf.cfg, ctx}
return httpRouter{rf.cfg, ctx, rf.cfg.RunServer}
}

type httpRouter struct {
cfg Config
ctx context.Context
cfg Config
ctx context.Context
RunServer RunServerFunc
}

// Run implements the router interface
Expand All @@ -81,23 +86,10 @@ func (r httpRouter) Run(cfg config.ServiceConfig) {

r.registerKrakendEndpoints(cfg.Endpoints)

server := http.Server{
Addr: fmt.Sprintf(":%d", cfg.Port),
Handler: r.handler(),
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
ReadHeaderTimeout: cfg.ReadHeaderTimeout,
IdleTimeout: cfg.IdleTimeout,
}

go func() {
r.cfg.Logger.Critical(server.ListenAndServe())
}()

<-r.ctx.Done()
if err := server.Shutdown(context.Background()); err != nil {
if err := r.RunServer(r.ctx, cfg, r.handler()); err != nil {
r.cfg.Logger.Error(err.Error())
}

r.cfg.Logger.Info("Router execution ended")
}

Expand Down
37 changes: 37 additions & 0 deletions router/mux/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ package mux
import (
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"testing"
"time"

Expand Down Expand Up @@ -141,6 +143,7 @@ func TestDefaultFactory_ko(t *testing.T) {
HandlerFactory: EndpointHandler,
ProxyFactory: noopProxyFactory(map[string]interface{}{"supu": "tupu"}),
Logger: logger,
RunServer: router.RunServer,
}).NewWithContext(ctx)

serviceCfg := config.ServiceConfig{
Expand Down Expand Up @@ -227,6 +230,40 @@ func TestDefaultFactory_proxyFactoryCrash(t *testing.T) {
}
}

func TestRunServer_ko(t *testing.T) {
buff := new(bytes.Buffer)
logger, err := logging.NewLogger("DEBUG", buff, "")
if err != nil {
t.Error("building the logger:", err.Error())
return
}

errorMsg := "runServer error"
runServerFunc := func(_ context.Context, _ config.ServiceConfig, _ http.Handler) error {
return errors.New(errorMsg)
}

pf := noopProxyFactory(map[string]interface{}{"supu": "tupu"})
r := NewFactory(
Config{
Engine: DefaultEngine(),
Middlewares: []HandlerMiddleware{},
HandlerFactory: EndpointHandler,
ProxyFactory: pf,
Logger: logger,
DebugPattern: DefaultDebugPattern,
RunServer: runServerFunc,
},
).New()

serviceCfg := config.ServiceConfig{}
r.Run(serviceCfg)
re := regexp.MustCompile(errorMsg)
if !re.MatchString(string(buff.Bytes())) {
t.Errorf("the logger doesn't contain the expected msg: %s", buff.Bytes())
}
}

func checkResponseIs404(t *testing.T, req *http.Request) {
expectedBody := "404 page not found\n"
resp, err := http.DefaultClient.Do(req)
Expand Down
26 changes: 26 additions & 0 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package router
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"sync"
Expand Down Expand Up @@ -80,3 +81,28 @@ func InitHTTPDefaultTransport(cfg config.ServiceConfig) {
}
})
}

// RunServer runs a http.Server with the given handler and configuration
func RunServer(ctx context.Context, cfg config.ServiceConfig, handler http.Handler) error {
done := make(chan error)
s := &http.Server{
Addr: fmt.Sprintf(":%d", cfg.Port),
Handler: handler,
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
ReadHeaderTimeout: cfg.ReadHeaderTimeout,
IdleTimeout: cfg.IdleTimeout,
}

go func() {
done <- s.ListenAndServe()
}()

select {
case err := <-done:
return err
case <-ctx.Done():
return s.Shutdown(context.Background())
}

}

0 comments on commit f003ffd

Please sign in to comment.