github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/httptransport/transport.go (about)

     1  package httptransport
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"net"
     8  	"net/http"
     9  	"os"
    10  	"os/signal"
    11  	"sort"
    12  	"strconv"
    13  	"strings"
    14  	"syscall"
    15  	"time"
    16  
    17  	"github.com/julienschmidt/httprouter"
    18  	"github.com/pkg/errors"
    19  
    20  	"github.com/machinefi/w3bstream/pkg/depends/kit/httptransport/handlers"
    21  	"github.com/machinefi/w3bstream/pkg/depends/kit/httptransport/transformer"
    22  	"github.com/machinefi/w3bstream/pkg/depends/kit/kit"
    23  	"github.com/machinefi/w3bstream/pkg/depends/kit/logr"
    24  	"github.com/machinefi/w3bstream/pkg/depends/kit/validator"
    25  	_ "github.com/machinefi/w3bstream/pkg/depends/kit/validator/strfmt"
    26  )
    27  
    28  func MiddlewareChain(mw ...HttpMiddleware) HttpMiddleware {
    29  	return func(final http.Handler) http.Handler {
    30  		last := final
    31  		for i := len(mw) - 1; i >= 0; i-- {
    32  			last = mw[i](last)
    33  		}
    34  		return last
    35  	}
    36  }
    37  
    38  type HttpMiddleware func(http.Handler) http.Handler
    39  
    40  func NewHttpTransport(modifiers ...ServerModifier) *HttpTransport {
    41  	return &HttpTransport{Modifiers: modifiers}
    42  }
    43  
    44  type HttpTransport struct {
    45  	ServiceMeta
    46  	Protocol    string
    47  	Addr        string
    48  	Port        int
    49  	Modifiers   []ServerModifier    // for modifying http.Server
    50  	Middlewares []HttpMiddleware    // Middlewares https://github.com/gorilla/handlers
    51  	Vldt        validator.Factory   // Vldt validator factory
    52  	Tsfm        transformer.Factory // transformer mgr for parameter transforming
    53  	CertFile    string
    54  	KeyFile     string
    55  	httpRouter  *httprouter.Router
    56  
    57  	srv *http.Server
    58  }
    59  
    60  type ServerModifier func(server *http.Server) error
    61  
    62  func (t *HttpTransport) SetDefault() {
    63  	t.ServiceMeta.SetDefault()
    64  
    65  	if t.Vldt == nil {
    66  		t.Vldt = validator.DefaultFactory
    67  	}
    68  
    69  	if t.Tsfm == nil {
    70  		t.Tsfm = transformer.DefaultFactory
    71  	}
    72  
    73  	if t.Middlewares == nil {
    74  		t.Middlewares = []HttpMiddleware{handlers.LogHandler()}
    75  	}
    76  
    77  	if t.Port == 0 {
    78  		t.Port = 80
    79  	}
    80  }
    81  
    82  func (t *HttpTransport) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    83  	t.httpRouter.ServeHTTP(w, req)
    84  }
    85  
    86  func (t *HttpTransport) Serve(router *kit.Router) error {
    87  	return t.ServeContext(context.Background(), router)
    88  }
    89  
    90  func (t *HttpTransport) IsTLS() bool { return t.CertFile != "" && t.KeyFile != "" }
    91  
    92  func (t *HttpTransport) ServeContext(ctx context.Context, router *kit.Router) error {
    93  	t.SetDefault()
    94  
    95  	logger := logr.FromContext(ctx)
    96  
    97  	t.httpRouter = t.toHttpRouter(router)
    98  
    99  	t.srv = &http.Server{
   100  		Addr:    fmt.Sprintf(":%d", t.Port),
   101  		Handler: MiddlewareChain(t.Middlewares...)(t),
   102  	}
   103  
   104  	for i := range t.Modifiers {
   105  		if err := t.Modifiers[i](t.srv); err != nil {
   106  			log.Fatal(err)
   107  		}
   108  	}
   109  
   110  	go func() {
   111  		outputln("%s listen on %s", t.ServiceMeta, t.srv.Addr)
   112  
   113  		var (
   114  			ln   net.Listener
   115  			err  error
   116  			addr = t.srv.Addr
   117  		)
   118  
   119  		defer func() {
   120  			if ln != nil {
   121  				_ = ln.Close()
   122  			}
   123  		}()
   124  
   125  		if strings.HasPrefix(addr, ":unix@") {
   126  			file := strings.Split(addr, "@")[1]
   127  			_ = os.Remove(file)
   128  			ln, err = net.Listen("unix", file)
   129  		} else {
   130  			if t.Port != 0 {
   131  				addr = ":" + strconv.Itoa(t.Port)
   132  			}
   133  			if addr == "" {
   134  				addr = ":http"
   135  				if t.IsTLS() {
   136  					addr = ":https"
   137  				}
   138  			}
   139  			ln, err = net.Listen("tcp", addr)
   140  		}
   141  
   142  		if err != nil {
   143  			logger.Error(err)
   144  			log.Fatal(err)
   145  		}
   146  
   147  		if t.IsTLS() {
   148  			err = t.srv.ServeTLS(ln, t.CertFile, t.KeyFile)
   149  		} else {
   150  			err = t.srv.Serve(ln)
   151  		}
   152  		logger.Error(err)
   153  		return
   154  	}()
   155  
   156  	stopCh := make(chan os.Signal, 1)
   157  	signal.Notify(stopCh, os.Interrupt, syscall.SIGTERM)
   158  	<-stopCh
   159  
   160  	timeout := 10 * time.Second
   161  
   162  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   163  	defer cancel()
   164  
   165  	log.Println("Server shutdown in 10 second")
   166  
   167  	return t.srv.Shutdown(ctx)
   168  }
   169  
   170  func (t *HttpTransport) Shutdown(ctx context.Context) error {
   171  	return t.srv.Shutdown(ctx)
   172  }
   173  
   174  func (t *HttpTransport) toHttpRouter(rt *kit.Router) *httprouter.Router {
   175  	routes := rt.Routes()
   176  
   177  	if len(routes) == 0 {
   178  		panic(errors.Errorf(
   179  			"need to register Operator to Router %#v before serve", rt,
   180  		))
   181  	}
   182  
   183  	metas := make([]*HttpRouteMeta, len(routes))
   184  	for i := range routes {
   185  		metas[i] = NewHttpRouteMeta(routes[i])
   186  	}
   187  
   188  	router := httprouter.New()
   189  
   190  	sort.Slice(metas, func(i, j int) bool {
   191  		return metas[i].Key() < metas[j].Key()
   192  	})
   193  
   194  	for i := range metas {
   195  		route := metas[i]
   196  		route.Log()
   197  
   198  		if err := tryCatch(func() {
   199  			router.HandlerFunc(
   200  				route.Method(),
   201  				route.Path(),
   202  				NewRouteHandler(
   203  					&t.ServiceMeta,
   204  					route,
   205  					NewRequestTsfmFactory(t.Tsfm, t.Vldt),
   206  				).ServeHTTP,
   207  			)
   208  		}); err != nil {
   209  			panic(errors.Errorf("register http route `%s` failed: %s", route, err))
   210  		}
   211  	}
   212  
   213  	return router
   214  }