github.com/alejandroesc/spdy@v0.0.0-20200317064415-01a02f0eb389/spdy3/conn.go (about) 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 "bufio" 9 "crypto/tls" 10 "crypto/x509" 11 "net" 12 "net/http" 13 "net/url" 14 "sync" 15 "time" 16 17 "github.com/SlyMarbo/spdy/common" 18 "github.com/SlyMarbo/spdy/spdy3/frames" 19 ) 20 21 // Conn is a spdy.Conn implementing SPDY/3. This is used in both 22 // servers and clients, and is created with either NewServerConn, 23 // or NewClientConn. 24 type Conn struct { 25 PushReceiver common.Receiver // Receiver to call for server Pushes. 26 Subversion int // SPDY 3 subversion (eg 0 for SPDY/3, 1 for SPDY/3.1). 27 28 // SPDY/3.1 29 connectionWindowLock sync.Mutex 30 dataBuffer []*frames.DATA // used to store frames witheld for flow control. 31 connectionWindowSize int64 32 initialWindowSizeThere uint32 33 connectionWindowSizeThere int64 34 35 // network state 36 remoteAddr string 37 server *http.Server // nil if client connection. 38 conn net.Conn // underlying network (TLS) connection. 39 connLock sync.Mutex // protects the interface value of the above conn. 40 buf *bufio.Reader // buffered reader on conn. 41 tlsState *tls.ConnectionState // underlying TLS connection state. 42 streams map[common.StreamID]common.Stream // map of active streams. 43 streamsLock sync.Mutex // protects streams. 44 output [8]chan common.Frame // one output channel per priority level. 45 46 // other state 47 compressor common.Compressor // outbound compression state. 48 decompressor common.Decompressor // inbound decompression state. 49 receivedSettings common.Settings // settings sent by client. 50 goawayReceived bool // goaway has been received. 51 goawaySent bool // goaway has been sent. 52 goawayLock sync.Mutex // protects goawaySent and goawayReceived. 53 numBenignErrors int // number of non-serious errors encountered. 54 readTimeout time.Duration // optional timeout for network reads. 55 writeTimeout time.Duration // optional timeout for network writes. 56 timeoutLock sync.Mutex // protects changes to readTimeout and writeTimeout. 57 vectorIndex uint16 // current limit on the credential vector size. 58 certificates map[uint16][]*x509.Certificate // certificates from CREDENTIALs and TLS handshake. 59 flowControl common.FlowControl // flow control module. 60 flowControlLock sync.Mutex // protects flowControl. 61 62 // SPDY features 63 pings map[uint32]chan<- bool // response channel for pings. 64 pingsLock sync.Mutex // protects pings. 65 nextPingID uint32 // next outbound ping ID. 66 nextPingIDLock sync.Mutex // protects nextPingID. 67 pushStreamLimit *common.StreamLimit // Limit on streams started by the server. 68 pushRequests map[common.StreamID]*http.Request // map of requests sent in server pushes. 69 lastPushStreamID common.StreamID // last push stream ID. (even) 70 lastPushStreamIDLock sync.Mutex // protects lastPushStreamID. 71 pushedResources map[common.Stream]map[string]struct{} // prevents duplicate headers being pushed. 72 73 // requests 74 lastRequestStreamID common.StreamID // last request stream ID. (odd) 75 lastRequestStreamIDLock sync.Mutex // protects lastRequestStreamID. 76 streamCreation sync.Mutex // ensures new streams are sent in order. 77 oddity common.StreamID // whether locally-sent streams are odd or even. 78 initialWindowSize uint32 // initial transport window. 79 initialWindowSizeLock sync.Mutex // lock for initialWindowSize 80 requestStreamLimit *common.StreamLimit // Limit on streams started by the client. 81 82 // startup and shutdown 83 stop chan bool // this channel is closed when the connection closes. 84 sending chan struct{} // this channel is used to ensure pending frames are sent. 85 sendingLock sync.Mutex // protects changes to sending's value. 86 init func() // this function is called before the connection begins. 87 shutdownOnce sync.Once // used to ensure clean shutdown. 88 shutdownError error // error that caused shutdown if non-nil 89 } 90 91 // NewConn produces an initialised spdy3 connection. 92 func NewConn(conn net.Conn, server *http.Server, subversion int) *Conn { 93 out := new(Conn) 94 95 // Common ground. 96 out.remoteAddr = conn.RemoteAddr().String() 97 out.server = server 98 out.conn = conn 99 out.buf = bufio.NewReader(conn) 100 if tlsConn, ok := conn.(*tls.Conn); ok { 101 out.tlsState = new(tls.ConnectionState) 102 *out.tlsState = tlsConn.ConnectionState() 103 } 104 out.streams = make(map[common.StreamID]common.Stream) 105 out.output[0] = make(chan common.Frame) 106 out.output[1] = make(chan common.Frame) 107 out.output[2] = make(chan common.Frame) 108 out.output[3] = make(chan common.Frame) 109 out.output[4] = make(chan common.Frame) 110 out.output[5] = make(chan common.Frame) 111 out.output[6] = make(chan common.Frame) 112 out.output[7] = make(chan common.Frame) 113 out.pings = make(map[uint32]chan<- bool) 114 out.compressor = common.NewCompressor(3) 115 out.decompressor = common.NewDecompressor(3) 116 out.receivedSettings = make(common.Settings) 117 out.lastPushStreamID = 0 118 out.lastRequestStreamID = 0 119 out.stop = make(chan bool) 120 out.Subversion = subversion 121 122 // Server/client specific. 123 if server != nil { // servers 124 out.nextPingID = 2 125 out.oddity = 0 126 out.initialWindowSize = common.DEFAULT_INITIAL_WINDOW_SIZE 127 out.requestStreamLimit = common.NewStreamLimit(common.DEFAULT_STREAM_LIMIT) 128 out.pushStreamLimit = common.NewStreamLimit(common.NO_STREAM_LIMIT) 129 out.vectorIndex = 8 130 out.init = func() { 131 // Initialise the connection by sending the connection settings. 132 settings := new(frames.SETTINGS) 133 settings.Settings = defaultServerSettings(common.DEFAULT_STREAM_LIMIT) 134 out.output[0] <- settings 135 } 136 if d := server.ReadTimeout; d != 0 { 137 out.SetReadTimeout(d) 138 } 139 if d := server.WriteTimeout; d != 0 { 140 out.SetWriteTimeout(d) 141 } 142 out.flowControl = DefaultFlowControl(common.DEFAULT_INITIAL_WINDOW_SIZE) 143 out.pushedResources = make(map[common.Stream]map[string]struct{}) 144 145 if subversion == 0 { 146 out.certificates = make(map[uint16][]*x509.Certificate, 8) 147 if out.tlsState != nil && out.tlsState.PeerCertificates != nil { 148 out.certificates[1] = out.tlsState.PeerCertificates 149 } 150 } else if subversion == 1 { 151 out.connectionWindowSize = common.DEFAULT_INITIAL_WINDOW_SIZE 152 } 153 154 } else { // clients 155 out.nextPingID = 1 156 out.oddity = 1 157 out.initialWindowSize = common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE 158 out.requestStreamLimit = common.NewStreamLimit(common.NO_STREAM_LIMIT) 159 out.pushStreamLimit = common.NewStreamLimit(common.DEFAULT_STREAM_LIMIT) 160 out.pushRequests = make(map[common.StreamID]*http.Request) 161 out.init = func() { 162 // Initialise the connection by sending the connection settings. 163 settings := new(frames.SETTINGS) 164 settings.Settings = defaultClientSettings(common.DEFAULT_STREAM_LIMIT) 165 out.output[0] <- settings 166 } 167 out.flowControl = DefaultFlowControl(common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE) 168 169 if subversion == 1 { 170 out.connectionWindowSize = common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE 171 } 172 } 173 174 if subversion == 1 { 175 out.initialWindowSizeThere = out.flowControl.InitialWindowSize() 176 out.connectionWindowSizeThere = int64(out.initialWindowSizeThere) 177 } 178 return out 179 } 180 181 // NextProto is intended for use in http.Server.TLSNextProto, 182 // using SPDY/3 for the connection. 183 func NextProto(s *http.Server, tlsConn *tls.Conn, handler http.Handler) { 184 NewConn(tlsConn, s, 0).Run() 185 } 186 187 // NextProto1 is intended for use in http.Server.TLSNextProto, 188 // using SPDY/3.1 for the connection. 189 func NextProto1(s *http.Server, tlsConn *tls.Conn, handler http.Handler) { 190 NewConn(tlsConn, s, 1).Run() 191 } 192 193 func (c *Conn) Run() error { 194 defer common.Recover() 195 go c.send() // Start the send loop. 196 if c.init != nil { // Must be after sending is enabled. 197 c.init() // Prepare any initialisation frames. 198 } 199 go c.readFrames() // Start the main loop. 200 <-c.stop // Run until the connection ends. 201 return nil 202 } 203 204 // newStream is used to create a new serverStream from a SYN_STREAM frame. 205 func (c *Conn) newStream(frame *frames.SYN_STREAM) *ResponseStream { 206 header := frame.Header 207 rawUrl := header.Get(":scheme") + "://" + header.Get(":host") + header.Get(":path") 208 209 url, err := url.Parse(rawUrl) 210 if c.check(err != nil, "Received SYN_STREAM with invalid request URL (%v)", err) { 211 return nil 212 } 213 214 vers := header.Get(":version") 215 major, minor, ok := http.ParseHTTPVersion(vers) 216 if c.check(!ok, "Invalid HTTP version: "+vers) { 217 return nil 218 } 219 220 method := header.Get(":method") 221 222 // Build this into a request to present to the Handler. 223 request := &http.Request{ 224 Method: method, 225 URL: url, 226 Proto: vers, 227 ProtoMajor: major, 228 ProtoMinor: minor, 229 RemoteAddr: c.remoteAddr, 230 Header: header, 231 Host: url.Host, 232 RequestURI: url.RequestURI(), 233 TLS: c.tlsState, 234 } 235 236 output := c.output[frame.Priority] 237 c.streamCreation.Lock() 238 out := NewResponseStream(c, frame, output, c.server.Handler, request) 239 c.streamCreation.Unlock() 240 c.flowControlLock.Lock() 241 f := c.flowControl 242 c.flowControlLock.Unlock() 243 out.AddFlowControl(f) 244 245 return out 246 }