github.com/ava-labs/avalanchego@v1.11.11/vms/rpcchainvm/ghttp/http_server.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package ghttp 5 6 import ( 7 "bytes" 8 "context" 9 "crypto/tls" 10 "crypto/x509" 11 "net/http" 12 "net/url" 13 14 "google.golang.org/protobuf/types/known/emptypb" 15 16 "github.com/ava-labs/avalanchego/vms/rpcchainvm/ghttp/gresponsewriter" 17 "github.com/ava-labs/avalanchego/vms/rpcchainvm/grpcutils" 18 19 httppb "github.com/ava-labs/avalanchego/proto/pb/http" 20 responsewriterpb "github.com/ava-labs/avalanchego/proto/pb/http/responsewriter" 21 ) 22 23 var ( 24 _ httppb.HTTPServer = (*Server)(nil) 25 _ http.ResponseWriter = (*ResponseWriter)(nil) 26 ) 27 28 // Server is an http.Handler that is managed over RPC. 29 type Server struct { 30 httppb.UnsafeHTTPServer 31 handler http.Handler 32 } 33 34 // NewServer returns an http.Handler instance managed remotely 35 func NewServer(handler http.Handler) *Server { 36 return &Server{ 37 handler: handler, 38 } 39 } 40 41 func (s *Server) Handle(ctx context.Context, req *httppb.HTTPRequest) (*emptypb.Empty, error) { 42 clientConn, err := grpcutils.Dial(req.ResponseWriter.ServerAddr) 43 if err != nil { 44 return nil, err 45 } 46 47 writerHeaders := make(http.Header) 48 for _, elem := range req.ResponseWriter.Header { 49 writerHeaders[elem.Key] = elem.Values 50 } 51 52 writer := gresponsewriter.NewClient(writerHeaders, responsewriterpb.NewWriterClient(clientConn)) 53 54 // create the request with the current context 55 request, err := http.NewRequestWithContext( 56 ctx, 57 req.Request.Method, 58 req.Request.RequestUri, 59 bytes.NewBuffer(req.Request.Body), 60 ) 61 if err != nil { 62 return nil, err 63 } 64 65 if req.Request.Url != nil { 66 request.URL = &url.URL{ 67 Scheme: req.Request.Url.Scheme, 68 Opaque: req.Request.Url.Opaque, 69 Host: req.Request.Url.Host, 70 Path: req.Request.Url.Path, 71 RawPath: req.Request.Url.RawPath, 72 ForceQuery: req.Request.Url.ForceQuery, 73 RawQuery: req.Request.Url.RawQuery, 74 Fragment: req.Request.Url.Fragment, 75 } 76 if req.Request.Url.User != nil { 77 if req.Request.Url.User.PasswordSet { 78 request.URL.User = url.UserPassword(req.Request.Url.User.Username, req.Request.Url.User.Password) 79 } else { 80 request.URL.User = url.User(req.Request.Url.User.Username) 81 } 82 } 83 } 84 85 request.Proto = req.Request.Proto 86 request.ProtoMajor = int(req.Request.ProtoMajor) 87 request.ProtoMinor = int(req.Request.ProtoMinor) 88 request.Header = make(http.Header, len(req.Request.Header)) 89 for _, elem := range req.Request.Header { 90 request.Header[elem.Key] = elem.Values 91 } 92 request.ContentLength = req.Request.ContentLength 93 request.TransferEncoding = req.Request.TransferEncoding 94 request.Host = req.Request.Host 95 request.Form = make(url.Values, len(req.Request.Form)) 96 for _, elem := range req.Request.Form { 97 request.Form[elem.Key] = elem.Values 98 } 99 request.PostForm = make(url.Values, len(req.Request.PostForm)) 100 for _, elem := range req.Request.PostForm { 101 request.PostForm[elem.Key] = elem.Values 102 } 103 request.Trailer = make(http.Header) 104 request.RemoteAddr = req.Request.RemoteAddr 105 request.RequestURI = req.Request.RequestUri 106 107 if req.Request.Tls != nil { 108 request.TLS = &tls.ConnectionState{ 109 Version: uint16(req.Request.Tls.Version), 110 HandshakeComplete: req.Request.Tls.HandshakeComplete, 111 DidResume: req.Request.Tls.DidResume, 112 CipherSuite: uint16(req.Request.Tls.CipherSuite), 113 NegotiatedProtocol: req.Request.Tls.NegotiatedProtocol, 114 NegotiatedProtocolIsMutual: true, // always true per https://pkg.go.dev/crypto/tls#ConnectionState 115 ServerName: req.Request.Tls.ServerName, 116 PeerCertificates: make([]*x509.Certificate, len(req.Request.Tls.PeerCertificates.Cert)), 117 VerifiedChains: make([][]*x509.Certificate, len(req.Request.Tls.VerifiedChains)), 118 SignedCertificateTimestamps: req.Request.Tls.SignedCertificateTimestamps, 119 OCSPResponse: req.Request.Tls.OcspResponse, 120 } 121 for i, certBytes := range req.Request.Tls.PeerCertificates.Cert { 122 cert, err := x509.ParseCertificate(certBytes) 123 if err != nil { 124 return nil, err 125 } 126 request.TLS.PeerCertificates[i] = cert 127 } 128 for i, chain := range req.Request.Tls.VerifiedChains { 129 request.TLS.VerifiedChains[i] = make([]*x509.Certificate, len(chain.Cert)) 130 for j, certBytes := range chain.Cert { 131 cert, err := x509.ParseCertificate(certBytes) 132 if err != nil { 133 return nil, err 134 } 135 request.TLS.VerifiedChains[i][j] = cert 136 } 137 } 138 } 139 140 s.handler.ServeHTTP(writer, request) 141 142 return &emptypb.Empty{}, clientConn.Close() 143 } 144 145 // HandleSimple handles http requests over http2 using a simple request response model. 146 // Websockets are not supported. Based on https://www.weave.works/blog/turtles-way-http-grpc/ 147 func (s *Server) HandleSimple(ctx context.Context, r *httppb.HandleSimpleHTTPRequest) (*httppb.HandleSimpleHTTPResponse, error) { 148 req, err := http.NewRequest(r.Method, r.Url, bytes.NewBuffer(r.Body)) 149 if err != nil { 150 return nil, err 151 } 152 153 grpcutils.MergeHTTPHeader(r.Headers, req.Header) 154 155 req = req.WithContext(ctx) 156 req.RequestURI = r.Url 157 req.ContentLength = int64(len(r.Body)) 158 159 w := newResponseWriter() 160 s.handler.ServeHTTP(w, req) 161 162 resp := &httppb.HandleSimpleHTTPResponse{ 163 Code: int32(w.statusCode), 164 Headers: grpcutils.GetHTTPHeader(w.Header()), 165 Body: w.body.Bytes(), 166 } 167 168 if w.statusCode == http.StatusInternalServerError { 169 return nil, grpcutils.GetGRPCErrorFromHTTPResponse(resp) 170 } 171 return resp, nil 172 } 173 174 type ResponseWriter struct { 175 body *bytes.Buffer 176 header http.Header 177 statusCode int 178 } 179 180 // newResponseWriter returns very basic implementation of the http.ResponseWriter 181 func newResponseWriter() *ResponseWriter { 182 return &ResponseWriter{ 183 body: new(bytes.Buffer), 184 header: make(http.Header), 185 statusCode: http.StatusOK, 186 } 187 } 188 189 func (w *ResponseWriter) Header() http.Header { 190 return w.header 191 } 192 193 func (w *ResponseWriter) Write(buf []byte) (int, error) { 194 return w.body.Write(buf) 195 } 196 197 func (w *ResponseWriter) WriteHeader(code int) { 198 w.statusCode = code 199 } 200 201 func (w *ResponseWriter) StatusCode() int { 202 return w.statusCode 203 } 204 205 func (w *ResponseWriter) Body() *bytes.Buffer { 206 return w.body 207 }