go-micro.dev/v5@v5.12.0/transport/http_listener.go (about)

     1  package transport
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"io"
     7  	"net"
     8  	"net/http"
     9  	"time"
    10  
    11  	log "go-micro.dev/v5/logger"
    12  
    13  	"golang.org/x/net/http2"
    14  	"golang.org/x/net/http2/h2c"
    15  )
    16  
    17  type httpTransportListener struct {
    18  	ht       *httpTransport
    19  	listener net.Listener
    20  }
    21  
    22  func (h *httpTransportListener) Addr() string {
    23  	return h.listener.Addr().String()
    24  }
    25  
    26  func (h *httpTransportListener) Close() error {
    27  	return h.listener.Close()
    28  }
    29  
    30  func (h *httpTransportListener) Accept(fn func(Socket)) error {
    31  	// Create handler mux
    32  	// TODO: see if we should make a plugin out of the mux
    33  	mux := http.NewServeMux()
    34  
    35  	// Register our transport handler
    36  	mux.HandleFunc("/", h.newHandler(fn))
    37  
    38  	// Get optional handlers
    39  	// TODO: This needs to be documented clearer, and examples provided
    40  	if h.ht.opts.Context != nil {
    41  		handlers, ok := h.ht.opts.Context.Value("http_handlers").(map[string]http.Handler)
    42  		if ok {
    43  			for pattern, handler := range handlers {
    44  				mux.Handle(pattern, handler)
    45  			}
    46  		}
    47  	}
    48  
    49  	// Server ONLY supports HTTP1 + H2C
    50  	srv := &http.Server{
    51  		Handler:           mux,
    52  		ReadHeaderTimeout: time.Second * 5,
    53  	}
    54  
    55  	// insecure connection use h2c
    56  	if !(h.ht.opts.Secure || h.ht.opts.TLSConfig != nil) {
    57  		srv.Handler = h2c.NewHandler(mux, &http2.Server{})
    58  	}
    59  
    60  	return srv.Serve(h.listener)
    61  }
    62  
    63  // newHandler creates a new HTTP transport handler passed to the mux.
    64  func (h *httpTransportListener) newHandler(serveConn func(Socket)) func(rsp http.ResponseWriter, req *http.Request) {
    65  	logger := h.ht.opts.Logger
    66  
    67  	return func(rsp http.ResponseWriter, req *http.Request) {
    68  		var (
    69  			buf *bufio.ReadWriter
    70  			con net.Conn
    71  		)
    72  
    73  		// HTTP1: read a regular request
    74  		if req.ProtoMajor == 1 {
    75  			b, err := io.ReadAll(req.Body)
    76  			if err != nil {
    77  				http.Error(rsp, err.Error(), http.StatusInternalServerError)
    78  				return
    79  			}
    80  
    81  			req.Body = io.NopCloser(bytes.NewReader(b))
    82  
    83  			// Hijack the conn
    84  			// We also don't close the connection here, as it will be closed by
    85  			// the httpTransportSocket
    86  			hj, ok := rsp.(http.Hijacker)
    87  			if !ok {
    88  				// We're screwed
    89  				http.Error(rsp, "cannot serve conn", http.StatusInternalServerError)
    90  				return
    91  			}
    92  
    93  			conn, bufrw, err := hj.Hijack()
    94  			if err != nil {
    95  				http.Error(rsp, err.Error(), http.StatusInternalServerError)
    96  				return
    97  			}
    98  			defer func() {
    99  				if err := conn.Close(); err != nil {
   100  					logger.Logf(log.ErrorLevel, "Failed to close TCP connection: %v", err)
   101  				}
   102  			}()
   103  
   104  			buf = bufrw
   105  			con = conn
   106  		}
   107  
   108  		// Buffered reader
   109  		bufr := bufio.NewReader(req.Body)
   110  
   111  		// Save the request
   112  		ch := make(chan *http.Request, 1)
   113  		ch <- req
   114  
   115  		// Create a new transport socket
   116  		sock := &httpTransportSocket{
   117  			ht:     h.ht,
   118  			w:      rsp,
   119  			r:      req,
   120  			rw:     buf,
   121  			buf:    bufr,
   122  			ch:     ch,
   123  			conn:   con,
   124  			local:  h.Addr(),
   125  			remote: req.RemoteAddr,
   126  			closed: make(chan bool),
   127  		}
   128  
   129  		// Execute the socket
   130  		serveConn(sock)
   131  	}
   132  }