github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/interop/http09/server.go (about)

     1  package http09
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"log"
     8  	"net"
     9  	"net/http"
    10  	"net/url"
    11  	"runtime"
    12  	"strings"
    13  	"sync"
    14  
    15  	"github.com/metacubex/quic-go"
    16  )
    17  
    18  const h09alpn = "hq-interop"
    19  
    20  type responseWriter struct {
    21  	io.Writer
    22  	headers http.Header
    23  }
    24  
    25  var _ http.ResponseWriter = &responseWriter{}
    26  
    27  func (w *responseWriter) Header() http.Header {
    28  	if w.headers == nil {
    29  		w.headers = make(http.Header)
    30  	}
    31  	return w.headers
    32  }
    33  
    34  func (w *responseWriter) WriteHeader(int) {}
    35  
    36  // Server is a HTTP/0.9 server listening for QUIC connections.
    37  type Server struct {
    38  	*http.Server
    39  
    40  	ForceRetry bool
    41  	QuicConfig *quic.Config
    42  
    43  	mutex    sync.Mutex
    44  	listener *quic.EarlyListener
    45  }
    46  
    47  // Close closes the server.
    48  func (s *Server) Close() error {
    49  	s.mutex.Lock()
    50  	defer s.mutex.Unlock()
    51  
    52  	return s.listener.Close()
    53  }
    54  
    55  // ListenAndServe listens and serves HTTP/0.9 over QUIC.
    56  func (s *Server) ListenAndServe() error {
    57  	if s.Server == nil {
    58  		return errors.New("use of http3.Server without http.Server")
    59  	}
    60  
    61  	udpAddr, err := net.ResolveUDPAddr("udp", s.Addr)
    62  	if err != nil {
    63  		return err
    64  	}
    65  	conn, err := net.ListenUDP("udp", udpAddr)
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	tlsConf := s.TLSConfig.Clone()
    71  	tlsConf.NextProtos = []string{h09alpn}
    72  	tr := quic.Transport{Conn: conn}
    73  	if s.ForceRetry {
    74  		tr.VerifySourceAddress = func(net.Addr) bool { return true }
    75  	}
    76  	ln, err := tr.ListenEarly(tlsConf, s.QuicConfig)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	s.mutex.Lock()
    81  	s.listener = ln
    82  	s.mutex.Unlock()
    83  
    84  	for {
    85  		conn, err := ln.Accept(context.Background())
    86  		if err != nil {
    87  			return err
    88  		}
    89  		go s.handleConn(conn)
    90  	}
    91  }
    92  
    93  func (s *Server) handleConn(conn quic.Connection) {
    94  	for {
    95  		str, err := conn.AcceptStream(context.Background())
    96  		if err != nil {
    97  			log.Printf("Error accepting stream: %s\n", err.Error())
    98  			return
    99  		}
   100  		go func() {
   101  			if err := s.handleStream(str); err != nil {
   102  				log.Printf("Handling stream failed: %s\n", err.Error())
   103  			}
   104  		}()
   105  	}
   106  }
   107  
   108  func (s *Server) handleStream(str quic.Stream) error {
   109  	reqBytes, err := io.ReadAll(str)
   110  	if err != nil {
   111  		return err
   112  	}
   113  	request := string(reqBytes)
   114  	request = strings.TrimRight(request, "\r\n")
   115  	request = strings.TrimRight(request, " ")
   116  
   117  	log.Printf("Received request: %s\n", request)
   118  
   119  	if request[:5] != "GET /" {
   120  		str.CancelWrite(42)
   121  		return nil
   122  	}
   123  
   124  	u, err := url.Parse(request[4:])
   125  	if err != nil {
   126  		return err
   127  	}
   128  	u.Scheme = "https"
   129  
   130  	req := &http.Request{
   131  		Method:     http.MethodGet,
   132  		Proto:      "HTTP/0.9",
   133  		ProtoMajor: 0,
   134  		ProtoMinor: 9,
   135  		Body:       str,
   136  		URL:        u,
   137  	}
   138  
   139  	handler := s.Handler
   140  	if handler == nil {
   141  		handler = http.DefaultServeMux
   142  	}
   143  
   144  	var panicked bool
   145  	func() {
   146  		defer func() {
   147  			if p := recover(); p != nil {
   148  				// Copied from net/http/server.go
   149  				const size = 64 << 10
   150  				buf := make([]byte, size)
   151  				buf = buf[:runtime.Stack(buf, false)]
   152  				log.Printf("http: panic serving: %v\n%s", p, buf)
   153  				panicked = true
   154  			}
   155  		}()
   156  		handler.ServeHTTP(&responseWriter{Writer: str}, req)
   157  	}()
   158  
   159  	if panicked {
   160  		if _, err := str.Write([]byte("500")); err != nil {
   161  			return err
   162  		}
   163  	}
   164  	return str.Close()
   165  }