github.com/MetalBlockchain/metalgo@v1.11.9/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/MetalBlockchain/metalgo/vms/rpcchainvm/ghttp/gresponsewriter"
    17  	"github.com/MetalBlockchain/metalgo/vms/rpcchainvm/grpcutils"
    18  
    19  	httppb "github.com/MetalBlockchain/metalgo/proto/pb/http"
    20  	responsewriterpb "github.com/MetalBlockchain/metalgo/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  }