├── .gitignore ├── LICENSE ├── README.md ├── client.go ├── client_test.go ├── common ├── compression.go ├── constants.go ├── doc.go ├── errors.go ├── interfaces.go ├── limits.go ├── log.go ├── response.go ├── stream_state.go ├── types.go └── utils.go ├── doc.go ├── examples ├── client │ └── client.go ├── proxy_client │ └── proxy_client.go ├── proxy_server │ └── proxy_server.go ├── server │ └── server.go └── spdy_only_server │ └── server.go ├── log.go ├── proxy.go ├── proxy_test.go ├── regression_test.go ├── server.go ├── shared_interfaces.go ├── spdy.go ├── spdy2 ├── conn.go ├── doc.go ├── error_handling.go ├── frames │ ├── common.go │ ├── data.go │ ├── doc.go │ ├── goaway.go │ ├── headers.go │ ├── noop.go │ ├── ping.go │ ├── rst_stream.go │ ├── settings.go │ ├── syn_reply.go │ ├── syn_stream.go │ └── window_update.go ├── interface.go ├── io.go ├── log.go ├── processing.go ├── push_stream.go ├── request_stream.go ├── requests.go ├── response_stream.go ├── shutdown.go ├── spdy_api.go └── utils.go ├── spdy3 ├── conn.go ├── doc.go ├── error_handling.go ├── flow.go ├── frames │ ├── common.go │ ├── credential.go │ ├── data.go │ ├── doc.go │ ├── goaway.go │ ├── headers.go │ ├── log.go │ ├── ping.go │ ├── rst_stream.go │ ├── settings.go │ ├── syn_reply.go │ ├── syn_stream.go │ └── window_update.go ├── interface.go ├── io.go ├── log.go ├── processing.go ├── push_stream.go ├── request_stream.go ├── requests.go ├── response_stream.go ├── shutdown.go ├── spdy_api.go └── utils.go ├── spdy_test.go ├── transport.go └── versions.go /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Jamie Hall 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated 2 | 3 | With the release of [Go1.6](https://golang.org/doc/go1.6) and the addition of [http2](https://golang.org/x/net/http2) 4 | to the standard library, this package is no longer under active development. It is highly recommended that former 5 | users of this package migrate to HTTP/2. 6 | 7 | # spdy 8 | 9 | [![GoDoc](https://godoc.org/github.com/SlyMarbo/spdy?status.png)](https://godoc.org/github.com/SlyMarbo/spdy) 10 | 11 | A full-featured SPDY library for the Go language. 12 | 13 | Note that this implementation currently supports SPDY drafts 2 and 3. 14 | 15 | See [these examples][examples] for a quick intro to the package. 16 | 17 | [examples]: https://github.com/SlyMarbo/spdy/tree/master/examples 18 | 19 | Note that using this package with [Martini][martini] is likely to result in strange and hard-to-diagnose 20 | bugs. For more information, read [this article][martini-article]. As a result, issues that arise when 21 | combining the two should be directed at the Martini developers. 22 | 23 | [martini]: https://github.com/go-martini/martini 24 | [martini-article]: http://stephensearles.com/?p=254 25 | 26 | Servers 27 | ------- 28 | 29 | 30 | The following examples use features specific to SPDY. 31 | 32 | Just the handler is shown. 33 | 34 | Use SPDY's pinging features to test the connection: 35 | ```go 36 | package main 37 | 38 | import ( 39 | "net/http" 40 | "time" 41 | 42 | "github.com/SlyMarbo/spdy" 43 | ) 44 | 45 | func Serve(w http.ResponseWriter, r *http.Request) { 46 | // Ping returns a channel which will send an empty struct. 47 | if ping, err := spdy.PingClient(w); err == nil { 48 | select { 49 | case response := <- ping: 50 | if response != nil { 51 | // Connection is fine. 52 | } else { 53 | // Something went wrong. 54 | } 55 | 56 | case <-time.After(timeout): 57 | // Ping took too long. 58 | } 59 | } else { 60 | // Not SPDY 61 | } 62 | 63 | // ... 64 | } 65 | ``` 66 | 67 | 68 | 69 | Sending a server push: 70 | ```go 71 | package main 72 | 73 | import ( 74 | "net/http" 75 | 76 | "github.com/SlyMarbo/spdy" 77 | ) 78 | 79 | func Serve(w http.ResponseWriter, r *http.Request) { 80 | // Push returns a separate http.ResponseWriter and an error. 81 | path := r.URL.Scheme + "://" + r.URL.Host + "/example.js" 82 | push, err := spdy.Push(path) 83 | if err != nil { 84 | // Not using SPDY. 85 | } 86 | http.ServeFile(push, r, "./content/example.js") 87 | 88 | // Note that a PushStream must be finished manually once 89 | // all writing has finished. 90 | push.Finish() 91 | 92 | // ... 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy 6 | 7 | import ( 8 | "errors" 9 | "net" 10 | "net/http" 11 | 12 | "github.com/SlyMarbo/spdy/common" 13 | "github.com/SlyMarbo/spdy/spdy2" 14 | "github.com/SlyMarbo/spdy/spdy3" 15 | ) 16 | 17 | // init modifies http.DefaultClient to use a spdy.Transport, enabling 18 | // support for SPDY in functions like http.Get. 19 | func init() { 20 | http.DefaultClient = NewClient(false) 21 | } 22 | 23 | // NewClientConn is used to create a SPDY connection, using the given 24 | // net.Conn for the underlying connection, and the given Receiver to 25 | // receive server pushes. 26 | func NewClientConn(conn net.Conn, push common.Receiver, version, subversion int) (common.Conn, error) { 27 | if conn == nil { 28 | return nil, errors.New("Error: Connection initialised with nil net.conn.") 29 | } 30 | 31 | switch version { 32 | case 3: 33 | out := spdy3.NewConn(conn, nil, subversion) 34 | out.PushReceiver = push 35 | return out, nil 36 | 37 | case 2: 38 | out := spdy2.NewConn(conn, nil) 39 | out.PushReceiver = push 40 | return out, nil 41 | 42 | default: 43 | return nil, errors.New("Error: Unrecognised SPDY version.") 44 | } 45 | } 46 | 47 | // NewClient creates an http.Client that supports SPDY. 48 | func NewClient(insecureSkipVerify bool) *http.Client { 49 | return &http.Client{Transport: NewTransport(insecureSkipVerify)} 50 | } 51 | -------------------------------------------------------------------------------- /common/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package common contains shared functionality for the spdy package. 6 | // 7 | package common 8 | -------------------------------------------------------------------------------- /common/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "path/filepath" 11 | "runtime" 12 | ) 13 | 14 | // MaxBenignErrors is the maximum number of minor errors each 15 | // connection will allow without ending the session. 16 | // 17 | // By default, MaxBenignErrors is set to 0, disabling checks 18 | // and allowing minor errors to go unchecked, although they 19 | // will still be reported to the debug logger. If it is 20 | // important that no errors go unchecked, such as when testing 21 | // another implementation, set MaxBenignErrors to 1 or higher. 22 | var MaxBenignErrors = 0 23 | 24 | var ( 25 | ErrConnNil = errors.New("Error: Connection is nil.") 26 | ErrConnClosed = errors.New("Error: Connection is closed.") 27 | ErrGoaway = errors.New("Error: GOAWAY received.") 28 | ErrNoFlowControl = errors.New("Error: This connection does not use flow control.") 29 | ErrConnectFail = errors.New("Error: Failed to connect.") 30 | ErrInvalidVersion = errors.New("Error: Invalid SPDY version.") 31 | 32 | // ErrNotSPDY indicates that a SPDY-specific feature was attempted 33 | // with a ResponseWriter using a non-SPDY connection. 34 | ErrNotSPDY = errors.New("Error: Not a SPDY connection.") 35 | 36 | // ErrNotConnected indicates that a SPDY-specific feature was 37 | // attempted with a Client not connected to the given server. 38 | ErrNotConnected = errors.New("Error: Not connected to given server.") 39 | ) 40 | 41 | type incorrectDataLength struct { 42 | got, expected int 43 | } 44 | 45 | func IncorrectDataLength(got, expected int) error { 46 | return &incorrectDataLength{got, expected} 47 | } 48 | 49 | func (i *incorrectDataLength) Error() string { 50 | return fmt.Sprintf("Error: Incorrect amount of data for frame: got %d bytes, expected %d.", i.got, i.expected) 51 | } 52 | 53 | var FrameTooLarge = errors.New("Error: Frame too large.") 54 | 55 | type invalidField struct { 56 | field string 57 | got, expected int 58 | } 59 | 60 | func InvalidField(field string, got, expected int) error { 61 | return &invalidField{field, got, expected} 62 | } 63 | 64 | func (i *invalidField) Error() string { 65 | return fmt.Sprintf("Error: Field %q recieved invalid data %d, expecting %d.", i.field, i.got, i.expected) 66 | } 67 | 68 | type incorrectFrame struct { 69 | got, expected, version int 70 | } 71 | 72 | func IncorrectFrame(got, expected, version int) error { 73 | return &incorrectFrame{got, expected, version} 74 | } 75 | 76 | func (i *incorrectFrame) Error() string { 77 | if i.version == 3 { 78 | return fmt.Sprintf("Error: Frame %s tried to parse data for a %s.", frameNamesV3[i.expected], frameNamesV3[i.got]) 79 | } 80 | return fmt.Sprintf("Error: Frame %s tried to parse data for a %s.", frameNamesV2[i.expected], frameNamesV2[i.got]) 81 | } 82 | 83 | var StreamIdTooLarge = errors.New("Error: Stream ID is too large.") 84 | 85 | var StreamIdIsZero = errors.New("Error: Stream ID is zero.") 86 | 87 | type UnsupportedVersion uint16 88 | 89 | func (u UnsupportedVersion) Error() string { 90 | return fmt.Sprintf("Error: Unsupported SPDY version: %d.\n", u) 91 | } 92 | 93 | func Recover() { 94 | v := recover() 95 | if v == nil { 96 | return 97 | } 98 | 99 | log.Printf("spdy: panic: %v (%[1]T)\n", v) 100 | for skip := 1; ; skip++ { 101 | pc, file, line, ok := runtime.Caller(skip) 102 | if ok { 103 | f := runtime.FuncForPC(pc) 104 | if filepath.Ext(file) != ".go" { 105 | continue 106 | } 107 | 108 | log.Printf("- %s:%d in %s()\n", file, line, f.Name()) 109 | if f.Name() == "main.main" { 110 | return 111 | } 112 | } else { 113 | log.Println("- ???:? in ???()") 114 | return 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /common/interfaces.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "net" 11 | "net/http" 12 | ) 13 | 14 | // Connection represents a SPDY connection. The connection should 15 | // be started with a call to Run, which will return once the 16 | // connection has been terminated. The connection can be ended 17 | // early by using Close. 18 | type Conn interface { 19 | http.CloseNotifier 20 | Close() error 21 | Closed() bool 22 | Conn() net.Conn 23 | Request(request *http.Request, receiver Receiver, priority Priority) (Stream, error) 24 | RequestResponse(request *http.Request, receiver Receiver, priority Priority) (*http.Response, error) 25 | Run() error 26 | } 27 | 28 | // Stream contains a single SPDY stream. 29 | type Stream interface { 30 | http.CloseNotifier 31 | http.ResponseWriter 32 | Close() error 33 | Conn() Conn 34 | ReceiveFrame(Frame) error 35 | Run() error 36 | State() *StreamState 37 | StreamID() StreamID 38 | } 39 | 40 | // PushStream contains a single SPDY push stream. 41 | type PushStream interface { 42 | Stream 43 | 44 | // Fin is used to close the 45 | // push stream once writing 46 | // has finished. 47 | Finish() 48 | } 49 | 50 | // PriorityStream represents a SPDY stream with a priority. 51 | type PriorityStream interface { 52 | Stream 53 | 54 | // Priority returns the stream's 55 | // priority. 56 | Priority() Priority 57 | } 58 | 59 | // Frame represents a single SPDY frame. 60 | type Frame interface { 61 | fmt.Stringer 62 | io.ReaderFrom 63 | io.WriterTo 64 | Compress(Compressor) error 65 | Decompress(Decompressor) error 66 | Name() string 67 | } 68 | 69 | // Compressor is used to compress the text header of a SPDY frame. 70 | type Compressor interface { 71 | io.Closer 72 | Compress(http.Header) ([]byte, error) 73 | } 74 | 75 | // Decompressor is used to decompress the text header of a SPDY frame. 76 | type Decompressor interface { 77 | Decompress([]byte) (http.Header, error) 78 | } 79 | 80 | // Pinger represents something able to send and 81 | // receive PING frames. 82 | type Pinger interface { 83 | Ping() (<-chan bool, error) 84 | } 85 | 86 | // Pusher represents something able to send 87 | // server puhes. 88 | type Pusher interface { 89 | Push(url string, origin Stream) (PushStream, error) 90 | } 91 | 92 | // SetFlowController represents a connection 93 | // which can have its flow control mechanism 94 | // customised. 95 | type SetFlowController interface { 96 | SetFlowControl(FlowControl) 97 | } 98 | 99 | // Objects implementing the Receiver interface can be 100 | // registered to receive requests on the Client. 101 | // 102 | // ReceiveData is passed the original request, the data 103 | // to receive and a bool indicating whether this is the 104 | // final batch of data. If the bool is set to true, the 105 | // data may be empty, but should not be nil. 106 | // 107 | // ReceiveHeaders is passed the request and any sent 108 | // text headers. This may be called multiple times. 109 | // Note that these headers may contain the status code 110 | // of the response, under the ":status" header. If the 111 | // Receiver is being used to proxy a request, and the 112 | // headers presented to ReceiveHeader are copied to 113 | // another ResponseWriter, take care to call its 114 | // WriteHeader method after copying all headers, since 115 | // this may flush headers received so far. 116 | // 117 | // ReceiveRequest is used when server pushes are sent. 118 | // The returned bool should inticate whether to accept 119 | // the push. The provided Request will be that sent by 120 | // the server with the push. 121 | type Receiver interface { 122 | ReceiveData(request *http.Request, data []byte, final bool) 123 | ReceiveHeader(request *http.Request, header http.Header) 124 | ReceiveRequest(request *http.Request) bool 125 | } 126 | 127 | // Objects conforming to the FlowControl interface can be 128 | // used to provide the flow control mechanism for a 129 | // connection using SPDY version 3 and above. 130 | // 131 | // InitialWindowSize is called whenever a new stream is 132 | // created, and the returned value used as the initial 133 | // flow control window size. Note that a values smaller 134 | // than the default (65535) will likely result in poor 135 | // network utilisation. 136 | // 137 | // ReceiveData is called whenever a stream's window is 138 | // consumed by inbound data. The stream's ID is provided, 139 | // along with the stream's initial window size and the 140 | // current window size after receiving the data that 141 | // caused the call. If the window is to be regrown, 142 | // ReceiveData should return the increase in size. A value 143 | // of 0 does not change the window. Note that in SPDY/3.1 144 | // and later, the streamID may be 0 to represent the 145 | // connection-level flow control window. 146 | type FlowControl interface { 147 | InitialWindowSize() uint32 148 | ReceiveData(streamID StreamID, initialWindowSize uint32, newWindowSize int64) (deltaSize uint32) 149 | } 150 | -------------------------------------------------------------------------------- /common/limits.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "sync" 9 | ) 10 | 11 | // StreamLimit is used to add and enforce 12 | // a limit on the number of concurrently 13 | // active streams. 14 | type StreamLimit struct { 15 | lock sync.Mutex 16 | limit uint32 17 | current uint32 18 | } 19 | 20 | func NewStreamLimit(limit uint32) *StreamLimit { 21 | out := new(StreamLimit) 22 | out.limit = limit 23 | return out 24 | } 25 | 26 | // SetLimit is used to modify the stream limit. If the 27 | // limit is set to NO_STREAM_LIMIT, then the limiting 28 | // is disabled. 29 | func (s *StreamLimit) SetLimit(l uint32) { 30 | s.lock.Lock() 31 | s.limit = l 32 | s.lock.Unlock() 33 | } 34 | 35 | // Limit returns the current limit. 36 | func (s *StreamLimit) Limit() uint32 { 37 | return s.limit 38 | } 39 | 40 | // Add is called when a new stream is to be opened. Add 41 | // returns a bool indicating whether the stream is safe 42 | // open. 43 | func (s *StreamLimit) Add() bool { 44 | s.lock.Lock() 45 | if s.current >= s.limit { 46 | s.lock.Unlock() 47 | return false 48 | } 49 | s.current++ 50 | s.lock.Unlock() 51 | return true 52 | } 53 | 54 | // Close is called when a stream is closed; thus freeing 55 | // up a slot. 56 | func (s *StreamLimit) Close() { 57 | s.lock.Lock() 58 | s.current-- 59 | s.lock.Unlock() 60 | } 61 | -------------------------------------------------------------------------------- /common/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "io" 9 | "io/ioutil" 10 | logging "log" 11 | "os" 12 | ) 13 | 14 | type Logger struct { 15 | *logging.Logger 16 | } 17 | 18 | var log = &Logger{logging.New(os.Stderr, "(spdy) ", logging.LstdFlags|logging.Lshortfile)} 19 | var debug = &Logger{logging.New(ioutil.Discard, "(spdy debug) ", logging.LstdFlags)} 20 | var VerboseLogging = false 21 | 22 | func GetLogger() *Logger { 23 | return log 24 | } 25 | 26 | func GetDebugLogger() *Logger { 27 | return debug 28 | } 29 | 30 | // SetLogger sets the package's error logger. 31 | func SetLogger(l *logging.Logger) { 32 | log.Logger = l 33 | } 34 | 35 | // SetLogOutput sets the output for the package's error logger. 36 | func SetLogOutput(w io.Writer) { 37 | log.Logger = logging.New(w, "(spdy) ", logging.LstdFlags|logging.Lshortfile) 38 | } 39 | 40 | // SetDebugLogger sets the package's debug info logger. 41 | func SetDebugLogger(l *logging.Logger) { 42 | debug.Logger = l 43 | } 44 | 45 | // SetDebugOutput sets the output for the package's debug info logger. 46 | func SetDebugOutput(w io.Writer) { 47 | debug.Logger = logging.New(w, "(spdy debug) ", logging.LstdFlags) 48 | } 49 | 50 | // EnableDebugOutput sets the output for the package's debug info logger to os.Stdout. 51 | func EnableDebugOutput() { 52 | SetDebugOutput(os.Stdout) 53 | } 54 | -------------------------------------------------------------------------------- /common/response.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "bytes" 9 | "compress/gzip" 10 | "fmt" 11 | "io" 12 | "io/ioutil" 13 | "net/http" 14 | "os" 15 | "strconv" 16 | "strings" 17 | "sync" 18 | ) 19 | 20 | // Response is used in handling responses; storing 21 | // the data as it's received, and producing an 22 | // http.Response once complete. 23 | // 24 | // Response may be given a Receiver to enable live 25 | // handling of the response data. This is provided 26 | // by setting spdy.Transport.Receiver. Note that 27 | // providing a Receiver disables the default data 28 | // storage so the returned http.Response.Body will 29 | // be empty. 30 | type Response struct { 31 | StatusCode int 32 | 33 | headerM sync.Mutex 34 | Header http.Header 35 | 36 | dataM sync.Mutex 37 | data *hybridBuffer 38 | 39 | Request *http.Request 40 | Receiver Receiver 41 | } 42 | 43 | func NewResponse(request *http.Request, receiver Receiver) *Response { 44 | resp := new(Response) 45 | resp.Request = request 46 | resp.Receiver = receiver 47 | if receiver == nil { 48 | resp.data = newHybridBuffer() 49 | } 50 | return resp 51 | } 52 | 53 | func (r *Response) ReceiveData(req *http.Request, data []byte, finished bool) { 54 | if r.Receiver != nil { 55 | r.Receiver.ReceiveData(req, data, finished) 56 | } else { 57 | r.dataM.Lock() 58 | r.data.Write(data) 59 | r.dataM.Unlock() 60 | } 61 | } 62 | 63 | func (r *Response) ReceiveHeader(req *http.Request, header http.Header) { 64 | r.headerM.Lock() 65 | if r.Header == nil { 66 | r.Header = make(http.Header) 67 | } 68 | UpdateHeader(r.Header, header) 69 | if status := r.Header.Get(":status"); status != "" { 70 | status = strings.TrimSpace(status) 71 | if i := strings.Index(status, " "); i >= 0 { 72 | status = status[:i] 73 | } 74 | s, err := strconv.Atoi(status) 75 | if err == nil { 76 | r.StatusCode = s 77 | } 78 | } 79 | if r.Receiver != nil { 80 | r.Receiver.ReceiveHeader(req, header) 81 | } 82 | r.headerM.Unlock() 83 | } 84 | 85 | func (r *Response) ReceiveRequest(req *http.Request) bool { 86 | if r.Receiver != nil { 87 | return r.Receiver.ReceiveRequest(req) 88 | } 89 | return false 90 | } 91 | 92 | func (r *Response) Response() *http.Response { 93 | out := new(http.Response) 94 | 95 | r.headerM.Lock() 96 | out.Status = fmt.Sprintf("%d %s", r.StatusCode, http.StatusText(r.StatusCode)) 97 | out.StatusCode = r.StatusCode 98 | out.Header = r.Header 99 | r.headerM.Unlock() 100 | 101 | out.Proto = "HTTP/1.1" 102 | out.ProtoMajor = 1 103 | out.ProtoMinor = 1 104 | 105 | r.dataM.Lock() 106 | if r.data == nil { 107 | out.Body = &ReadCloser{new(bytes.Buffer)} 108 | } else if unrequestedGzip(r) { 109 | // User-agents MUST support gzip compression. 110 | // Regardless of the Accept-Encoding sent by the user-agent, the server may 111 | // always send content encoded with gzip or deflate encoding. 112 | r.data.Prep() 113 | out.Header.Del("Content-Encoding") 114 | out.Header.Del("Content-Length") 115 | out.ContentLength = -1 116 | out.Body = &gzipReader{body: r.data} 117 | } else { 118 | r.data.Prep() 119 | out.Body = r.data 120 | out.ContentLength = r.data.written 121 | } 122 | r.dataM.Unlock() 123 | 124 | out.TransferEncoding = nil 125 | out.Close = true 126 | out.Trailer = make(http.Header) 127 | out.Request = r.Request 128 | return out 129 | } 130 | 131 | // 10 MB 132 | var _MAX_MEM_STORAGE = 10 * 1024 * 1024 133 | 134 | // hybridBuffer is used in Response to make sure that 135 | // large volumes of data can be stored safely. 136 | type hybridBuffer struct { 137 | io.Reader 138 | 139 | buf *bytes.Buffer 140 | file *os.File 141 | written int64 142 | } 143 | 144 | func newHybridBuffer() *hybridBuffer { 145 | hb := new(hybridBuffer) 146 | hb.buf = new(bytes.Buffer) 147 | hb.Reader = hb.buf 148 | return hb 149 | } 150 | 151 | func (h *hybridBuffer) Close() error { 152 | h.buf.Reset() 153 | if h.file != nil { 154 | err := h.file.Close() 155 | if err != nil { 156 | return err 157 | } 158 | return os.Remove(h.file.Name()) 159 | } 160 | return nil 161 | } 162 | 163 | func (h *hybridBuffer) Prep() error { 164 | if h.file != nil { 165 | name := h.file.Name() 166 | err := h.file.Close() 167 | if err != nil { 168 | return err 169 | } 170 | h.file, err = os.Open(name) 171 | if err != nil { 172 | return err 173 | } 174 | h.Reader = io.MultiReader(h.buf, h.file) 175 | } 176 | return nil 177 | } 178 | 179 | func (h *hybridBuffer) Write(b []byte) (int, error) { 180 | buffered := h.buf.Len() 181 | var err error 182 | 183 | // Straight to memory 184 | if len(b)+buffered < _MAX_MEM_STORAGE { 185 | n, err := h.buf.Write(b) 186 | h.written += int64(n) 187 | return n, err 188 | } 189 | 190 | // Partially to disk 191 | if buffered < _MAX_MEM_STORAGE { 192 | mem := _MAX_MEM_STORAGE - buffered 193 | n, err := h.buf.Write(b[:mem]) 194 | h.written += int64(n) 195 | if err != nil { 196 | return n, err 197 | } 198 | if h.file == nil { 199 | h.file, err = ioutil.TempFile("", "spdy_content") 200 | if err != nil { 201 | return n, err 202 | } 203 | h.Reader = io.MultiReader(h.buf, h.file) 204 | } 205 | m, err := h.file.Write(b[mem:]) 206 | h.written += int64(m) 207 | return m + n, err 208 | } 209 | 210 | // Fully to disk 211 | if h.file == nil { 212 | h.file, err = ioutil.TempFile("", "spdy_content") 213 | if err != nil { 214 | return 0, err 215 | } 216 | h.Reader = io.MultiReader(h.buf, h.file) 217 | } 218 | n, err := h.file.Write(b) 219 | h.written += int64(n) 220 | return n, err 221 | } 222 | 223 | // unrequestedGzip returns true iff the request did 224 | // not ask for the returned content encoding and that 225 | // encoding is gzip or deflate, which is allowed in 226 | // the SPDY spec. 227 | func unrequestedGzip(r *Response) bool { 228 | got := r.Header.Get("Content-Encoding") 229 | switch got { 230 | case "gzip", "deflate": 231 | default: 232 | return false 233 | } 234 | 235 | requested := r.Request.Header.Get("Accept-Encoding") 236 | return !strings.Contains(requested, got) 237 | } 238 | 239 | // gzipReader wraps a response body so it can lazily 240 | // call gzip.NewReader on the first call to Read 241 | type gzipReader struct { 242 | body io.ReadCloser // underlying Response.Body 243 | zr io.Reader // lazily-initialized gzip reader 244 | } 245 | 246 | func (gz *gzipReader) Read(p []byte) (n int, err error) { 247 | if gz.zr == nil { 248 | gz.zr, err = gzip.NewReader(gz.body) 249 | if err != nil { 250 | return 0, err 251 | } 252 | } 253 | return gz.zr.Read(p) 254 | } 255 | 256 | func (gz *gzipReader) Close() error { 257 | return gz.body.Close() 258 | } 259 | -------------------------------------------------------------------------------- /common/stream_state.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "sync" 9 | ) 10 | 11 | // State variables used internally in StreamState. 12 | const ( 13 | stateOpen uint8 = iota 14 | stateHalfClosedHere 15 | stateHalfClosedThere 16 | stateClosed 17 | ) 18 | 19 | // StreamState is used to store and query the stream's state. The active methods 20 | // do not directly affect the stream's state, but it will use that information 21 | // to effect the changes. 22 | type StreamState struct { 23 | l sync.Mutex 24 | s uint8 25 | } 26 | 27 | // Check whether the stream is open. 28 | func (s *StreamState) Open() bool { 29 | s.l.Lock() 30 | open := s.s == stateOpen 31 | s.l.Unlock() 32 | return open 33 | } 34 | 35 | // Check whether the stream is closed. 36 | func (s *StreamState) Closed() bool { 37 | s.l.Lock() 38 | closed := s.s == stateClosed 39 | s.l.Unlock() 40 | return closed 41 | } 42 | 43 | // Check whether the stream is half-closed at the other endpoint. 44 | func (s *StreamState) ClosedThere() bool { 45 | s.l.Lock() 46 | closedThere := s.s == stateClosed || s.s == stateHalfClosedThere 47 | s.l.Unlock() 48 | return closedThere 49 | } 50 | 51 | // Check whether the stream is open at the other endpoint. 52 | func (s *StreamState) OpenThere() bool { 53 | return !s.ClosedThere() 54 | } 55 | 56 | // Check whether the stream is half-closed at the other endpoint. 57 | func (s *StreamState) ClosedHere() bool { 58 | s.l.Lock() 59 | closedHere := s.s == stateClosed || s.s == stateHalfClosedHere 60 | s.l.Unlock() 61 | return closedHere 62 | } 63 | 64 | // Check whether the stream is open locally. 65 | func (s *StreamState) OpenHere() bool { 66 | return !s.ClosedHere() 67 | } 68 | 69 | // Closes the stream. 70 | func (s *StreamState) Close() { 71 | s.l.Lock() 72 | s.s = stateClosed 73 | s.l.Unlock() 74 | } 75 | 76 | // Half-close the stream locally. 77 | func (s *StreamState) CloseHere() { 78 | s.l.Lock() 79 | if s.s == stateOpen { 80 | s.s = stateHalfClosedHere 81 | } else if s.s == stateHalfClosedThere { 82 | s.s = stateClosed 83 | } 84 | s.l.Unlock() 85 | } 86 | 87 | // Half-close the stream at the other endpoint. 88 | func (s *StreamState) CloseThere() { 89 | s.l.Lock() 90 | if s.s == stateOpen { 91 | s.s = stateHalfClosedThere 92 | } else if s.s == stateHalfClosedHere { 93 | s.s = stateClosed 94 | } 95 | s.l.Unlock() 96 | } 97 | 98 | // State description. 99 | func (s *StreamState) String() string { 100 | var str string 101 | if s.OpenHere() { 102 | str = "open here, " 103 | } else { 104 | str = "closed here, " 105 | } 106 | if s.OpenThere() { 107 | str += "open there" 108 | } else { 109 | str += "closed there" 110 | } 111 | return str 112 | } 113 | -------------------------------------------------------------------------------- /common/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "fmt" 9 | "sort" 10 | ) 11 | 12 | /************ 13 | * StreamID * 14 | ************/ 15 | 16 | // StreamID is the unique identifier for a single SPDY stream. 17 | type StreamID uint32 18 | 19 | func (s StreamID) B1() byte { 20 | return byte(s >> 24) 21 | } 22 | 23 | func (s StreamID) B2() byte { 24 | return byte(s >> 16) 25 | } 26 | 27 | func (s StreamID) B3() byte { 28 | return byte(s >> 8) 29 | } 30 | 31 | func (s StreamID) B4() byte { 32 | return byte(s) 33 | } 34 | 35 | // Client indicates whether the ID should belong to a client-sent stream. 36 | func (s StreamID) Client() bool { 37 | return s != 0 && s&1 != 0 38 | } 39 | 40 | // Server indicates whether the ID should belong to a server-sent stream. 41 | func (s StreamID) Server() bool { 42 | return s != 0 && s&1 == 0 43 | } 44 | 45 | // Valid indicates whether the ID is in the range of legal values (including 0). 46 | func (s StreamID) Valid() bool { 47 | return s <= MAX_STREAM_ID 48 | } 49 | 50 | // Zero indicates whether the ID is zero. 51 | func (s StreamID) Zero() bool { 52 | return s == 0 53 | } 54 | 55 | /********* 56 | * Flags * 57 | *********/ 58 | 59 | // Flags represent a frame's Flags. 60 | type Flags byte 61 | 62 | // CLEAR_SETTINGS indicates whether the CLEAR_SETTINGS 63 | // flag is set. 64 | func (f Flags) CLEAR_SETTINGS() bool { 65 | return f&FLAG_SETTINGS_CLEAR_SETTINGS != 0 66 | } 67 | 68 | // FIN indicates whether the FIN flag is set. 69 | func (f Flags) FIN() bool { 70 | return f&FLAG_FIN != 0 71 | } 72 | 73 | // PERSIST_VALUE indicates whether the PERSIST_VALUE 74 | // flag is set. 75 | func (f Flags) PERSIST_VALUE() bool { 76 | return f&FLAG_SETTINGS_PERSIST_VALUE != 0 77 | } 78 | 79 | // PERSISTED indicates whether the PERSISTED flag is 80 | // set. 81 | func (f Flags) PERSISTED() bool { 82 | return f&FLAG_SETTINGS_PERSISTED != 0 83 | } 84 | 85 | // UNIDIRECTIONAL indicates whether the UNIDIRECTIONAL 86 | // flag is set. 87 | func (f Flags) UNIDIRECTIONAL() bool { 88 | return f&FLAG_UNIDIRECTIONAL != 0 89 | } 90 | 91 | /************ 92 | * Priority * 93 | ************/ 94 | 95 | // Priority represents a stream's priority. 96 | type Priority byte 97 | 98 | // Byte returns the priority in binary form, adjusted 99 | // for the given SPDY version. 100 | func (p Priority) Byte(version uint16) byte { 101 | switch version { 102 | case 3: 103 | return byte((p & 7) << 5) 104 | case 2: 105 | return byte((p & 3) << 6) 106 | default: 107 | return 0 108 | } 109 | } 110 | 111 | // Valid indicates whether the priority is in the valid 112 | // range for the given SPDY version. 113 | func (p Priority) Valid(version uint16) bool { 114 | switch version { 115 | case 3: 116 | return p <= 7 117 | case 2: 118 | return p <= 3 119 | default: 120 | return false 121 | } 122 | } 123 | 124 | /************** 125 | * StatusCode * 126 | **************/ 127 | 128 | // StatusCode represents a status code sent in 129 | // certain SPDY frames, such as RST_STREAM and 130 | // GOAWAY. 131 | type StatusCode uint32 132 | 133 | func (r StatusCode) B1() byte { 134 | return byte(r >> 24) 135 | } 136 | 137 | func (r StatusCode) B2() byte { 138 | return byte(r >> 16) 139 | } 140 | 141 | func (r StatusCode) B3() byte { 142 | return byte(r >> 8) 143 | } 144 | 145 | func (r StatusCode) B4() byte { 146 | return byte(r) 147 | } 148 | 149 | // IsFatal returns a bool indicating 150 | // whether receiving the given status 151 | // code should end the connection. 152 | func (r StatusCode) IsFatal() bool { 153 | switch r { 154 | case RST_STREAM_PROTOCOL_ERROR: 155 | return true 156 | case RST_STREAM_INTERNAL_ERROR: 157 | return true 158 | case RST_STREAM_FRAME_TOO_LARGE: 159 | return true 160 | case RST_STREAM_UNSUPPORTED_VERSION: 161 | return true 162 | 163 | default: 164 | return false 165 | } 166 | } 167 | 168 | // String gives the StatusCode in text form. 169 | func (r StatusCode) String() string { 170 | return statusCodeText[r] 171 | } 172 | 173 | /************ 174 | * Settings * 175 | ************/ 176 | 177 | // Setting represents a single setting as sent 178 | // in a SPDY SETTINGS frame. 179 | type Setting struct { 180 | Flags Flags 181 | ID uint32 182 | Value uint32 183 | } 184 | 185 | // String gives the textual representation of a Setting. 186 | func (s *Setting) String() string { 187 | id := settingText[s.ID] + ":" 188 | Flags := "" 189 | if s.Flags.PERSIST_VALUE() { 190 | Flags += " FLAG_SETTINGS_PERSIST_VALUE" 191 | } 192 | if s.Flags.PERSISTED() { 193 | Flags += " FLAG_SETTINGS_PERSISTED" 194 | } 195 | if Flags == "" { 196 | Flags = "[NONE]" 197 | } else { 198 | Flags = Flags[1:] 199 | } 200 | 201 | return fmt.Sprintf("%-31s %-10d %s", id, s.Value, Flags) 202 | } 203 | 204 | // Settings represents a series of settings, stored in a map 205 | // by setting ID. This ensures that duplicate settings are 206 | // not sent, since the new value will replace the old. 207 | type Settings map[uint32]*Setting 208 | 209 | // Settings returns a slice of Setting, sorted into order by 210 | // ID, as in the SPDY specification. 211 | func (s Settings) Settings() []*Setting { 212 | if len(s) == 0 { 213 | return []*Setting{} 214 | } 215 | 216 | ids := make([]int, 0, len(s)) 217 | for id := range s { 218 | ids = append(ids, int(id)) 219 | } 220 | 221 | sort.Sort(sort.IntSlice(ids)) 222 | 223 | out := make([]*Setting, len(s)) 224 | 225 | for i, id := range ids { 226 | out[i] = s[uint32(id)] 227 | } 228 | 229 | return out 230 | } 231 | -------------------------------------------------------------------------------- /common/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "io" 9 | "net/http" 10 | ) 11 | 12 | // CloneHeader returns a duplicate of the provided Header. 13 | func CloneHeader(h http.Header) http.Header { 14 | h2 := make(http.Header, len(h)) 15 | for k, vv := range h { 16 | vv2 := make([]string, len(vv)) 17 | copy(vv2, vv) 18 | h2[k] = vv2 19 | } 20 | return h2 21 | } 22 | 23 | // UpdateHeader adds and new name/value pairs and replaces 24 | // those already existing in the older header. 25 | func UpdateHeader(older, newer http.Header) { 26 | for name, values := range newer { 27 | for i, value := range values { 28 | if i == 0 { 29 | older.Set(name, value) 30 | } else { 31 | older.Add(name, value) 32 | } 33 | } 34 | } 35 | } 36 | 37 | func BytesToUint16(b []byte) uint16 { 38 | return (uint16(b[0]) << 8) + uint16(b[1]) 39 | } 40 | 41 | func BytesToUint24(b []byte) uint32 { 42 | return (uint32(b[0]) << 16) + (uint32(b[1]) << 8) + uint32(b[2]) 43 | } 44 | 45 | func BytesToUint24Reverse(b []byte) uint32 { 46 | return (uint32(b[2]) << 16) + (uint32(b[1]) << 8) + uint32(b[0]) 47 | } 48 | 49 | func BytesToUint32(b []byte) uint32 { 50 | return (uint32(b[0]) << 24) + (uint32(b[1]) << 16) + (uint32(b[2]) << 8) + uint32(b[3]) 51 | } 52 | 53 | // ReadExactly is used to ensure that the given number of bytes 54 | // are read if possible, even if multiple calls to Read 55 | // are required. 56 | func ReadExactly(r io.Reader, i int) ([]byte, error) { 57 | out := make([]byte, i) 58 | in := out[:] 59 | for i > 0 { 60 | if r == nil { 61 | return nil, ErrConnNil 62 | } 63 | if n, err := r.Read(in); err != nil { 64 | return nil, err 65 | } else { 66 | in = in[n:] 67 | i -= n 68 | } 69 | } 70 | return out, nil 71 | } 72 | 73 | // WriteExactly is used to ensure that the given data is written 74 | // if possible, even if multiple calls to Write are 75 | // required. 76 | func WriteExactly(w io.Writer, data []byte) error { 77 | i := len(data) 78 | for i > 0 { 79 | if w == nil { 80 | return ErrConnNil 81 | } 82 | if n, err := w.Write(data); err != nil { 83 | return err 84 | } else { 85 | data = data[n:] 86 | i -= n 87 | } 88 | } 89 | return nil 90 | } 91 | 92 | // ReadCloser is a helper structure to allow 93 | // an io.Reader to satisfy the io.ReadCloser 94 | // interface. 95 | type ReadCloser struct { 96 | io.Reader 97 | } 98 | 99 | func (r *ReadCloser) Close() error { 100 | return nil 101 | } 102 | 103 | // ReadCounter is a helper structure for 104 | // keeping track of the number of bytes 105 | // read from an io.Reader 106 | type ReadCounter struct { 107 | N int64 108 | R io.Reader 109 | } 110 | 111 | func (r *ReadCounter) Read(b []byte) (n int, err error) { 112 | n, err = r.R.Read(b) 113 | r.N += int64(n) 114 | return 115 | } 116 | 117 | // WriteCounter is a helper structure for 118 | // keeping track of the number of bytes 119 | // written from an io.Writer 120 | type WriteCounter struct { 121 | N int64 122 | W io.Writer 123 | } 124 | 125 | func (w *WriteCounter) Write(b []byte) (n int, err error) { 126 | n, err = w.W.Write(b) 127 | w.N += int64(n) 128 | return 129 | } 130 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package spdy is a full-featured SPDY library for the Go language (still under very active development). 7 | 8 | Note that this implementation currently supports SPDY drafts 2 and 3, and support for SPDY/4, and HTTP/2.0 is upcoming. 9 | 10 | See examples for various simple examples that use the package. 11 | 12 | ------------------------------- 13 | 14 | Note that using this package with Martini (https://github.com/go-martini/martini) is likely to result 15 | in strange and hard-to-diagnose bugs. For more information, read http://stephensearles.com/?p=254. 16 | As a result, issues that arise when combining the two should be directed at the Martini developers. 17 | 18 | ------------------------------- 19 | 20 | Servers 21 | 22 | The following examples use features specific to SPDY. 23 | 24 | Just the handler is shown. 25 | 26 | Use SPDY's pinging features to test the connection: 27 | 28 | package main 29 | 30 | import ( 31 | "net/http" 32 | "time" 33 | 34 | "github.com/SlyMarbo/spdy" 35 | ) 36 | 37 | func Serve(w http.ResponseWriter, r *http.Request) { 38 | // Ping returns a channel which will send a bool. 39 | if ping, err := spdy.PingClient(w); err == nil { 40 | select { 41 | case _, ok := <- ping: 42 | if ok { 43 | // Connection is fine. 44 | } else { 45 | // Something went wrong. 46 | } 47 | 48 | case <-time.After(timeout): 49 | // Ping took too long. 50 | } 51 | } else { 52 | // Not SPDY. 53 | } 54 | 55 | // ... 56 | } 57 | 58 | 59 | Sending a server push: 60 | 61 | package main 62 | 63 | import ( 64 | "net/http" 65 | 66 | "github.com/SlyMarbo/spdy" 67 | ) 68 | 69 | func Serve(w http.ResponseWriter, r *http.Request) { 70 | // Push returns a separate http.ResponseWriter and an error. 71 | path := r.URL.Scheme + "://" + r.URL.Host + "/example.js" 72 | push, err := spdy.Push(path) 73 | if err != nil { 74 | // Not using SPDY. 75 | } 76 | http.ServeFile(push, r, "./content/example.js") 77 | 78 | // Note that a PushStream must be finished manually once 79 | // all writing has finished. 80 | push.Finish() 81 | 82 | // ... 83 | } 84 | 85 | */ 86 | package spdy 87 | -------------------------------------------------------------------------------- /examples/client/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "io/ioutil" 10 | "net/http" 11 | 12 | _ "github.com/SlyMarbo/spdy" // This adds SPDY support to net/http 13 | ) 14 | 15 | func main() { 16 | res, err := http.Get("https://example.com/") 17 | if err != nil { 18 | panic(err) 19 | } 20 | defer res.Body.Close() 21 | 22 | bytes, err := ioutil.ReadAll(res.Body) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | fmt.Printf("Received: %s\n", bytes) 28 | } 29 | -------------------------------------------------------------------------------- /examples/proxy_client/proxy_client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "crypto/tls" 9 | "net/http" 10 | 11 | "github.com/SlyMarbo/spdy" 12 | ) 13 | 14 | func handle(err error) { 15 | if err != nil { 16 | panic(err) 17 | } 18 | } 19 | 20 | func serveHTTP(w http.ResponseWriter, r *http.Request) { 21 | w.Write([]byte("Testing, testing, 1, 2, 3.")) 22 | } 23 | 24 | func main() { 25 | http.HandleFunc("/", serveHTTP) 26 | handle(spdy.ConnectAndServe("http://localhost:8080/", &tls.Config{InsecureSkipVerify: true}, nil)) 27 | } 28 | -------------------------------------------------------------------------------- /examples/proxy_server/proxy_server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "net/http" 12 | 13 | "github.com/SlyMarbo/spdy" 14 | ) 15 | 16 | func handle(err error) { 17 | if err != nil { 18 | panic(err) 19 | } 20 | } 21 | 22 | func handleProxy(conn spdy.Conn) { 23 | url := "http://" + conn.Conn().RemoteAddr().String() + "/" 24 | 25 | req, err := http.NewRequest("GET", url, nil) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | res, err := conn.RequestResponse(req, nil, 1) 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | buf := new(bytes.Buffer) 36 | _, err = io.Copy(buf, res.Body) 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | res.Body.Close() 42 | 43 | fmt.Println(buf.String()) 44 | } 45 | 46 | func main() { 47 | handler := spdy.ProxyConnHandlerFunc(handleProxy) 48 | http.Handle("/", spdy.ProxyConnections(handler)) 49 | handle(http.ListenAndServeTLS(":8080", "cert.pem", "key.pem", nil)) 50 | } 51 | -------------------------------------------------------------------------------- /examples/server/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "log" 9 | "net/http" 10 | 11 | "github.com/SlyMarbo/spdy" 12 | ) 13 | 14 | func httpHandler(w http.ResponseWriter, req *http.Request) { 15 | w.Header().Set("Content-Type", "text/plain") 16 | w.Write([]byte("This is an example server.\n")) 17 | } 18 | 19 | func main() { 20 | http.HandleFunc("/", httpHandler) 21 | log.Printf("About to listen on 10443. Go to https://127.0.0.1:10443/") 22 | err := spdy.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/spdy_only_server/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "log" 9 | "net/http" 10 | 11 | "github.com/SlyMarbo/spdy" 12 | ) 13 | 14 | func httpHandler(w http.ResponseWriter, req *http.Request) { 15 | w.Header().Set("Content-Type", "text/plain") 16 | w.Write([]byte("This is an example server.\n")) 17 | } 18 | 19 | func main() { 20 | http.HandleFunc("/", httpHandler) 21 | log.Printf("About to listen on 10443. Go to https://127.0.0.1:10443/") 22 | err := spdy.ListenAndServeSpdyOnly(":10443", "cert.pem", "key.pem", nil) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy 6 | 7 | import ( 8 | "io" 9 | logging "log" 10 | 11 | "github.com/SlyMarbo/spdy/common" 12 | ) 13 | 14 | var log = common.GetLogger() 15 | var debug = common.GetDebugLogger() 16 | 17 | // SetLogger sets the package's error logger. 18 | func SetLogger(l *logging.Logger) { 19 | common.SetLogger(l) 20 | } 21 | 22 | // SetLogOutput sets the output for the package's error logger. 23 | func SetLogOutput(w io.Writer) { 24 | common.SetLogOutput(w) 25 | } 26 | 27 | // SetDebugLogger sets the package's debug info logger. 28 | func SetDebugLogger(l *logging.Logger) { 29 | common.SetDebugLogger(l) 30 | } 31 | 32 | // SetDebugOutput sets the output for the package's debug info logger. 33 | func SetDebugOutput(w io.Writer) { 34 | common.SetDebugOutput(w) 35 | } 36 | 37 | // EnableDebugOutput sets the output for the package's debug info logger to os.Stdout. 38 | func EnableDebugOutput() { 39 | common.EnableDebugOutput() 40 | } 41 | -------------------------------------------------------------------------------- /proxy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy 6 | 7 | import ( 8 | "crypto/tls" 9 | "fmt" 10 | "net" 11 | "net/http" 12 | "net/http/httputil" 13 | "net/url" 14 | 15 | "github.com/SlyMarbo/spdy/common" 16 | ) 17 | 18 | // Connect is used to perform connection 19 | // reversal where the client (who is normally 20 | // behind a NAT of some kind) connects to a server 21 | // on the internet. The connection is then reversed 22 | // so that the 'server' sends requests to the 'client'. 23 | // See ConnectAndServe() for a blocking version of this 24 | func Connect(addr string, config *tls.Config, srv *http.Server) (Conn, error) { 25 | if config == nil { 26 | config = new(tls.Config) 27 | } 28 | if srv == nil { 29 | srv = &http.Server{Handler: http.DefaultServeMux} 30 | } 31 | AddSPDY(srv) 32 | 33 | u, err := url.Parse(addr) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | var conn net.Conn 39 | 40 | conn, err = tls.Dial("tcp", u.Host, config) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | req, err := http.NewRequest("GET", addr, nil) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | client := httputil.NewClientConn(conn, nil) 51 | err = client.Write(req) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | res, err := client.Read(req) 57 | if err != nil && err != httputil.ErrPersistEOF { 58 | fmt.Println(res) 59 | return nil, err 60 | } 61 | 62 | if res.StatusCode != http.StatusOK { 63 | log.Printf("Proxy responded with status code %d\n", res.StatusCode) 64 | return nil, common.ErrConnectFail 65 | } 66 | 67 | conn, _ = client.Hijack() 68 | 69 | server, err := NewServerConn(conn, srv, 3, 1) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return server, nil 75 | } 76 | 77 | // ConnectAndServe is used to perform connection 78 | // reversal. (See Connect() for more details.) 79 | // 80 | // This works very similarly to ListenAndServeTLS, 81 | // except that addr and config are used to connect 82 | // to the client. If srv is nil, a new http.Server 83 | // is used, with http.DefaultServeMux as the handler. 84 | func ConnectAndServe(addr string, config *tls.Config, srv *http.Server) error { 85 | server, err := Connect(addr, config, srv) 86 | if err != nil { 87 | return err 88 | } 89 | 90 | return server.Run() 91 | } 92 | 93 | type ProxyConnHandler interface { 94 | ProxyConnHandle(Conn) 95 | } 96 | 97 | type ProxyConnHandlerFunc func(Conn) 98 | 99 | func (p ProxyConnHandlerFunc) ProxyConnHandle(c Conn) { 100 | p(c) 101 | } 102 | 103 | type proxyHandler struct { 104 | ProxyConnHandler 105 | } 106 | 107 | func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 108 | r.Body.Close() 109 | 110 | conn, _, err := w.(http.Hijacker).Hijack() 111 | if err != nil { 112 | log.Println("Failed to hijack connection in ProxyConnections.", err) 113 | return 114 | } 115 | 116 | defer conn.Close() 117 | 118 | if _, ok := conn.(*tls.Conn); !ok { 119 | log.Println("Recieved a non-TLS connection in ProxyConnections.") 120 | return 121 | } 122 | 123 | // Send the connection accepted response. 124 | res := new(http.Response) 125 | res.Status = "200 Connection Established" 126 | res.StatusCode = http.StatusOK 127 | res.Proto = "HTTP/1.1" 128 | res.ProtoMajor = 1 129 | res.ProtoMinor = 1 130 | if err = res.Write(conn); err != nil { 131 | log.Println("Failed to send connection established message in ProxyConnections.", err) 132 | return 133 | } 134 | 135 | client, err := NewClientConn(conn, nil, 3, 1) 136 | if err != nil { 137 | log.Println("Error creating SPDY connection in ProxyConnections.", err) 138 | return 139 | } 140 | 141 | go client.Run() 142 | 143 | // Call user code. 144 | p.ProxyConnHandle(client) 145 | 146 | client.Close() 147 | } 148 | 149 | // ProxyConnections is used with ConnectAndServe in connection- 150 | // reversing proxies. This returns an http.Handler which will call 151 | // handler each time a client connects. The call is treated as 152 | // an event loop and the connection may be terminated if the call 153 | // returns. The returned Handler should then be used in a normal 154 | // HTTP server, like the following: 155 | // 156 | // package main 157 | // 158 | // import ( 159 | // "net/http" 160 | // 161 | // "github.com/SlyMarbo/spdy" 162 | // ) 163 | // 164 | // func handleProxy(conn spdy.Conn) { 165 | // // make requests... 166 | // } 167 | // 168 | // func main() { 169 | // handler := spdy.ProxyConnHandlerFunc(handleProxy) 170 | // http.Handle("/", spdy.ProxyConnections(handler)) 171 | // http.ListenAndServeTLS(":80", "cert.pem", "key.pem", nil) 172 | // } 173 | // 174 | // Use Conn.Request to make requests to the client and Conn.Conn 175 | // to access the underlying connection for further details like 176 | // the client's address. 177 | func ProxyConnections(handler ProxyConnHandler) http.Handler { 178 | return proxyHandler{handler} 179 | } 180 | -------------------------------------------------------------------------------- /proxy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy_test 6 | 7 | import ( 8 | "bytes" 9 | "crypto/tls" 10 | "fmt" 11 | "io/ioutil" 12 | "net" 13 | "net/http" 14 | "testing" 15 | "time" 16 | 17 | "github.com/SlyMarbo/spdy" 18 | ) 19 | 20 | func init() { 21 | // spdy.EnableDebugOutput() 22 | } 23 | 24 | func TestProxyConnect(t *testing.T) { 25 | cert, err := tls.X509KeyPair(localhostCert, localhostKey) 26 | if err != nil { 27 | panic(fmt.Sprintf("could not read certificate: %v", err)) 28 | } 29 | 30 | serverTLSConfig := new(tls.Config) 31 | serverTLSConfig.Certificates = []tls.Certificate{cert} 32 | 33 | conn, err := net.Listen("tcp", "localhost:0") 34 | if err != nil { 35 | panic(fmt.Sprintf("could not listen: %v", err)) 36 | } 37 | 38 | listener := tls.NewListener(conn, serverTLSConfig) 39 | 40 | errChan := make(chan error) 41 | 42 | go func() { 43 | srv := &http.Server{ 44 | Addr: conn.Addr().String(), 45 | Handler: spdy.ProxyConnections(spdy.ProxyConnHandlerFunc(func(conn spdy.Conn) { 46 | req, err := http.NewRequest("GET", "http://example.com/", nil) 47 | if err != nil { 48 | errChan <- err 49 | return 50 | } 51 | resp, err := conn.RequestResponse(req, nil, 2) 52 | if err != nil { 53 | errChan <- err 54 | return 55 | } 56 | body, err := ioutil.ReadAll(resp.Body) 57 | if err != nil { 58 | errChan <- err 59 | return 60 | } 61 | 62 | if !bytes.Equal(body, []byte("HELLO")) { 63 | errChan <- fmt.Errorf("Expected HELLO. Got %v", string(body)) 64 | return 65 | } 66 | 67 | close(errChan) 68 | })), 69 | } 70 | srv.Serve(listener) 71 | println("Serve done") 72 | }() 73 | 74 | clientTLSConfig := &tls.Config{InsecureSkipVerify: true} 75 | 76 | url := "https://" + conn.Addr().String() 77 | 78 | go func() { 79 | err = spdy.ConnectAndServe(url, clientTLSConfig, &http.Server{ 80 | Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 81 | if r.Method != "GET" { 82 | errChan <- fmt.Errorf("Expected method GET. Got: %v", r.Method) 83 | } 84 | if r.URL.String() != "http://example.com/" { 85 | errChan <- fmt.Errorf("Expected http://example.com. Got %v", r.URL) 86 | } 87 | w.Write([]byte("HELLO")) 88 | }), 89 | }) 90 | if err != nil { 91 | errChan <- fmt.Errorf("ConnectAndServeFailed: %v", err) 92 | } 93 | }() 94 | 95 | select { 96 | case err = <-errChan: 97 | if err != nil { 98 | t.Error(err) 99 | } 100 | case <-time.After(time.Second): 101 | t.Error("Timeout") 102 | } 103 | } 104 | 105 | // localhostCert is a PEM-encoded TLS cert with SAN IPs 106 | // "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end 107 | // of ASN.1 time). 108 | // generated from src/pkg/crypto/tls: 109 | // go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h 110 | var localhostCert = []byte(`-----BEGIN CERTIFICATE----- 111 | MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD 112 | bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj 113 | bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa 114 | IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA 115 | AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud 116 | EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA 117 | AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk 118 | Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA== 119 | -----END CERTIFICATE-----`) 120 | 121 | // localhostKey is the private key for localhostCert. 122 | var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- 123 | MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0 124 | 0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV 125 | NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d 126 | AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW 127 | MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD 128 | EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA 129 | 1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE= 130 | -----END RSA PRIVATE KEY-----`) 131 | -------------------------------------------------------------------------------- /regression_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy_test 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "io/ioutil" 11 | "net/http" 12 | "testing" 13 | 14 | "github.com/SlyMarbo/spdy/common" 15 | ) 16 | 17 | func issue82handler(n int64) http.HandlerFunc { 18 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 19 | w.Header().Set("Content-Type", "text/plain") 20 | w.Write(bytes.Repeat([]byte{'X'}, int(n))) 21 | }) 22 | } 23 | 24 | func TestIssue82(t *testing.T) { 25 | var max int64 = common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE 26 | tests := []struct { 27 | Name string 28 | Path string 29 | Msg string 30 | Want int64 31 | }{ 32 | {"Issue 82", "/1", "Sending less than window size", max - 16}, 33 | {"Issue 82", "/2", "Sending just under window size", max - 15}, 34 | {"Issue 82", "/3", "Sending exactly window size", max}, 35 | {"Issue 82", "/4", "Sending more than window size", max + 16}, 36 | } 37 | 38 | // start server 39 | mux := http.NewServeMux() 40 | for _, test := range tests { 41 | mux.HandleFunc(test.Path, issue82handler(test.Want)) 42 | } 43 | 44 | srv := newServer(mux) 45 | defer srv.Close() 46 | 47 | client := newClient() 48 | 49 | for _, test := range tests { 50 | r, err := client.Get(srv.URL + test.Path) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | 55 | n, err := io.Copy(ioutil.Discard, r.Body) 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | if err = r.Body.Close(); err != nil { 61 | t.Fatal(err) 62 | } 63 | 64 | if n != test.Want { 65 | t.Errorf("%s: %s, got %d, expected %d", test.Name, test.Msg, n, test.Want) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy 6 | 7 | import ( 8 | "crypto/tls" 9 | "errors" 10 | "net" 11 | "net/http" 12 | "time" 13 | 14 | "github.com/SlyMarbo/spdy/common" 15 | "github.com/SlyMarbo/spdy/spdy2" 16 | "github.com/SlyMarbo/spdy/spdy3" 17 | ) 18 | 19 | // NewServerConn is used to create a SPDY connection, using the given 20 | // net.Conn for the underlying connection, and the given http.Server to 21 | // configure the request serving. 22 | func NewServerConn(conn net.Conn, server *http.Server, version, subversion int) (common.Conn, error) { 23 | if conn == nil { 24 | return nil, errors.New("Error: Connection initialised with nil net.conn.") 25 | } 26 | if server == nil { 27 | return nil, errors.New("Error: Connection initialised with nil server.") 28 | } 29 | 30 | switch version { 31 | case 3: 32 | return spdy3.NewConn(conn, server, subversion), nil 33 | 34 | case 2: 35 | return spdy2.NewConn(conn, server), nil 36 | 37 | default: 38 | return nil, errors.New("Error: Unsupported SPDY version.") 39 | } 40 | } 41 | 42 | // ListenAndServeTLS listens on the TCP network address addr 43 | // and then calls Serve with handler to handle requests on 44 | // incoming connections. Handler is typically nil, in which 45 | // case the DefaultServeMux is used. Additionally, files 46 | // containing a certificate and matching private key for the 47 | // server must be provided. If the certificate is signed by 48 | // a certificate authority, the certFile should be the 49 | // concatenation of the server's certificate followed by the 50 | // CA's certificate. 51 | // 52 | // See examples/server/server.go for a simple example server. 53 | func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error { 54 | npnStrings := npn() 55 | server := &http.Server{ 56 | Addr: addr, 57 | Handler: handler, 58 | TLSConfig: &tls.Config{ 59 | NextProtos: npnStrings, 60 | }, 61 | TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), 62 | } 63 | 64 | for _, str := range npnStrings { 65 | switch str { 66 | case "spdy/2": 67 | server.TLSNextProto[str] = spdy2.NextProto 68 | case "spdy/3": 69 | server.TLSNextProto[str] = spdy3.NextProto 70 | case "spdy/3.1": 71 | server.TLSNextProto[str] = spdy3.NextProto1 72 | } 73 | } 74 | 75 | return server.ListenAndServeTLS(certFile, keyFile) 76 | } 77 | 78 | // ListenAndServeSpdyOnly listens on the TCP network address addr 79 | // and then calls Serve with handler to handle requests on 80 | // incoming connections. Handler is typically nil, in which 81 | // case the DefaultServeMux is used. Additionally, files 82 | // containing a certificate and matching private key for the 83 | // server must be provided. If the certificate is signed by 84 | // a certificate authority, the certFile should be the 85 | // concatenation of the server's certificate followed by the 86 | // CA's certificate. 87 | // 88 | // IMPORTANT NOTE: Unlike spdy.ListenAndServeTLS, this function 89 | // will ONLY serve SPDY. HTTPS requests are refused. 90 | // 91 | // See examples/spdy_only_server/server.go for a simple example server. 92 | func ListenAndServeSpdyOnly(addr string, certFile string, keyFile string, handler http.Handler) error { 93 | npnStrings := npn() 94 | if addr == "" { 95 | addr = ":https" 96 | } 97 | if handler == nil { 98 | handler = http.DefaultServeMux 99 | } 100 | server := &http.Server{ 101 | Addr: addr, 102 | Handler: handler, 103 | TLSConfig: &tls.Config{ 104 | NextProtos: npnStrings, 105 | Certificates: make([]tls.Certificate, 1), 106 | }, 107 | TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), 108 | } 109 | 110 | for _, str := range npnStrings { 111 | switch str { 112 | case "spdy/2": 113 | server.TLSNextProto[str] = spdy2.NextProto 114 | case "spdy/3": 115 | server.TLSNextProto[str] = spdy3.NextProto 116 | case "spdy/3.1": 117 | server.TLSNextProto[str] = spdy3.NextProto1 118 | } 119 | } 120 | 121 | var err error 122 | server.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) 123 | if err != nil { 124 | return err 125 | } 126 | 127 | conn, err := net.Listen("tcp", addr) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | tlsListener := tls.NewListener(conn, server.TLSConfig) 133 | defer tlsListener.Close() 134 | 135 | // Main loop 136 | var tempDelay time.Duration 137 | for { 138 | rw, e := tlsListener.Accept() 139 | if e != nil { 140 | if ne, ok := e.(net.Error); ok && ne.Temporary() { 141 | if tempDelay == 0 { 142 | tempDelay = 5 * time.Millisecond 143 | } else { 144 | tempDelay *= 2 145 | } 146 | if max := 1 * time.Second; tempDelay > max { 147 | tempDelay = max 148 | } 149 | log.Printf("Accept error: %v; retrying in %v", e, tempDelay) 150 | time.Sleep(tempDelay) 151 | continue 152 | } 153 | return e 154 | } 155 | tempDelay = 0 156 | go serveSPDY(rw, server) 157 | } 158 | } 159 | 160 | // ListenAndServeSPDYNoNPN creates a server that listens exclusively 161 | // for SPDY and (unlike the rest of the package) will not support 162 | // HTTPS. 163 | func ListenAndServeSPDYNoNPN(addr string, certFile string, keyFile string, handler http.Handler, version, subversion int) error { 164 | if addr == "" { 165 | addr = ":https" 166 | } 167 | if handler == nil { 168 | handler = http.DefaultServeMux 169 | } 170 | server := &http.Server{ 171 | Addr: addr, 172 | Handler: handler, 173 | TLSConfig: &tls.Config{ 174 | Certificates: make([]tls.Certificate, 1), 175 | }, 176 | } 177 | 178 | var err error 179 | server.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) 180 | if err != nil { 181 | return err 182 | } 183 | 184 | conn, err := net.Listen("tcp", addr) 185 | if err != nil { 186 | return err 187 | } 188 | 189 | tlsListener := tls.NewListener(conn, server.TLSConfig) 190 | defer tlsListener.Close() 191 | 192 | // Main loop 193 | var tempDelay time.Duration 194 | for { 195 | rw, e := tlsListener.Accept() 196 | if e != nil { 197 | if ne, ok := e.(net.Error); ok && ne.Temporary() { 198 | if tempDelay == 0 { 199 | tempDelay = 5 * time.Millisecond 200 | } else { 201 | tempDelay *= 2 202 | } 203 | if max := 1 * time.Second; tempDelay > max { 204 | tempDelay = max 205 | } 206 | log.Printf("Accept error: %v; retrying in %v", e, tempDelay) 207 | time.Sleep(tempDelay) 208 | continue 209 | } 210 | return e 211 | } 212 | tempDelay = 0 213 | go serveSPDYNoNPN(rw, server, version, subversion) 214 | } 215 | } 216 | 217 | func serveSPDY(conn net.Conn, srv *http.Server) { 218 | defer common.Recover() 219 | 220 | tlsConn, ok := conn.(*tls.Conn) 221 | if !ok { // Only allow TLS connections. 222 | return 223 | } 224 | 225 | if d := srv.ReadTimeout; d != 0 { 226 | conn.SetReadDeadline(time.Now().Add(d)) 227 | } 228 | if d := srv.WriteTimeout; d != 0 { 229 | conn.SetWriteDeadline(time.Now().Add(d)) 230 | } 231 | if err := tlsConn.Handshake(); err != nil { 232 | return 233 | } 234 | 235 | tlsState := new(tls.ConnectionState) 236 | *tlsState = tlsConn.ConnectionState() 237 | proto := tlsState.NegotiatedProtocol 238 | if fn := srv.TLSNextProto[proto]; fn != nil { 239 | fn(srv, tlsConn, nil) 240 | } 241 | return 242 | } 243 | 244 | func serveSPDYNoNPN(conn net.Conn, srv *http.Server, version, subversion int) { 245 | defer common.Recover() 246 | 247 | tlsConn, ok := conn.(*tls.Conn) 248 | if !ok { // Only allow TLS connections. 249 | return 250 | } 251 | 252 | if d := srv.ReadTimeout; d != 0 { 253 | conn.SetReadDeadline(time.Now().Add(d)) 254 | } 255 | if d := srv.WriteTimeout; d != 0 { 256 | conn.SetWriteDeadline(time.Now().Add(d)) 257 | } 258 | if err := tlsConn.Handshake(); err != nil { 259 | return 260 | } 261 | 262 | serverConn, err := NewServerConn(tlsConn, srv, version, subversion) 263 | if err != nil { 264 | log.Println(err) 265 | return 266 | } 267 | serverConn.Run() 268 | } 269 | -------------------------------------------------------------------------------- /shared_interfaces.go: -------------------------------------------------------------------------------- 1 | package spdy 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "net/http" 7 | 8 | "github.com/SlyMarbo/spdy/common" 9 | "github.com/SlyMarbo/spdy/spdy2" 10 | "github.com/SlyMarbo/spdy/spdy3" 11 | ) 12 | 13 | // Connection represents a SPDY connection. The connection should 14 | // be started with a call to Run, which will return once the 15 | // connection has been terminated. The connection can be ended 16 | // early by using Close. 17 | type Conn interface { 18 | http.CloseNotifier 19 | Close() error 20 | Conn() net.Conn 21 | Request(request *http.Request, receiver common.Receiver, priority common.Priority) (common.Stream, error) 22 | RequestResponse(request *http.Request, receiver common.Receiver, priority common.Priority) (*http.Response, error) 23 | Run() error 24 | } 25 | 26 | var _ = Conn(&spdy2.Conn{}) 27 | var _ = Conn(&spdy3.Conn{}) 28 | 29 | // Stream contains a single SPDY stream. 30 | type Stream interface { 31 | http.CloseNotifier 32 | http.ResponseWriter 33 | Close() error 34 | Conn() common.Conn 35 | ReceiveFrame(common.Frame) error 36 | Run() error 37 | State() *common.StreamState 38 | StreamID() common.StreamID 39 | } 40 | 41 | var _ = Stream(&spdy2.PushStream{}) 42 | var _ = Stream(&spdy2.RequestStream{}) 43 | var _ = Stream(&spdy2.ResponseStream{}) 44 | var _ = Stream(&spdy3.PushStream{}) 45 | var _ = Stream(&spdy3.RequestStream{}) 46 | var _ = Stream(&spdy3.ResponseStream{}) 47 | 48 | // PriorityStream represents a SPDY stream with a priority. 49 | type PriorityStream interface { 50 | Stream 51 | 52 | // Priority returns the stream's 53 | // priority. 54 | Priority() common.Priority 55 | } 56 | 57 | var _ = PriorityStream(&spdy2.ResponseStream{}) 58 | var _ = PriorityStream(&spdy3.ResponseStream{}) 59 | 60 | // Compressor is used to compress the text header of a SPDY frame. 61 | type Compressor interface { 62 | io.Closer 63 | Compress(http.Header) ([]byte, error) 64 | } 65 | 66 | // Decompressor is used to decompress the text header of a SPDY frame. 67 | type Decompressor interface { 68 | Decompress([]byte) (http.Header, error) 69 | } 70 | 71 | // Pinger represents something able to send and 72 | // receive PING frames. 73 | type Pinger interface { 74 | Ping() (<-chan bool, error) 75 | } 76 | 77 | var _ = Pinger(&spdy2.Conn{}) 78 | var _ = Pinger(&spdy3.Conn{}) 79 | 80 | // Pusher represents something able to send 81 | // server puhes. 82 | type Pusher interface { 83 | Push(url string, origin common.Stream) (common.PushStream, error) 84 | } 85 | 86 | var _ = Pusher(&spdy2.Conn{}) 87 | var _ = Pusher(&spdy3.Conn{}) 88 | 89 | // SetFlowController represents a connection 90 | // which can have its flow control mechanism 91 | // customised. 92 | type SetFlowController interface { 93 | SetFlowControl(common.FlowControl) 94 | } 95 | 96 | var _ = SetFlowController(&spdy3.Conn{}) 97 | -------------------------------------------------------------------------------- /spdy2/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy2 6 | 7 | import ( 8 | "bufio" 9 | "crypto/tls" 10 | "net" 11 | "net/http" 12 | "net/url" 13 | "sync" 14 | "time" 15 | 16 | "github.com/SlyMarbo/spdy/common" 17 | "github.com/SlyMarbo/spdy/spdy2/frames" 18 | ) 19 | 20 | // Conn is a spdy.Conn implementing SPDY/2. This is used in both 21 | // servers and clients, and is created with either NewServerConn, 22 | // or NewClientConn. 23 | type Conn struct { 24 | PushReceiver common.Receiver // Receiver to call for server Pushes. 25 | 26 | // network state 27 | remoteAddr string 28 | server *http.Server // nil if client connection. 29 | conn net.Conn // underlying network (TLS) connection. 30 | connLock sync.Mutex // protects the interface value of the above conn. 31 | buf *bufio.Reader // buffered reader on conn. 32 | tlsState *tls.ConnectionState // underlying TLS connection state. 33 | streams map[common.StreamID]common.Stream // map of active streams. 34 | streamsLock sync.Mutex // protects streams. 35 | output [8]chan common.Frame // one output channel per priority level. 36 | 37 | // other state 38 | compressor common.Compressor // outbound compression state. 39 | decompressor common.Decompressor // inbound decompression state. 40 | receivedSettings common.Settings // settings sent by client. 41 | goawayReceived bool // goaway has been received. 42 | goawaySent bool // goaway has been sent. 43 | goawayLock sync.Mutex // protects goawaySent and goawayReceived. 44 | numBenignErrors int // number of non-serious errors encountered. 45 | readTimeout time.Duration // optional timeout for network reads. 46 | writeTimeout time.Duration // optional timeout for network writes. 47 | timeoutLock sync.Mutex // protects changes to readTimeout and writeTimeout. 48 | 49 | // SPDY features 50 | pings map[uint32]chan<- bool // response channel for pings. 51 | pingsLock sync.Mutex // protects pings. 52 | nextPingID uint32 // next outbound ping ID. 53 | nextPingIDLock sync.Mutex // protects nextPingID. 54 | pushStreamLimit *common.StreamLimit // Limit on streams started by the server. 55 | pushRequests map[common.StreamID]*http.Request // map of requests sent in server pushes. 56 | lastPushStreamID common.StreamID // last push stream ID. (even) 57 | lastPushStreamIDLock sync.Mutex // protects lastPushStreamID. 58 | pushedResources map[common.Stream]map[string]struct{} // prevents duplicate headers being pushed. 59 | 60 | // requests 61 | lastRequestStreamID common.StreamID // last request stream ID. (odd) 62 | lastRequestStreamIDLock sync.Mutex // protects lastRequestStreamID. 63 | streamCreation sync.Mutex // ensures new streams are sent in order. 64 | oddity common.StreamID // whether locally-sent streams are odd or even. 65 | initialWindowSize uint32 // initial transport window. 66 | initialWindowSizeLock sync.Mutex // lock for initialWindowSize 67 | requestStreamLimit *common.StreamLimit // Limit on streams started by the client. 68 | 69 | // startup and shutdown 70 | stop chan bool // this channel is closed when the connection closes. 71 | sending chan struct{} // this channel is used to ensure pending frames are sent. 72 | sendingLock sync.Mutex // protects changes to sending's value. 73 | init func() // this function is called before the connection begins. 74 | shutdownOnce sync.Once // used to ensure clean shutdown. 75 | shutdownError error // error that caused shutdown if non-nil 76 | } 77 | 78 | // NewConn produces an initialised spdy3 connection. 79 | func NewConn(conn net.Conn, server *http.Server) *Conn { 80 | out := new(Conn) 81 | 82 | // Common ground. 83 | out.remoteAddr = conn.RemoteAddr().String() 84 | out.server = server 85 | out.conn = conn 86 | out.buf = bufio.NewReader(conn) 87 | if tlsConn, ok := conn.(*tls.Conn); ok { 88 | out.tlsState = new(tls.ConnectionState) 89 | *out.tlsState = tlsConn.ConnectionState() 90 | } 91 | out.streams = make(map[common.StreamID]common.Stream) 92 | out.output[0] = make(chan common.Frame) 93 | out.output[1] = make(chan common.Frame) 94 | out.output[2] = make(chan common.Frame) 95 | out.output[3] = make(chan common.Frame) 96 | out.output[4] = make(chan common.Frame) 97 | out.output[5] = make(chan common.Frame) 98 | out.output[6] = make(chan common.Frame) 99 | out.output[7] = make(chan common.Frame) 100 | out.pings = make(map[uint32]chan<- bool) 101 | out.compressor = common.NewCompressor(2) 102 | out.decompressor = common.NewDecompressor(2) 103 | out.receivedSettings = make(common.Settings) 104 | out.lastPushStreamID = 0 105 | out.lastRequestStreamID = 0 106 | out.stop = make(chan bool) 107 | 108 | // Server/client specific. 109 | if server != nil { // servers 110 | out.nextPingID = 2 111 | out.oddity = 0 112 | out.initialWindowSize = common.DEFAULT_INITIAL_WINDOW_SIZE 113 | out.requestStreamLimit = common.NewStreamLimit(common.DEFAULT_STREAM_LIMIT) 114 | out.pushStreamLimit = common.NewStreamLimit(common.NO_STREAM_LIMIT) 115 | out.init = func() { 116 | // Initialise the connection by sending the connection settings. 117 | settings := new(frames.SETTINGS) 118 | settings.Settings = defaultServerSettings(common.DEFAULT_STREAM_LIMIT) 119 | out.output[0] <- settings 120 | } 121 | if d := server.ReadTimeout; d != 0 { 122 | out.SetReadTimeout(d) 123 | } 124 | if d := server.WriteTimeout; d != 0 { 125 | out.SetWriteTimeout(d) 126 | } 127 | out.pushedResources = make(map[common.Stream]map[string]struct{}) 128 | 129 | } else { // clients 130 | out.nextPingID = 1 131 | out.oddity = 1 132 | out.initialWindowSize = common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE 133 | out.requestStreamLimit = common.NewStreamLimit(common.NO_STREAM_LIMIT) 134 | out.pushStreamLimit = common.NewStreamLimit(common.DEFAULT_STREAM_LIMIT) 135 | out.pushRequests = make(map[common.StreamID]*http.Request) 136 | out.init = func() { 137 | // Initialise the connection by sending the connection settings. 138 | settings := new(frames.SETTINGS) 139 | settings.Settings = defaultClientSettings(common.DEFAULT_STREAM_LIMIT) 140 | out.output[0] <- settings 141 | } 142 | } 143 | return out 144 | } 145 | 146 | // NextProto is intended for use in http.Server.TLSNextProto, 147 | // using SPDY/2 for the connection. 148 | func NextProto(s *http.Server, tlsConn *tls.Conn, handler http.Handler) { 149 | NewConn(tlsConn, s).Run() 150 | } 151 | 152 | func (c *Conn) Run() error { 153 | defer common.Recover() 154 | go c.send() // Start the send loop. 155 | if c.init != nil { // Must be after sending is enabled. 156 | c.init() // Prepare any initialisation frames. 157 | } 158 | go c.readFrames() // Start the main loop. 159 | <-c.stop // Run until the connection ends. 160 | return nil 161 | } 162 | 163 | // newStream is used to create a new serverStream from a SYN_STREAM frame. 164 | func (c *Conn) newStream(frame *frames.SYN_STREAM) *ResponseStream { 165 | header := frame.Header 166 | rawUrl := header.Get("scheme") + "://" + header.Get("host") + header.Get("url") 167 | 168 | url, err := url.Parse(rawUrl) 169 | if c.check(err != nil, "Received SYN_STREAM with invalid request URL (%v)", err) { 170 | return nil 171 | } 172 | 173 | vers := header.Get("version") 174 | major, minor, ok := http.ParseHTTPVersion(vers) 175 | if c.check(!ok, "Invalid HTTP version: "+vers) { 176 | return nil 177 | } 178 | 179 | method := header.Get("method") 180 | 181 | // Build this into a request to present to the Handler. 182 | request := &http.Request{ 183 | Method: method, 184 | URL: url, 185 | Proto: vers, 186 | ProtoMajor: major, 187 | ProtoMinor: minor, 188 | RemoteAddr: c.remoteAddr, 189 | Header: header, 190 | Host: url.Host, 191 | RequestURI: url.RequestURI(), 192 | TLS: c.tlsState, 193 | } 194 | 195 | output := c.output[frame.Priority] 196 | c.streamCreation.Lock() 197 | out := NewResponseStream(c, frame, output, c.server.Handler, request) 198 | c.streamCreation.Unlock() 199 | 200 | return out 201 | } 202 | -------------------------------------------------------------------------------- /spdy2/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package spdy2 contains functionality for SPDY/2. 6 | package spdy2 7 | -------------------------------------------------------------------------------- /spdy2/error_handling.go: -------------------------------------------------------------------------------- 1 | package spdy2 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "time" 7 | 8 | "github.com/SlyMarbo/spdy/common" 9 | "github.com/SlyMarbo/spdy/spdy2/frames" 10 | ) 11 | 12 | // check returns the error condition and 13 | // updates the connection accordingly. 14 | func (c *Conn) check(condition bool, format string, v ...interface{}) bool { 15 | if !condition { 16 | return false 17 | } 18 | log.Printf("Error: "+format+".\n", v...) 19 | c.numBenignErrors++ 20 | return true 21 | } 22 | 23 | // criticalCheck returns the error condition 24 | // and ends the connection accordingly. 25 | func (c *Conn) criticalCheck(condition bool, sid common.StreamID, format string, v ...interface{}) bool { 26 | if !condition { 27 | return false 28 | } 29 | log.Printf("Error: "+format+".\n", v...) 30 | c.protocolError(sid) 31 | return true 32 | } 33 | 34 | func (c *Conn) _RST_STREAM(streamID common.StreamID, status common.StatusCode) { 35 | rst := new(frames.RST_STREAM) 36 | rst.StreamID = streamID 37 | rst.Status = status 38 | c.output[0] <- rst 39 | } 40 | 41 | func (c *Conn) _GOAWAY() { 42 | c.output[0] <- new(frames.GOAWAY) 43 | c.Close() 44 | } 45 | 46 | // handleReadWriteError differentiates between normal and 47 | // unexpected errors when performing I/O with the network, 48 | // then shuts down the connection. 49 | func (c *Conn) handleReadWriteError(err error) { 50 | if _, ok := err.(*net.OpError); ok || err == io.EOF || err == common.ErrConnNil || 51 | err.Error() == "use of closed network connection" { 52 | // Server has closed the TCP connection. 53 | debug.Println("Note: Endpoint has discected.") 54 | } else { 55 | // Unexpected error which prevented a read/write. 56 | log.Printf("Error: Encountered error: %q (%T)\n", err.Error(), err) 57 | } 58 | 59 | // Make sure c.Close succeeds and sending stops. 60 | c.sendingLock.Lock() 61 | if c.sending == nil { 62 | c.sending = make(chan struct{}) 63 | } 64 | c.sendingLock.Unlock() 65 | 66 | c.Close() 67 | } 68 | 69 | // protocolError informs the other endpoint that a protocol error has 70 | // occurred, stops all running streams, and ends the connection. 71 | func (c *Conn) protocolError(streamID common.StreamID) { 72 | reply := new(frames.RST_STREAM) 73 | reply.StreamID = streamID 74 | reply.Status = common.RST_STREAM_PROTOCOL_ERROR 75 | select { 76 | case c.output[0] <- reply: 77 | case <-time.After(100 * time.Millisecond): 78 | debug.Println("Failed to send PROTOCOL_ERROR RST_STREAM.") 79 | } 80 | c.shutdownError = reply 81 | c.Close() 82 | } 83 | -------------------------------------------------------------------------------- /spdy2/frames/common.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bufio" 9 | "errors" 10 | 11 | "github.com/SlyMarbo/spdy/common" 12 | ) 13 | 14 | // ReadFrame reads and parses a frame from reader. 15 | func ReadFrame(reader *bufio.Reader) (frame common.Frame, err error) { 16 | start, err := reader.Peek(4) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | if start[0] != 128 { 22 | frame = new(DATA) 23 | _, err = frame.ReadFrom(reader) 24 | return frame, err 25 | } 26 | 27 | switch common.BytesToUint16(start[2:4]) { 28 | case _SYN_STREAM: 29 | frame = new(SYN_STREAM) 30 | case _SYN_REPLY: 31 | frame = new(SYN_REPLY) 32 | case _RST_STREAM: 33 | frame = new(RST_STREAM) 34 | case _SETTINGS: 35 | frame = new(SETTINGS) 36 | case _NOOP: 37 | frame = new(NOOP) 38 | case _PING: 39 | frame = new(PING) 40 | case _GOAWAY: 41 | frame = new(GOAWAY) 42 | case _HEADERS: 43 | frame = new(HEADERS) 44 | case _WINDOW_UPDATE: 45 | frame = new(WINDOW_UPDATE) 46 | 47 | default: 48 | return nil, errors.New("Error Failed to parse frame type.") 49 | } 50 | 51 | _, err = frame.ReadFrom(reader) 52 | return frame, err 53 | } 54 | 55 | // controlFrameCommonProcessing performs checks identical between 56 | // all control frames. This includes the control bit, the version 57 | // number, the type byte (which is checked against the byte 58 | // provided), and the flags (which are checked against the bitwise 59 | // OR of valid flags provided). 60 | func controlFrameCommonProcessing(data []byte, frameType uint16, flags byte) error { 61 | // Check it's a control frame. 62 | if data[0] != 128 { 63 | return common.IncorrectFrame(_DATA_FRAME, int(frameType), 2) 64 | } 65 | 66 | // Check version. 67 | version := (uint16(data[0]&0x7f) << 8) + uint16(data[1]) 68 | if version != 2 { 69 | return common.UnsupportedVersion(version) 70 | } 71 | 72 | // Check its type. 73 | realType := common.BytesToUint16(data[2:]) 74 | if realType != frameType { 75 | return common.IncorrectFrame(int(realType), int(frameType), 2) 76 | } 77 | 78 | // Check the flags. 79 | if data[4] & ^flags != 0 { 80 | return common.InvalidField("flags", int(data[4]), int(flags)) 81 | } 82 | 83 | return nil 84 | } 85 | 86 | // Frame types in SPDY/2 87 | const ( 88 | _SYN_STREAM = 1 89 | _SYN_REPLY = 2 90 | _RST_STREAM = 3 91 | _SETTINGS = 4 92 | _NOOP = 5 93 | _PING = 6 94 | _GOAWAY = 7 95 | _HEADERS = 8 96 | _WINDOW_UPDATE = 9 97 | _CONTROL_FRAME = -1 98 | _DATA_FRAME = -2 99 | ) 100 | 101 | // frameNames provides the name for a particular SPDY/2 102 | // frame type. 103 | var frameNames = map[int]string{ 104 | _SYN_STREAM: "SYN_STREAM", 105 | _SYN_REPLY: "SYN_REPLY", 106 | _RST_STREAM: "RST_STREAM", 107 | _SETTINGS: "SETTINGS", 108 | _NOOP: "NOOP", 109 | _PING: "PING", 110 | _GOAWAY: "GOAWAY", 111 | _HEADERS: "HEADERS", 112 | _WINDOW_UPDATE: "WINDOW_UPDATE", 113 | _CONTROL_FRAME: "CONTROL_FRAME", 114 | _DATA_FRAME: "DATA_FRAME", 115 | } 116 | -------------------------------------------------------------------------------- /spdy2/frames/data.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | 13 | "github.com/SlyMarbo/spdy/common" 14 | ) 15 | 16 | type DATA struct { 17 | StreamID common.StreamID 18 | Flags common.Flags 19 | Data []byte 20 | } 21 | 22 | func (frame *DATA) Compress(comp common.Compressor) error { 23 | return nil 24 | } 25 | 26 | func (frame *DATA) Decompress(decomp common.Decompressor) error { 27 | return nil 28 | } 29 | 30 | func (frame *DATA) Name() string { 31 | return "DATA" 32 | } 33 | 34 | func (frame *DATA) ReadFrom(reader io.Reader) (int64, error) { 35 | c := common.ReadCounter{R: reader} 36 | data, err := common.ReadExactly(&c, 8) 37 | if err != nil { 38 | return c.N, err 39 | } 40 | 41 | // Check it's a data frame. 42 | if data[0]&0x80 == 1 { 43 | return c.N, common.IncorrectFrame(_CONTROL_FRAME, _DATA_FRAME, 2) 44 | } 45 | 46 | // Check flags. 47 | if data[4] & ^byte(common.FLAG_FIN) != 0 { 48 | return c.N, common.InvalidField("flags", int(data[4]), common.FLAG_FIN) 49 | } 50 | 51 | // Get and check length. 52 | length := int(common.BytesToUint24(data[5:8])) 53 | if length > common.MAX_FRAME_SIZE-8 { 54 | return c.N, common.FrameTooLarge 55 | } 56 | 57 | // Read in data. 58 | if length != 0 { 59 | frame.Data, err = common.ReadExactly(&c, length) 60 | if err != nil { 61 | return c.N, err 62 | } 63 | } 64 | 65 | frame.StreamID = common.StreamID(common.BytesToUint32(data[0:4])) 66 | frame.Flags = common.Flags(data[4]) 67 | if frame.Data == nil { 68 | frame.Data = []byte{} 69 | } 70 | 71 | return c.N, nil 72 | } 73 | 74 | func (frame *DATA) String() string { 75 | buf := new(bytes.Buffer) 76 | 77 | flags := "" 78 | if frame.Flags.FIN() { 79 | flags += " common.FLAG_FIN" 80 | } 81 | if flags == "" { 82 | flags = "[NONE]" 83 | } else { 84 | flags = flags[1:] 85 | } 86 | 87 | buf.WriteString("DATA {\n\t") 88 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 89 | buf.WriteString(fmt.Sprintf("Flags: %s\n\t", flags)) 90 | buf.WriteString(fmt.Sprintf("Length: %d\n\t", len(frame.Data))) 91 | if common.VerboseLogging || len(frame.Data) <= 21 { 92 | buf.WriteString(fmt.Sprintf("Data: [% x]\n}\n", frame.Data)) 93 | } else { 94 | buf.WriteString(fmt.Sprintf("Data: [% x ... % x]\n}\n", frame.Data[:9], 95 | frame.Data[len(frame.Data)-9:])) 96 | } 97 | 98 | return buf.String() 99 | } 100 | 101 | func (frame *DATA) WriteTo(writer io.Writer) (int64, error) { 102 | c := common.WriteCounter{W: writer} 103 | length := len(frame.Data) 104 | if length > common.MAX_DATA_SIZE { 105 | return c.N, errors.New("Error: Data size too large.") 106 | } 107 | if length == 0 && !frame.Flags.FIN() { 108 | return c.N, errors.New("Error: Data is empty.") 109 | } 110 | 111 | out := make([]byte, 8) 112 | 113 | out[0] = frame.StreamID.B1() // Control bit and Stream ID 114 | out[1] = frame.StreamID.B2() // Stream ID 115 | out[2] = frame.StreamID.B3() // Stream ID 116 | out[3] = frame.StreamID.B4() // Stream ID 117 | out[4] = byte(frame.Flags) // Flags 118 | out[5] = byte(length >> 16) // Length 119 | out[6] = byte(length >> 8) // Length 120 | out[7] = byte(length) // Length 121 | 122 | err := common.WriteExactly(&c, out) 123 | if err != nil { 124 | return c.N, err 125 | } 126 | 127 | err = common.WriteExactly(&c, frame.Data) 128 | if err != nil { 129 | return c.N, err 130 | } 131 | 132 | return c.N, nil 133 | } 134 | -------------------------------------------------------------------------------- /spdy2/frames/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package frames contains an implementation of the SPDY/2 frames. 6 | package frames 7 | -------------------------------------------------------------------------------- /spdy2/frames/goaway.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | 12 | "github.com/SlyMarbo/spdy/common" 13 | ) 14 | 15 | type GOAWAY struct { 16 | LastGoodStreamID common.StreamID 17 | } 18 | 19 | func (frame *GOAWAY) Compress(comp common.Compressor) error { 20 | return nil 21 | } 22 | 23 | func (frame *GOAWAY) Decompress(decomp common.Decompressor) error { 24 | return nil 25 | } 26 | 27 | func (frame *GOAWAY) Name() string { 28 | return "GOAWAY" 29 | } 30 | 31 | func (frame *GOAWAY) ReadFrom(reader io.Reader) (int64, error) { 32 | c := common.ReadCounter{R: reader} 33 | data, err := common.ReadExactly(&c, 12) 34 | if err != nil { 35 | return c.N, err 36 | } 37 | 38 | err = controlFrameCommonProcessing(data[:5], _GOAWAY, 0) 39 | if err != nil { 40 | return c.N, err 41 | } 42 | 43 | // Get and check length. 44 | length := int(common.BytesToUint24(data[5:8])) 45 | if length != 4 { 46 | return c.N, common.IncorrectDataLength(length, 4) 47 | } 48 | 49 | frame.LastGoodStreamID = common.StreamID(common.BytesToUint32(data[8:12])) 50 | 51 | if !frame.LastGoodStreamID.Valid() { 52 | return c.N, common.StreamIdTooLarge 53 | } 54 | 55 | return c.N, nil 56 | } 57 | 58 | func (frame *GOAWAY) String() string { 59 | buf := new(bytes.Buffer) 60 | 61 | buf.WriteString("GOAWAY {\n\t") 62 | buf.WriteString(fmt.Sprintf("Version: 2\n\t")) 63 | buf.WriteString(fmt.Sprintf("Last good stream ID: %d\n}\n", frame.LastGoodStreamID)) 64 | 65 | return buf.String() 66 | } 67 | 68 | func (frame *GOAWAY) WriteTo(writer io.Writer) (int64, error) { 69 | c := common.WriteCounter{W: writer} 70 | if !frame.LastGoodStreamID.Valid() { 71 | return c.N, common.StreamIdTooLarge 72 | } 73 | 74 | out := make([]byte, 12) 75 | 76 | out[0] = 128 // Control bit and Version 77 | out[1] = 2 // Version 78 | out[2] = 0 // Type 79 | out[3] = 7 // Type 80 | out[4] = 0 // Flags 81 | out[5] = 0 // Length 82 | out[6] = 0 // Length 83 | out[7] = 4 // Length 84 | out[8] = frame.LastGoodStreamID.B1() // Last Good Stream ID 85 | out[9] = frame.LastGoodStreamID.B2() // Last Good Stream ID 86 | out[10] = frame.LastGoodStreamID.B3() // Last Good Stream ID 87 | out[11] = frame.LastGoodStreamID.B4() // Last Good Stream ID 88 | 89 | err := common.WriteExactly(&c, out) 90 | if err != nil { 91 | return c.N, err 92 | } 93 | 94 | return c.N, nil 95 | } 96 | -------------------------------------------------------------------------------- /spdy2/frames/headers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "net/http" 13 | 14 | "github.com/SlyMarbo/spdy/common" 15 | ) 16 | 17 | type HEADERS struct { 18 | Flags common.Flags 19 | StreamID common.StreamID 20 | Header http.Header 21 | rawHeader []byte 22 | } 23 | 24 | func (frame *HEADERS) Compress(com common.Compressor) error { 25 | if frame.rawHeader != nil { 26 | return nil 27 | } 28 | 29 | data, err := com.Compress(frame.Header) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | frame.rawHeader = data 35 | return nil 36 | } 37 | 38 | func (frame *HEADERS) Decompress(decom common.Decompressor) error { 39 | if frame.Header != nil { 40 | return nil 41 | } 42 | 43 | header, err := decom.Decompress(frame.rawHeader) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | frame.Header = header 49 | frame.rawHeader = nil 50 | return nil 51 | } 52 | 53 | func (frame *HEADERS) Name() string { 54 | return "HEADERS" 55 | } 56 | 57 | func (frame *HEADERS) ReadFrom(reader io.Reader) (int64, error) { 58 | c := common.ReadCounter{R: reader} 59 | data, err := common.ReadExactly(&c, 16) 60 | if err != nil { 61 | return c.N, err 62 | } 63 | 64 | err = controlFrameCommonProcessing(data[:5], _HEADERS, common.FLAG_FIN) 65 | if err != nil { 66 | return c.N, err 67 | } 68 | 69 | // Get and check length. 70 | length := int(common.BytesToUint24(data[5:8])) 71 | if length < 6 { 72 | return c.N, common.IncorrectDataLength(length, 6) 73 | } else if length > common.MAX_FRAME_SIZE-8 { 74 | return c.N, common.FrameTooLarge 75 | } 76 | 77 | // Read in data. 78 | header, err := common.ReadExactly(&c, length-8) 79 | if err != nil { 80 | return c.N, err 81 | } 82 | 83 | frame.Flags = common.Flags(data[4]) 84 | frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12])) 85 | frame.rawHeader = header 86 | 87 | if !frame.StreamID.Valid() { 88 | return c.N, common.StreamIdTooLarge 89 | } 90 | if frame.StreamID.Zero() { 91 | return c.N, common.StreamIdIsZero 92 | } 93 | 94 | return c.N, nil 95 | } 96 | 97 | func (frame *HEADERS) String() string { 98 | buf := new(bytes.Buffer) 99 | 100 | Flags := "" 101 | if frame.Flags.FIN() { 102 | Flags += " common.FLAG_FIN" 103 | } 104 | if Flags == "" { 105 | Flags = "[NONE]" 106 | } else { 107 | Flags = Flags[1:] 108 | } 109 | 110 | buf.WriteString("HEADERS {\n\t") 111 | buf.WriteString(fmt.Sprintf("Version: 2\n\t")) 112 | buf.WriteString(fmt.Sprintf("Flags: %s\n\t", Flags)) 113 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 114 | buf.WriteString(fmt.Sprintf("Header: %v\n}\n", frame.Header)) 115 | 116 | return buf.String() 117 | } 118 | 119 | func (frame *HEADERS) WriteTo(writer io.Writer) (int64, error) { 120 | c := common.WriteCounter{W: writer} 121 | if frame.rawHeader == nil { 122 | return c.N, errors.New("Error: Headers not written.") 123 | } 124 | if !frame.StreamID.Valid() { 125 | return c.N, common.StreamIdTooLarge 126 | } 127 | if frame.StreamID.Zero() { 128 | return c.N, common.StreamIdIsZero 129 | } 130 | 131 | header := frame.rawHeader 132 | length := 4 + len(header) 133 | out := make([]byte, 16) 134 | 135 | out[0] = 128 // Control bit and Version 136 | out[1] = 2 // Version 137 | out[2] = 0 // Type 138 | out[3] = 8 // Type 139 | out[4] = byte(frame.Flags) // Flags 140 | out[5] = byte(length >> 16) // Length 141 | out[6] = byte(length >> 8) // Length 142 | out[7] = byte(length) // Length 143 | out[8] = frame.StreamID.B1() // Stream ID 144 | out[9] = frame.StreamID.B2() // Stream ID 145 | out[10] = frame.StreamID.B3() // Stream ID 146 | out[11] = frame.StreamID.B4() // Stream ID 147 | 148 | err := common.WriteExactly(&c, out) 149 | if err != nil { 150 | return c.N, err 151 | } 152 | 153 | err = common.WriteExactly(&c, header) 154 | if err != nil { 155 | return c.N, err 156 | } 157 | 158 | return c.N, nil 159 | } 160 | -------------------------------------------------------------------------------- /spdy2/frames/noop.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "io" 9 | 10 | "github.com/SlyMarbo/spdy/common" 11 | ) 12 | 13 | type NOOP struct{} 14 | 15 | func (frame *NOOP) Compress(comp common.Compressor) error { 16 | return nil 17 | } 18 | 19 | func (frame *NOOP) Decompress(decomp common.Decompressor) error { 20 | return nil 21 | } 22 | 23 | func (frame *NOOP) Name() string { 24 | return "NOOP" 25 | } 26 | 27 | func (frame *NOOP) ReadFrom(reader io.Reader) (int64, error) { 28 | c := common.ReadCounter{R: reader} 29 | data, err := common.ReadExactly(&c, 8) 30 | if err != nil { 31 | return c.N, err 32 | } 33 | 34 | err = controlFrameCommonProcessing(data[:5], _NOOP, 0) 35 | if err != nil { 36 | return c.N, err 37 | } 38 | 39 | // Get and check length. 40 | length := int(common.BytesToUint24(data[5:8])) 41 | if length != 0 { 42 | return c.N, common.IncorrectDataLength(length, 0) 43 | } 44 | 45 | return c.N, nil 46 | } 47 | 48 | func (frame *NOOP) String() string { 49 | return "NOOP {\n\tVersion: 2\n}\n" 50 | } 51 | 52 | func (frame *NOOP) WriteTo(writer io.Writer) (int64, error) { 53 | return 0, nil 54 | } 55 | -------------------------------------------------------------------------------- /spdy2/frames/ping.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | 12 | "github.com/SlyMarbo/spdy/common" 13 | ) 14 | 15 | type PING struct { 16 | PingID uint32 17 | } 18 | 19 | func (frame *PING) Compress(comp common.Compressor) error { 20 | return nil 21 | } 22 | 23 | func (frame *PING) Decompress(decomp common.Decompressor) error { 24 | return nil 25 | } 26 | 27 | func (frame *PING) Name() string { 28 | return "PING" 29 | } 30 | 31 | func (frame *PING) ReadFrom(reader io.Reader) (int64, error) { 32 | c := common.ReadCounter{R: reader} 33 | data, err := common.ReadExactly(&c, 12) 34 | if err != nil { 35 | return c.N, err 36 | } 37 | 38 | err = controlFrameCommonProcessing(data[:5], _PING, 0) 39 | if err != nil { 40 | return c.N, err 41 | } 42 | 43 | // Get and check length. 44 | length := int(common.BytesToUint24(data[5:8])) 45 | if length != 4 { 46 | return c.N, common.IncorrectDataLength(length, 4) 47 | } 48 | 49 | frame.PingID = common.BytesToUint32(data[8:12]) 50 | 51 | return c.N, nil 52 | } 53 | 54 | func (frame *PING) String() string { 55 | buf := new(bytes.Buffer) 56 | 57 | buf.WriteString("PING {\n\t") 58 | buf.WriteString(fmt.Sprintf("Version: 2\n\t")) 59 | buf.WriteString(fmt.Sprintf("Ping ID: %d\n}\n", frame.PingID)) 60 | 61 | return buf.String() 62 | } 63 | 64 | func (frame *PING) WriteTo(writer io.Writer) (int64, error) { 65 | c := common.WriteCounter{W: writer} 66 | out := make([]byte, 12) 67 | 68 | out[0] = 128 // Control bit and Version 69 | out[1] = 2 // Version 70 | out[2] = 0 // Type 71 | out[3] = 6 // Type 72 | out[4] = 0 // Flags 73 | out[5] = 0 // Length 74 | out[6] = 0 // Length 75 | out[7] = 4 // Length 76 | out[8] = byte(frame.PingID >> 24) // Ping ID 77 | out[9] = byte(frame.PingID >> 16) // Ping ID 78 | out[10] = byte(frame.PingID >> 8) // Ping ID 79 | out[11] = byte(frame.PingID) // Ping ID 80 | 81 | err := common.WriteExactly(&c, out) 82 | if err != nil { 83 | return c.N, err 84 | } 85 | 86 | return c.N, nil 87 | } 88 | -------------------------------------------------------------------------------- /spdy2/frames/rst_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | 12 | "github.com/SlyMarbo/spdy/common" 13 | ) 14 | 15 | type RST_STREAM struct { 16 | StreamID common.StreamID 17 | Status common.StatusCode 18 | } 19 | 20 | func (frame *RST_STREAM) Compress(comp common.Compressor) error { 21 | return nil 22 | } 23 | 24 | func (frame *RST_STREAM) Decompress(decomp common.Decompressor) error { 25 | return nil 26 | } 27 | 28 | func (frame *RST_STREAM) Error() string { 29 | if err := frame.Status.String(); err != "" { 30 | return err 31 | } 32 | 33 | return fmt.Sprintf("[unknown status code %d]", frame.Status) 34 | } 35 | 36 | func (frame *RST_STREAM) Name() string { 37 | return "RST_STREAM" 38 | } 39 | 40 | func (frame *RST_STREAM) ReadFrom(reader io.Reader) (int64, error) { 41 | c := common.ReadCounter{R: reader} 42 | data, err := common.ReadExactly(&c, 16) 43 | if err != nil { 44 | return c.N, err 45 | } 46 | 47 | err = controlFrameCommonProcessing(data[:5], _RST_STREAM, 0) 48 | if err != nil { 49 | return c.N, err 50 | } 51 | 52 | // Get and check length. 53 | length := int(common.BytesToUint24(data[5:8])) 54 | if length != 8 { 55 | return c.N, common.IncorrectDataLength(length, 8) 56 | } else if length > common.MAX_FRAME_SIZE-8 { 57 | return c.N, common.FrameTooLarge 58 | } 59 | 60 | frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12])) 61 | frame.Status = common.StatusCode(common.BytesToUint32(data[12:16])) 62 | 63 | if !frame.StreamID.Valid() { 64 | return c.N, common.StreamIdTooLarge 65 | } 66 | 67 | return c.N, nil 68 | } 69 | 70 | func (frame *RST_STREAM) String() string { 71 | buf := new(bytes.Buffer) 72 | 73 | buf.WriteString("RST_STREAM {\n\t") 74 | buf.WriteString(fmt.Sprintf("Version: 2\n\t")) 75 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 76 | buf.WriteString(fmt.Sprintf("Status code: %s\n}\n", frame.Status)) 77 | 78 | return buf.String() 79 | } 80 | 81 | func (frame *RST_STREAM) WriteTo(writer io.Writer) (int64, error) { 82 | c := common.WriteCounter{W: writer} 83 | if !frame.StreamID.Valid() { 84 | return c.N, common.StreamIdTooLarge 85 | } 86 | 87 | out := make([]byte, 16) 88 | 89 | out[0] = 128 // Control bit and Version 90 | out[1] = 2 // Version 91 | out[2] = 0 // Type 92 | out[3] = 3 // Type 93 | out[4] = 0 // Flags 94 | out[5] = 0 // Length 95 | out[6] = 0 // Length 96 | out[7] = 8 // Length 97 | out[8] = frame.StreamID.B1() // Stream ID 98 | out[9] = frame.StreamID.B2() // Stream ID 99 | out[10] = frame.StreamID.B3() // Stream ID 100 | out[11] = frame.StreamID.B4() // Stream ID 101 | out[12] = frame.Status.B1() // Status 102 | out[13] = frame.Status.B2() // Status 103 | out[14] = frame.Status.B3() // Status 104 | out[15] = frame.Status.B4() // Status 105 | 106 | err := common.WriteExactly(&c, out) 107 | if err != nil { 108 | return c.N, err 109 | } 110 | 111 | return c.N, nil 112 | } 113 | -------------------------------------------------------------------------------- /spdy2/frames/settings.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "sort" 13 | 14 | "github.com/SlyMarbo/spdy/common" 15 | ) 16 | 17 | type SETTINGS struct { 18 | Flags common.Flags 19 | Settings common.Settings 20 | } 21 | 22 | func (frame *SETTINGS) Add(Flags common.Flags, id uint32, value uint32) { 23 | frame.Settings[id] = &common.Setting{Flags, id, value} 24 | } 25 | 26 | func (frame *SETTINGS) Compress(comp common.Compressor) error { 27 | return nil 28 | } 29 | 30 | func (frame *SETTINGS) Decompress(decomp common.Decompressor) error { 31 | return nil 32 | } 33 | 34 | func (frame *SETTINGS) Name() string { 35 | return "SETTINGS" 36 | } 37 | 38 | func (frame *SETTINGS) ReadFrom(reader io.Reader) (int64, error) { 39 | c := common.ReadCounter{R: reader} 40 | data, err := common.ReadExactly(&c, 12) 41 | if err != nil { 42 | return c.N, err 43 | } 44 | 45 | err = controlFrameCommonProcessing(data[:5], _SETTINGS, common.FLAG_SETTINGS_CLEAR_SETTINGS) 46 | if err != nil { 47 | return c.N, err 48 | } 49 | 50 | // Get and check length. 51 | length := int(common.BytesToUint24(data[5:8])) 52 | if length < 4 { 53 | return c.N, common.IncorrectDataLength(length, 8) 54 | } else if length > common.MAX_FRAME_SIZE-8 { 55 | return c.N, common.FrameTooLarge 56 | } 57 | 58 | // Check size. 59 | numSettings := int(common.BytesToUint32(data[8:12])) 60 | if length != 4+(8*numSettings) { 61 | return c.N, common.IncorrectDataLength(length, 4+(8*numSettings)) 62 | } 63 | 64 | // Read in data. 65 | settings, err := common.ReadExactly(&c, 8*numSettings) 66 | if err != nil { 67 | return c.N, err 68 | } 69 | 70 | frame.Flags = common.Flags(data[4]) 71 | frame.Settings = make(common.Settings) 72 | for i := 0; i < numSettings; i++ { 73 | j := i * 8 74 | setting := decodeSetting(settings[j:]) 75 | if setting == nil { 76 | return c.N, errors.New("Error: Failed to parse settings.") 77 | } 78 | frame.Settings[setting.ID] = setting 79 | } 80 | 81 | return c.N, nil 82 | } 83 | 84 | func (frame *SETTINGS) String() string { 85 | buf := new(bytes.Buffer) 86 | flags := "" 87 | if frame.Flags.CLEAR_SETTINGS() { 88 | flags += " FLAG_SETTINGS_CLEAR_SETTINGS" 89 | } 90 | if flags == "" { 91 | flags = "[NONE]" 92 | } else { 93 | flags = flags[1:] 94 | } 95 | 96 | buf.WriteString("SETTINGS {\n\t") 97 | buf.WriteString(fmt.Sprintf("Version: 2\n\t")) 98 | buf.WriteString(fmt.Sprintf("Flags: %s\n\t", flags)) 99 | buf.WriteString(fmt.Sprintf("Settings:\n")) 100 | settings := frame.Settings.Settings() 101 | for _, setting := range settings { 102 | buf.WriteString("\t\t" + setting.String() + "\n") 103 | } 104 | buf.WriteString("}\n") 105 | 106 | return buf.String() 107 | } 108 | 109 | func (frame *SETTINGS) WriteTo(writer io.Writer) (int64, error) { 110 | c := common.WriteCounter{W: writer} 111 | settings := encodeSettings(frame.Settings) 112 | numSettings := uint32(len(frame.Settings)) 113 | length := 4 + len(settings) 114 | out := make([]byte, 12) 115 | 116 | out[0] = 128 // Control bit and Version 117 | out[1] = 2 // Version 118 | out[2] = 0 // Type 119 | out[3] = 4 // Type 120 | out[4] = byte(frame.Flags) // Flags 121 | out[5] = byte(length >> 16) // Length 122 | out[6] = byte(length >> 8) // Length 123 | out[7] = byte(length) // Length 124 | out[8] = byte(numSettings >> 24) // Number of Entries 125 | out[9] = byte(numSettings >> 16) // Number of Entries 126 | out[10] = byte(numSettings >> 8) // Number of Entries 127 | out[11] = byte(numSettings) // Number of Entries 128 | 129 | err := common.WriteExactly(&c, out) 130 | if err != nil { 131 | return c.N, err 132 | } 133 | 134 | err = common.WriteExactly(&c, settings) 135 | if err != nil { 136 | return c.N, err 137 | } 138 | 139 | return c.N, nil 140 | } 141 | 142 | func decodeSetting(data []byte) *common.Setting { 143 | if len(data) < 8 { 144 | return nil 145 | } 146 | 147 | setting := new(common.Setting) 148 | setting.ID = common.BytesToUint24Reverse(data[0:]) // Might need to reverse this. 149 | setting.Flags = common.Flags(data[3]) 150 | setting.Value = common.BytesToUint32(data[4:]) 151 | 152 | return setting 153 | } 154 | 155 | func encodeSettings(s common.Settings) []byte { 156 | if len(s) == 0 { 157 | return []byte{} 158 | } 159 | 160 | ids := make([]int, 0, len(s)) 161 | for id := range s { 162 | ids = append(ids, int(id)) 163 | } 164 | 165 | sort.Sort(sort.IntSlice(ids)) 166 | 167 | out := make([]byte, 8*len(s)) 168 | 169 | offset := 0 170 | for _, id := range ids { 171 | setting := s[uint32(id)] 172 | out[offset] = byte(setting.ID) // Might need to reverse this. 173 | out[offset+1] = byte(setting.ID >> 8) // Might need to reverse this. 174 | out[offset+2] = byte(setting.ID >> 16) // Might need to reverse this. 175 | out[offset+3] = byte(setting.Flags) 176 | out[offset+4] = byte(setting.Value >> 24) 177 | out[offset+5] = byte(setting.Value >> 16) 178 | out[offset+6] = byte(setting.Value >> 8) 179 | out[offset+7] = byte(setting.Value) 180 | offset += 8 181 | } 182 | 183 | return out 184 | } 185 | -------------------------------------------------------------------------------- /spdy2/frames/syn_reply.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "net/http" 13 | 14 | "github.com/SlyMarbo/spdy/common" 15 | ) 16 | 17 | type SYN_REPLY struct { 18 | Flags common.Flags 19 | StreamID common.StreamID 20 | Header http.Header 21 | rawHeader []byte 22 | } 23 | 24 | func (frame *SYN_REPLY) Compress(com common.Compressor) error { 25 | if frame.rawHeader != nil { 26 | return nil 27 | } 28 | 29 | data, err := com.Compress(frame.Header) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | frame.rawHeader = data 35 | return nil 36 | } 37 | 38 | func (frame *SYN_REPLY) Decompress(decom common.Decompressor) error { 39 | if frame.Header != nil { 40 | return nil 41 | } 42 | 43 | header, err := decom.Decompress(frame.rawHeader) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | frame.Header = header 49 | frame.rawHeader = nil 50 | return nil 51 | } 52 | 53 | func (frame *SYN_REPLY) Name() string { 54 | return "SYN_REPLY" 55 | } 56 | 57 | func (frame *SYN_REPLY) ReadFrom(reader io.Reader) (int64, error) { 58 | c := common.ReadCounter{R: reader} 59 | data, err := common.ReadExactly(&c, 14) 60 | if err != nil { 61 | return c.N, err 62 | } 63 | 64 | err = controlFrameCommonProcessing(data[:5], _SYN_REPLY, common.FLAG_FIN) 65 | if err != nil { 66 | return c.N, err 67 | } 68 | 69 | // Get and check length. 70 | length := int(common.BytesToUint24(data[5:8])) 71 | if length < 8 { 72 | return c.N, common.IncorrectDataLength(length, 8) 73 | } else if length > common.MAX_FRAME_SIZE-8 { 74 | return c.N, common.FrameTooLarge 75 | } 76 | 77 | // Read in data. 78 | header, err := common.ReadExactly(&c, length-6) 79 | if err != nil { 80 | return c.N, err 81 | } 82 | 83 | frame.Flags = common.Flags(data[4]) 84 | frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12])) 85 | frame.rawHeader = header 86 | 87 | return c.N, nil 88 | } 89 | 90 | func (frame *SYN_REPLY) String() string { 91 | buf := new(bytes.Buffer) 92 | flags := "" 93 | if frame.Flags.FIN() { 94 | flags += " common.FLAG_FIN" 95 | } 96 | if flags == "" { 97 | flags = "[NONE]" 98 | } else { 99 | flags = flags[1:] 100 | } 101 | 102 | buf.WriteString("SYN_REPLY {\n\t") 103 | buf.WriteString(fmt.Sprintf("Version: 2\n\t")) 104 | buf.WriteString(fmt.Sprintf("Flags: %s\n\t", flags)) 105 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 106 | buf.WriteString(fmt.Sprintf("Header: %v\n}\n", frame.Header)) 107 | 108 | return buf.String() 109 | } 110 | 111 | func (frame *SYN_REPLY) WriteTo(writer io.Writer) (int64, error) { 112 | c := common.WriteCounter{W: writer} 113 | if frame.rawHeader == nil { 114 | return c.N, errors.New("Error: Header not written.") 115 | } 116 | if !frame.StreamID.Valid() { 117 | return c.N, common.StreamIdTooLarge 118 | } 119 | if frame.StreamID.Zero() { 120 | return c.N, common.StreamIdIsZero 121 | } 122 | 123 | header := frame.rawHeader 124 | length := 6 + len(header) 125 | out := make([]byte, 14) 126 | 127 | out[0] = 128 // Control bit and Version 128 | out[1] = 2 // Version 129 | out[2] = 0 // Type 130 | out[3] = 2 // Type 131 | out[4] = byte(frame.Flags) // Flags 132 | out[5] = byte(length >> 16) // Length 133 | out[6] = byte(length >> 8) // Length 134 | out[7] = byte(length) // Length 135 | out[8] = frame.StreamID.B1() // Stream ID 136 | out[9] = frame.StreamID.B2() // Stream ID 137 | out[10] = frame.StreamID.B3() // Stream ID 138 | out[11] = frame.StreamID.B4() // Stream ID 139 | out[12] = 0 // Unused 140 | out[13] = 0 // Unused 141 | 142 | err := common.WriteExactly(&c, out) 143 | if err != nil { 144 | return c.N, err 145 | } 146 | 147 | err = common.WriteExactly(&c, header) 148 | if err != nil { 149 | return c.N, err 150 | } 151 | 152 | return c.N, nil 153 | } 154 | -------------------------------------------------------------------------------- /spdy2/frames/syn_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "net/http" 13 | 14 | "github.com/SlyMarbo/spdy/common" 15 | ) 16 | 17 | type SYN_STREAM struct { 18 | Flags common.Flags 19 | StreamID common.StreamID 20 | AssocStreamID common.StreamID 21 | Priority common.Priority 22 | Header http.Header 23 | rawHeader []byte 24 | } 25 | 26 | func (frame *SYN_STREAM) Compress(com common.Compressor) error { 27 | if frame.rawHeader != nil { 28 | return nil 29 | } 30 | 31 | data, err := com.Compress(frame.Header) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | frame.rawHeader = data 37 | return nil 38 | } 39 | 40 | func (frame *SYN_STREAM) Decompress(decom common.Decompressor) error { 41 | if frame.Header != nil { 42 | return nil 43 | } 44 | 45 | header, err := decom.Decompress(frame.rawHeader) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | frame.Header = header 51 | frame.rawHeader = nil 52 | return nil 53 | } 54 | 55 | func (frame *SYN_STREAM) Name() string { 56 | return "SYN_STREAM" 57 | } 58 | 59 | func (frame *SYN_STREAM) ReadFrom(reader io.Reader) (int64, error) { 60 | c := common.ReadCounter{R: reader} 61 | data, err := common.ReadExactly(&c, 18) 62 | if err != nil { 63 | return c.N, err 64 | } 65 | 66 | err = controlFrameCommonProcessing(data[:5], _SYN_STREAM, common.FLAG_FIN|common.FLAG_UNIDIRECTIONAL) 67 | if err != nil { 68 | return c.N, err 69 | } 70 | 71 | // Get and check length. 72 | length := int(common.BytesToUint24(data[5:8])) 73 | if length < 12 { 74 | return c.N, common.IncorrectDataLength(length, 12) 75 | } else if length > common.MAX_FRAME_SIZE-18 { 76 | return c.N, common.FrameTooLarge 77 | } 78 | 79 | // Read in data. 80 | header, err := common.ReadExactly(&c, length-10) 81 | if err != nil { 82 | return c.N, err 83 | } 84 | 85 | frame.Flags = common.Flags(data[4]) 86 | frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12])) 87 | frame.AssocStreamID = common.StreamID(common.BytesToUint32(data[12:16])) 88 | frame.Priority = common.Priority(data[16] >> 6) 89 | frame.rawHeader = header 90 | 91 | if !frame.StreamID.Valid() { 92 | return c.N, common.StreamIdTooLarge 93 | } 94 | if frame.StreamID.Zero() { 95 | return c.N, common.StreamIdIsZero 96 | } 97 | if !frame.AssocStreamID.Valid() { 98 | return c.N, common.StreamIdTooLarge 99 | } 100 | 101 | return c.N, nil 102 | } 103 | 104 | func (frame *SYN_STREAM) String() string { 105 | buf := new(bytes.Buffer) 106 | flags := "" 107 | if frame.Flags.FIN() { 108 | flags += " common.FLAG_FIN" 109 | } 110 | if frame.Flags.UNIDIRECTIONAL() { 111 | flags += " FLAG_UNIDIRECTIONAL" 112 | } 113 | if flags == "" { 114 | flags = "[NONE]" 115 | } else { 116 | flags = flags[1:] 117 | } 118 | 119 | buf.WriteString("SYN_STREAM {\n\t") 120 | buf.WriteString(fmt.Sprintf("Version: 2\n\t")) 121 | buf.WriteString(fmt.Sprintf("Flags: %s\n\t", flags)) 122 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 123 | buf.WriteString(fmt.Sprintf("Associated Stream ID: %d\n\t", frame.AssocStreamID)) 124 | buf.WriteString(fmt.Sprintf("Priority: %d\n\t", frame.Priority)) 125 | buf.WriteString(fmt.Sprintf("Header: %v\n}\n", frame.Header)) 126 | 127 | return buf.String() 128 | } 129 | 130 | func (frame *SYN_STREAM) WriteTo(writer io.Writer) (int64, error) { 131 | c := common.WriteCounter{W: writer} 132 | if frame.rawHeader == nil { 133 | return c.N, errors.New("Error: Headers not written.") 134 | } 135 | if !frame.StreamID.Valid() { 136 | return c.N, common.StreamIdTooLarge 137 | } 138 | if frame.StreamID.Zero() { 139 | return c.N, common.StreamIdIsZero 140 | } 141 | if !frame.AssocStreamID.Valid() { 142 | return c.N, common.StreamIdTooLarge 143 | } 144 | 145 | header := frame.rawHeader 146 | length := 10 + len(header) 147 | out := make([]byte, 18) 148 | 149 | out[0] = 128 // Control bit and Version 150 | out[1] = 2 // Version 151 | out[2] = 0 // Type 152 | out[3] = 1 // Type 153 | out[4] = byte(frame.Flags) // Flags 154 | out[5] = byte(length >> 16) // Length 155 | out[6] = byte(length >> 8) // Length 156 | out[7] = byte(length) // Length 157 | out[8] = frame.StreamID.B1() // Stream ID 158 | out[9] = frame.StreamID.B2() // Stream ID 159 | out[10] = frame.StreamID.B3() // Stream ID 160 | out[11] = frame.StreamID.B4() // Stream ID 161 | out[12] = frame.AssocStreamID.B1() // Associated Stream ID 162 | out[13] = frame.AssocStreamID.B2() // Associated Stream ID 163 | out[14] = frame.AssocStreamID.B3() // Associated Stream ID 164 | out[15] = frame.AssocStreamID.B4() // Associated Stream ID 165 | out[16] = frame.Priority.Byte(2) // Priority and Unused 166 | out[17] = 0 // Unused 167 | 168 | err := common.WriteExactly(&c, out) 169 | if err != nil { 170 | return c.N, err 171 | } 172 | 173 | err = common.WriteExactly(&c, header) 174 | if err != nil { 175 | return c.N, err 176 | } 177 | 178 | return c.N, nil 179 | } 180 | -------------------------------------------------------------------------------- /spdy2/frames/window_update.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | 13 | "github.com/SlyMarbo/spdy/common" 14 | ) 15 | 16 | type WINDOW_UPDATE struct { 17 | StreamID common.StreamID 18 | DeltaWindowSize uint32 19 | } 20 | 21 | func (frame *WINDOW_UPDATE) Compress(comp common.Compressor) error { 22 | return nil 23 | } 24 | 25 | func (frame *WINDOW_UPDATE) Decompress(decomp common.Decompressor) error { 26 | return nil 27 | } 28 | 29 | func (frame *WINDOW_UPDATE) Name() string { 30 | return "WINDOW_UPDATE" 31 | } 32 | 33 | func (frame *WINDOW_UPDATE) ReadFrom(reader io.Reader) (int64, error) { 34 | c := common.ReadCounter{R: reader} 35 | data, err := common.ReadExactly(&c, 16) 36 | if err != nil { 37 | return c.N, err 38 | } 39 | 40 | err = controlFrameCommonProcessing(data[:5], _WINDOW_UPDATE, 0) 41 | if err != nil { 42 | return c.N, err 43 | } 44 | 45 | // Get and check length. 46 | length := int(common.BytesToUint24(data[5:8])) 47 | if length != 8 { 48 | return c.N, common.IncorrectDataLength(length, 8) 49 | } 50 | 51 | frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12])) 52 | frame.DeltaWindowSize = common.BytesToUint32(data[12:16]) 53 | 54 | if !frame.StreamID.Valid() { 55 | return c.N, common.StreamIdTooLarge 56 | } 57 | if frame.StreamID.Zero() { 58 | return c.N, common.StreamIdIsZero 59 | } 60 | if frame.DeltaWindowSize > common.MAX_DELTA_WINDOW_SIZE { 61 | return c.N, errors.New("Error: Delta Window Size too large.") 62 | } 63 | 64 | return c.N, nil 65 | } 66 | 67 | func (frame *WINDOW_UPDATE) String() string { 68 | buf := new(bytes.Buffer) 69 | 70 | buf.WriteString("WINDOW_UPDATE {\n\t") 71 | buf.WriteString(fmt.Sprintf("Version: 2\n\t")) 72 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 73 | buf.WriteString(fmt.Sprintf("Delta window size: %d\n}\n", frame.DeltaWindowSize)) 74 | 75 | return buf.String() 76 | } 77 | 78 | func (frame *WINDOW_UPDATE) WriteTo(writer io.Writer) (int64, error) { 79 | return 0, nil 80 | } 81 | -------------------------------------------------------------------------------- /spdy2/interface.go: -------------------------------------------------------------------------------- 1 | package spdy2 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | func (c *Conn) CloseNotify() <-chan bool { 9 | return c.stop 10 | } 11 | 12 | func (c *Conn) Conn() net.Conn { 13 | return c.conn 14 | } 15 | 16 | func (c *Conn) SetReadTimeout(d time.Duration) { 17 | c.timeoutLock.Lock() 18 | c.readTimeout = d 19 | c.timeoutLock.Unlock() 20 | } 21 | 22 | func (c *Conn) SetWriteTimeout(d time.Duration) { 23 | c.timeoutLock.Lock() 24 | c.writeTimeout = d 25 | c.timeoutLock.Unlock() 26 | } 27 | 28 | func (c *Conn) refreshReadTimeout() { 29 | c.timeoutLock.Lock() 30 | if d := c.readTimeout; d != 0 && c.conn != nil { 31 | c.conn.SetReadDeadline(time.Now().Add(d)) 32 | } 33 | c.timeoutLock.Unlock() 34 | } 35 | 36 | func (c *Conn) refreshWriteTimeout() { 37 | c.timeoutLock.Lock() 38 | if d := c.writeTimeout; d != 0 && c.conn != nil { 39 | c.conn.SetWriteDeadline(time.Now().Add(d)) 40 | } 41 | c.timeoutLock.Unlock() 42 | } 43 | -------------------------------------------------------------------------------- /spdy2/io.go: -------------------------------------------------------------------------------- 1 | package spdy2 2 | 3 | import ( 4 | "runtime" 5 | 6 | "github.com/SlyMarbo/spdy/common" 7 | "github.com/SlyMarbo/spdy/spdy2/frames" 8 | ) 9 | 10 | // readFrames is the main processing loop, where frames 11 | // are read from the connection and processed individually. 12 | // Returning from readFrames begins the cleanup and exit 13 | // process for this connection. 14 | func (c *Conn) readFrames() { 15 | // Ensure no panics happen. 16 | defer func() { 17 | if v := recover(); v != nil { 18 | if !c.Closed() { 19 | log.Printf("Encountered receive error: %v (%[1]T)\n", v) 20 | } 21 | } 22 | }() 23 | 24 | for { 25 | 26 | // This is the mechanism for handling too many benign errors. 27 | // By default MaxBenignErrors is 0, which ignores errors. 28 | if c.numBenignErrors > common.MaxBenignErrors && common.MaxBenignErrors > 0 { 29 | log.Println("Warning: Too many invalid stream IDs received. Ending connection.") 30 | c.protocolError(0) 31 | return 32 | } 33 | 34 | // ReadFrame takes care of the frame parsing for us. 35 | c.refreshReadTimeout() 36 | frame, err := frames.ReadFrame(c.buf) 37 | if err != nil { 38 | c.handleReadWriteError(err) 39 | return 40 | } 41 | 42 | // Print frame type. 43 | debug.Printf("Receiving %s:\n", frame.Name()) 44 | 45 | // Decompress the frame's headers, if there are any. 46 | err = frame.Decompress(c.decompressor) 47 | if err != nil { 48 | log.Printf("Error in decompression: %v (%T).\n", err, frame) 49 | c.protocolError(0) 50 | return 51 | } 52 | 53 | // Print frame once the content's been decompressed. 54 | debug.Println(frame) 55 | 56 | // This is the main frame handling. 57 | if c.processFrame(frame) { 58 | return 59 | } 60 | } 61 | } 62 | 63 | // send is run in a separate goroutine. It's used 64 | // to ensure clear interleaving of frames and to 65 | // provide assurances of priority and structure. 66 | func (c *Conn) send() { 67 | // Catch any panics. 68 | defer func() { 69 | if v := recover(); v != nil { 70 | if !c.Closed() { 71 | log.Printf("Encountered send error: %v (%[1]T)\n", v) 72 | } 73 | } 74 | }() 75 | 76 | // Enter the processing loop. 77 | i := 1 78 | for { 79 | 80 | // Once per 5 frames, pick randomly. 81 | var frame common.Frame 82 | if i == 0 { // Ignore priority. 83 | frame = c.selectFrameToSend(false) 84 | } else { // Normal selection. 85 | frame = c.selectFrameToSend(true) 86 | } 87 | 88 | i++ 89 | if i >= 5 { 90 | i = 0 91 | } 92 | 93 | if frame == nil { 94 | c.Close() 95 | return 96 | } 97 | 98 | // Compress any name/value header blocks. 99 | err := frame.Compress(c.compressor) 100 | if err != nil { 101 | log.Printf("Error in compression: %v (%T).\n", err, frame) 102 | return 103 | } 104 | 105 | debug.Printf("Sending %s:\n", frame.Name()) 106 | debug.Println(frame) 107 | 108 | // Leave the specifics of writing to the 109 | // connection up to the frame. 110 | c.refreshWriteTimeout() 111 | _, err = frame.WriteTo(c.conn) 112 | if err != nil { 113 | c.handleReadWriteError(err) 114 | return 115 | } 116 | } 117 | } 118 | 119 | // selectFrameToSend follows the specification's guidance 120 | // on frame priority, sending frames with higher priority 121 | // (a smaller number) first. If the given boolean is false, 122 | // this priority is temporarily ignored, which can be used 123 | // when high load is ignoring low-priority frames. 124 | func (c *Conn) selectFrameToSend(prioritise bool) (frame common.Frame) { 125 | if c.Closed() { 126 | return nil 127 | } 128 | 129 | // Try in priority order first. 130 | if prioritise { 131 | for i := 0; i < 8; i++ { 132 | select { 133 | case frame = <-c.output[i]: 134 | return frame 135 | default: 136 | } 137 | } 138 | 139 | // No frames are immediately pending, so if the 140 | // connection is being closed, cease sending 141 | // safely. 142 | c.sendingLock.Lock() 143 | if c.sending != nil { 144 | close(c.sending) 145 | c.sendingLock.Unlock() 146 | runtime.Goexit() 147 | } 148 | c.sendingLock.Unlock() 149 | } 150 | 151 | // Wait for any frame. 152 | select { 153 | case frame = <-c.output[0]: 154 | return frame 155 | case frame = <-c.output[1]: 156 | return frame 157 | case frame = <-c.output[2]: 158 | return frame 159 | case frame = <-c.output[3]: 160 | return frame 161 | case frame = <-c.output[4]: 162 | return frame 163 | case frame = <-c.output[5]: 164 | return frame 165 | case frame = <-c.output[6]: 166 | return frame 167 | case frame = <-c.output[7]: 168 | return frame 169 | case _ = <-c.stop: 170 | return nil 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /spdy2/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy2 6 | 7 | import ( 8 | "github.com/SlyMarbo/spdy/common" 9 | ) 10 | 11 | var log = common.GetLogger() 12 | var debug = common.GetDebugLogger() 13 | -------------------------------------------------------------------------------- /spdy2/push_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy2 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "net/http" 11 | "sync" 12 | 13 | "github.com/SlyMarbo/spdy/common" 14 | "github.com/SlyMarbo/spdy/spdy2/frames" 15 | ) 16 | 17 | // PushStream is a structure that implements the 18 | // Stream and PushWriter interfaces. this is used 19 | // for performing server pushes. 20 | type PushStream struct { 21 | sync.Mutex 22 | shutdownOnce sync.Once 23 | conn *Conn 24 | streamID common.StreamID 25 | origin common.Stream 26 | state *common.StreamState 27 | output chan<- common.Frame 28 | header http.Header 29 | stop <-chan bool 30 | } 31 | 32 | func NewPushStream(conn *Conn, streamID common.StreamID, origin common.Stream, output chan<- common.Frame) *PushStream { 33 | out := new(PushStream) 34 | out.conn = conn 35 | out.streamID = streamID 36 | out.origin = origin 37 | out.output = output 38 | out.stop = conn.stop 39 | out.state = new(common.StreamState) 40 | out.header = make(http.Header) 41 | return out 42 | } 43 | 44 | /*********************** 45 | * http.ResponseWriter * 46 | ***********************/ 47 | 48 | func (p *PushStream) Header() http.Header { 49 | return p.header 50 | } 51 | 52 | // Write is used for sending data in the push. 53 | func (p *PushStream) Write(inputData []byte) (int, error) { 54 | if p.closed() || p.state.ClosedHere() { 55 | return 0, errors.New("Error: Stream already closed.") 56 | } 57 | 58 | state := p.origin.State() 59 | if p.origin == nil || state.ClosedHere() { 60 | return 0, errors.New("Error: Origin stream is closed.") 61 | } 62 | 63 | p.writeHeader() 64 | 65 | // Copy the data locally to avoid any pointer issues. 66 | data := make([]byte, len(inputData)) 67 | copy(data, inputData) 68 | 69 | // Chunk the response if necessary. 70 | written := 0 71 | for len(data) > common.MAX_DATA_SIZE { 72 | dataFrame := new(frames.DATA) 73 | dataFrame.StreamID = p.streamID 74 | dataFrame.Data = data[:common.MAX_DATA_SIZE] 75 | p.output <- dataFrame 76 | 77 | written += common.MAX_DATA_SIZE 78 | } 79 | 80 | n := len(data) 81 | if n == 0 { 82 | return written, nil 83 | } 84 | 85 | dataFrame := new(frames.DATA) 86 | dataFrame.StreamID = p.streamID 87 | dataFrame.Data = data 88 | p.output <- dataFrame 89 | 90 | return written + n, nil 91 | } 92 | 93 | // WriteHeader is provided to satisfy the Stream 94 | // interface, but has no effect. 95 | func (p *PushStream) WriteHeader(int) { 96 | p.writeHeader() 97 | return 98 | } 99 | 100 | /***************** 101 | * io.Closer * 102 | *****************/ 103 | 104 | func (p *PushStream) Close() error { 105 | defer common.Recover() 106 | p.Lock() 107 | p.shutdownOnce.Do(p.shutdown) 108 | p.Unlock() 109 | return nil 110 | } 111 | 112 | func (p *PushStream) shutdown() { 113 | p.writeHeader() 114 | if p.state != nil { 115 | p.state.Close() 116 | } 117 | p.conn.pushStreamLimit.Close() 118 | p.origin = nil 119 | p.output = nil 120 | p.header = nil 121 | p.stop = nil 122 | } 123 | 124 | /********** 125 | * Stream * 126 | **********/ 127 | 128 | func (p *PushStream) Conn() common.Conn { 129 | return p.conn 130 | } 131 | 132 | func (p *PushStream) ReceiveFrame(frame common.Frame) error { 133 | p.Lock() 134 | defer p.Unlock() 135 | 136 | if frame == nil { 137 | return errors.New("Error: Nil frame received.") 138 | } 139 | 140 | // Process the frame depending on its type. 141 | switch frame := frame.(type) { 142 | case *frames.WINDOW_UPDATE: 143 | // Ignore. 144 | 145 | default: 146 | return errors.New(fmt.Sprintf("Received unexpected frame of type %T.", frame)) 147 | } 148 | 149 | return nil 150 | } 151 | 152 | func (p *PushStream) CloseNotify() <-chan bool { 153 | return p.stop 154 | } 155 | 156 | func (p *PushStream) Run() error { 157 | return nil 158 | } 159 | 160 | func (p *PushStream) State() *common.StreamState { 161 | return p.state 162 | } 163 | 164 | func (p *PushStream) StreamID() common.StreamID { 165 | return p.streamID 166 | } 167 | 168 | /************** 169 | * PushStream * 170 | **************/ 171 | 172 | func (p *PushStream) Finish() { 173 | p.writeHeader() 174 | end := new(frames.DATA) 175 | end.StreamID = p.streamID 176 | end.Data = []byte{} 177 | end.Flags = common.FLAG_FIN 178 | p.output <- end 179 | p.Close() 180 | } 181 | 182 | /********** 183 | * Others * 184 | **********/ 185 | 186 | func (p *PushStream) closed() bool { 187 | if p.conn == nil || p.state == nil { 188 | return true 189 | } 190 | select { 191 | case _ = <-p.stop: 192 | return true 193 | default: 194 | return false 195 | } 196 | } 197 | 198 | // writeHeader is used to send HTTP headers to 199 | // the client. 200 | func (p *PushStream) writeHeader() { 201 | if len(p.header) == 0 || p.closed() { 202 | return 203 | } 204 | 205 | header := new(frames.HEADERS) 206 | header.StreamID = p.streamID 207 | header.Header = common.CloneHeader(p.header) 208 | for name := range header.Header { 209 | p.header.Del(name) 210 | } 211 | p.output <- header 212 | } 213 | -------------------------------------------------------------------------------- /spdy2/request_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy2 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "net/http" 11 | "sync" 12 | 13 | "github.com/SlyMarbo/spdy/common" 14 | "github.com/SlyMarbo/spdy/spdy2/frames" 15 | ) 16 | 17 | // RequestStream is a structure that implements 18 | // the Stream and ResponseWriter interfaces. This 19 | // is used for responding to client requests. 20 | type RequestStream struct { 21 | sync.Mutex 22 | Request *http.Request 23 | Receiver common.Receiver 24 | 25 | recvMutex sync.Mutex 26 | shutdownOnce sync.Once 27 | conn *Conn 28 | streamID common.StreamID 29 | state *common.StreamState 30 | output chan<- common.Frame 31 | header http.Header 32 | headerChan chan func() 33 | responseCode int 34 | stop <-chan bool 35 | finished chan struct{} 36 | } 37 | 38 | func NewRequestStream(conn *Conn, streamID common.StreamID, output chan<- common.Frame) *RequestStream { 39 | out := new(RequestStream) 40 | out.conn = conn 41 | out.streamID = streamID 42 | out.output = output 43 | out.stop = conn.stop 44 | out.state = new(common.StreamState) 45 | out.state.CloseHere() 46 | out.header = make(http.Header) 47 | out.finished = make(chan struct{}) 48 | out.headerChan = make(chan func(), 5) 49 | go out.processFrames() 50 | return out 51 | } 52 | 53 | /*********************** 54 | * http.ResponseWriter * 55 | ***********************/ 56 | 57 | func (s *RequestStream) Header() http.Header { 58 | return s.header 59 | } 60 | 61 | // Write is one method with which request data is sent. 62 | func (s *RequestStream) Write(inputData []byte) (int, error) { 63 | if s.closed() || s.state.ClosedHere() { 64 | return 0, errors.New("Error: Stream already closed.") 65 | } 66 | 67 | // Copy the data locally to avoid any pointer issues. 68 | data := make([]byte, len(inputData)) 69 | copy(data, inputData) 70 | 71 | // Send any new headers. 72 | s.writeHeader() 73 | 74 | // Chunk the response if necessary. 75 | written := 0 76 | for len(data) > common.MAX_DATA_SIZE { 77 | dataFrame := new(frames.DATA) 78 | dataFrame.StreamID = s.streamID 79 | dataFrame.Data = data[:common.MAX_DATA_SIZE] 80 | s.output <- dataFrame 81 | 82 | written += common.MAX_DATA_SIZE 83 | } 84 | 85 | n := len(data) 86 | if n == 0 { 87 | return written, nil 88 | } 89 | 90 | dataFrame := new(frames.DATA) 91 | dataFrame.StreamID = s.streamID 92 | dataFrame.Data = data 93 | s.output <- dataFrame 94 | 95 | return written + n, nil 96 | } 97 | 98 | // WriteHeader is used to set the HTTP status code. 99 | func (s *RequestStream) WriteHeader(int) { 100 | s.writeHeader() 101 | } 102 | 103 | /***************** 104 | * io.Closer * 105 | *****************/ 106 | 107 | // Close is used to cancel a mid-air 108 | // request. 109 | func (s *RequestStream) Close() error { 110 | defer common.Recover() 111 | s.Lock() 112 | s.shutdownOnce.Do(s.shutdown) 113 | s.Unlock() 114 | return nil 115 | } 116 | 117 | func (s *RequestStream) shutdown() { 118 | s.writeHeader() 119 | if s.state != nil { 120 | if s.state.OpenThere() { 121 | // Send the RST_STREAM. 122 | rst := new(frames.RST_STREAM) 123 | rst.StreamID = s.streamID 124 | rst.Status = common.RST_STREAM_CANCEL 125 | s.output <- rst 126 | } 127 | s.state.Close() 128 | } 129 | select { 130 | case <-s.finished: 131 | default: 132 | close(s.finished) 133 | } 134 | select { 135 | case <-s.headerChan: 136 | default: 137 | close(s.headerChan) 138 | } 139 | s.conn.requestStreamLimit.Close() 140 | s.output = nil 141 | s.Request = nil 142 | s.Receiver = nil 143 | s.header = nil 144 | s.stop = nil 145 | 146 | s.conn.streamsLock.Lock() 147 | delete(s.conn.streams, s.streamID) 148 | s.conn.streamsLock.Unlock() 149 | } 150 | 151 | /********** 152 | * Stream * 153 | **********/ 154 | 155 | func (s *RequestStream) Conn() common.Conn { 156 | return s.conn 157 | } 158 | 159 | func (s *RequestStream) ReceiveFrame(frame common.Frame) error { 160 | s.recvMutex.Lock() 161 | defer s.recvMutex.Unlock() 162 | 163 | if frame == nil { 164 | return errors.New("Nil frame received.") 165 | } 166 | 167 | // Process the frame depending on its type. 168 | switch frame := frame.(type) { 169 | case *frames.DATA: 170 | 171 | // Extract the data. 172 | data := frame.Data 173 | if data == nil { 174 | data = []byte{} 175 | } 176 | 177 | // Give to the client. 178 | s.headerChan <- func() { 179 | s.Receiver.ReceiveData(s.Request, data, frame.Flags.FIN()) 180 | 181 | if frame.Flags.FIN() { 182 | s.state.CloseThere() 183 | s.Close() 184 | } 185 | } 186 | 187 | case *frames.SYN_REPLY: 188 | s.headerChan <- func() { 189 | s.Receiver.ReceiveHeader(s.Request, frame.Header) 190 | 191 | if frame.Flags.FIN() { 192 | s.state.CloseThere() 193 | s.Close() 194 | } 195 | } 196 | 197 | case *frames.HEADERS: 198 | s.headerChan <- func() { 199 | s.Receiver.ReceiveHeader(s.Request, frame.Header) 200 | 201 | if frame.Flags.FIN() { 202 | s.state.CloseThere() 203 | s.Close() 204 | } 205 | } 206 | 207 | case *frames.WINDOW_UPDATE: 208 | // Ignore. 209 | 210 | default: 211 | return errors.New(fmt.Sprintf("Received unknown frame of type %T.", frame)) 212 | } 213 | 214 | return nil 215 | } 216 | 217 | func (s *RequestStream) CloseNotify() <-chan bool { 218 | return s.stop 219 | } 220 | 221 | // run is the main control path of 222 | // the stream. Data is recieved, 223 | // processed, and then the stream 224 | // is cleaned up and closed. 225 | func (s *RequestStream) Run() error { 226 | // Receive and process inbound frames. 227 | <-s.finished 228 | 229 | // Clean up state. 230 | s.state.CloseHere() 231 | return nil 232 | } 233 | 234 | func (s *RequestStream) State() *common.StreamState { 235 | return s.state 236 | } 237 | 238 | func (s *RequestStream) StreamID() common.StreamID { 239 | return s.streamID 240 | } 241 | 242 | func (s *RequestStream) closed() bool { 243 | if s.conn == nil || s.state == nil || s.Receiver == nil { 244 | return true 245 | } 246 | select { 247 | case _ = <-s.stop: 248 | return true 249 | default: 250 | return false 251 | } 252 | } 253 | 254 | // writeHeader is used to flush HTTP headers. 255 | func (s *RequestStream) writeHeader() { 256 | if len(s.header) == 0 { 257 | return 258 | } 259 | 260 | // Create the HEADERS frame. 261 | header := new(frames.HEADERS) 262 | header.StreamID = s.streamID 263 | header.Header = common.CloneHeader(s.header) 264 | 265 | // Clear the headers that have been sent. 266 | for name := range header.Header { 267 | s.header.Del(name) 268 | } 269 | 270 | s.output <- header 271 | } 272 | 273 | func (s *RequestStream) processFrames() { 274 | defer common.Recover() 275 | for f := range s.headerChan { 276 | f() 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /spdy2/requests.go: -------------------------------------------------------------------------------- 1 | package spdy2 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "strings" 9 | 10 | "github.com/SlyMarbo/spdy/common" 11 | "github.com/SlyMarbo/spdy/spdy2/frames" 12 | ) 13 | 14 | // Request is used to make a client request. 15 | func (c *Conn) Request(request *http.Request, receiver common.Receiver, priority common.Priority) (common.Stream, error) { 16 | if c.Closed() { 17 | return nil, common.ErrConnClosed 18 | } 19 | c.goawayLock.Lock() 20 | goaway := c.goawayReceived || c.goawaySent 21 | c.goawayLock.Unlock() 22 | if goaway { 23 | return nil, common.ErrGoaway 24 | } 25 | 26 | if c.server != nil { 27 | return nil, errors.New("Error: Only clients can send requests.") 28 | } 29 | 30 | // Check stream limit would allow the new stream. 31 | if !c.requestStreamLimit.Add() { 32 | return nil, errors.New("Error: Max concurrent streams limit exceeded.") 33 | } 34 | 35 | if !priority.Valid(2) { 36 | return nil, errors.New("Error: Priority must be in the range 0 - 7.") 37 | } 38 | 39 | url := request.URL 40 | if url == nil || url.Scheme == "" || url.Host == "" { 41 | return nil, errors.New("Error: Incomplete path provided to resource.") 42 | } 43 | 44 | // Prepare the SYN_STREAM. 45 | path := url.Path 46 | if url.RawQuery != "" { 47 | path += "?" + url.RawQuery 48 | } 49 | if url.Fragment != "" { 50 | path += "#" + url.Fragment 51 | } 52 | if !strings.HasPrefix(path, "/") { 53 | path = "/" + path 54 | } 55 | 56 | host := url.Host 57 | if request.Host != "" { 58 | host = request.Host 59 | } 60 | 61 | syn := new(frames.SYN_STREAM) 62 | syn.Priority = priority 63 | syn.Header = request.Header 64 | syn.Header.Set("method", request.Method) 65 | syn.Header.Set("url", path) 66 | syn.Header.Set("version", "HTTP/1.1") 67 | syn.Header.Set("host", host) 68 | syn.Header.Set("scheme", url.Scheme) 69 | 70 | // Prepare the request body, if any. 71 | body := make([]*frames.DATA, 0, 1) 72 | if request.Body != nil { 73 | buf := make([]byte, 32*1024) 74 | n, err := request.Body.Read(buf) 75 | if err != nil && err != io.EOF { 76 | return nil, err 77 | } 78 | total := n 79 | for n > 0 { 80 | data := new(frames.DATA) 81 | data.Data = make([]byte, n) 82 | copy(data.Data, buf[:n]) 83 | body = append(body, data) 84 | n, err = request.Body.Read(buf) 85 | if err != nil && err != io.EOF { 86 | return nil, err 87 | } 88 | total += n 89 | } 90 | 91 | // Half-close the stream. 92 | if len(body) == 0 { 93 | syn.Flags = common.FLAG_FIN 94 | } else { 95 | syn.Header.Set("Content-Length", fmt.Sprint(total)) 96 | body[len(body)-1].Flags = common.FLAG_FIN 97 | } 98 | request.Body.Close() 99 | } else { 100 | syn.Flags = common.FLAG_FIN 101 | } 102 | 103 | // Send. 104 | c.streamCreation.Lock() 105 | defer c.streamCreation.Unlock() 106 | 107 | c.lastRequestStreamIDLock.Lock() 108 | if c.lastRequestStreamID == 0 { 109 | c.lastRequestStreamID = 1 110 | } else { 111 | c.lastRequestStreamID += 2 112 | } 113 | syn.StreamID = c.lastRequestStreamID 114 | c.lastRequestStreamIDLock.Unlock() 115 | if syn.StreamID > common.MAX_STREAM_ID { 116 | return nil, errors.New("Error: All client streams exhausted.") 117 | } 118 | c.output[0] <- syn 119 | for _, frame := range body { 120 | frame.StreamID = syn.StreamID 121 | c.output[0] <- frame 122 | } 123 | 124 | // // Create the request stream. 125 | out := NewRequestStream(c, syn.StreamID, c.output[0]) 126 | out.Request = request 127 | out.Receiver = receiver 128 | 129 | // Store in the connection map. 130 | c.streamsLock.Lock() 131 | c.streams[syn.StreamID] = out 132 | c.streamsLock.Unlock() 133 | 134 | return out, nil 135 | } 136 | 137 | func (c *Conn) RequestResponse(request *http.Request, receiver common.Receiver, priority common.Priority) (*http.Response, error) { 138 | res := common.NewResponse(request, receiver) 139 | 140 | // Send the request. 141 | stream, err := c.Request(request, res, priority) 142 | if err != nil { 143 | return nil, err 144 | } 145 | 146 | // Let the request run its course. 147 | stream.Run() 148 | 149 | return res.Response(), c.shutdownError 150 | } 151 | -------------------------------------------------------------------------------- /spdy2/shutdown.go: -------------------------------------------------------------------------------- 1 | package spdy2 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/SlyMarbo/spdy/common" 7 | "github.com/SlyMarbo/spdy/spdy2/frames" 8 | ) 9 | 10 | // Close ends the connection, cleaning up relevant resources. 11 | // Close can be called multiple times safely. 12 | func (c *Conn) Close() (err error) { 13 | c.shutdownOnce.Do(c.shutdown) 14 | return nil 15 | } 16 | 17 | // Closed indicates whether the connection has 18 | // been closed. 19 | func (c *Conn) Closed() bool { 20 | select { 21 | case _ = <-c.stop: 22 | return true 23 | default: 24 | return false 25 | } 26 | } 27 | 28 | func (c *Conn) shutdown() { 29 | if c.Closed() { 30 | return 31 | } 32 | 33 | // Try to inform the other endpoint that the connection is closing. 34 | c.sendingLock.Lock() 35 | isSending := c.sending != nil 36 | c.sendingLock.Unlock() 37 | c.goawayLock.Lock() 38 | sent := c.goawaySent 39 | c.goawayReceived = true 40 | c.goawayLock.Unlock() 41 | if !sent && !isSending { 42 | goaway := new(frames.GOAWAY) 43 | if c.server != nil { 44 | c.lastRequestStreamIDLock.Lock() 45 | goaway.LastGoodStreamID = c.lastRequestStreamID 46 | c.lastRequestStreamIDLock.Unlock() 47 | } else { 48 | c.lastPushStreamIDLock.Lock() 49 | goaway.LastGoodStreamID = c.lastPushStreamID 50 | c.lastPushStreamIDLock.Unlock() 51 | } 52 | select { 53 | case c.output[0] <- goaway: 54 | c.goawayLock.Lock() 55 | c.goawaySent = true 56 | c.goawayLock.Unlock() 57 | case <-time.After(100 * time.Millisecond): 58 | debug.Println("Failed to send closing GOAWAY.") 59 | } 60 | } 61 | 62 | // Close all streams. Make a copy so close() can modify the map. 63 | var streams []common.Stream 64 | c.streamsLock.Lock() 65 | for key, stream := range c.streams { 66 | streams = append(streams, stream) 67 | delete(c.streams, key) 68 | } 69 | c.streamsLock.Unlock() 70 | 71 | for _, stream := range streams { 72 | if err := stream.Close(); err != nil { 73 | debug.Println(err) 74 | } 75 | } 76 | 77 | // Give any pending frames 200ms to send. 78 | c.sendingLock.Lock() 79 | if c.sending == nil { 80 | c.sending = make(chan struct{}) 81 | c.sendingLock.Unlock() 82 | select { 83 | case <-c.sending: 84 | case <-time.After(200 * time.Millisecond): 85 | } 86 | c.sendingLock.Lock() 87 | } 88 | c.sending = nil 89 | c.sendingLock.Unlock() 90 | 91 | select { 92 | case _, ok := <-c.stop: 93 | if ok { 94 | close(c.stop) 95 | } 96 | default: 97 | close(c.stop) 98 | } 99 | 100 | c.connLock.Lock() 101 | if c.conn != nil { 102 | c.conn.Close() 103 | c.conn = nil 104 | } 105 | c.connLock.Unlock() 106 | 107 | if compressor := c.compressor; compressor != nil { 108 | compressor.Close() 109 | } 110 | 111 | c.pushedResources = nil 112 | 113 | for _, stream := range c.output { 114 | select { 115 | case _, ok := <-stream: 116 | if ok { 117 | close(stream) 118 | } 119 | default: 120 | close(stream) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /spdy2/spdy_api.go: -------------------------------------------------------------------------------- 1 | package spdy2 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | "net/url" 7 | "strings" 8 | 9 | "github.com/SlyMarbo/spdy/common" 10 | "github.com/SlyMarbo/spdy/spdy2/frames" 11 | ) 12 | 13 | // Ping is used by spdy.PingServer and spdy.PingClient to send 14 | // SPDY PINGs. 15 | func (c *Conn) Ping() (<-chan bool, error) { 16 | if c.Closed() { 17 | return nil, errors.New("Error: Conn has been closed.") 18 | } 19 | 20 | ping := new(frames.PING) 21 | c.nextPingIDLock.Lock() 22 | pid := c.nextPingID 23 | if pid+2 < pid { 24 | if pid&1 == 0 { 25 | c.nextPingID = 2 26 | } else { 27 | c.nextPingID = 1 28 | } 29 | } else { 30 | c.nextPingID += 2 31 | } 32 | c.nextPingIDLock.Unlock() 33 | ping.PingID = pid 34 | c.output[0] <- ping 35 | ch := make(chan bool, 1) 36 | c.pingsLock.Lock() 37 | c.pings[pid] = ch 38 | c.pingsLock.Unlock() 39 | 40 | return ch, nil 41 | } 42 | 43 | // Push is used to issue a server push to the client. Note that this cannot be performed 44 | // by clients. 45 | func (c *Conn) Push(resource string, origin common.Stream) (common.PushStream, error) { 46 | c.goawayLock.Lock() 47 | goaway := c.goawayReceived || c.goawaySent 48 | c.goawayLock.Unlock() 49 | if goaway { 50 | return nil, common.ErrGoaway 51 | } 52 | 53 | if c.server == nil { 54 | return nil, errors.New("Error: Only servers can send pushes.") 55 | } 56 | 57 | // Parse and check URL. 58 | url, err := url.Parse(resource) 59 | if err != nil { 60 | return nil, err 61 | } 62 | if url.Scheme == "" || url.Host == "" { 63 | return nil, errors.New("Error: Incomplete path provided to resource.") 64 | } 65 | resource = url.String() 66 | 67 | // Ensure the resource hasn't been pushed on the given stream already. 68 | if c.pushedResources[origin] == nil { 69 | c.pushedResources[origin] = map[string]struct{}{ 70 | resource: struct{}{}, 71 | } 72 | } else if _, ok := c.pushedResources[origin][url.String()]; !ok { 73 | c.pushedResources[origin][resource] = struct{}{} 74 | } else { 75 | return nil, errors.New("Error: Resource already pushed to this stream.") 76 | } 77 | 78 | // Check stream limit would allow the new stream. 79 | if !c.pushStreamLimit.Add() { 80 | return nil, errors.New("Error: Max concurrent streams limit exceeded.") 81 | } 82 | 83 | // Verify that path is prefixed with / as required by spec. 84 | path := url.Path 85 | if !strings.HasPrefix(path, "/") { 86 | path = "/" + path 87 | } 88 | 89 | // Prepare the SYN_STREAM. 90 | push := new(frames.SYN_STREAM) 91 | push.Flags = common.FLAG_UNIDIRECTIONAL 92 | push.AssocStreamID = origin.StreamID() 93 | push.Priority = 3 94 | push.Header = make(http.Header) 95 | push.Header.Set("scheme", url.Scheme) 96 | push.Header.Set("host", url.Host) 97 | push.Header.Set("url", path) 98 | push.Header.Set("version", "HTTP/1.1") 99 | 100 | // Send. 101 | c.streamCreation.Lock() 102 | defer c.streamCreation.Unlock() 103 | 104 | c.lastPushStreamIDLock.Lock() 105 | c.lastPushStreamID += 2 106 | newID := c.lastPushStreamID 107 | c.lastPushStreamIDLock.Unlock() 108 | if newID > common.MAX_STREAM_ID { 109 | return nil, errors.New("Error: All server streams exhausted.") 110 | } 111 | push.StreamID = newID 112 | c.output[0] <- push 113 | 114 | // Create the PushStream. 115 | out := NewPushStream(c, newID, origin, c.output[3]) 116 | 117 | // Store in the connection map. 118 | c.streamsLock.Lock() 119 | c.streams[newID] = out 120 | c.streamsLock.Unlock() 121 | 122 | return out, nil 123 | } 124 | -------------------------------------------------------------------------------- /spdy2/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy2 6 | 7 | import ( 8 | "github.com/SlyMarbo/spdy/common" 9 | ) 10 | 11 | // defaultServerSettings are used in initialising the connection. 12 | // It takes the max concurrent streams. 13 | func defaultServerSettings(m uint32) common.Settings { 14 | return common.Settings{ 15 | common.SETTINGS_MAX_CONCURRENT_STREAMS: &common.Setting{ 16 | Flags: common.FLAG_SETTINGS_PERSIST_VALUE, 17 | ID: common.SETTINGS_MAX_CONCURRENT_STREAMS, 18 | Value: m, 19 | }, 20 | } 21 | } 22 | 23 | // defaultClientSettings are used in initialising the connection. 24 | // It takes the max concurrent streams. 25 | func defaultClientSettings(m uint32) common.Settings { 26 | return common.Settings{ 27 | common.SETTINGS_MAX_CONCURRENT_STREAMS: &common.Setting{ 28 | ID: common.SETTINGS_MAX_CONCURRENT_STREAMS, 29 | Value: m, 30 | }, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spdy3/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package spdy3 contains functionality for SPDY/3. 6 | package spdy3 7 | -------------------------------------------------------------------------------- /spdy3/error_handling.go: -------------------------------------------------------------------------------- 1 | package spdy3 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "time" 7 | 8 | "github.com/SlyMarbo/spdy/common" 9 | "github.com/SlyMarbo/spdy/spdy3/frames" 10 | ) 11 | 12 | // check returns the error condition and 13 | // updates the connection accordingly. 14 | func (c *Conn) check(condition bool, format string, v ...interface{}) bool { 15 | if !condition { 16 | return false 17 | } 18 | log.Printf("Error: "+format+".\n", v...) 19 | c.numBenignErrors++ 20 | return true 21 | } 22 | 23 | // criticalCheck returns the error condition 24 | // and ends the connection accordingly. 25 | func (c *Conn) criticalCheck(condition bool, sid common.StreamID, format string, v ...interface{}) bool { 26 | if !condition { 27 | return false 28 | } 29 | log.Printf("Error: "+format+".\n", v...) 30 | c.protocolError(sid) 31 | return true 32 | } 33 | 34 | func (c *Conn) _RST_STREAM(streamID common.StreamID, status common.StatusCode) { 35 | rst := new(frames.RST_STREAM) 36 | rst.StreamID = streamID 37 | rst.Status = status 38 | c.output[0] <- rst 39 | } 40 | 41 | func (c *Conn) _GOAWAY(status common.StatusCode) { 42 | goaway := new(frames.GOAWAY) 43 | goaway.Status = status 44 | c.output[0] <- goaway 45 | c.Close() 46 | } 47 | 48 | // handleReadWriteError differentiates between normal and 49 | // unexpected errors when performing I/O with the network, 50 | // then shuts down the connection. 51 | func (c *Conn) handleReadWriteError(err error) { 52 | if _, ok := err.(*net.OpError); ok || err == io.EOF || err == common.ErrConnNil || 53 | err.Error() == "use of closed network connection" { 54 | // Client has closed the TCP connection. 55 | debug.Println("Note: Endpoint has disconnected.") 56 | } else { 57 | // Unexpected error which prevented a read/write. 58 | log.Printf("Error: Encountered error: %q (%T)\n", err.Error(), err) 59 | } 60 | 61 | // Make sure c.Close succeeds and sending stops. 62 | c.sendingLock.Lock() 63 | if c.sending == nil { 64 | c.sending = make(chan struct{}) 65 | } 66 | c.sendingLock.Unlock() 67 | 68 | c.Close() 69 | } 70 | 71 | // protocolError informs the other endpoint that a protocol error has 72 | // occurred, stops all running streams, and ends the connection. 73 | func (c *Conn) protocolError(streamID common.StreamID) { 74 | reply := new(frames.RST_STREAM) 75 | reply.StreamID = streamID 76 | reply.Status = common.RST_STREAM_PROTOCOL_ERROR 77 | select { 78 | case c.output[0] <- reply: 79 | case <-time.After(100 * time.Millisecond): 80 | debug.Println("Failed to send PROTOCOL_ERROR RST_STREAM.") 81 | } 82 | if c.shutdownError == nil { 83 | c.shutdownError = reply 84 | } 85 | c.Close() 86 | } 87 | -------------------------------------------------------------------------------- /spdy3/frames/common.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bufio" 9 | "errors" 10 | "fmt" 11 | 12 | "github.com/SlyMarbo/spdy/common" 13 | ) 14 | 15 | // ReadFrame reads and parses a frame from reader. 16 | func ReadFrame(reader *bufio.Reader, subversion int) (frame common.Frame, err error) { 17 | start, err := reader.Peek(4) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | if start[0] != 128 { 23 | frame = new(DATA) 24 | _, err = frame.ReadFrom(reader) 25 | return frame, err 26 | } 27 | 28 | switch common.BytesToUint16(start[2:4]) { 29 | case _SYN_STREAM: 30 | switch subversion { 31 | case 0: 32 | frame = new(SYN_STREAM) 33 | case 1: 34 | frame = new(SYN_STREAMV3_1) 35 | default: 36 | return nil, fmt.Errorf("Error: Given subversion %d is unrecognised.", subversion) 37 | } 38 | case _SYN_REPLY: 39 | frame = new(SYN_REPLY) 40 | case _RST_STREAM: 41 | frame = new(RST_STREAM) 42 | case _SETTINGS: 43 | frame = new(SETTINGS) 44 | case _PING: 45 | frame = new(PING) 46 | case _GOAWAY: 47 | frame = new(GOAWAY) 48 | case _HEADERS: 49 | frame = new(HEADERS) 50 | case _WINDOW_UPDATE: 51 | frame = &WINDOW_UPDATE{subversion: subversion} 52 | case _CREDENTIAL: 53 | frame = new(CREDENTIAL) 54 | 55 | default: 56 | return nil, errors.New("Error Failed to parse frame type.") 57 | } 58 | 59 | _, err = frame.ReadFrom(reader) 60 | return frame, err 61 | } 62 | 63 | // controlFrameCommonProcessing performs checks identical between 64 | // all control frames. This includes the control bit, the version 65 | // number, the type byte (which is checked against the byte 66 | // provided), and the flags (which are checked against the bitwise 67 | // OR of valid flags provided). 68 | func controlFrameCommonProcessing(data []byte, frameType uint16, flags byte) error { 69 | // Check it's a control frame. 70 | if data[0] != 128 { 71 | return common.IncorrectFrame(_DATA_FRAME, int(frameType), 3) 72 | } 73 | 74 | // Check version. 75 | version := (uint16(data[0]&0x7f) << 8) + uint16(data[1]) 76 | if version != 3 { 77 | return common.UnsupportedVersion(version) 78 | } 79 | 80 | // Check its type. 81 | realType := common.BytesToUint16(data[2:]) 82 | if realType != frameType { 83 | return common.IncorrectFrame(int(realType), int(frameType), 3) 84 | } 85 | 86 | // Check the flags. 87 | if data[4] & ^flags != 0 { 88 | return common.InvalidField("flags", int(data[4]), int(flags)) 89 | } 90 | 91 | return nil 92 | } 93 | 94 | // Frame types in SPDY/3 95 | const ( 96 | _SYN_STREAM = 1 97 | _SYN_REPLY = 2 98 | _RST_STREAM = 3 99 | _SETTINGS = 4 100 | _PING = 6 101 | _GOAWAY = 7 102 | _HEADERS = 8 103 | _WINDOW_UPDATE = 9 104 | _CREDENTIAL = 10 105 | _CONTROL_FRAME = -1 106 | _DATA_FRAME = -2 107 | ) 108 | 109 | // frameNames provides the name for a particular SPDY/3 110 | // frame type. 111 | var frameNames = map[int]string{ 112 | _SYN_STREAM: "SYN_STREAM", 113 | _SYN_REPLY: "SYN_REPLY", 114 | _RST_STREAM: "RST_STREAM", 115 | _SETTINGS: "SETTINGS", 116 | _PING: "PING", 117 | _GOAWAY: "GOAWAY", 118 | _HEADERS: "HEADERS", 119 | _WINDOW_UPDATE: "WINDOW_UPDATE", 120 | _CREDENTIAL: "CREDENTIAL", 121 | _CONTROL_FRAME: "CONTROL_FRAME", 122 | _DATA_FRAME: "DATA_FRAME", 123 | } 124 | -------------------------------------------------------------------------------- /spdy3/frames/credential.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "crypto/x509" 10 | "fmt" 11 | "io" 12 | 13 | "github.com/SlyMarbo/spdy/common" 14 | ) 15 | 16 | type CREDENTIAL struct { 17 | Slot uint16 18 | Proof []byte 19 | Certificates []*x509.Certificate 20 | } 21 | 22 | func (frame *CREDENTIAL) Compress(comp common.Compressor) error { 23 | return nil 24 | } 25 | 26 | func (frame *CREDENTIAL) Decompress(decomp common.Decompressor) error { 27 | return nil 28 | } 29 | 30 | func (frame *CREDENTIAL) Name() string { 31 | return "CREDENTIAL" 32 | } 33 | 34 | func (frame *CREDENTIAL) ReadFrom(reader io.Reader) (int64, error) { 35 | c := common.ReadCounter{R: reader} 36 | data, err := common.ReadExactly(&c, 18) 37 | if err != nil { 38 | return c.N, err 39 | } 40 | 41 | err = controlFrameCommonProcessing(data[:5], _CREDENTIAL, 0) 42 | if err != nil { 43 | return c.N, err 44 | } 45 | 46 | // Get and check length. 47 | length := int(common.BytesToUint24(data[5:8])) 48 | if length < 6 { 49 | return c.N, common.IncorrectDataLength(length, 6) 50 | } else if length > common.MAX_FRAME_SIZE-8 { 51 | return c.N, common.FrameTooLarge 52 | } 53 | 54 | // Read in data. 55 | certs, err := common.ReadExactly(&c, length-10) 56 | if err != nil { 57 | return c.N, err 58 | } 59 | 60 | frame.Slot = common.BytesToUint16(data[8:10]) 61 | proofLen := int(common.BytesToUint32(data[10:14])) 62 | if proofLen > 0 { 63 | frame.Proof = data[14 : 14+proofLen] 64 | } else { 65 | frame.Proof = []byte{} 66 | } 67 | 68 | numCerts := 0 69 | for offset := 0; offset < length-10; { 70 | offset += int(common.BytesToUint32(certs[offset:offset+4])) + 4 71 | numCerts++ 72 | } 73 | 74 | frame.Certificates = make([]*x509.Certificate, numCerts) 75 | for i, offset := 0, 0; offset < length-10; i++ { 76 | length := int(common.BytesToUint32(certs[offset : offset+4])) 77 | rawCert := certs[offset+4 : offset+4+length] 78 | frame.Certificates[i], err = x509.ParseCertificate(rawCert) 79 | if err != nil { 80 | return c.N, err 81 | } 82 | offset += length + 4 83 | } 84 | 85 | return c.N, nil 86 | } 87 | 88 | func (frame *CREDENTIAL) String() string { 89 | buf := new(bytes.Buffer) 90 | 91 | buf.WriteString("CREDENTIAL {\n\t") 92 | buf.WriteString(fmt.Sprintf("Version: 3\n\t")) 93 | buf.WriteString(fmt.Sprintf("Slot: %d\n\t", frame.Slot)) 94 | buf.WriteString(fmt.Sprintf("Proof: %v\n\t", frame.Proof)) 95 | buf.WriteString(fmt.Sprintf("Certificates: %v\n}\n", frame.Certificates)) 96 | 97 | return buf.String() 98 | } 99 | 100 | func (frame *CREDENTIAL) WriteTo(writer io.Writer) (int64, error) { 101 | c := common.WriteCounter{W: writer} 102 | proofLength := len(frame.Proof) 103 | certsLength := 0 104 | for _, cert := range frame.Certificates { 105 | certsLength += len(cert.Raw) 106 | } 107 | 108 | length := 6 + proofLength + certsLength 109 | out := make([]byte, 14) 110 | 111 | out[0] = 128 // Control bit and Version 112 | out[1] = 3 // Version 113 | out[2] = 0 // Type 114 | out[3] = 10 // Type 115 | out[4] = 0 // common.Flags 116 | out[5] = byte(length >> 16) // Length 117 | out[6] = byte(length >> 8) // Length 118 | out[7] = byte(length) // Length 119 | out[8] = byte(frame.Slot >> 8) // Slot 120 | out[9] = byte(frame.Slot) // Slot 121 | out[10] = byte(proofLength >> 24) // Proof Length 122 | out[11] = byte(proofLength >> 16) // Proof Length 123 | out[12] = byte(proofLength >> 8) // Proof Length 124 | out[13] = byte(proofLength) // Proof Length 125 | 126 | err := common.WriteExactly(&c, out) 127 | if err != nil { 128 | return c.N, err 129 | } 130 | 131 | if len(frame.Proof) > 0 { 132 | err = common.WriteExactly(&c, frame.Proof) 133 | if err != nil { 134 | return c.N, err 135 | } 136 | } 137 | 138 | written := int64(14 + len(frame.Proof)) 139 | for _, cert := range frame.Certificates { 140 | err = common.WriteExactly(&c, cert.Raw) 141 | if err != nil { 142 | return c.N, err 143 | } 144 | written += int64(len(cert.Raw)) 145 | } 146 | 147 | return c.N, nil 148 | } 149 | -------------------------------------------------------------------------------- /spdy3/frames/data.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | 13 | "github.com/SlyMarbo/spdy/common" 14 | ) 15 | 16 | type DATA struct { 17 | StreamID common.StreamID 18 | Flags common.Flags 19 | Data []byte 20 | } 21 | 22 | func (frame *DATA) Compress(comp common.Compressor) error { 23 | return nil 24 | } 25 | 26 | func (frame *DATA) Decompress(decomp common.Decompressor) error { 27 | return nil 28 | } 29 | 30 | func (frame *DATA) Name() string { 31 | return "DATA" 32 | } 33 | 34 | func (frame *DATA) ReadFrom(reader io.Reader) (int64, error) { 35 | c := common.ReadCounter{R: reader} 36 | data, err := common.ReadExactly(&c, 8) 37 | if err != nil { 38 | return c.N, err 39 | } 40 | 41 | // Check it's a data frame. 42 | if data[0]&0x80 == 1 { 43 | return c.N, common.IncorrectFrame(_CONTROL_FRAME, _DATA_FRAME, 3) 44 | } 45 | 46 | // Check flags. 47 | if data[4] & ^byte(common.FLAG_FIN) != 0 { 48 | return c.N, common.InvalidField("flags", int(data[4]), common.FLAG_FIN) 49 | } 50 | 51 | // Get and check length. 52 | length := int(common.BytesToUint24(data[5:8])) 53 | if length == 0 && data[4] == 0 { 54 | return c.N, common.IncorrectDataLength(length, 1) 55 | } else if length > common.MAX_FRAME_SIZE-8 { 56 | return c.N, common.FrameTooLarge 57 | } 58 | 59 | // Read in data. 60 | if length != 0 { 61 | frame.Data, err = common.ReadExactly(&c, length) 62 | if err != nil { 63 | return c.N, err 64 | } 65 | } 66 | 67 | frame.StreamID = common.StreamID(common.BytesToUint32(data[0:4])) 68 | frame.Flags = common.Flags(data[4]) 69 | if frame.Data == nil { 70 | frame.Data = []byte{} 71 | } 72 | 73 | return c.N, nil 74 | } 75 | 76 | func (frame *DATA) String() string { 77 | buf := new(bytes.Buffer) 78 | 79 | flags := "" 80 | if frame.Flags.FIN() { 81 | flags += " common.FLAG_FIN" 82 | } 83 | if flags == "" { 84 | flags = "[NONE]" 85 | } else { 86 | flags = flags[1:] 87 | } 88 | 89 | buf.WriteString("DATA {\n\t") 90 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 91 | buf.WriteString(fmt.Sprintf("Flags: %s\n\t", flags)) 92 | buf.WriteString(fmt.Sprintf("Length: %d\n\t", len(frame.Data))) 93 | if common.VerboseLogging || len(frame.Data) <= 21 { 94 | buf.WriteString(fmt.Sprintf("Data: [% x]\n}\n", frame.Data)) 95 | } else { 96 | buf.WriteString(fmt.Sprintf("Data: [% x ... % x]\n}\n", frame.Data[:9], 97 | frame.Data[len(frame.Data)-9:])) 98 | } 99 | 100 | return buf.String() 101 | } 102 | 103 | func (frame *DATA) WriteTo(writer io.Writer) (int64, error) { 104 | c := common.WriteCounter{W: writer} 105 | length := len(frame.Data) 106 | if length > common.MAX_DATA_SIZE { 107 | return c.N, errors.New("Error: Data size too large.") 108 | } 109 | if length == 0 && !frame.Flags.FIN() { 110 | return c.N, errors.New("Error: Data is empty.") 111 | } 112 | 113 | out := make([]byte, 8) 114 | 115 | out[0] = frame.StreamID.B1() // Control bit and Stream ID 116 | out[1] = frame.StreamID.B2() // Stream ID 117 | out[2] = frame.StreamID.B3() // Stream ID 118 | out[3] = frame.StreamID.B4() // Stream ID 119 | out[4] = byte(frame.Flags) // Flags 120 | out[5] = byte(length >> 16) // Length 121 | out[6] = byte(length >> 8) // Length 122 | out[7] = byte(length) // Length 123 | 124 | if err := common.WriteExactly(&c, out); err != nil { 125 | return c.N, err 126 | } 127 | 128 | if err := common.WriteExactly(&c, frame.Data); err != nil { 129 | return c.N, err 130 | } 131 | 132 | return c.N, nil 133 | } 134 | -------------------------------------------------------------------------------- /spdy3/frames/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package frames contains an implementation of the SPDY/3 frames. 6 | package frames 7 | -------------------------------------------------------------------------------- /spdy3/frames/goaway.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | 12 | "github.com/SlyMarbo/spdy/common" 13 | ) 14 | 15 | type GOAWAY struct { 16 | LastGoodStreamID common.StreamID 17 | Status common.StatusCode 18 | } 19 | 20 | func (frame *GOAWAY) Compress(comp common.Compressor) error { 21 | return nil 22 | } 23 | 24 | func (frame *GOAWAY) Decompress(decomp common.Decompressor) error { 25 | return nil 26 | } 27 | 28 | func (frame *GOAWAY) Error() string { 29 | if err := frame.Status.String(); err != "" { 30 | return err 31 | } 32 | 33 | return fmt.Sprintf("[unknown status code %d]", frame.Status) 34 | } 35 | 36 | func (frame *GOAWAY) Name() string { 37 | return "GOAWAY" 38 | } 39 | 40 | func (frame *GOAWAY) ReadFrom(reader io.Reader) (int64, error) { 41 | c := common.ReadCounter{R: reader} 42 | data, err := common.ReadExactly(&c, 16) 43 | if err != nil { 44 | return c.N, err 45 | } 46 | 47 | err = controlFrameCommonProcessing(data[:5], _GOAWAY, 0) 48 | if err != nil { 49 | return c.N, err 50 | } 51 | 52 | // Get and check length. 53 | length := int(common.BytesToUint24(data[5:8])) 54 | if length != 8 { 55 | return c.N, common.IncorrectDataLength(length, 8) 56 | } 57 | 58 | frame.LastGoodStreamID = common.StreamID(common.BytesToUint32(data[8:12])) 59 | frame.Status = common.StatusCode(common.BytesToUint32(data[12:16])) 60 | 61 | if !frame.LastGoodStreamID.Valid() { 62 | return c.N, common.StreamIdTooLarge 63 | } 64 | 65 | return c.N, nil 66 | } 67 | 68 | func (frame *GOAWAY) String() string { 69 | buf := new(bytes.Buffer) 70 | 71 | buf.WriteString("GOAWAY {\n\t") 72 | buf.WriteString(fmt.Sprintf("Version: 3\n\t")) 73 | buf.WriteString(fmt.Sprintf("Last good stream ID: %d\n\t", frame.LastGoodStreamID)) 74 | buf.WriteString(fmt.Sprintf("Status code: %s (%d)\n}\n", frame.Status, frame.Status)) 75 | 76 | return buf.String() 77 | } 78 | 79 | func (frame *GOAWAY) WriteTo(writer io.Writer) (int64, error) { 80 | c := common.WriteCounter{W: writer} 81 | if !frame.LastGoodStreamID.Valid() { 82 | return c.N, common.StreamIdTooLarge 83 | } 84 | 85 | out := make([]byte, 16) 86 | 87 | out[0] = 128 // Control bit and Version 88 | out[1] = 3 // Version 89 | out[2] = 0 // Type 90 | out[3] = 7 // Type 91 | out[4] = 0 // Flags 92 | out[5] = 0 // Length 93 | out[6] = 0 // Length 94 | out[7] = 8 // Length 95 | out[8] = frame.LastGoodStreamID.B1() // Last Good Stream ID 96 | out[9] = frame.LastGoodStreamID.B2() // Last Good Stream ID 97 | out[10] = frame.LastGoodStreamID.B3() // Last Good Stream ID 98 | out[11] = frame.LastGoodStreamID.B4() // Last Good Stream ID 99 | out[12] = byte(frame.Status >> 24) // Status Code 100 | out[13] = byte(frame.Status >> 16) // Status Code 101 | out[14] = byte(frame.Status >> 8) // Status Code 102 | out[15] = byte(frame.Status) // Status Code 103 | 104 | err := common.WriteExactly(&c, out) 105 | if err != nil { 106 | return c.N, err 107 | } 108 | 109 | return c.N, nil 110 | } 111 | -------------------------------------------------------------------------------- /spdy3/frames/headers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "net/http" 13 | 14 | "github.com/SlyMarbo/spdy/common" 15 | ) 16 | 17 | type HEADERS struct { 18 | Flags common.Flags 19 | StreamID common.StreamID 20 | Header http.Header 21 | rawHeader []byte 22 | } 23 | 24 | func (frame *HEADERS) Compress(com common.Compressor) error { 25 | if frame.rawHeader != nil { 26 | return nil 27 | } 28 | 29 | data, err := com.Compress(frame.Header) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | frame.rawHeader = data 35 | return nil 36 | } 37 | 38 | func (frame *HEADERS) Decompress(decom common.Decompressor) error { 39 | if frame.Header != nil { 40 | return nil 41 | } 42 | 43 | header, err := decom.Decompress(frame.rawHeader) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | frame.Header = header 49 | frame.rawHeader = nil 50 | return nil 51 | } 52 | 53 | func (frame *HEADERS) Name() string { 54 | return "HEADERS" 55 | } 56 | 57 | func (frame *HEADERS) ReadFrom(reader io.Reader) (int64, error) { 58 | c := common.ReadCounter{R: reader} 59 | data, err := common.ReadExactly(&c, 12) 60 | if err != nil { 61 | return c.N, err 62 | } 63 | 64 | err = controlFrameCommonProcessing(data[:5], _HEADERS, common.FLAG_FIN) 65 | if err != nil { 66 | return c.N, err 67 | } 68 | 69 | // Get and check length. 70 | length := int(common.BytesToUint24(data[5:8])) 71 | if length < 4 { 72 | return c.N, common.IncorrectDataLength(length, 4) 73 | } else if length > common.MAX_FRAME_SIZE-8 { 74 | return c.N, common.FrameTooLarge 75 | } 76 | 77 | // Read in data. 78 | header, err := common.ReadExactly(&c, length-4) 79 | if err != nil { 80 | return c.N, err 81 | } 82 | 83 | frame.Flags = common.Flags(data[4]) 84 | frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12])) 85 | frame.rawHeader = header 86 | 87 | if !frame.StreamID.Valid() { 88 | return c.N, common.StreamIdTooLarge 89 | } 90 | if frame.StreamID.Zero() { 91 | return c.N, common.StreamIdIsZero 92 | } 93 | 94 | return c.N, nil 95 | } 96 | 97 | func (frame *HEADERS) String() string { 98 | buf := new(bytes.Buffer) 99 | 100 | flags := "" 101 | if frame.Flags.FIN() { 102 | flags += " common.FLAG_FIN" 103 | } 104 | if flags == "" { 105 | flags = "[NONE]" 106 | } else { 107 | flags = flags[1:] 108 | } 109 | 110 | buf.WriteString("HEADERS {\n\t") 111 | buf.WriteString(fmt.Sprintf("Version: 3\n\t")) 112 | buf.WriteString(fmt.Sprintf("Flags: %s\n\t", flags)) 113 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 114 | buf.WriteString(fmt.Sprintf("Header: %#v\n}\n", frame.Header)) 115 | 116 | return buf.String() 117 | } 118 | 119 | func (frame *HEADERS) WriteTo(writer io.Writer) (int64, error) { 120 | c := common.WriteCounter{W: writer} 121 | if frame.rawHeader == nil { 122 | return c.N, errors.New("Error: Headers not written.") 123 | } 124 | if !frame.StreamID.Valid() { 125 | return c.N, common.StreamIdTooLarge 126 | } 127 | if frame.StreamID.Zero() { 128 | return c.N, common.StreamIdIsZero 129 | } 130 | 131 | header := frame.rawHeader 132 | length := 4 + len(header) 133 | out := make([]byte, 12) 134 | 135 | out[0] = 128 // Control bit and Version 136 | out[1] = 3 // Version 137 | out[2] = 0 // Type 138 | out[3] = 8 // Type 139 | out[4] = byte(frame.Flags) // Flags 140 | out[5] = byte(length >> 16) // Length 141 | out[6] = byte(length >> 8) // Length 142 | out[7] = byte(length) // Length 143 | out[8] = frame.StreamID.B1() // Stream ID 144 | out[9] = frame.StreamID.B2() // Stream ID 145 | out[10] = frame.StreamID.B3() // Stream ID 146 | out[11] = frame.StreamID.B4() // Stream ID 147 | 148 | err := common.WriteExactly(&c, out) 149 | if err != nil { 150 | return c.N, err 151 | } 152 | 153 | err = common.WriteExactly(&c, header) 154 | if err != nil { 155 | return c.N, err 156 | } 157 | 158 | return c.N, nil 159 | } 160 | -------------------------------------------------------------------------------- /spdy3/frames/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "github.com/SlyMarbo/spdy/common" 9 | ) 10 | 11 | var log = common.GetLogger() 12 | var debug = common.GetDebugLogger() 13 | -------------------------------------------------------------------------------- /spdy3/frames/ping.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | 12 | "github.com/SlyMarbo/spdy/common" 13 | ) 14 | 15 | type PING struct { 16 | PingID uint32 17 | } 18 | 19 | func (frame *PING) Compress(comp common.Compressor) error { 20 | return nil 21 | } 22 | 23 | func (frame *PING) Decompress(decomp common.Decompressor) error { 24 | return nil 25 | } 26 | 27 | func (frame *PING) Name() string { 28 | return "PING" 29 | } 30 | 31 | func (frame *PING) ReadFrom(reader io.Reader) (int64, error) { 32 | c := common.ReadCounter{R: reader} 33 | data, err := common.ReadExactly(&c, 12) 34 | if err != nil { 35 | return c.N, err 36 | } 37 | 38 | err = controlFrameCommonProcessing(data[:5], _PING, 0) 39 | if err != nil { 40 | return c.N, err 41 | } 42 | 43 | // Get and check length. 44 | length := int(common.BytesToUint24(data[5:8])) 45 | if length != 4 { 46 | return c.N, common.IncorrectDataLength(length, 4) 47 | } 48 | 49 | frame.PingID = common.BytesToUint32(data[8:12]) 50 | 51 | return c.N, nil 52 | } 53 | 54 | func (frame *PING) String() string { 55 | buf := new(bytes.Buffer) 56 | 57 | buf.WriteString("PING {\n\t") 58 | buf.WriteString(fmt.Sprintf("Version: 3\n\t")) 59 | buf.WriteString(fmt.Sprintf("Ping ID: %d\n}\n", frame.PingID)) 60 | 61 | return buf.String() 62 | } 63 | 64 | func (frame *PING) WriteTo(writer io.Writer) (int64, error) { 65 | c := common.WriteCounter{W: writer} 66 | out := make([]byte, 12) 67 | 68 | out[0] = 128 // Control bit and Version 69 | out[1] = 3 // Version 70 | out[2] = 0 // Type 71 | out[3] = 6 // Type 72 | out[4] = 0 // common.Flags 73 | out[5] = 0 // Length 74 | out[6] = 0 // Length 75 | out[7] = 4 // Length 76 | out[8] = byte(frame.PingID >> 24) // Ping ID 77 | out[9] = byte(frame.PingID >> 16) // Ping ID 78 | out[10] = byte(frame.PingID >> 8) // Ping ID 79 | out[11] = byte(frame.PingID) // Ping ID 80 | 81 | err := common.WriteExactly(&c, out) 82 | if err != nil { 83 | return c.N, err 84 | } 85 | 86 | return c.N, nil 87 | } 88 | -------------------------------------------------------------------------------- /spdy3/frames/rst_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | 12 | "github.com/SlyMarbo/spdy/common" 13 | ) 14 | 15 | type RST_STREAM struct { 16 | StreamID common.StreamID 17 | Status common.StatusCode 18 | } 19 | 20 | func (frame *RST_STREAM) Compress(comp common.Compressor) error { 21 | return nil 22 | } 23 | 24 | func (frame *RST_STREAM) Decompress(decomp common.Decompressor) error { 25 | return nil 26 | } 27 | 28 | func (frame *RST_STREAM) Error() string { 29 | if err := frame.Status.String(); err != "" { 30 | return err 31 | } 32 | 33 | return fmt.Sprintf("[unknown status code %d]", frame.Status) 34 | } 35 | 36 | func (frame *RST_STREAM) Name() string { 37 | return "RST_STREAM" 38 | } 39 | 40 | func (frame *RST_STREAM) ReadFrom(reader io.Reader) (int64, error) { 41 | c := common.ReadCounter{R: reader} 42 | data, err := common.ReadExactly(&c, 16) 43 | if err != nil { 44 | return c.N, err 45 | } 46 | 47 | err = controlFrameCommonProcessing(data[:5], _RST_STREAM, 0) 48 | if err != nil { 49 | return c.N, err 50 | } 51 | 52 | // Get and check length. 53 | length := int(common.BytesToUint24(data[5:8])) 54 | if length != 8 { 55 | return c.N, common.IncorrectDataLength(length, 8) 56 | } else if length > common.MAX_FRAME_SIZE-8 { 57 | return c.N, common.FrameTooLarge 58 | } 59 | 60 | frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12])) 61 | frame.Status = common.StatusCode(common.BytesToUint32(data[12:16])) 62 | 63 | if !frame.StreamID.Valid() { 64 | return c.N, common.StreamIdTooLarge 65 | } 66 | 67 | return c.N, nil 68 | } 69 | 70 | func (frame *RST_STREAM) String() string { 71 | buf := new(bytes.Buffer) 72 | 73 | buf.WriteString("RST_STREAM {\n\t") 74 | buf.WriteString(fmt.Sprintf("Version: 3\n\t")) 75 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 76 | buf.WriteString(fmt.Sprintf("Status code: %s\n}\n", frame.Status)) 77 | 78 | return buf.String() 79 | } 80 | 81 | func (frame *RST_STREAM) WriteTo(writer io.Writer) (int64, error) { 82 | c := common.WriteCounter{W: writer} 83 | if !frame.StreamID.Valid() { 84 | return c.N, common.StreamIdTooLarge 85 | } 86 | 87 | out := make([]byte, 16) 88 | 89 | out[0] = 128 // Control bit and Version 90 | out[1] = 3 // Version 91 | out[2] = 0 // Type 92 | out[3] = 3 // Type 93 | out[4] = 0 // Flags 94 | out[5] = 0 // Length 95 | out[6] = 0 // Length 96 | out[7] = 8 // Length 97 | out[8] = frame.StreamID.B1() // Stream ID 98 | out[9] = frame.StreamID.B2() // Stream ID 99 | out[10] = frame.StreamID.B3() // Stream ID 100 | out[11] = frame.StreamID.B4() // Stream ID 101 | out[12] = frame.Status.B1() // Status 102 | out[13] = frame.Status.B2() // Status 103 | out[14] = frame.Status.B3() // Status 104 | out[15] = frame.Status.B4() // Status 105 | 106 | err := common.WriteExactly(&c, out) 107 | if err != nil { 108 | return c.N, err 109 | } 110 | 111 | return c.N, nil 112 | } 113 | -------------------------------------------------------------------------------- /spdy3/frames/settings.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "sort" 13 | 14 | "github.com/SlyMarbo/spdy/common" 15 | ) 16 | 17 | type SETTINGS struct { 18 | Flags common.Flags 19 | Settings common.Settings 20 | } 21 | 22 | func (frame *SETTINGS) Add(flags common.Flags, id uint32, value uint32) { 23 | frame.Settings[id] = &common.Setting{flags, id, value} 24 | } 25 | 26 | func (frame *SETTINGS) Compress(comp common.Compressor) error { 27 | return nil 28 | } 29 | 30 | func (frame *SETTINGS) Decompress(decomp common.Decompressor) error { 31 | return nil 32 | } 33 | 34 | func (frame *SETTINGS) Name() string { 35 | return "SETTINGS" 36 | } 37 | 38 | func (frame *SETTINGS) ReadFrom(reader io.Reader) (int64, error) { 39 | c := common.ReadCounter{R: reader} 40 | 41 | data, err := common.ReadExactly(&c, 12) 42 | if err != nil { 43 | return c.N, err 44 | } 45 | 46 | err = controlFrameCommonProcessing(data[:5], _SETTINGS, common.FLAG_SETTINGS_CLEAR_SETTINGS) 47 | if err != nil { 48 | return c.N, err 49 | } 50 | 51 | // Get and check length. 52 | length := int(common.BytesToUint24(data[5:8])) 53 | if length < 4 { 54 | return c.N, common.IncorrectDataLength(length, 8) 55 | } else if length > common.MAX_FRAME_SIZE-8 { 56 | return c.N, common.FrameTooLarge 57 | } 58 | 59 | // Check size. 60 | numSettings := int(common.BytesToUint32(data[8:12])) 61 | if length != 4+(8*numSettings) { 62 | return c.N, common.IncorrectDataLength(length, 4+(8*numSettings)) 63 | } 64 | 65 | // Read in data. 66 | settings, err := common.ReadExactly(&c, 8*numSettings) 67 | if err != nil { 68 | return c.N, err 69 | } 70 | 71 | frame.Flags = common.Flags(data[4]) 72 | frame.Settings = make(common.Settings) 73 | for i := 0; i < numSettings; i++ { 74 | j := i * 8 75 | setting := decodeSetting(settings[j:]) 76 | if setting == nil { 77 | return c.N, errors.New("Error: Failed to parse settings.") 78 | } 79 | frame.Settings[setting.ID] = setting 80 | } 81 | 82 | return c.N, nil 83 | } 84 | 85 | func (frame *SETTINGS) String() string { 86 | buf := new(bytes.Buffer) 87 | flags := "" 88 | if frame.Flags.CLEAR_SETTINGS() { 89 | flags += " FLAG_SETTINGS_CLEAR_SETTINGS" 90 | } 91 | if flags == "" { 92 | flags = "[NONE]" 93 | } else { 94 | flags = flags[1:] 95 | } 96 | 97 | buf.WriteString("SETTINGS {\n\t") 98 | buf.WriteString(fmt.Sprintf("Version: 3\n\t")) 99 | buf.WriteString(fmt.Sprintf("Flags: %s\n\t", flags)) 100 | buf.WriteString(fmt.Sprintf("Settings:\n")) 101 | settings := frame.Settings.Settings() 102 | for _, setting := range settings { 103 | buf.WriteString("\t\t" + setting.String() + "\n") 104 | } 105 | buf.WriteString("}\n") 106 | 107 | return buf.String() 108 | } 109 | 110 | func (frame *SETTINGS) WriteTo(writer io.Writer) (int64, error) { 111 | c := common.WriteCounter{W: writer} 112 | settings := encodeSettings(frame.Settings) 113 | numSettings := uint32(len(frame.Settings)) 114 | length := 4 + len(settings) 115 | out := make([]byte, 12) 116 | 117 | out[0] = 128 // Control bit and Version 118 | out[1] = 3 // Version 119 | out[2] = 0 // Type 120 | out[3] = 4 // Type 121 | out[4] = byte(frame.Flags) // Flags 122 | out[5] = byte(length >> 16) // Length 123 | out[6] = byte(length >> 8) // Length 124 | out[7] = byte(length) // Length 125 | out[8] = byte(numSettings >> 24) // Number of Entries 126 | out[9] = byte(numSettings >> 16) // Number of Entries 127 | out[10] = byte(numSettings >> 8) // Number of Entries 128 | out[11] = byte(numSettings) // Number of Entries 129 | 130 | err := common.WriteExactly(&c, out) 131 | if err != nil { 132 | return c.N, err 133 | } 134 | 135 | err = common.WriteExactly(&c, settings) 136 | if err != nil { 137 | return c.N, err 138 | } 139 | 140 | return c.N, nil 141 | } 142 | 143 | func decodeSetting(data []byte) *common.Setting { 144 | if len(data) < 8 { 145 | return nil 146 | } 147 | 148 | setting := new(common.Setting) 149 | setting.Flags = common.Flags(data[0]) 150 | setting.ID = common.BytesToUint24(data[1:]) 151 | setting.Value = common.BytesToUint32(data[4:]) 152 | 153 | return setting 154 | } 155 | 156 | func encodeSettings(s common.Settings) []byte { 157 | if len(s) == 0 { 158 | return []byte{} 159 | } 160 | 161 | ids := make([]int, 0, len(s)) 162 | for id := range s { 163 | ids = append(ids, int(id)) 164 | } 165 | 166 | sort.Sort(sort.IntSlice(ids)) 167 | 168 | out := make([]byte, 8*len(s)) 169 | 170 | offset := 0 171 | for _, id := range ids { 172 | setting := s[uint32(id)] 173 | out[offset] = byte(setting.Flags) 174 | out[offset+1] = byte(setting.ID >> 16) 175 | out[offset+2] = byte(setting.ID >> 8) 176 | out[offset+3] = byte(setting.ID) 177 | out[offset+4] = byte(setting.Value >> 24) 178 | out[offset+5] = byte(setting.Value >> 16) 179 | out[offset+6] = byte(setting.Value >> 8) 180 | out[offset+7] = byte(setting.Value) 181 | offset += 8 182 | } 183 | 184 | return out 185 | } 186 | -------------------------------------------------------------------------------- /spdy3/frames/syn_reply.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "net/http" 13 | 14 | "github.com/SlyMarbo/spdy/common" 15 | ) 16 | 17 | type SYN_REPLY struct { 18 | Flags common.Flags 19 | StreamID common.StreamID 20 | Header http.Header 21 | rawHeader []byte 22 | } 23 | 24 | func (frame *SYN_REPLY) Compress(com common.Compressor) error { 25 | if frame.rawHeader != nil { 26 | return nil 27 | } 28 | 29 | data, err := com.Compress(frame.Header) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | frame.rawHeader = data 35 | return nil 36 | } 37 | 38 | func (frame *SYN_REPLY) Decompress(decom common.Decompressor) error { 39 | if frame.Header != nil { 40 | return nil 41 | } 42 | 43 | header, err := decom.Decompress(frame.rawHeader) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | frame.Header = header 49 | frame.rawHeader = nil 50 | return nil 51 | } 52 | 53 | func (frame *SYN_REPLY) Name() string { 54 | return "SYN_REPLY" 55 | } 56 | 57 | func (frame *SYN_REPLY) ReadFrom(reader io.Reader) (int64, error) { 58 | c := common.ReadCounter{R: reader} 59 | data, err := common.ReadExactly(&c, 12) 60 | if err != nil { 61 | return c.N, err 62 | } 63 | 64 | err = controlFrameCommonProcessing(data[:5], _SYN_REPLY, common.FLAG_FIN) 65 | if err != nil { 66 | return c.N, err 67 | } 68 | 69 | // Get and check length. 70 | length := int(common.BytesToUint24(data[5:8])) 71 | if length < 4 { 72 | return c.N, common.IncorrectDataLength(length, 4) 73 | } else if length > common.MAX_FRAME_SIZE-8 { 74 | return c.N, common.FrameTooLarge 75 | } 76 | 77 | // Read in data. 78 | header, err := common.ReadExactly(&c, length-4) 79 | if err != nil { 80 | return c.N, err 81 | } 82 | 83 | frame.Flags = common.Flags(data[4]) 84 | frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12])) 85 | frame.rawHeader = header 86 | 87 | return c.N, nil 88 | } 89 | 90 | func (frame *SYN_REPLY) String() string { 91 | buf := new(bytes.Buffer) 92 | flags := "" 93 | if frame.Flags.FIN() { 94 | flags += " common.FLAG_FIN" 95 | } 96 | if flags == "" { 97 | flags = "[NONE]" 98 | } else { 99 | flags = flags[1:] 100 | } 101 | 102 | buf.WriteString("SYN_REPLY {\n\t") 103 | buf.WriteString(fmt.Sprintf("Version: 3\n\t")) 104 | buf.WriteString(fmt.Sprintf("Flags: %s\n\t", flags)) 105 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 106 | buf.WriteString(fmt.Sprintf("Header: %#v\n}\n", frame.Header)) 107 | 108 | return buf.String() 109 | } 110 | 111 | func (frame *SYN_REPLY) WriteTo(writer io.Writer) (int64, error) { 112 | c := common.WriteCounter{W: writer} 113 | if frame.rawHeader == nil { 114 | return c.N, errors.New("Error: Header not written.") 115 | } 116 | if !frame.StreamID.Valid() { 117 | return c.N, common.StreamIdTooLarge 118 | } 119 | if frame.StreamID.Zero() { 120 | return c.N, common.StreamIdIsZero 121 | } 122 | 123 | header := frame.rawHeader 124 | length := 4 + len(header) 125 | out := make([]byte, 12) 126 | 127 | out[0] = 128 // Control bit and Version 128 | out[1] = 3 // Version 129 | out[2] = 0 // Type 130 | out[3] = 2 // Type 131 | out[4] = byte(frame.Flags) // Flags 132 | out[5] = byte(length >> 16) // Length 133 | out[6] = byte(length >> 8) // Length 134 | out[7] = byte(length) // Length 135 | out[8] = frame.StreamID.B1() // Stream ID 136 | out[9] = frame.StreamID.B2() // Stream ID 137 | out[10] = frame.StreamID.B3() // Stream ID 138 | out[11] = frame.StreamID.B4() // Stream ID 139 | 140 | err := common.WriteExactly(&c, out) 141 | if err != nil { 142 | return c.N, err 143 | } 144 | 145 | err = common.WriteExactly(&c, header) 146 | if err != nil { 147 | return c.N, err 148 | } 149 | 150 | return c.N, nil 151 | } 152 | -------------------------------------------------------------------------------- /spdy3/frames/window_update.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package frames 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | 13 | "github.com/SlyMarbo/spdy/common" 14 | ) 15 | 16 | type WINDOW_UPDATE struct { 17 | StreamID common.StreamID 18 | DeltaWindowSize uint32 19 | subversion int 20 | } 21 | 22 | func (frame *WINDOW_UPDATE) Compress(comp common.Compressor) error { 23 | return nil 24 | } 25 | 26 | func (frame *WINDOW_UPDATE) Decompress(decomp common.Decompressor) error { 27 | return nil 28 | } 29 | 30 | func (frame *WINDOW_UPDATE) Name() string { 31 | return "WINDOW_UPDATE" 32 | } 33 | 34 | func (frame *WINDOW_UPDATE) ReadFrom(reader io.Reader) (int64, error) { 35 | c := common.ReadCounter{R: reader} 36 | data, err := common.ReadExactly(&c, 16) 37 | if err != nil { 38 | return c.N, err 39 | } 40 | 41 | err = controlFrameCommonProcessing(data[:5], _WINDOW_UPDATE, 0) 42 | if err != nil { 43 | return c.N, err 44 | } 45 | 46 | // Get and check length. 47 | length := int(common.BytesToUint24(data[5:8])) 48 | if length != 8 { 49 | return c.N, common.IncorrectDataLength(length, 8) 50 | } 51 | 52 | frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12])) 53 | frame.DeltaWindowSize = common.BytesToUint32(data[12:16]) 54 | 55 | if !frame.StreamID.Valid() { 56 | return c.N, common.StreamIdTooLarge 57 | } 58 | if frame.StreamID.Zero() && frame.subversion == 0 { 59 | return c.N, common.StreamIdIsZero 60 | } 61 | if frame.DeltaWindowSize > common.MAX_DELTA_WINDOW_SIZE { 62 | return c.N, errors.New("Error: Delta Window Size too large.") 63 | } 64 | 65 | return c.N, nil 66 | } 67 | 68 | func (frame *WINDOW_UPDATE) String() string { 69 | buf := new(bytes.Buffer) 70 | 71 | buf.WriteString("WINDOW_UPDATE {\n\t") 72 | buf.WriteString(fmt.Sprintf("Version: 3\n\t")) 73 | buf.WriteString(fmt.Sprintf("Stream ID: %d\n\t", frame.StreamID)) 74 | buf.WriteString(fmt.Sprintf("Delta window size: %d\n}\n", frame.DeltaWindowSize)) 75 | 76 | return buf.String() 77 | } 78 | 79 | func (frame *WINDOW_UPDATE) WriteTo(writer io.Writer) (int64, error) { 80 | c := common.WriteCounter{W: writer} 81 | out := make([]byte, 16) 82 | 83 | out[0] = 128 // Control bit and Version 84 | out[1] = 3 // Version 85 | out[2] = 0 // Type 86 | out[3] = 9 // Type 87 | out[4] = 0 // Flags 88 | out[5] = 0 // Length 89 | out[6] = 0 // Length 90 | out[7] = 8 // Length 91 | out[8] = frame.StreamID.B1() // Stream ID 92 | out[9] = frame.StreamID.B2() // Stream ID 93 | out[10] = frame.StreamID.B3() // Stream ID 94 | out[11] = frame.StreamID.B4() // Stream ID 95 | out[12] = byte(frame.DeltaWindowSize>>24) & 0x7f // Delta Window Size 96 | out[13] = byte(frame.DeltaWindowSize >> 16) // Delta Window Size 97 | out[14] = byte(frame.DeltaWindowSize >> 8) // Delta Window Size 98 | out[15] = byte(frame.DeltaWindowSize) // Delta Window Size 99 | 100 | err := common.WriteExactly(&c, out) 101 | if err != nil { 102 | return c.N, err 103 | } 104 | 105 | return c.N, nil 106 | } 107 | -------------------------------------------------------------------------------- /spdy3/interface.go: -------------------------------------------------------------------------------- 1 | package spdy3 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | func (c *Conn) CloseNotify() <-chan bool { 9 | return c.stop 10 | } 11 | 12 | func (c *Conn) Conn() net.Conn { 13 | return c.conn 14 | } 15 | 16 | func (c *Conn) SetReadTimeout(d time.Duration) { 17 | c.timeoutLock.Lock() 18 | c.readTimeout = d 19 | c.timeoutLock.Unlock() 20 | } 21 | 22 | func (c *Conn) SetWriteTimeout(d time.Duration) { 23 | c.timeoutLock.Lock() 24 | c.writeTimeout = d 25 | c.timeoutLock.Unlock() 26 | } 27 | 28 | func (c *Conn) refreshReadTimeout() { 29 | c.timeoutLock.Lock() 30 | if d := c.readTimeout; d != 0 && c.conn != nil { 31 | c.conn.SetReadDeadline(time.Now().Add(d)) 32 | } 33 | c.timeoutLock.Unlock() 34 | } 35 | 36 | func (c *Conn) refreshWriteTimeout() { 37 | c.timeoutLock.Lock() 38 | if d := c.writeTimeout; d != 0 && c.conn != nil { 39 | c.conn.SetWriteDeadline(time.Now().Add(d)) 40 | } 41 | c.timeoutLock.Unlock() 42 | } 43 | -------------------------------------------------------------------------------- /spdy3/io.go: -------------------------------------------------------------------------------- 1 | package spdy3 2 | 3 | import ( 4 | "runtime" 5 | 6 | "github.com/SlyMarbo/spdy/common" 7 | "github.com/SlyMarbo/spdy/spdy3/frames" 8 | ) 9 | 10 | // readFrames is the main processing loop, where frames 11 | // are read from the connection and processed individually. 12 | // Returning from readFrames begins the cleanup and exit 13 | // process for this connection. 14 | func (c *Conn) readFrames() { 15 | // Ensure no panics happen. 16 | defer func() { 17 | if v := recover(); v != nil { 18 | if !c.Closed() { 19 | log.Printf("Encountered receive error: %v (%[1]T)\n", v) 20 | } 21 | } 22 | }() 23 | 24 | for { 25 | // This is the mechanism for handling too many benign errors. 26 | // By default MaxBenignErrors is 0, which ignores errors. 27 | too_many := c.numBenignErrors > common.MaxBenignErrors && common.MaxBenignErrors > 0 28 | if c.criticalCheck(too_many, 0, "Ending connection for benign error buildup") { 29 | return 30 | } 31 | 32 | // ReadFrame takes care of the frame parsing for us. 33 | c.refreshReadTimeout() 34 | frame, err := frames.ReadFrame(c.buf, c.Subversion) 35 | if err != nil { 36 | c.handleReadWriteError(err) 37 | return 38 | } 39 | 40 | debug.Printf("Receiving %s:\n", frame.Name()) // Print frame type. 41 | 42 | // Decompress the frame's headers, if there are any. 43 | err = frame.Decompress(c.decompressor) 44 | if c.criticalCheck(err != nil, 0, "Decompression: %v", err) { 45 | return 46 | } 47 | 48 | debug.Println(frame) // Print frame once the content's been decompressed. 49 | 50 | if c.processFrame(frame) { 51 | return 52 | } 53 | } 54 | } 55 | 56 | // send is run in a separate goroutine. It's used 57 | // to ensure clear interleaving of frames and to 58 | // provide assurances of priority and structure. 59 | func (c *Conn) send() { 60 | // Catch any panics. 61 | defer func() { 62 | if v := recover(); v != nil { 63 | if !c.Closed() { 64 | log.Printf("Encountered send error: %v (%[1]T)\n", v) 65 | } 66 | } 67 | }() 68 | 69 | for i := 1; ; i++ { 70 | if i >= 5 { 71 | i = 0 // Once per 5 frames, pick randomly. 72 | } 73 | 74 | var frame common.Frame 75 | if i == 0 { // Ignore priority. 76 | frame = c.selectFrameToSend(false) 77 | } else { // Normal selection. 78 | frame = c.selectFrameToSend(true) 79 | } 80 | 81 | if frame == nil { 82 | c.Close() 83 | return 84 | } 85 | 86 | // Process connection-level flow control. 87 | if c.Subversion > 0 { 88 | c.connectionWindowLock.Lock() 89 | if frame, ok := frame.(*frames.DATA); ok { 90 | size := int64(len(frame.Data)) 91 | constrained := false 92 | sending := size 93 | if sending > c.connectionWindowSize { 94 | sending = c.connectionWindowSize 95 | constrained = true 96 | } 97 | if sending < 0 { 98 | sending = 0 99 | } 100 | 101 | c.connectionWindowSize -= sending 102 | 103 | if constrained { 104 | // Chop off what we can send now. 105 | partial := new(frames.DATA) 106 | partial.Flags = frame.Flags 107 | partial.StreamID = frame.StreamID 108 | partial.Data = make([]byte, int(sending)) 109 | copy(partial.Data, frame.Data[:sending]) 110 | frame.Data = frame.Data[sending:] 111 | 112 | // Buffer this frame and try again. 113 | if c.dataBuffer == nil { 114 | c.dataBuffer = []*frames.DATA{frame} 115 | } else { 116 | buffer := make([]*frames.DATA, 1, len(c.dataBuffer)+1) 117 | buffer[0] = frame 118 | buffer = append(buffer, c.dataBuffer...) 119 | c.dataBuffer = buffer 120 | } 121 | 122 | frame = partial 123 | } 124 | } 125 | c.connectionWindowLock.Unlock() 126 | } 127 | 128 | // Compress any name/value header blocks. 129 | err := frame.Compress(c.compressor) 130 | if err != nil { 131 | log.Printf("Error in compression: %v (type %T).\n", err, frame) 132 | c.Close() 133 | return 134 | } 135 | 136 | debug.Printf("Sending %s:\n", frame.Name()) 137 | debug.Println(frame) 138 | 139 | // Leave the specifics of writing to the 140 | // connection up to the frame. 141 | c.refreshWriteTimeout() 142 | if _, err = frame.WriteTo(c.conn); err != nil { 143 | c.handleReadWriteError(err) 144 | return 145 | } 146 | } 147 | } 148 | 149 | // selectFrameToSend follows the specification's guidance 150 | // on frame priority, sending frames with higher priority 151 | // (a smaller number) first. If the given boolean is false, 152 | // this priority is temporarily ignored, which can be used 153 | // when high load is ignoring low-priority frames. 154 | func (c *Conn) selectFrameToSend(prioritise bool) (frame common.Frame) { 155 | if c.Closed() { 156 | return nil 157 | } 158 | 159 | // Try buffered DATA frames first. 160 | if c.Subversion > 0 { 161 | if c.dataBuffer != nil { 162 | if len(c.dataBuffer) == 0 { 163 | c.dataBuffer = nil 164 | } else { 165 | first := c.dataBuffer[0] 166 | if c.connectionWindowSize >= int64(8+len(first.Data)) { 167 | if len(c.dataBuffer) > 1 { 168 | c.dataBuffer = c.dataBuffer[1:] 169 | } else { 170 | c.dataBuffer = nil 171 | } 172 | return first 173 | } 174 | } 175 | } 176 | } 177 | 178 | // Then in priority order. 179 | if prioritise { 180 | for i := 0; i < 8; i++ { 181 | select { 182 | case frame = <-c.output[i]: 183 | return frame 184 | default: 185 | } 186 | } 187 | 188 | // No frames are immediately pending, so if the 189 | // cection is being closed, cease sending 190 | // safely. 191 | c.sendingLock.Lock() 192 | if c.sending != nil { 193 | close(c.sending) 194 | c.sendingLock.Unlock() 195 | runtime.Goexit() 196 | } 197 | c.sendingLock.Unlock() 198 | } 199 | 200 | // Wait for any frame. 201 | select { 202 | case frame = <-c.output[0]: 203 | return frame 204 | case frame = <-c.output[1]: 205 | return frame 206 | case frame = <-c.output[2]: 207 | return frame 208 | case frame = <-c.output[3]: 209 | return frame 210 | case frame = <-c.output[4]: 211 | return frame 212 | case frame = <-c.output[5]: 213 | return frame 214 | case frame = <-c.output[6]: 215 | return frame 216 | case frame = <-c.output[7]: 217 | return frame 218 | case _ = <-c.stop: 219 | return nil 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /spdy3/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy3 6 | 7 | import ( 8 | "github.com/SlyMarbo/spdy/common" 9 | ) 10 | 11 | var log = common.GetLogger() 12 | var debug = common.GetDebugLogger() 13 | -------------------------------------------------------------------------------- /spdy3/push_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy3 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "net/http" 11 | "sync" 12 | 13 | "github.com/SlyMarbo/spdy/common" 14 | "github.com/SlyMarbo/spdy/spdy3/frames" 15 | ) 16 | 17 | // PushStream is a structure that implements the 18 | // Stream and PushWriter interfaces. this is used 19 | // for performing server pushes. 20 | type PushStream struct { 21 | sync.Mutex 22 | 23 | shutdownOnce sync.Once 24 | conn *Conn 25 | streamID common.StreamID 26 | flow *flowControl 27 | origin common.Stream 28 | state *common.StreamState 29 | output chan<- common.Frame 30 | header http.Header 31 | stop <-chan bool 32 | } 33 | 34 | func NewPushStream(conn *Conn, streamID common.StreamID, origin common.Stream, output chan<- common.Frame) *PushStream { 35 | out := new(PushStream) 36 | out.conn = conn 37 | out.streamID = streamID 38 | out.origin = origin 39 | out.output = output 40 | out.stop = conn.stop 41 | out.state = new(common.StreamState) 42 | out.header = make(http.Header) 43 | return out 44 | } 45 | 46 | /*********************** 47 | * http.ResponseWriter * 48 | ***********************/ 49 | 50 | func (p *PushStream) Header() http.Header { 51 | return p.header 52 | } 53 | 54 | // Write is used for sending data in the push. 55 | func (p *PushStream) Write(inputData []byte) (int, error) { 56 | if p.closed() || p.state.ClosedHere() { 57 | return 0, errors.New("Error: Stream already closed.") 58 | } 59 | 60 | state := p.origin.State() 61 | if p.origin == nil || state.ClosedHere() { 62 | return 0, errors.New("Error: Origin stream is closed.") 63 | } 64 | 65 | p.writeHeader() 66 | 67 | // Copy the data locally to avoid any pointer issues. 68 | data := make([]byte, len(inputData)) 69 | copy(data, inputData) 70 | 71 | // Chunk the response if necessary. 72 | // Data is sent to the flow control to 73 | // ensure that the protocol is followed. 74 | written := 0 75 | for len(data) > common.MAX_DATA_SIZE { 76 | n, err := p.flow.Write(data[:common.MAX_DATA_SIZE]) 77 | if err != nil { 78 | return written, err 79 | } 80 | written += n 81 | data = data[common.MAX_DATA_SIZE:] 82 | } 83 | 84 | n, err := p.flow.Write(data) 85 | written += n 86 | 87 | return written, err 88 | } 89 | 90 | // WriteHeader is provided to satisfy the Stream 91 | // interface, but has no effect. 92 | func (p *PushStream) WriteHeader(int) { 93 | p.writeHeader() 94 | return 95 | } 96 | 97 | /***************** 98 | * io.Closer * 99 | *****************/ 100 | 101 | func (p *PushStream) Close() error { 102 | defer common.Recover() 103 | p.Lock() 104 | p.shutdownOnce.Do(p.shutdown) 105 | p.Unlock() 106 | return nil 107 | } 108 | 109 | func (p *PushStream) shutdown() { 110 | p.writeHeader() 111 | if p.state != nil { 112 | p.state.Close() 113 | } 114 | if p.flow != nil { 115 | p.flow.Close() 116 | } 117 | p.conn.pushStreamLimit.Close() 118 | p.origin = nil 119 | p.output = nil 120 | p.header = nil 121 | p.stop = nil 122 | } 123 | 124 | /********** 125 | * Stream * 126 | **********/ 127 | 128 | func (p *PushStream) Conn() common.Conn { 129 | return p.conn 130 | } 131 | 132 | func (p *PushStream) ReceiveFrame(frame common.Frame) error { 133 | p.Lock() 134 | defer p.Unlock() 135 | 136 | if frame == nil { 137 | return errors.New("Error: Nil frame received.") 138 | } 139 | 140 | // Process the frame depending on its type. 141 | switch frame := frame.(type) { 142 | case *frames.WINDOW_UPDATE: 143 | err := p.flow.UpdateWindow(frame.DeltaWindowSize) 144 | if err != nil { 145 | reply := new(frames.RST_STREAM) 146 | reply.StreamID = p.streamID 147 | reply.Status = common.RST_STREAM_FLOW_CONTROL_ERROR 148 | p.output <- reply 149 | return err 150 | } 151 | 152 | default: 153 | return errors.New(fmt.Sprintf("Received unexpected frame of type %T.", frame)) 154 | } 155 | 156 | return nil 157 | } 158 | 159 | func (p *PushStream) CloseNotify() <-chan bool { 160 | return p.stop 161 | } 162 | 163 | func (p *PushStream) Run() error { 164 | return nil 165 | } 166 | 167 | func (p *PushStream) State() *common.StreamState { 168 | return p.state 169 | } 170 | 171 | func (p *PushStream) StreamID() common.StreamID { 172 | return p.streamID 173 | } 174 | 175 | /************** 176 | * PushStream * 177 | **************/ 178 | 179 | func (p *PushStream) Finish() { 180 | p.writeHeader() 181 | end := new(frames.DATA) 182 | end.StreamID = p.streamID 183 | end.Data = []byte{} 184 | end.Flags = common.FLAG_FIN 185 | p.output <- end 186 | p.Close() 187 | } 188 | 189 | /********** 190 | * Others * 191 | **********/ 192 | 193 | func (p *PushStream) closed() bool { 194 | if p.conn == nil || p.state == nil { 195 | return true 196 | } 197 | select { 198 | case _ = <-p.stop: 199 | return true 200 | default: 201 | return false 202 | } 203 | } 204 | 205 | // writeHeader is used to send HTTP headers to 206 | // the client. 207 | func (p *PushStream) writeHeader() { 208 | if len(p.header) == 0 || p.closed() { 209 | return 210 | } 211 | 212 | header := new(frames.HEADERS) 213 | header.StreamID = p.streamID 214 | header.Header = make(http.Header) 215 | 216 | for name, values := range p.header { 217 | for _, value := range values { 218 | header.Header.Add(name, value) 219 | } 220 | p.header.Del(name) 221 | } 222 | 223 | if len(header.Header) == 0 { 224 | return 225 | } 226 | 227 | p.output <- header 228 | } 229 | -------------------------------------------------------------------------------- /spdy3/request_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy3 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "net/http" 11 | "sync" 12 | 13 | "github.com/SlyMarbo/spdy/common" 14 | "github.com/SlyMarbo/spdy/spdy3/frames" 15 | ) 16 | 17 | // RequestStream is a structure that implements 18 | // the Stream and ResponseWriter interfaces. This 19 | // is used for responding to client requests. 20 | type RequestStream struct { 21 | sync.Mutex 22 | Request *http.Request 23 | Receiver common.Receiver 24 | 25 | recvMutex sync.Mutex 26 | shutdownOnce sync.Once 27 | conn *Conn 28 | streamID common.StreamID 29 | flow *flowControl 30 | state *common.StreamState 31 | output chan<- common.Frame 32 | header http.Header 33 | headerChan chan func() 34 | responseCode int 35 | stop <-chan bool 36 | finished chan struct{} 37 | } 38 | 39 | func NewRequestStream(conn *Conn, streamID common.StreamID, output chan<- common.Frame) *RequestStream { 40 | out := new(RequestStream) 41 | out.conn = conn 42 | out.streamID = streamID 43 | out.output = output 44 | out.stop = conn.stop 45 | out.state = new(common.StreamState) 46 | out.state.CloseHere() 47 | out.header = make(http.Header) 48 | out.finished = make(chan struct{}) 49 | out.headerChan = make(chan func(), 5) 50 | go out.processFrames() 51 | return out 52 | } 53 | 54 | /*********************** 55 | * http.ResponseWriter * 56 | ***********************/ 57 | 58 | func (s *RequestStream) Header() http.Header { 59 | return s.header 60 | } 61 | 62 | // Write is one method with which request data is sent. 63 | func (s *RequestStream) Write(inputData []byte) (int, error) { 64 | if s.closed() || s.state.ClosedHere() { 65 | return 0, errors.New("Error: Stream already closed.") 66 | } 67 | 68 | // Copy the data locally to avoid any pointer issues. 69 | data := make([]byte, len(inputData)) 70 | copy(data, inputData) 71 | 72 | // Send any new headers. 73 | s.writeHeader() 74 | 75 | // Chunk the response if necessary. 76 | // Data is sent to the flow control to 77 | // ensure that the protocol is followed. 78 | written := 0 79 | for len(data) > common.MAX_DATA_SIZE { 80 | n, err := s.flow.Write(data[:common.MAX_DATA_SIZE]) 81 | if err != nil { 82 | return written, err 83 | } 84 | written += n 85 | data = data[common.MAX_DATA_SIZE:] 86 | } 87 | 88 | if len(data) > 0 { 89 | n, err := s.flow.Write(data) 90 | written += n 91 | if err != nil { 92 | return written, err 93 | } 94 | } 95 | 96 | return written, nil 97 | } 98 | 99 | // WriteHeader is used to set the HTTP status code. 100 | func (s *RequestStream) WriteHeader(int) { 101 | s.writeHeader() 102 | } 103 | 104 | /***************** 105 | * io.Closer * 106 | *****************/ 107 | 108 | // Close is used to stop the stream safely. 109 | func (s *RequestStream) Close() error { 110 | defer common.Recover() 111 | s.Lock() 112 | s.shutdownOnce.Do(s.shutdown) 113 | s.Unlock() 114 | return nil 115 | } 116 | 117 | func (s *RequestStream) shutdown() { 118 | s.writeHeader() 119 | if s.state != nil { 120 | if s.state.OpenThere() { 121 | // Send the RST_STREAM. 122 | rst := new(frames.RST_STREAM) 123 | rst.StreamID = s.streamID 124 | rst.Status = common.RST_STREAM_CANCEL 125 | s.output <- rst 126 | } 127 | s.state.Close() 128 | } 129 | if s.flow != nil { 130 | s.flow.Close() 131 | } 132 | select { 133 | case <-s.finished: 134 | default: 135 | close(s.finished) 136 | } 137 | select { 138 | case <-s.headerChan: 139 | default: 140 | close(s.headerChan) 141 | } 142 | s.conn.requestStreamLimit.Close() 143 | s.output = nil 144 | s.Request = nil 145 | s.Receiver = nil 146 | s.header = nil 147 | s.stop = nil 148 | 149 | s.conn.streamsLock.Lock() 150 | delete(s.conn.streams, s.streamID) 151 | s.conn.streamsLock.Unlock() 152 | } 153 | 154 | /********** 155 | * Stream * 156 | **********/ 157 | 158 | func (s *RequestStream) Conn() common.Conn { 159 | return s.conn 160 | } 161 | 162 | func (s *RequestStream) ReceiveFrame(frame common.Frame) error { 163 | s.recvMutex.Lock() 164 | defer s.recvMutex.Unlock() 165 | 166 | if frame == nil { 167 | return errors.New("Nil frame received.") 168 | } 169 | 170 | // Process the frame depending on its type. 171 | switch frame := frame.(type) { 172 | case *frames.DATA: 173 | 174 | // Extract the data. 175 | data := frame.Data 176 | if data == nil { 177 | data = []byte{} 178 | } 179 | 180 | // Give to the client. 181 | s.flow.Receive(frame.Data) 182 | s.headerChan <- func() { 183 | s.Receiver.ReceiveData(s.Request, data, frame.Flags.FIN()) 184 | 185 | if frame.Flags.FIN() { 186 | s.state.CloseThere() 187 | s.Close() 188 | } 189 | } 190 | 191 | case *frames.SYN_REPLY: 192 | s.headerChan <- func() { 193 | s.Receiver.ReceiveHeader(s.Request, frame.Header) 194 | 195 | if frame.Flags.FIN() { 196 | s.state.CloseThere() 197 | s.Close() 198 | } 199 | } 200 | 201 | case *frames.HEADERS: 202 | s.headerChan <- func() { 203 | s.Receiver.ReceiveHeader(s.Request, frame.Header) 204 | 205 | if frame.Flags.FIN() { 206 | s.state.CloseThere() 207 | s.Close() 208 | } 209 | } 210 | 211 | case *frames.WINDOW_UPDATE: 212 | err := s.flow.UpdateWindow(frame.DeltaWindowSize) 213 | if err != nil { 214 | reply := new(frames.RST_STREAM) 215 | reply.StreamID = s.streamID 216 | reply.Status = common.RST_STREAM_FLOW_CONTROL_ERROR 217 | s.output <- reply 218 | } 219 | 220 | default: 221 | return errors.New(fmt.Sprintf("Received unknown frame of type %T.", frame)) 222 | } 223 | 224 | return nil 225 | } 226 | 227 | func (s *RequestStream) CloseNotify() <-chan bool { 228 | return s.stop 229 | } 230 | 231 | // run is the main control path of 232 | // the stream. Data is recieved, 233 | // processed, and then the stream 234 | // is cleaned up and closed. 235 | func (s *RequestStream) Run() error { 236 | // Receive and process inbound frames. 237 | <-s.finished 238 | 239 | // Make sure any queued data has been sent. 240 | if s.flow.Paused() { 241 | return errors.New(fmt.Sprintf("Error: Stream %d has been closed with data still buffered.\n", s.streamID)) 242 | } 243 | 244 | // Clean up state. 245 | s.state.CloseHere() 246 | return nil 247 | } 248 | 249 | func (s *RequestStream) State() *common.StreamState { 250 | return s.state 251 | } 252 | 253 | func (s *RequestStream) StreamID() common.StreamID { 254 | return s.streamID 255 | } 256 | 257 | func (s *RequestStream) closed() bool { 258 | if s.conn == nil || s.state == nil || s.Receiver == nil { 259 | return true 260 | } 261 | select { 262 | case _ = <-s.stop: 263 | return true 264 | default: 265 | return false 266 | } 267 | } 268 | 269 | // writeHeader is used to flush HTTP headers. 270 | func (s *RequestStream) writeHeader() { 271 | if len(s.header) == 0 { 272 | return 273 | } 274 | 275 | // Create the HEADERS frame. 276 | header := new(frames.HEADERS) 277 | header.StreamID = s.streamID 278 | header.Header = make(http.Header) 279 | 280 | // Clear the headers that have been sent. 281 | for name, values := range s.header { 282 | for _, value := range values { 283 | header.Header.Add(name, value) 284 | } 285 | s.header.Del(name) 286 | } 287 | 288 | s.output <- header 289 | } 290 | 291 | func (s *RequestStream) processFrames() { 292 | defer common.Recover() 293 | for f := range s.headerChan { 294 | f() 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /spdy3/requests.go: -------------------------------------------------------------------------------- 1 | package spdy3 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "strings" 9 | 10 | "github.com/SlyMarbo/spdy/common" 11 | "github.com/SlyMarbo/spdy/spdy3/frames" 12 | ) 13 | 14 | // Request is used to make a client request. 15 | func (c *Conn) Request(request *http.Request, receiver common.Receiver, priority common.Priority) (common.Stream, error) { 16 | if c.Closed() { 17 | return nil, common.ErrConnClosed 18 | } 19 | c.goawayLock.Lock() 20 | goaway := c.goawayReceived || c.goawaySent 21 | c.goawayLock.Unlock() 22 | if goaway { 23 | return nil, common.ErrGoaway 24 | } 25 | 26 | if c.server != nil { 27 | return nil, errors.New("Error: Only clients can send requests.") 28 | } 29 | 30 | // Check stream limit would allow the new stream. 31 | if !c.requestStreamLimit.Add() { 32 | return nil, errors.New("Error: Max concurrent streams limit exceeded.") 33 | } 34 | 35 | if !priority.Valid(3) { 36 | return nil, errors.New("Error: Priority must be in the range 0 - 7.") 37 | } 38 | 39 | url := request.URL 40 | if url == nil || url.Scheme == "" || url.Host == "" { 41 | return nil, errors.New("Error: Incomplete path provided to resource.") 42 | } 43 | 44 | // Prepare the SYN_STREAM. 45 | path := url.Path 46 | if url.RawQuery != "" { 47 | path += "?" + url.RawQuery 48 | } 49 | if url.Fragment != "" { 50 | path += "#" + url.Fragment 51 | } 52 | if !strings.HasPrefix(path, "/") { 53 | path = "/" + path 54 | } 55 | 56 | host := url.Host 57 | if len(request.Host) > 0 { 58 | host = request.Host 59 | } 60 | syn := new(frames.SYN_STREAM) 61 | syn.Priority = priority 62 | syn.Header = request.Header 63 | syn.Header.Set(":method", request.Method) 64 | syn.Header.Set(":path", path) 65 | syn.Header.Set(":version", "HTTP/1.1") 66 | syn.Header.Set(":host", host) 67 | syn.Header.Set(":scheme", url.Scheme) 68 | 69 | // Prepare the request body, if any. 70 | body := make([]*frames.DATA, 0, 1) 71 | if request.Body != nil { 72 | buf := make([]byte, 32*1024) 73 | n, err := request.Body.Read(buf) 74 | if err != nil && err != io.EOF { 75 | return nil, err 76 | } 77 | total := n 78 | for n > 0 { 79 | data := new(frames.DATA) 80 | data.Data = make([]byte, n) 81 | copy(data.Data, buf[:n]) 82 | body = append(body, data) 83 | n, err = request.Body.Read(buf) 84 | if err != nil && err != io.EOF { 85 | return nil, err 86 | } 87 | total += n 88 | } 89 | 90 | // Half-close the stream. 91 | if len(body) == 0 { 92 | syn.Flags = common.FLAG_FIN 93 | } else { 94 | syn.Header.Set("Content-Length", fmt.Sprint(total)) 95 | body[len(body)-1].Flags = common.FLAG_FIN 96 | } 97 | request.Body.Close() 98 | } else { 99 | syn.Flags = common.FLAG_FIN 100 | } 101 | 102 | // Send. 103 | c.streamCreation.Lock() 104 | defer c.streamCreation.Unlock() 105 | 106 | c.lastRequestStreamIDLock.Lock() 107 | if c.lastRequestStreamID == 0 { 108 | c.lastRequestStreamID = 1 109 | } else { 110 | c.lastRequestStreamID += 2 111 | } 112 | syn.StreamID = c.lastRequestStreamID 113 | c.lastRequestStreamIDLock.Unlock() 114 | if syn.StreamID > common.MAX_STREAM_ID { 115 | return nil, errors.New("Error: All client streams exhausted.") 116 | } 117 | c.output[0] <- syn 118 | for _, frame := range body { 119 | frame.StreamID = syn.StreamID 120 | c.output[0] <- frame 121 | } 122 | 123 | // Create the request stream. 124 | out := NewRequestStream(c, syn.StreamID, c.output[0]) 125 | out.Request = request 126 | out.Receiver = receiver 127 | out.AddFlowControl(c.flowControl) 128 | c.streamsLock.Lock() 129 | c.streams[syn.StreamID] = out // Store in the connection map. 130 | c.streamsLock.Unlock() 131 | 132 | return out, nil 133 | } 134 | 135 | func (c *Conn) RequestResponse(request *http.Request, receiver common.Receiver, priority common.Priority) (*http.Response, error) { 136 | res := common.NewResponse(request, receiver) 137 | 138 | // Send the request. 139 | stream, err := c.Request(request, res, priority) 140 | if err != nil { 141 | return nil, err 142 | } 143 | 144 | // Let the request run its course. 145 | stream.Run() 146 | 147 | return res.Response(), c.shutdownError 148 | } 149 | -------------------------------------------------------------------------------- /spdy3/shutdown.go: -------------------------------------------------------------------------------- 1 | package spdy3 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/SlyMarbo/spdy/common" 7 | "github.com/SlyMarbo/spdy/spdy3/frames" 8 | ) 9 | 10 | // Close ends the connection, cleaning up relevant resources. 11 | // Close can be called multiple times safely. 12 | func (c *Conn) Close() (err error) { 13 | c.shutdownOnce.Do(c.shutdown) 14 | return nil 15 | } 16 | 17 | // Closed indicates whether the connection has 18 | // been closed. 19 | func (c *Conn) Closed() bool { 20 | select { 21 | case <-c.stop: 22 | return true 23 | default: 24 | return false 25 | } 26 | } 27 | 28 | func (c *Conn) shutdown() { 29 | if c.Closed() { 30 | return 31 | } 32 | 33 | // Try to inform the other endpoint that the connection is closing. 34 | c.sendingLock.Lock() 35 | isSending := c.sending != nil 36 | c.sendingLock.Unlock() 37 | c.goawayLock.Lock() 38 | sent := c.goawaySent 39 | c.goawayReceived = true 40 | c.goawayLock.Unlock() 41 | if !sent && !isSending { 42 | goaway := new(frames.GOAWAY) 43 | if c.server != nil { 44 | c.lastRequestStreamIDLock.Lock() 45 | goaway.LastGoodStreamID = c.lastRequestStreamID 46 | c.lastRequestStreamIDLock.Unlock() 47 | } else { 48 | c.lastPushStreamIDLock.Lock() 49 | goaway.LastGoodStreamID = c.lastPushStreamID 50 | c.lastPushStreamIDLock.Unlock() 51 | } 52 | select { 53 | case c.output[0] <- goaway: 54 | c.goawayLock.Lock() 55 | c.goawaySent = true 56 | c.goawayLock.Unlock() 57 | case <-time.After(100 * time.Millisecond): 58 | debug.Println("Failed to send closing GOAWAY.") 59 | } 60 | } 61 | 62 | // Close all streams. Make a copy so close() can modify the map. 63 | var streams []common.Stream 64 | c.streamsLock.Lock() 65 | for key, stream := range c.streams { 66 | streams = append(streams, stream) 67 | delete(c.streams, key) 68 | } 69 | c.streamsLock.Unlock() 70 | 71 | for _, stream := range streams { 72 | stream.Close() 73 | } 74 | 75 | // Ensure any pending frames are sent. 76 | c.sendingLock.Lock() 77 | if c.sending == nil { 78 | c.sending = make(chan struct{}) 79 | c.sendingLock.Unlock() 80 | select { 81 | case <-c.sending: 82 | case <-time.After(200 * time.Millisecond): 83 | } 84 | c.sendingLock.Lock() 85 | } 86 | c.sending = nil 87 | c.sendingLock.Unlock() 88 | 89 | select { 90 | case _, ok := <-c.stop: 91 | if ok { 92 | close(c.stop) 93 | } 94 | default: 95 | close(c.stop) 96 | } 97 | 98 | c.connLock.Lock() 99 | if c.conn != nil { 100 | c.conn.Close() 101 | c.conn = nil 102 | } 103 | c.connLock.Unlock() 104 | 105 | if compressor := c.compressor; compressor != nil { 106 | compressor.Close() 107 | } 108 | 109 | c.pushedResources = nil 110 | 111 | for _, stream := range c.output { 112 | select { 113 | case _, ok := <-stream: 114 | if ok { 115 | close(stream) 116 | } 117 | default: 118 | close(stream) 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /spdy3/spdy_api.go: -------------------------------------------------------------------------------- 1 | package spdy3 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | "net/url" 7 | "strings" 8 | 9 | "github.com/SlyMarbo/spdy/common" 10 | "github.com/SlyMarbo/spdy/spdy3/frames" 11 | ) 12 | 13 | // Ping is used by spdy.PingServer and spdy.PingClient to send 14 | // SPDY PINGs. 15 | func (c *Conn) Ping() (<-chan bool, error) { 16 | if c.Closed() { 17 | return nil, errors.New("Error: Conn has been closed.") 18 | } 19 | 20 | ping := new(frames.PING) 21 | c.nextPingIDLock.Lock() 22 | pid := c.nextPingID 23 | if pid+2 < pid { 24 | if pid&1 == 0 { 25 | c.nextPingID = 2 26 | } else { 27 | c.nextPingID = 1 28 | } 29 | } else { 30 | c.nextPingID += 2 31 | } 32 | c.nextPingIDLock.Unlock() 33 | 34 | ping.PingID = pid 35 | c.output[0] <- ping 36 | ch := make(chan bool, 1) 37 | c.pingsLock.Lock() 38 | c.pings[pid] = ch 39 | c.pingsLock.Unlock() 40 | 41 | return ch, nil 42 | } 43 | 44 | // Push is used to issue a server push to the client. Note that this cannot be performed 45 | // by clients. 46 | func (c *Conn) Push(resource string, origin common.Stream) (common.PushStream, error) { 47 | c.goawayLock.Lock() 48 | goaway := c.goawayReceived || c.goawaySent 49 | c.goawayLock.Unlock() 50 | if goaway { 51 | return nil, common.ErrGoaway 52 | } 53 | 54 | if c.server == nil { 55 | return nil, errors.New("Error: Only servers can send pushes.") 56 | } 57 | 58 | // Parse and check URL. 59 | url, err := url.Parse(resource) 60 | if err != nil { 61 | return nil, err 62 | } 63 | if url.Scheme == "" || url.Host == "" { 64 | return nil, errors.New("Error: Incomplete path provided to resource.") 65 | } 66 | resource = url.String() 67 | 68 | // Ensure the resource hasn't been pushed on the given stream already. 69 | if c.pushedResources[origin] == nil { 70 | c.pushedResources[origin] = map[string]struct{}{ 71 | resource: struct{}{}, 72 | } 73 | } else if _, ok := c.pushedResources[origin][url.String()]; !ok { 74 | c.pushedResources[origin][resource] = struct{}{} 75 | } else { 76 | return nil, errors.New("Error: Resource already pushed to this stream.") 77 | } 78 | 79 | // Check stream limit would allow the new stream. 80 | if !c.pushStreamLimit.Add() { 81 | return nil, errors.New("Error: Max concurrent streams limit exceeded.") 82 | } 83 | 84 | // Verify that path is prefixed with / as required by spec. 85 | path := url.Path 86 | if !strings.HasPrefix(path, "/") { 87 | path = "/" + path 88 | } 89 | 90 | // Prepare the SYN_STREAM. 91 | push := new(frames.SYN_STREAM) 92 | push.Flags = common.FLAG_UNIDIRECTIONAL 93 | push.AssocStreamID = origin.StreamID() 94 | push.Priority = 7 95 | push.Header = make(http.Header) 96 | push.Header.Set(":scheme", url.Scheme) 97 | push.Header.Set(":host", url.Host) 98 | push.Header.Set(":path", path) 99 | push.Header.Set(":version", "HTTP/1.1") 100 | 101 | // Send. 102 | c.streamCreation.Lock() 103 | defer c.streamCreation.Unlock() 104 | 105 | c.lastPushStreamIDLock.Lock() 106 | c.lastPushStreamID += 2 107 | newID := c.lastPushStreamID 108 | c.lastPushStreamIDLock.Unlock() 109 | if newID > common.MAX_STREAM_ID { 110 | return nil, errors.New("Error: All server streams exhausted.") 111 | } 112 | push.StreamID = newID 113 | c.output[0] <- push 114 | 115 | // Create the pushStream. 116 | out := NewPushStream(c, newID, origin, c.output[7]) 117 | out.AddFlowControl(c.flowControl) 118 | 119 | // Store in the connection map. 120 | c.streamsLock.Lock() 121 | c.streams[newID] = out 122 | c.streamsLock.Unlock() 123 | 124 | return out, nil 125 | } 126 | 127 | func (c *Conn) SetFlowControl(f common.FlowControl) { 128 | c.flowControlLock.Lock() 129 | c.flowControl = f 130 | c.flowControlLock.Unlock() 131 | } 132 | -------------------------------------------------------------------------------- /spdy3/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy3 6 | 7 | import ( 8 | "github.com/SlyMarbo/spdy/common" 9 | ) 10 | 11 | // defaultServerSettings are used in initialising the connection. 12 | // It takes the max concurrent streams. 13 | func defaultServerSettings(m uint32) common.Settings { 14 | return common.Settings{ 15 | common.SETTINGS_INITIAL_WINDOW_SIZE: &common.Setting{ 16 | Flags: common.FLAG_SETTINGS_PERSIST_VALUE, 17 | ID: common.SETTINGS_INITIAL_WINDOW_SIZE, 18 | Value: common.DEFAULT_INITIAL_WINDOW_SIZE, 19 | }, 20 | common.SETTINGS_MAX_CONCURRENT_STREAMS: &common.Setting{ 21 | Flags: common.FLAG_SETTINGS_PERSIST_VALUE, 22 | ID: common.SETTINGS_MAX_CONCURRENT_STREAMS, 23 | Value: m, 24 | }, 25 | } 26 | } 27 | 28 | // defaultClientSettings are used in initialising the connection. 29 | // It takes the max concurrent streams. 30 | func defaultClientSettings(m uint32) common.Settings { 31 | return common.Settings{ 32 | common.SETTINGS_INITIAL_WINDOW_SIZE: &common.Setting{ 33 | ID: common.SETTINGS_INITIAL_WINDOW_SIZE, 34 | Value: common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE, 35 | }, 36 | common.SETTINGS_MAX_CONCURRENT_STREAMS: &common.Setting{ 37 | ID: common.SETTINGS_MAX_CONCURRENT_STREAMS, 38 | Value: m, 39 | }, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spdy_test.go: -------------------------------------------------------------------------------- 1 | package spdy 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "testing" 10 | ) 11 | 12 | func TestExamples(t *testing.T) { 13 | cwd, err := os.Getwd() 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | 18 | examples, err := ioutil.ReadDir("examples") 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | 23 | buf := new(bytes.Buffer) 24 | 25 | for _, example := range examples { 26 | err = os.Chdir(filepath.Join(cwd, "examples", example.Name())) 27 | if err != nil { 28 | t.Error(err) 29 | continue 30 | } 31 | 32 | buf.Reset() 33 | 34 | cmd := exec.Command("go", "build", "-o", "test") 35 | cmd.Stderr = buf 36 | if err = cmd.Run(); err != nil { 37 | t.Errorf("Example %q failed to compile:\n%s", example.Name(), buf.String()) 38 | continue 39 | } 40 | 41 | if err = os.Remove("test"); err != nil { 42 | t.Error(err) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /versions.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Jamie Hall. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package spdy 6 | 7 | import ( 8 | "errors" 9 | "sort" 10 | ) 11 | 12 | // SPDY version of this implementation. 13 | const DEFAULT_SPDY_VERSION = 3.1 14 | 15 | // Version factors. 16 | var supportedVersions = map[float64]struct{}{ 17 | 2: struct{}{}, 18 | 3: struct{}{}, 19 | 3.1: struct{}{}, 20 | } 21 | 22 | const minVersion = 2 23 | const maxVersion = 3.1 24 | 25 | // SupportedVersions will return a slice of supported SPDY versions. 26 | // The returned versions are sorted into order of most recent first. 27 | func SupportedVersions() []float64 { 28 | s := make([]float64, 0, len(supportedVersions)) 29 | for v, _ := range supportedVersions { 30 | s = append(s, v) 31 | } 32 | sort.Sort(sort.Reverse(sort.Float64Slice(s))) 33 | return s 34 | } 35 | 36 | var npnStrings = map[float64]string{ 37 | 2: "spdy/2", 38 | 3: "spdy/3", 39 | 3.1: "spdy/3.1", 40 | } 41 | 42 | // npn returns the NPN version strings for the SPDY versions 43 | // currently enabled, plus HTTP/1.1. 44 | func npn() []string { 45 | v := SupportedVersions() 46 | s := make([]string, 0, len(v)+1) 47 | for _, v := range v { 48 | if str := npnStrings[float64(v)]; str != "" { 49 | s = append(s, str) 50 | } 51 | } 52 | s = append(s, "http/1.1") 53 | return s 54 | } 55 | 56 | // SupportedVersion determines if the provided SPDY version is 57 | // supported by this instance of the library. This can be modified 58 | // with EnableSpdyVersion and DisableSpdyVersion. 59 | func SupportedVersion(v float64) bool { 60 | _, s := supportedVersions[v] 61 | return s 62 | } 63 | 64 | // EnableSpdyVersion can re-enable support for versions of SPDY 65 | // that have been disabled by DisableSpdyVersion. 66 | func EnableSpdyVersion(v float64) error { 67 | if v == 0 { 68 | return errors.New("Error: version 0 is invalid.") 69 | } 70 | if v < minVersion { 71 | return errors.New("Error: SPDY version too old.") 72 | } 73 | if v > maxVersion { 74 | return errors.New("Error: SPDY version too new.") 75 | } 76 | supportedVersions[v] = struct{}{} 77 | return nil 78 | } 79 | 80 | // DisableSpdyVersion can be used to disable support for the 81 | // given SPDY version. This process can be undone by using 82 | // EnableSpdyVersion. 83 | func DisableSpdyVersion(v float64) error { 84 | if v == 0 { 85 | return errors.New("Error: version 0 is invalid.") 86 | } 87 | if v < minVersion { 88 | return errors.New("Error: SPDY version too old.") 89 | } 90 | if v > maxVersion { 91 | return errors.New("Error: SPDY version too new.") 92 | } 93 | delete(supportedVersions, v) 94 | return nil 95 | } 96 | --------------------------------------------------------------------------------