github.com/quic-go/quic-go@v0.44.0/example/main.go (about) 1 package main 2 3 import ( 4 "crypto/md5" 5 "errors" 6 "flag" 7 "fmt" 8 "io" 9 "log" 10 "mime/multipart" 11 "net/http" 12 "strconv" 13 "strings" 14 "sync" 15 16 _ "net/http/pprof" 17 18 "github.com/quic-go/quic-go" 19 "github.com/quic-go/quic-go/http3" 20 "github.com/quic-go/quic-go/internal/testdata" 21 "github.com/quic-go/quic-go/qlog" 22 ) 23 24 type binds []string 25 26 func (b binds) String() string { 27 return strings.Join(b, ",") 28 } 29 30 func (b *binds) Set(v string) error { 31 *b = strings.Split(v, ",") 32 return nil 33 } 34 35 // Size is needed by the /demo/upload handler to determine the size of the uploaded file 36 type Size interface { 37 Size() int64 38 } 39 40 // See https://en.wikipedia.org/wiki/Lehmer_random_number_generator 41 func generatePRData(l int) []byte { 42 res := make([]byte, l) 43 seed := uint64(1) 44 for i := 0; i < l; i++ { 45 seed = seed * 48271 % 2147483647 46 res[i] = byte(seed) 47 } 48 return res 49 } 50 51 func setupHandler(www string) http.Handler { 52 mux := http.NewServeMux() 53 54 if len(www) > 0 { 55 mux.Handle("/", http.FileServer(http.Dir(www))) 56 } else { 57 mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 58 fmt.Printf("%#v\n", r) 59 const maxSize = 1 << 30 // 1 GB 60 num, err := strconv.ParseInt(strings.ReplaceAll(r.RequestURI, "/", ""), 10, 64) 61 if err != nil || num <= 0 || num > maxSize { 62 w.WriteHeader(400) 63 return 64 } 65 w.Write(generatePRData(int(num))) 66 }) 67 } 68 69 mux.HandleFunc("/demo/tile", func(w http.ResponseWriter, r *http.Request) { 70 // Small 40x40 png 71 w.Write([]byte{ 72 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 73 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 74 0x01, 0x03, 0x00, 0x00, 0x00, 0xb6, 0x30, 0x2a, 0x2e, 0x00, 0x00, 0x00, 75 0x03, 0x50, 0x4c, 0x54, 0x45, 0x5a, 0xc3, 0x5a, 0xad, 0x38, 0xaa, 0xdb, 76 0x00, 0x00, 0x00, 0x0b, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0x63, 0x18, 77 0x61, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0xe2, 0xb8, 0x75, 0x22, 0x00, 78 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 79 }) 80 }) 81 82 mux.HandleFunc("/demo/tiles", func(w http.ResponseWriter, r *http.Request) { 83 io.WriteString(w, "<html><head><style>img{width:40px;height:40px;}</style></head><body>") 84 for i := 0; i < 200; i++ { 85 fmt.Fprintf(w, `<img src="/demo/tile?cachebust=%d">`, i) 86 } 87 io.WriteString(w, "</body></html>") 88 }) 89 90 mux.HandleFunc("/demo/echo", func(w http.ResponseWriter, r *http.Request) { 91 body, err := io.ReadAll(r.Body) 92 if err != nil { 93 fmt.Printf("error reading body while handling /echo: %s\n", err.Error()) 94 } 95 w.Write(body) 96 }) 97 98 // accept file uploads and return the MD5 of the uploaded file 99 // maximum accepted file size is 1 GB 100 mux.HandleFunc("/demo/upload", func(w http.ResponseWriter, r *http.Request) { 101 if r.Method == http.MethodPost { 102 err := r.ParseMultipartForm(1 << 30) // 1 GB 103 if err == nil { 104 var file multipart.File 105 file, _, err = r.FormFile("uploadfile") 106 if err == nil { 107 var size int64 108 if sizeInterface, ok := file.(Size); ok { 109 size = sizeInterface.Size() 110 b := make([]byte, size) 111 file.Read(b) 112 md5 := md5.Sum(b) 113 fmt.Fprintf(w, "%x", md5) 114 return 115 } 116 err = errors.New("couldn't get uploaded file size") 117 } 118 } 119 log.Printf("Error receiving upload: %#v", err) 120 } 121 io.WriteString(w, `<html><body><form action="/demo/upload" method="post" enctype="multipart/form-data"> 122 <input type="file" name="uploadfile"><br> 123 <input type="submit"> 124 </form></body></html>`) 125 }) 126 127 return mux 128 } 129 130 func main() { 131 // defer profile.Start().Stop() 132 go func() { 133 log.Println(http.ListenAndServe("localhost:6060", nil)) 134 }() 135 // runtime.SetBlockProfileRate(1) 136 137 bs := binds{} 138 flag.Var(&bs, "bind", "bind to") 139 www := flag.String("www", "", "www data") 140 tcp := flag.Bool("tcp", false, "also listen on TCP") 141 key := flag.String("key", "", "TLS key (requires -cert option)") 142 cert := flag.String("cert", "", "TLS certificate (requires -key option)") 143 flag.Parse() 144 145 if len(bs) == 0 { 146 bs = binds{"localhost:6121"} 147 } 148 149 handler := setupHandler(*www) 150 151 var wg sync.WaitGroup 152 wg.Add(len(bs)) 153 var certFile, keyFile string 154 if *key != "" && *cert != "" { 155 keyFile = *key 156 certFile = *cert 157 } else { 158 certFile, keyFile = testdata.GetCertificatePaths() 159 } 160 for _, b := range bs { 161 fmt.Println("listening on", b) 162 bCap := b 163 go func() { 164 var err error 165 if *tcp { 166 err = http3.ListenAndServeTLS(bCap, certFile, keyFile, handler) 167 } else { 168 server := http3.Server{ 169 Handler: handler, 170 Addr: bCap, 171 QUICConfig: &quic.Config{ 172 Tracer: qlog.DefaultTracer, 173 }, 174 } 175 err = server.ListenAndServeTLS(certFile, keyFile) 176 } 177 if err != nil { 178 fmt.Println(err) 179 } 180 wg.Done() 181 }() 182 } 183 wg.Wait() 184 }