func logHandler() gin.HandlerFunc {
	logger := log.New("module", "RPC")
	return func(c *gin.Context) {
		path := c.Request.URL.Path
		c.Next()
		clientIP := c.ClientIP()
		method := c.Request.Method
		statusCode := c.Writer.Status()
		comment := c.Errors.String()
		logger.Info("client_ip", clientIP,
			"status_code", statusCode,
			"method", method,
			"path", path,
			"error", comment)
	}
}
Used to enable log15 logging instead of the default Gin logging.
package rpc
import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"time"
	rpcConf "github.com/gallactic/gallactic/rpc/config"
	"github.com/gin-gonic/gin"
	log "github.com/inconshreveable/log15"
	cors "github.com/tommy351/gin-cors"
	graceful "gopkg.in/tylerb/graceful.v1"
)
var (
	killTime = 100 * time.Millisecond
)
type HttpService interface {
	Process(*http.Request, http.ResponseWriter)
}
type Server interface {
	Start(*rpcConf.ServerConfig, *gin.Engine)
	Running() bool
	Shutdown(ctx context.Context) error
}
type ServeProcess struct {
	config           *rpcConf.ServerConfig
	servers          []Server
	stopChan         chan struct{}
	startListenChans []chan struct{}
	stopListenChans  []chan struct{}
	srv              *graceful.Server
}
func (serveProcess *ServeProcess) Start() error {
	gin.SetMode(gin.ReleaseMode)
	router := gin.New()
	config := serveProcess.config
	ch := NewCORSMiddleware(config.CORS)
	router.Use(gin.Recovery(), logHandler(), contentTypeMW, ch)
	address := config.Bind.Address
	port := config.Bind.Port
	if port == 0 {
		return fmt.Errorf("0 is not a valid port.")
	}
	listenAddress := address + ":" + fmt.Sprintf("%d", port)
	srv := &graceful.Server{
		Server: &http.Server{
			Handler: router,
		},
	}
	for _, s := range serveProcess.servers {
		s.Start(config, router)
	}
	var lst net.Listener
	l, lErr := net.Listen("tcp", listenAddress)
	if lErr != nil {
		return lErr
	}
	if config.TLS.TLS {
		addr := srv.Addr
		if addr == "" {
			addr = ":https"
		}
		tConfig := &tls.Config{}
		if tConfig.NextProtos == nil {
			tConfig.NextProtos = []string{"http/1.1"}
		}
		var tErr error
		tConfig.Certificates = make([]tls.Certificate, 1)
		tConfig.Certificates[0], tErr = tls.LoadX509KeyPair(config.TLS.CertPath, config.TLS.KeyPath)
		if tErr != nil {
			return tErr
		}
		lst = tls.NewListener(l, tConfig)
	} else {
		lst = l
	}
	serveProcess.srv = srv
	log.Info("RPC server started.",
		"address", serveProcess.config.Bind.Address,
		"port", serveProcess.config.Bind.Port)
	for _, c := range serveProcess.startListenChans {
		c <- struct{}{}
	}
	go func() {
		serveProcess.srv.Serve(lst)
		for _, s := range serveProcess.servers {
			s.Shutdown(context.Background())
		}
	}()
	go func() {
		<-serveProcess.stopChan
		log.Info("Close signal sent to RPC server.")
		serveProcess.srv.Stop(killTime)
	}()
	go func() {
		<-serveProcess.srv.StopChan()
		log.Info("RPC server stop event fired. Good bye.")
		for _, c := range serveProcess.stopListenChans {
			c <- struct{}{}
		}
	}()
	return nil
}
func (serveProcess *ServeProcess) Shutdown(ctx context.Context) error {
	var err error
	for _, s := range serveProcess.servers {
		serr := s.Shutdown(ctx)
		if serr != nil && err == nil {
			err = serr
		}
	}
	lChan := serveProcess.StopEventChannel()
	serveProcess.stopChan <- struct{}{}
	select {
	case <-lChan:
		return err
	case <-ctx.Done():
		return ctx.Err()
	}
}
func (serveProcess *ServeProcess) StartEventChannel() <-chan struct{} {
	lChan := make(chan struct{}, 1)
	serveProcess.startListenChans = append(serveProcess.startListenChans, lChan)
	return lChan
}
func (serveProcess *ServeProcess) StopEventChannel() <-chan struct{} {
	lChan := make(chan struct{}, 1)
	serveProcess.stopListenChans = append(serveProcess.stopListenChans, lChan)
	return lChan
}
func NewServeProcess(config *rpcConf.ServerConfig,
	servers ...Server) (*ServeProcess, error) {
	var scfg rpcConf.ServerConfig
	if config == nil {
		return nil, fmt.Errorf("Nil passed as server configuration")
	} else {
		scfg = *config
	}
	stopChan := make(chan struct{}, 1)
	startListeners := make([]chan struct{}, 0)
	stopListeners := make([]chan struct{}, 0)
	sp := &ServeProcess{
		config:           &scfg,
		servers:          servers,
		stopChan:         stopChan,
		startListenChans: startListeners,
		stopListenChans:  stopListeners,
		srv:              nil,
	}
	return sp, nil
}