├── README.md ├── example_test.go ├── LICENSE └── server.go /README.md: -------------------------------------------------------------------------------- 1 | # h2c 2 | (Old) golang HTTP/2 h2c server implementation 3 | 4 | Please visit [x/net/http2/h2c](https://godoc.org/golang.org/x/net/http2/h2c) before using this code. 5 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package h2c_test 2 | 3 | import ( 4 | "fmt" 5 | . "h2c" 6 | "http" 7 | "log" 8 | ) 9 | 10 | // Server implements http.Handler interface. 11 | // Server wraps the entire http site under HTTP/2 h2c by default. 12 | // if Client wants to upgrade the connection, then connection will be 13 | // automatically switch to HTTP/2 protocol. 14 | // If you'd like to control the path, you can manually set the inner 15 | // http.Handler. 16 | func ExampleServer_serveHTTP() { 17 | http.HandleFunc("/", func(w http.ResposneWriter, r *http.Request) { 18 | fmt.Fprintf(w, "hello") 19 | }) 20 | log.Fatal(http.ListenAndServe(":8080", &h2c.Server{})) 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Hiroaki KAWAI 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | // Package h2c implements HTTP/2 h2c. 2 | // 3 | // HTTP/2 h2c server implementation would not available in golang standard library 4 | // because of the policy. This package provides h2c handy implementation. 5 | package h2c 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "encoding/base64" 11 | "encoding/binary" 12 | "golang.org/x/net/http2" 13 | "golang.org/x/net/http2/hpack" 14 | "io" 15 | "log" 16 | "net" 17 | "net/http" 18 | "net/textproto" 19 | "strings" 20 | "unicode" 21 | ) 22 | 23 | type Server struct { 24 | // Actual content handler 25 | http.Handler 26 | 27 | // Disable RFC 7540 3.4 behavior 28 | DisableDirect bool 29 | } 30 | 31 | // Converts byte slice of SETTINGS frame payload into http2.Setting slice. 32 | func ParseSettings(b []byte) []http2.Setting { 33 | var r []http2.Setting 34 | for i := 0; i < len(b)/6; i++ { 35 | r = append(r, http2.Setting{ 36 | ID: http2.SettingID(binary.BigEndian.Uint16(b[i*6:])), 37 | Val: binary.BigEndian.Uint32(b[i*6+2:]), 38 | }) 39 | } 40 | return r 41 | } 42 | 43 | var sensitiveHeaders []string = []string{ 44 | "Cookie", 45 | "Set-Cookie", 46 | "Authorization", 47 | } 48 | 49 | // RFC7540 8.1.2.2 50 | var connectionHeaders []string = []string{ 51 | "Connection", 52 | "Keep-Alive", 53 | "Proxy-Connection", 54 | "Transfer-Encoding", 55 | "Upgrade", 56 | "HTTP2-Settings", 57 | } 58 | 59 | type InitRequest struct { 60 | Settings []http2.Setting // HTTP2-Settings parameters 61 | HeaderBlock []byte // http2 header block hpack-ed from HTTP/1.1 headers 62 | } 63 | 64 | // Prepares an http/2 request binaries from http.Request. 65 | // Returns nil if upgrade failed. 66 | func InitH2c(req *http.Request) *InitRequest { 67 | upgrade := false 68 | if vs, ok := req.Header[textproto.CanonicalMIMEHeaderKey("Upgrade")]; ok { 69 | sp := func(c rune) bool { return unicode.IsSpace(c) || c == ',' } 70 | // RFC 7230 3.2.2 comma-separated field value 71 | for _, v := range vs { 72 | for _, s := range strings.FieldsFunc(v, sp) { 73 | if s == "h2c" { 74 | upgrade = true 75 | } 76 | } 77 | } 78 | } 79 | if !upgrade { 80 | return nil 81 | } 82 | 83 | var settings []http2.Setting 84 | if vs, ok := req.Header[textproto.CanonicalMIMEHeaderKey("HTTP2-Settings")]; ok && len(vs) == 1 { 85 | if b, e := base64.RawURLEncoding.DecodeString(vs[0]); e != nil { 86 | return nil 87 | } else if len(b)%6 != 0 { 88 | return nil 89 | } else { 90 | settings = ParseSettings(b) 91 | } 92 | } else { 93 | return nil 94 | } 95 | 96 | buf := bytes.NewBuffer(nil) 97 | enc := hpack.NewEncoder(buf) 98 | for _, s := range settings { 99 | switch s.ID { 100 | case http2.SettingHeaderTableSize: 101 | enc.SetMaxDynamicTableSize(s.Val) 102 | } 103 | } 104 | 105 | wh := func(name, value string, sensitive bool) error { 106 | if len(value) == 0 { 107 | return nil 108 | } 109 | return enc.WriteField(hpack.HeaderField{ 110 | Name: strings.ToLower(name), 111 | Value: value, 112 | Sensitive: sensitive, 113 | }) 114 | } 115 | if err := wh(":method", req.Method, false); err != nil { 116 | return nil 117 | } 118 | host := req.URL.Host 119 | if len(host) == 0 { 120 | host = req.Host 121 | } 122 | if err := wh(":authority", host, false); err != nil { 123 | return nil 124 | } 125 | if req.Method != "CONNECT" { 126 | scheme := "http" 127 | if len(req.URL.Scheme) > 0 { 128 | scheme = req.URL.Scheme 129 | } 130 | if err := wh(":scheme", scheme, false); err != nil { 131 | return nil 132 | } 133 | if err := wh(":path", req.URL.Path, false); err != nil { 134 | return nil 135 | } 136 | } 137 | for k, vs := range req.Header { 138 | skip := false 139 | for _, h := range connectionHeaders { 140 | if k == textproto.CanonicalMIMEHeaderKey(h) { 141 | skip = true 142 | } 143 | } 144 | if skip { 145 | continue 146 | } 147 | 148 | sensitive := false 149 | for _, h := range sensitiveHeaders { 150 | if k == textproto.CanonicalMIMEHeaderKey(h) { 151 | sensitive = true 152 | } 153 | } 154 | for _, v := range vs { 155 | if err := wh(k, v, sensitive); err != nil { 156 | return nil 157 | } 158 | } 159 | } 160 | 161 | return &InitRequest{ 162 | Settings: settings, 163 | HeaderBlock: buf.Bytes(), 164 | } 165 | } 166 | 167 | type h2cInitReqBody struct { 168 | *http2.Framer 169 | *bytes.Buffer // Framer writes to this 170 | Body io.Reader 171 | FrameSize uint32 172 | buf []byte 173 | streamEnd bool 174 | } 175 | 176 | func (rd *h2cInitReqBody) Read(b []byte) (int, error) { 177 | if !rd.streamEnd && rd.Buffer.Len() < len(b) { 178 | if len(rd.buf) == 0 { 179 | rd.buf = make([]byte, rd.FrameSize) 180 | } 181 | n, err := rd.Body.Read(rd.buf) 182 | if n == 0 || err == io.EOF { 183 | rd.streamEnd = true 184 | } 185 | if err := rd.Framer.WriteData(1, rd.streamEnd, rd.buf[:n]); err != nil { 186 | return 0, err 187 | } 188 | } 189 | return rd.Buffer.Read(b) 190 | } 191 | 192 | type vacuumPreface struct { 193 | io.Reader 194 | } 195 | 196 | func (rd vacuumPreface) Read(b []byte) (int, error) { 197 | n, err := rd.Reader.Read([]byte(http2.ClientPreface)) 198 | if n != len(http2.ClientPreface) { 199 | return n, io.ErrUnexpectedEOF 200 | } 201 | if err != nil && err != io.EOF { 202 | return n, err 203 | } 204 | return 0, io.EOF 205 | } 206 | 207 | type conn struct { 208 | net.Conn // embed for methods 209 | io.Reader 210 | *bufio.Writer 211 | vacuumAck bool 212 | buf []byte 213 | } 214 | 215 | func (c conn) Read(b []byte) (int, error) { 216 | return c.Reader.Read(b) 217 | } 218 | 219 | func (c *conn) Write(b []byte) (int, error) { 220 | if c.vacuumAck { 221 | c.buf = append(c.buf, b...) 222 | for c.vacuumAck { 223 | if len(c.buf) < 9 { 224 | return len(b), nil // just buffered into c.buf 225 | } 226 | fh, err := http2.ReadFrameHeader(bytes.NewBuffer(c.buf)) 227 | if err != nil { 228 | return 0, err // in case frame was broken 229 | } else if uint32(len(c.buf)) < 9+fh.Length { 230 | return len(b), nil // just buffered into c.buf 231 | } 232 | buf := c.buf[:9+fh.Length] 233 | c.buf = c.buf[9+fh.Length:] 234 | if http2.FrameSettings == fh.Type && fh.Flags.Has(http2.FlagSettingsAck) { 235 | c.vacuumAck = false 236 | } else if n, err := c.Writer.Write(buf); err != nil { 237 | return n, err 238 | } 239 | } 240 | n, err := c.Writer.Write(c.buf) 241 | c.Writer.Flush() 242 | return n, err 243 | } 244 | n, err := c.Writer.Write(b) 245 | c.Writer.Flush() 246 | return n, err 247 | } 248 | 249 | // ServeHTTP implements http.Handler interface for HTTP/2 h2c upgrade. 250 | func (u Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 251 | if hijacker, ok := w.(http.Hijacker); ok { 252 | if !u.DisableDirect && r.Method == "PRI" && r.URL.Path == "*" && r.Proto == "HTTP/2.0" { 253 | body := "SM\r\n\r\n" 254 | con, rw, err := hijacker.Hijack() 255 | defer con.Close() 256 | if err != nil { 257 | log.Printf("Hijack failed %v", err) 258 | } else if n, err := io.MultiReader(r.Body, rw).Read([]byte(body)); n != len(body) { 259 | log.Printf("%d %v", n, err) 260 | } else { 261 | wrap := io.MultiReader(bytes.NewBuffer([]byte(http2.ClientPreface)), rw) 262 | nc := &conn{ 263 | Conn: con, 264 | Writer: rw.Writer, 265 | Reader: wrap, 266 | } 267 | h2c := &http2.Server{} 268 | h2c.ServeConn(nc, &http2.ServeConnOpts{Handler: u.Handler}) 269 | return 270 | } 271 | http.Error(w, "Server could not handle the request.", http.StatusMethodNotAllowed) 272 | return 273 | } 274 | if initReq := InitH2c(r); initReq != nil { 275 | fsz := uint32(1 << 14) // RFC default 276 | for _, s := range initReq.Settings { 277 | if s.ID == http2.SettingMaxFrameSize && s.Val != 0 { 278 | fsz = s.Val 279 | } 280 | } 281 | onError := func(e error) { 282 | log.Print(e) 283 | http.Error(w, "Error in upgrading initial request", http.StatusInternalServerError) 284 | } 285 | 286 | h2req := bytes.NewBuffer([]byte(http2.ClientPreface)) 287 | fr := http2.NewFramer(h2req, nil) 288 | 289 | if err := fr.WriteSettings(initReq.Settings...); err != nil { 290 | onError(err) 291 | return 292 | } 293 | 294 | hdr := initReq.HeaderBlock 295 | if uint32(len(hdr)) <= fsz { 296 | if err := fr.WriteHeaders(http2.HeadersFrameParam{ 297 | StreamID: 1, 298 | BlockFragment: hdr, 299 | EndHeaders: true, 300 | }); err != nil { 301 | onError(err) 302 | return 303 | } 304 | } else { 305 | if err := fr.WriteHeaders(http2.HeadersFrameParam{ 306 | StreamID: 1, 307 | BlockFragment: hdr[:fsz], 308 | }); err != nil { 309 | onError(err) 310 | return 311 | } 312 | hdr = hdr[fsz:] 313 | for len(hdr) > 0 { 314 | if uint32(len(hdr)) > fsz { 315 | if err := fr.WriteContinuation(1, false, hdr[:fsz]); err != nil { 316 | onError(err) 317 | return 318 | } 319 | hdr = hdr[fsz:] 320 | } else { 321 | if err := fr.WriteContinuation(1, true, hdr); err != nil { 322 | onError(err) 323 | return 324 | } 325 | hdr = nil 326 | } 327 | } 328 | } 329 | 330 | con, rw, err := hijacker.Hijack() 331 | // Note: It seems rw is a wrapper for con. 332 | // r.Body.Read still looks working unless rw.Read call. 333 | defer con.Close() 334 | if err != nil { 335 | onError(err) 336 | rw.Flush() 337 | return 338 | } 339 | 340 | rw.Write([]byte( 341 | "HTTP/1.1 101 Switching Protocols\r\n" + 342 | "Connection: upgrade\r\n" + 343 | "Upgrade: h2c\r\n" + 344 | "\r\n")) 345 | 346 | h2req2 := &h2cInitReqBody{ 347 | Framer: fr, 348 | Buffer: h2req, 349 | Body: r.Body, 350 | FrameSize: fsz, 351 | } 352 | nc := &conn{ 353 | Conn: con, 354 | Writer: rw.Writer, 355 | Reader: io.MultiReader(h2req2, vacuumPreface{rw}, rw), 356 | vacuumAck: true, // because we sent HTTP2-Settings payload 357 | } 358 | h2c := &http2.Server{} 359 | for _, s := range initReq.Settings { 360 | switch s.ID { 361 | case http2.SettingMaxConcurrentStreams: 362 | h2c.MaxConcurrentStreams = s.Val 363 | case http2.SettingMaxFrameSize: 364 | h2c.MaxReadFrameSize = s.Val 365 | default: 366 | // just ignore 367 | } 368 | } 369 | h2c.ServeConn(nc, &http2.ServeConnOpts{Handler: u.Handler}) 370 | return 371 | } 372 | } 373 | if u.Handler != nil { 374 | u.Handler.ServeHTTP(w, r) 375 | } else { 376 | http.DefaultServeMux.ServeHTTP(w, r) 377 | } 378 | return 379 | } 380 | --------------------------------------------------------------------------------