예제 #1
0
파일: processing.go 프로젝트: vonwenm/spdy
// processFrame handles the initial processing of the given
// frame, before passing it on to the relevant helper func,
// if necessary. The returned boolean indicates whether the
// connection is closing.
func (c *Conn) processFrame(frame common.Frame) bool {
	switch frame := frame.(type) {

	case *frames.SYN_STREAM:
		if c.server == nil {
			c.handlePush(frame)
		} else {
			c.handleRequest(frame)
		}
	case *frames.SYN_STREAMV3_1:
		f3 := new(frames.SYN_STREAM)
		f3.Flags = frame.Flags
		f3.StreamID = frame.StreamID
		f3.AssocStreamID = frame.AssocStreamID
		f3.Priority = frame.Priority
		f3.Slot = 0
		f3.Header = frame.Header
		if c.server == nil {
			c.handlePush(f3)
		} else {
			c.handleRequest(f3)
		}

	case *frames.SYN_REPLY:
		c.handleSynReply(frame)

	case *frames.RST_STREAM:
		if frame.Status.IsFatal() {
			code := frame.Status.String()
			log.Printf("Warning: Received %s on stream %d. Closing connection.\n", code, frame.StreamID)
			c.shutdownError = frame
			c.Close()
			return true
		}
		c.handleRstStream(frame)

	case *frames.SETTINGS:
		for _, setting := range frame.Settings {
			c.receivedSettings[setting.ID] = setting
			switch setting.ID {
			case common.SETTINGS_INITIAL_WINDOW_SIZE:
				c.initialWindowSizeLock.Lock()
				initial := int64(c.initialWindowSize)
				current := c.connectionWindowSize
				inbound := int64(setting.Value)
				if initial != inbound {
					if initial > inbound {
						c.connectionWindowSize = inbound - (initial - current)
					} else {
						c.connectionWindowSize += (inbound - initial)
					}
					c.initialWindowSize = setting.Value
				}
				c.initialWindowSizeLock.Unlock()

			case common.SETTINGS_MAX_CONCURRENT_STREAMS:
				if c.server == nil {
					c.requestStreamLimit.SetLimit(setting.Value)
				} else {
					c.pushStreamLimit.SetLimit(setting.Value)
				}
			}
		}

	case *frames.PING:
		// Check whether Ping ID is a response.
		c.nextPingIDLock.Lock()
		next := c.nextPingID
		c.nextPingIDLock.Unlock()
		if frame.PingID&1 == next&1 {
			c.pingsLock.Lock()
			if c.check(c.pings[frame.PingID] == nil, "Ignored unrequested PING %d", frame.PingID) {
				c.pingsLock.Unlock()
				return false
			}
			c.pings[frame.PingID] <- true
			close(c.pings[frame.PingID])
			delete(c.pings, frame.PingID)
			c.pingsLock.Unlock()
		} else {
			debug.Println("Received PING. Replying...")
			c.output[0] <- frame
		}

	case *frames.GOAWAY:
		lastProcessed := frame.LastGoodStreamID
		c.streamsLock.Lock()
		for streamID, stream := range c.streams {
			if streamID&1 == c.oddity && streamID > lastProcessed {
				// Stream is locally-sent and has not been processed.
				// TODO: Inform the server that the push has not been successful.
				stream.Close()
			}
		}
		c.streamsLock.Unlock()
		c.shutdownError = frame
		c.goawayLock.Lock()
		c.goawayReceived = true
		c.goawayLock.Unlock()

	case *frames.HEADERS:
		c.handleHeaders(frame)

	case *frames.WINDOW_UPDATE:
		c.handleWindowUpdate(frame)

	case *frames.CREDENTIAL:
		if c.Subversion > 0 {
			return false
		}
		if c.server == nil || c.certificates == nil {
			log.Println("Ignored unexpected CREDENTIAL.")
			return false
		}
		if frame.Slot >= c.vectorIndex {
			setting := new(frames.SETTINGS)
			setting.Settings = common.Settings{
				common.SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE: &common.Setting{
					ID:    common.SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE,
					Value: uint32(frame.Slot + 4),
				},
			}
			c.output[0] <- setting
			c.vectorIndex += 4
		}
		c.certificates[frame.Slot] = frame.Certificates

	case *frames.DATA:
		if c.Subversion > 0 {
			// The transfer window shouldn't already be negative.
			if c.connectionWindowSizeThere < 0 {
				c._GOAWAY(common.GOAWAY_FLOW_CONTROL_ERROR)
				return false
			}

			c.connectionWindowSizeThere -= int64(len(frame.Data))

			c.flowControlLock.Lock()
			f := c.flowControl
			c.flowControlLock.Unlock()
			delta := f.ReceiveData(0, c.initialWindowSizeThere, c.connectionWindowSizeThere)
			if delta != 0 {
				grow := new(frames.WINDOW_UPDATE)
				grow.StreamID = 0
				grow.DeltaWindowSize = delta
				c.output[0] <- grow
				c.connectionWindowSizeThere += int64(grow.DeltaWindowSize)
			}
		}
		if c.server == nil {
			c.handleServerData(frame)
		} else {
			c.handleClientData(frame)
		}

	default:
		log.Println(fmt.Sprintf("Ignored unexpected frame type %T", frame))
	}
	return false
}
예제 #2
0
파일: conn.go 프로젝트: vonwenm/spdy
// NewConn produces an initialised spdy3 connection.
func NewConn(conn net.Conn, server *http.Server, subversion int) *Conn {
	out := new(Conn)

	// Common ground.
	out.remoteAddr = conn.RemoteAddr().String()
	out.server = server
	out.conn = conn
	out.buf = bufio.NewReader(conn)
	if tlsConn, ok := conn.(*tls.Conn); ok {
		out.tlsState = new(tls.ConnectionState)
		*out.tlsState = tlsConn.ConnectionState()
	}
	out.streams = make(map[common.StreamID]common.Stream)
	out.output[0] = make(chan common.Frame)
	out.output[1] = make(chan common.Frame)
	out.output[2] = make(chan common.Frame)
	out.output[3] = make(chan common.Frame)
	out.output[4] = make(chan common.Frame)
	out.output[5] = make(chan common.Frame)
	out.output[6] = make(chan common.Frame)
	out.output[7] = make(chan common.Frame)
	out.pings = make(map[uint32]chan<- bool)
	out.compressor = common.NewCompressor(3)
	out.decompressor = common.NewDecompressor(3)
	out.receivedSettings = make(common.Settings)
	out.lastPushStreamID = 0
	out.lastRequestStreamID = 0
	out.stop = make(chan bool)
	out.Subversion = subversion

	// Server/client specific.
	if server != nil { // servers
		out.nextPingID = 2
		out.oddity = 0
		out.initialWindowSize = common.DEFAULT_INITIAL_WINDOW_SIZE
		out.requestStreamLimit = common.NewStreamLimit(common.DEFAULT_STREAM_LIMIT)
		out.pushStreamLimit = common.NewStreamLimit(common.NO_STREAM_LIMIT)
		out.vectorIndex = 8
		out.init = func() {
			// Initialise the connection by sending the connection settings.
			settings := new(frames.SETTINGS)
			settings.Settings = defaultServerSettings(common.DEFAULT_STREAM_LIMIT)
			out.output[0] <- settings
		}
		if d := server.ReadTimeout; d != 0 {
			out.SetReadTimeout(d)
		}
		if d := server.WriteTimeout; d != 0 {
			out.SetWriteTimeout(d)
		}
		out.flowControl = DefaultFlowControl(common.DEFAULT_INITIAL_WINDOW_SIZE)
		out.pushedResources = make(map[common.Stream]map[string]struct{})

		if subversion == 0 {
			out.certificates = make(map[uint16][]*x509.Certificate, 8)
			if out.tlsState != nil && out.tlsState.PeerCertificates != nil {
				out.certificates[1] = out.tlsState.PeerCertificates
			}
		} else if subversion == 1 {
			out.connectionWindowSize = common.DEFAULT_INITIAL_WINDOW_SIZE
		}

	} else { // clients
		out.nextPingID = 1
		out.oddity = 1
		out.initialWindowSize = common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE
		out.requestStreamLimit = common.NewStreamLimit(common.NO_STREAM_LIMIT)
		out.pushStreamLimit = common.NewStreamLimit(common.DEFAULT_STREAM_LIMIT)
		out.pushRequests = make(map[common.StreamID]*http.Request)
		out.init = func() {
			// Initialise the connection by sending the connection settings.
			settings := new(frames.SETTINGS)
			settings.Settings = defaultClientSettings(common.DEFAULT_STREAM_LIMIT)
			out.output[0] <- settings
		}
		out.flowControl = DefaultFlowControl(common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE)

		if subversion == 1 {
			out.connectionWindowSize = common.DEFAULT_INITIAL_CLIENT_WINDOW_SIZE
		}
	}

	if subversion == 1 {
		out.initialWindowSizeThere = out.flowControl.InitialWindowSize()
		out.connectionWindowSizeThere = int64(out.initialWindowSizeThere)
	}
	return out
}