github.com/gramework/gramework@v1.8.1-0.20231027140105-82555c9057f5/adaptor.go (about)

     1  package gramework
     2  
     3  import (
     4  	"io"
     5  	"net/http"
     6  	"net/url"
     7  )
     8  
     9  type (
    10  	netHTTPBody struct {
    11  		b []byte
    12  	}
    13  
    14  	netHTTPResponseWriter struct {
    15  		statusCode int
    16  		h          http.Header
    17  		body       []byte
    18  	}
    19  )
    20  
    21  // NewGrameHandlerFunc wraps net/http handler func to gramework
    22  // request handler, so it can be passed to gramework router.
    23  //
    24  // While this function may be used for easy switching from net/http to gramework,
    25  // it has the following drawbacks comparing to using manually written gramework
    26  // request handler:
    27  //
    28  //     * A lot of useful functionality provided by fasthttp is missing
    29  //       from net/http handler.
    30  //     * net/http -> fasthttp handler conversion has some overhead,
    31  //       so the returned handler will be always slower than manually written
    32  //       fasthttp handler.
    33  //
    34  // So it is advisable using this function only for quick net/http -> fasthttp
    35  // switching. Then manually convert net/http handlers to fasthttp handlers
    36  // according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp .
    37  //
    38  // This adaptor is a fork of https://github.com/valyala/fasthttp/tree/master/fasthttpadaptor
    39  // We're embedding it because we don't want additional allocation, but we need exactly gramework
    40  // request handler, not fasthttp request handler.
    41  // The package provides helper functions for converting net/http
    42  // request handlers to fasthttp request handlers.
    43  // See the original license in /3rd-Party Licenses/fasthttp
    44  func NewGrameHandlerFunc(h http.HandlerFunc) RequestHandler {
    45  	return NewGrameHandler(h)
    46  }
    47  
    48  // NewGrameHandler wraps net/http handler to fasthttp request handler,
    49  // so it can be passed to fasthttp server.
    50  //
    51  // While this function may be used for easy switching from net/http to fasthttp,
    52  // it has the following drawbacks comparing to using manually written fasthttp
    53  // request handler:
    54  //
    55  //     * A lot of useful functionality provided by fasthttp is missing
    56  //       from net/http handler.
    57  //     * net/http -> fasthttp handler conversion has some overhead,
    58  //       so the returned handler will be always slower than manually written
    59  //       fasthttp handler.
    60  //
    61  // So it is advisable using this function only for quick net/http -> fasthttp
    62  // switching. Then manually convert net/http handlers to fasthttp handlers
    63  // according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp .
    64  //
    65  // This adaptor is a fork of https://github.com/valyala/fasthttp/tree/master/fasthttpadaptor
    66  // We're embedding it because we don't want additional allocation, but we need exactly gramework
    67  // request handler, not fasthttp request handler.
    68  // The package provides helper functions for converting net/http
    69  // request handlers to fasthttp request handlers.
    70  // See the original license in /3rd-Party Licenses/fasthttp
    71  func NewGrameHandler(h http.Handler) RequestHandler {
    72  	return func(ctx *Context) {
    73  		var r http.Request
    74  
    75  		body := ctx.PostBody()
    76  		r.Method = string(ctx.Method())
    77  		r.Proto = "HTTP/1.1"
    78  		r.ProtoMajor = 1
    79  		r.ProtoMinor = 1
    80  		r.RequestURI = string(ctx.RequestURI())
    81  		r.ContentLength = int64(len(body))
    82  		r.Host = string(ctx.Host())
    83  		r.RemoteAddr = ctx.RemoteAddr().String()
    84  
    85  		hdr := make(http.Header)
    86  		ctx.Request.Header.VisitAll(func(k, v []byte) {
    87  			sk := string(k)
    88  			sv := string(v)
    89  			switch sk {
    90  			case "Transfer-Encoding":
    91  				r.TransferEncoding = append(r.TransferEncoding, sv)
    92  			default:
    93  				hdr.Set(sk, sv)
    94  			}
    95  		})
    96  		r.Header = hdr
    97  		r.Body = &netHTTPBody{body}
    98  		rURL, err := url.ParseRequestURI(r.RequestURI)
    99  		if err != nil {
   100  			ctx.Logger.Errorf("cannot parse requestURI %q: %s", r.RequestURI, err)
   101  			ctx.Err500("Internal Server Error")
   102  			return
   103  		}
   104  		r.URL = rURL
   105  
   106  		var w netHTTPResponseWriter
   107  		h.ServeHTTP(&w, &r)
   108  
   109  		ctx.SetStatusCode(w.StatusCode())
   110  		for k, vv := range w.Header() {
   111  			for _, v := range vv {
   112  				ctx.Response.Header.Set(k, v)
   113  			}
   114  		}
   115  		_, e := ctx.Write(w.body)
   116  		_ = e // handled separately, but got a false-positive linter check
   117  	}
   118  }
   119  
   120  func (r *netHTTPBody) Read(p []byte) (int, error) {
   121  	if len(r.b) == 0 {
   122  		return 0, io.EOF
   123  	}
   124  	n := copy(p, r.b)
   125  	r.b = r.b[n:]
   126  	return n, nil
   127  }
   128  
   129  func (r *netHTTPBody) Close() error {
   130  	r.b = r.b[:0]
   131  	return nil
   132  }
   133  
   134  func (w *netHTTPResponseWriter) StatusCode() int {
   135  	if w.statusCode == 0 {
   136  		return http.StatusOK
   137  	}
   138  	return w.statusCode
   139  }
   140  
   141  func (w *netHTTPResponseWriter) Header() http.Header {
   142  	if w.h == nil {
   143  		w.h = make(http.Header)
   144  	}
   145  	return w.h
   146  }
   147  
   148  func (w *netHTTPResponseWriter) WriteHeader(statusCode int) {
   149  	w.statusCode = statusCode
   150  }
   151  
   152  func (w *netHTTPResponseWriter) Write(p []byte) (int, error) {
   153  	w.body = append(w.body, p...)
   154  	return len(p), nil
   155  }