github.com/MerlinKodo/quic-go@v0.39.2/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/MerlinKodo/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 }