|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-kratos/kratos/v2/internal/host"
|
|
|
|
"github.com/go-kratos/kratos/v2/log"
|
|
|
|
"github.com/go-kratos/kratos/v2/middleware"
|
|
|
|
"github.com/go-kratos/kratos/v2/middleware/recovery"
|
|
|
|
"github.com/go-kratos/kratos/v2/transport"
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
)
|
|
|
|
|
|
|
|
const loggerName = "transport/http"
|
|
|
|
|
|
|
|
var _ transport.Server = (*Server)(nil)
|
|
|
|
|
|
|
|
// DecodeRequestFunc deocder request func.
|
|
|
|
type DecodeRequestFunc func(req *http.Request, v interface{}) error
|
|
|
|
|
|
|
|
// EncodeResponseFunc is encode response func.
|
|
|
|
type EncodeResponseFunc func(res http.ResponseWriter, req *http.Request, v interface{}) error
|
|
|
|
|
|
|
|
// EncodeErrorFunc is encode error func.
|
|
|
|
type EncodeErrorFunc func(res http.ResponseWriter, req *http.Request, err error)
|
|
|
|
|
|
|
|
// ServerOption is HTTP server option.
|
|
|
|
type ServerOption func(*Server)
|
|
|
|
|
|
|
|
// Network with server network.
|
|
|
|
func Network(network string) ServerOption {
|
|
|
|
return func(s *Server) {
|
|
|
|
s.network = network
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Address with server address.
|
|
|
|
func Address(addr string) ServerOption {
|
|
|
|
return func(s *Server) {
|
|
|
|
s.address = addr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Timeout with server timeout.
|
|
|
|
func Timeout(timeout time.Duration) ServerOption {
|
|
|
|
return func(s *Server) {
|
|
|
|
s.timeout = timeout
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Logger with server logger.
|
|
|
|
func Logger(logger log.Logger) ServerOption {
|
|
|
|
return func(s *Server) {
|
|
|
|
s.log = log.NewHelper(loggerName, logger)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Middleware with server middleware option.
|
|
|
|
func Middleware(m middleware.Middleware) ServerOption {
|
|
|
|
return func(s *Server) {
|
|
|
|
s.middleware = m
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ErrorEncoder with error handler option.
|
|
|
|
func ErrorEncoder(fn EncodeErrorFunc) ServerOption {
|
|
|
|
return func(s *Server) {
|
|
|
|
s.errorEncoder = fn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Server is a HTTP server wrapper.
|
|
|
|
type Server struct {
|
|
|
|
*http.Server
|
|
|
|
lis net.Listener
|
|
|
|
network string
|
|
|
|
address string
|
|
|
|
timeout time.Duration
|
|
|
|
middleware middleware.Middleware
|
|
|
|
requestDecoder DecodeRequestFunc
|
|
|
|
responseEncoder EncodeResponseFunc
|
|
|
|
errorEncoder EncodeErrorFunc
|
|
|
|
router *mux.Router
|
|
|
|
log *log.Helper
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewServer creates a HTTP server by options.
|
|
|
|
func NewServer(opts ...ServerOption) *Server {
|
|
|
|
srv := &Server{
|
|
|
|
network: "tcp",
|
|
|
|
address: ":0",
|
|
|
|
timeout: time.Second,
|
|
|
|
requestDecoder: defaultRequestDecoder,
|
|
|
|
responseEncoder: defaultResponseEncoder,
|
|
|
|
errorEncoder: defaultErrorEncoder,
|
|
|
|
middleware: recovery.Recovery(),
|
|
|
|
log: log.NewHelper(loggerName, log.DefaultLogger),
|
|
|
|
}
|
|
|
|
for _, o := range opts {
|
|
|
|
o(srv)
|
|
|
|
}
|
|
|
|
srv.router = mux.NewRouter()
|
|
|
|
srv.Server = &http.Server{Handler: srv}
|
|
|
|
return srv
|
|
|
|
}
|
|
|
|
|
|
|
|
// RouteGroup .
|
|
|
|
func (s *Server) RouteGroup(prefix string) *RouteGroup {
|
|
|
|
return &RouteGroup{prefix: prefix, router: s.router}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle registers a new route with a matcher for the URL path.
|
|
|
|
func (s *Server) Handle(path string, h http.Handler) {
|
|
|
|
s.router.Handle(path, h)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleFunc registers a new route with a matcher for the URL path.
|
|
|
|
func (s *Server) HandleFunc(path string, h http.HandlerFunc) {
|
|
|
|
s.router.HandleFunc(path, h)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServeHTTP should write reply headers and data to the ResponseWriter and then return.
|
|
|
|
func (s *Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
|
|
|
ctx, cancel := context.WithTimeout(req.Context(), s.timeout)
|
|
|
|
defer cancel()
|
|
|
|
ctx = transport.NewContext(ctx, transport.Transport{Kind: "HTTP"})
|
|
|
|
ctx = NewServerContext(ctx, ServerInfo{Request: req, Response: res})
|
|
|
|
|
|
|
|
h := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
|
|
s.router.ServeHTTP(res, req.(*http.Request))
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
if s.middleware != nil {
|
|
|
|
h = s.middleware(h)
|
|
|
|
}
|
|
|
|
h(ctx, req.WithContext(ctx))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Endpoint return a real address to registry endpoint.
|
|
|
|
// examples:
|
|
|
|
// http://127.0.0.1:8000?isSecure=false
|
|
|
|
func (s *Server) Endpoint() (string, error) {
|
|
|
|
addr, err := host.Extract(s.address, s.lis)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("http://%s", addr), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start start the HTTP server.
|
|
|
|
func (s *Server) Start() error {
|
|
|
|
lis, err := net.Listen(s.network, s.address)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.lis = lis
|
|
|
|
s.log.Infof("[HTTP] server listening on: %s", lis.Addr().String())
|
|
|
|
if err := s.Serve(lis); !errors.Is(err, http.ErrServerClosed) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop stop the HTTP server.
|
|
|
|
func (s *Server) Stop() error {
|
|
|
|
s.log.Info("[HTTP] server stopping")
|
|
|
|
return s.Shutdown(context.Background())
|
|
|
|
}
|