github.com/tumi8/quic-go@v0.37.4-tum/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/tumi8/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  	QuicConfig *quic.Config
    41  
    42  	mutex    sync.Mutex
    43  	listener *quic.EarlyListener
    44  }
    45  
    46  // Close closes the server.
    47  func (s *Server) Close() error {
    48  	s.mutex.Lock()
    49  	defer s.mutex.Unlock()
    50  
    51  	return s.listener.Close()
    52  }
    53  
    54  // ListenAndServe listens and serves HTTP/0.9 over QUIC.
    55  func (s *Server) ListenAndServe() error {
    56  	if s.Server == nil {
    57  		return errors.New("use of http3.Server without http.Server")
    58  	}
    59  
    60  	udpAddr, err := net.ResolveUDPAddr("udp", s.Addr)
    61  	if err != nil {
    62  		return err
    63  	}
    64  	conn, err := net.ListenUDP("udp", udpAddr)
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	tlsConf := s.TLSConfig.Clone()
    70  	tlsConf.NextProtos = []string{h09alpn}
    71  	ln, err := quic.ListenEarly(conn, tlsConf, s.QuicConfig)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	s.mutex.Lock()
    76  	s.listener = ln
    77  	s.mutex.Unlock()
    78  
    79  	for {
    80  		conn, err := ln.Accept(context.Background())
    81  		if err != nil {
    82  			return err
    83  		}
    84  		go s.handleConn(conn)
    85  	}
    86  }
    87  
    88  func (s *Server) handleConn(conn quic.Connection) {
    89  	for {
    90  		str, err := conn.AcceptStream(context.Background())
    91  		if err != nil {
    92  			log.Printf("Error accepting stream: %s\n", err.Error())
    93  			return
    94  		}
    95  		go func() {
    96  			if err := s.handleStream(str); err != nil {
    97  				log.Printf("Handling stream failed: %s\n", err.Error())
    98  			}
    99  		}()
   100  	}
   101  }
   102  
   103  func (s *Server) handleStream(str quic.Stream) error {
   104  	reqBytes, err := io.ReadAll(str)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	request := string(reqBytes)
   109  	request = strings.TrimRight(request, "\r\n")
   110  	request = strings.TrimRight(request, " ")
   111  
   112  	log.Printf("Received request: %s\n", request)
   113  
   114  	if request[:5] != "GET /" {
   115  		str.CancelWrite(42)
   116  		return nil
   117  	}
   118  
   119  	u, err := url.Parse(request[4:])
   120  	if err != nil {
   121  		return err
   122  	}
   123  	u.Scheme = "https"
   124  
   125  	req := &http.Request{
   126  		Method:     http.MethodGet,
   127  		Proto:      "HTTP/0.9",
   128  		ProtoMajor: 0,
   129  		ProtoMinor: 9,
   130  		Body:       str,
   131  		URL:        u,
   132  	}
   133  
   134  	handler := s.Handler
   135  	if handler == nil {
   136  		handler = http.DefaultServeMux
   137  	}
   138  
   139  	var panicked bool
   140  	func() {
   141  		defer func() {
   142  			if p := recover(); p != nil {
   143  				// Copied from net/http/server.go
   144  				const size = 64 << 10
   145  				buf := make([]byte, size)
   146  				buf = buf[:runtime.Stack(buf, false)]
   147  				log.Printf("http: panic serving: %v\n%s", p, buf)
   148  				panicked = true
   149  			}
   150  		}()
   151  		handler.ServeHTTP(&responseWriter{Writer: str}, req)
   152  	}()
   153  
   154  	if panicked {
   155  		if _, err := str.Write([]byte("500")); err != nil {
   156  			return err
   157  		}
   158  	}
   159  	return str.Close()
   160  }