| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| // See https://code.google.com/p/go/source/browse/CONTRIBUTORS |
| // Licensed under the same terms as Go itself: |
| // https://code.google.com/p/go/source/browse/LICENSE |
| |
| // Package http2 implements the HTTP/2 protocol. |
| // |
| // This is a work in progress. This package is low-level and intended |
| // to be used directly by very few people. Most users will use it |
| // indirectly through integration with the net/http package. See |
| // ConfigureServer. That ConfigureServer call will likely be automatic |
| // or available via an empty import in the future. |
| // |
| // This package currently targets draft-14. See http://http2.github.io/ |
| package http2 |
| |
| import ( |
| "bufio" |
| "fmt" |
| "io" |
| "net/http" |
| "strconv" |
| "sync" |
| ) |
| |
| var VerboseLogs = false |
| |
| const ( |
| // ClientPreface is the string that must be sent by new |
| // connections from clients. |
| ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" |
| |
| // SETTINGS_MAX_FRAME_SIZE default |
| // http://http2.github.io/http2-spec/#rfc.section.6.5.2 |
| initialMaxFrameSize = 16384 |
| |
| // NextProtoTLS is the NPN/ALPN protocol negotiated during |
| // HTTP/2's TLS setup. |
| NextProtoTLS = "h2-14" |
| |
| // http://http2.github.io/http2-spec/#SettingValues |
| initialHeaderTableSize = 4096 |
| |
| initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size |
| |
| defaultMaxReadFrameSize = 1 << 20 |
| ) |
| |
| var ( |
| clientPreface = []byte(ClientPreface) |
| ) |
| |
| type streamState int |
| |
| const ( |
| stateIdle streamState = iota |
| stateOpen |
| stateHalfClosedLocal |
| stateHalfClosedRemote |
| stateResvLocal |
| stateResvRemote |
| stateClosed |
| ) |
| |
| var stateName = [...]string{ |
| stateIdle: "Idle", |
| stateOpen: "Open", |
| stateHalfClosedLocal: "HalfClosedLocal", |
| stateHalfClosedRemote: "HalfClosedRemote", |
| stateResvLocal: "ResvLocal", |
| stateResvRemote: "ResvRemote", |
| stateClosed: "Closed", |
| } |
| |
| func (st streamState) String() string { |
| return stateName[st] |
| } |
| |
| // Setting is a setting parameter: which setting it is, and its value. |
| type Setting struct { |
| // ID is which setting is being set. |
| // See http://http2.github.io/http2-spec/#SettingValues |
| ID SettingID |
| |
| // Val is the value. |
| Val uint32 |
| } |
| |
| func (s Setting) String() string { |
| return fmt.Sprintf("[%v = %d]", s.ID, s.Val) |
| } |
| |
| // Valid reports whether the setting is valid. |
| func (s Setting) Valid() error { |
| // Limits and error codes from 6.5.2 Defined SETTINGS Parameters |
| switch s.ID { |
| case SettingEnablePush: |
| if s.Val != 1 && s.Val != 0 { |
| return ConnectionError(ErrCodeProtocol) |
| } |
| case SettingInitialWindowSize: |
| if s.Val > 1<<31-1 { |
| return ConnectionError(ErrCodeFlowControl) |
| } |
| case SettingMaxFrameSize: |
| if s.Val < 16384 || s.Val > 1<<24-1 { |
| return ConnectionError(ErrCodeProtocol) |
| } |
| } |
| return nil |
| } |
| |
| // A SettingID is an HTTP/2 setting as defined in |
| // http://http2.github.io/http2-spec/#iana-settings |
| type SettingID uint16 |
| |
| const ( |
| SettingHeaderTableSize SettingID = 0x1 |
| SettingEnablePush SettingID = 0x2 |
| SettingMaxConcurrentStreams SettingID = 0x3 |
| SettingInitialWindowSize SettingID = 0x4 |
| SettingMaxFrameSize SettingID = 0x5 |
| SettingMaxHeaderListSize SettingID = 0x6 |
| ) |
| |
| var settingName = map[SettingID]string{ |
| SettingHeaderTableSize: "HEADER_TABLE_SIZE", |
| SettingEnablePush: "ENABLE_PUSH", |
| SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS", |
| SettingInitialWindowSize: "INITIAL_WINDOW_SIZE", |
| SettingMaxFrameSize: "MAX_FRAME_SIZE", |
| SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE", |
| } |
| |
| func (s SettingID) String() string { |
| if v, ok := settingName[s]; ok { |
| return v |
| } |
| return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s)) |
| } |
| |
| func validHeader(v string) bool { |
| if len(v) == 0 { |
| return false |
| } |
| for _, r := range v { |
| // "Just as in HTTP/1.x, header field names are |
| // strings of ASCII characters that are compared in a |
| // case-insensitive fashion. However, header field |
| // names MUST be converted to lowercase prior to their |
| // encoding in HTTP/2. " |
| if r >= 127 || ('A' <= r && r <= 'Z') { |
| return false |
| } |
| } |
| return true |
| } |
| |
| var httpCodeStringCommon = map[int]string{} // n -> strconv.Itoa(n) |
| |
| func init() { |
| for i := 100; i <= 999; i++ { |
| if v := http.StatusText(i); v != "" { |
| httpCodeStringCommon[i] = strconv.Itoa(i) |
| } |
| } |
| } |
| |
| func httpCodeString(code int) string { |
| if s, ok := httpCodeStringCommon[code]; ok { |
| return s |
| } |
| return strconv.Itoa(code) |
| } |
| |
| // from pkg io |
| type stringWriter interface { |
| WriteString(s string) (n int, err error) |
| } |
| |
| // A gate lets two goroutines coordinate their activities. |
| type gate chan struct{} |
| |
| func (g gate) Done() { g <- struct{}{} } |
| func (g gate) Wait() { <-g } |
| |
| // A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed). |
| type closeWaiter chan struct{} |
| |
| // Init makes a closeWaiter usable. |
| // It exists because so a closeWaiter value can be placed inside a |
| // larger struct and have the Mutex and Cond's memory in the same |
| // allocation. |
| func (cw *closeWaiter) Init() { |
| *cw = make(chan struct{}) |
| } |
| |
| // Close marks the closeWaiter as closed and unblocks any waiters. |
| func (cw closeWaiter) Close() { |
| close(cw) |
| } |
| |
| // Wait waits for the closeWaiter to become closed. |
| func (cw closeWaiter) Wait() { |
| <-cw |
| } |
| |
| // bufferedWriter is a buffered writer that writes to w. |
| // Its buffered writer is lazily allocated as needed, to minimize |
| // idle memory usage with many connections. |
| type bufferedWriter struct { |
| w io.Writer // immutable |
| bw *bufio.Writer // non-nil when data is buffered |
| } |
| |
| func newBufferedWriter(w io.Writer) *bufferedWriter { |
| return &bufferedWriter{w: w} |
| } |
| |
| var bufWriterPool = sync.Pool{ |
| New: func() interface{} { |
| // TODO: pick something better? this is a bit under |
| // (3 x typical 1500 byte MTU) at least. |
| return bufio.NewWriterSize(nil, 4<<10) |
| }, |
| } |
| |
| func (w *bufferedWriter) Write(p []byte) (n int, err error) { |
| if w.bw == nil { |
| bw := bufWriterPool.Get().(*bufio.Writer) |
| bw.Reset(w.w) |
| w.bw = bw |
| } |
| return w.bw.Write(p) |
| } |
| |
| func (w *bufferedWriter) Flush() error { |
| bw := w.bw |
| if bw == nil { |
| return nil |
| } |
| err := bw.Flush() |
| bw.Reset(nil) |
| bufWriterPool.Put(bw) |
| w.bw = nil |
| return err |
| } |