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  }