//TODO Think about: writing push_promise is available in any time now. Need to handle it. public void WritePushPromise(IDictionary<string, string[]> pairs, Int32 promisedId) { if (Id % 2 != 0 && promisedId % 2 != 0) throw new InvalidOperationException("Client cant send push_promise frames"); if (Closed) return; var headers = new HeadersList(pairs); var frame = new PushPromiseFrame(Id, promisedId, true, true, headers); ReservedLocal = true; _writeQueue.WriteFrame(frame); if (OnFrameSent != null) { OnFrameSent(this, new FrameSentEventArgs(frame)); } }
private void HandlePushPromiseFrame(PushPromiseFrame frame, out Http2Stream stream) { Http2Logger.LogDebug("New push_promise with id = " + frame.StreamId); Http2Logger.LogDebug("Promised id = " + frame.PromisedStreamId); //09 -> 6.6. PUSH_PROMISE //PUSH_PROMISE frames MUST be associated with an existing, peer- //initiated stream. If the stream identifier field specifies the value //0x0, a recipient MUST respond with a connection error (Section 5.4.1) //of type PROTOCOL_ERROR. //... a receiver MUST //treat the receipt of a PUSH_PROMISE that promises an illegal stream //identifier (Section 5.1.1) (that is, an identifier for a stream that //is not currently in the "idle" state) as a connection error //(Section 5.4.1) of type PROTOCOL_ERROR, unless the receiver recently //sent a RST_STREAM frame to cancel the associated stream (see //Section 5.1). if (frame.StreamId == 0 || frame.PromisedStreamId == 0 || (frame.PromisedStreamId % 2) != 0 || ActiveStreams.Any(str => (str.Key % 2) == 0 && str.Key > frame.PromisedStreamId)) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Incorrect promised stream id"); } var serializedHeaders = new byte[frame.CompressedHeaders.Count]; Buffer.BlockCopy(frame.CompressedHeaders.Array, frame.CompressedHeaders.Offset, serializedHeaders, 0, serializedHeaders.Length); var decompressedHeaders = _comprProc.Decompress(serializedHeaders); var headers = new HeadersList(decompressedHeaders); foreach (var header in headers) { Http2Logger.LogDebug("Stream {0} header: {1}={2}", frame.StreamId, header.Key, header.Value); frame.Headers.Add(header); } var sequence = _headersSequences.Find(frame.PromisedStreamId); if (sequence == null) { sequence = new HeadersSequence(frame.PromisedStreamId, frame); _headersSequences.Add(sequence); } else { //09 -> 6.6. PUSH_PROMISE //A receiver MUST //treat the receipt of a PUSH_PROMISE on a stream that is neither //"open" nor "half-closed (local)" as a connection error //(Section 5.4.1) of type PROTOCOL_ERROR. //This means that we already got push_promise with the same PromisedId. //Hence Stream is in the reserved state. throw new ProtocolError(ResetStatusCode.ProtocolError, "Got multiple push_promises with same Promised id's"); } //09 -> 6.6. PUSH_PROMISE //A PUSH_PROMISE frame without the END_PUSH_PROMISE flag set MUST be //followed by a CONTINUATION frame for the same stream. A receiver //MUST treat the receipt of any other type of frame or a frame on a //different stream as a connection error (Section 5.4.1) of type //PROTOCOL_ERROR. if (!sequence.IsComplete) { stream = null; return; } //09 -> 8.2.1. Push Requests //The server MUST include a method in the ":method" //header field that is safe (see [HTTP-p2], Section 4.2.1). If a //client receives a PUSH_PROMISE that does not include a complete and // valid set of header fields, or the ":method" header field identifies //a method that is not safe, it MUST respond with a stream error //(Section 5.4.2) of type PROTOCOL_ERROR. //Lets think that only GET method is safe for now var method = sequence.Headers.GetValue(CommonHeaders.Method); if (method == null || !method.Equals(Verbs.Get, StringComparison.OrdinalIgnoreCase)) { var frameReceiveStream = GetStream(frame.StreamId); frameReceiveStream.WriteRst(ResetStatusCode.ProtocolError); frameReceiveStream.Dispose(ResetStatusCode.None); stream = null; return; } stream = GetStream(frame.PromisedStreamId); if (stream == null) { stream = CreateStream(sequence); stream.EndStreamSent = true; ValidateHeaders(stream); } else { //09 -> 6.6. PUSH_PROMISE //Similarly, a receiver MUST //treat the receipt of a PUSH_PROMISE that promises an illegal stream //identifier (Section 5.1.1) (that is, an identifier for a stream that //is not currently in the "idle" state) as a connection error //(Section 5.4.1) of type PROTOCOL_ERROR throw new ProtocolError(ResetStatusCode.ProtocolError, "Remote ep tried to promise incorrect stream id"); } }
//TODO Think about: writing push_promise is available in any time now. Need to handle it. public void WritePushPromise(IDictionary<string, string[]> pairs, Int32 promisedId) { if (Id % 2 != 0 && promisedId % 2 != 0) throw new InvalidOperationException("Client cant send push_promise frames"); if (Disposed) return; var headers = new HeadersList(pairs); //TODO IsEndPushPromise should be computationable var frame = new PushPromiseFrame(Id, promisedId, true, headers); _writeQueue.WriteFrame(frame); if (OnFrameSent != null) { OnFrameSent(this, new FrameSentEventArgs(frame)); } }
private void HandlePushPromiseFrame(PushPromiseFrame frame, out Http2Stream stream) { Http2Logger.LogDebug("PUSH_PROMISE frame: stream id={0}, payload len={1}, promised id={2}, " + "has pad={3}, pad high={4}, pad low={5}, end headers={6}", frame.StreamId, frame.PayloadLength, frame.PromisedStreamId, frame.HasPadding, frame.PadHigh, frame.PadLow, frame.IsEndHeaders); /* 12 -> 6.6. PUSH_PROMISE frames MUST be associated with an existing, peer- initiated stream. If the stream identifier field specifies the value 0x0, a recipient MUST respond with a connection error of type PROTOCOL_ERROR. */ if (frame.StreamId == 0) throw new ProtocolError(ResetStatusCode.ProtocolError, "push promise frame with stream id=0"); /* 12 -> 5.1 An endpoint that receives any frame after receiving a RST_STREAM MUST treat that as a stream error of type STREAM_CLOSED. */ if (StreamDictionary[frame.StreamId].Closed) throw new Http2StreamNotFoundException(frame.StreamId); //... a receiver MUST //treat the receipt of a PUSH_PROMISE that promises an illegal stream //identifier (Section 5.1.1) (that is, an identifier for a stream that //is not currently in the "idle" state) as a connection error //(Section 5.4.1) of type PROTOCOL_ERROR, unless the receiver recently //sent a RST_STREAM frame to cancel the associated stream (see //Section 5.1). if (frame.StreamId % 2 == 0 || frame.PromisedStreamId == 0 || (frame.PromisedStreamId % 2) != 0 || frame.PromisedStreamId < _lastPromisedId || !((StreamDictionary[frame.StreamId].Opened || StreamDictionary[frame.StreamId].HalfClosedLocal))) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Incorrect Promised Stream id"); } var serializedHeaders = new byte[frame.CompressedHeaders.Count]; Buffer.BlockCopy(frame.CompressedHeaders.Array, frame.CompressedHeaders.Offset, serializedHeaders, 0, serializedHeaders.Length); var decompressedHeaders = _comprProc.Decompress(serializedHeaders); var headers = new HeadersList(decompressedHeaders); foreach (var header in headers) { Http2Logger.LogDebug("Stream {0} header: {1}={2}", frame.StreamId, header.Key, header.Value); frame.Headers.Add(header); } var sequence = _headersSequences.Find(frame.PromisedStreamId); if (sequence == null) { sequence = new HeadersSequence(frame.PromisedStreamId, frame); _headersSequences.Add(sequence); } else { //09 -> 6.6. PUSH_PROMISE //A receiver MUST //treat the receipt of a PUSH_PROMISE on a stream that is neither //"open" nor "half-closed (local)" as a connection error //(Section 5.4.1) of type PROTOCOL_ERROR. //This means that we already got push_promise with the same PromisedId. //Hence Stream is in the reserved state. throw new ProtocolError(ResetStatusCode.ProtocolError, "Got multiple push promises with same Promised Stream id's"); } //09 -> 6.6. PUSH_PROMISE //A PUSH_PROMISE frame without the END_PUSH_PROMISE flag set MUST be //followed by a CONTINUATION frame for the same stream. A receiver //MUST treat the receipt of any other type of frame or a frame on a //different stream as a connection error (Section 5.4.1) of type //PROTOCOL_ERROR. if (!sequence.IsComplete) { stream = null; return; } //09 -> 8.2.1. Push Requests //The server MUST include a method in the ":method" //header field that is safe (see [HTTP-p2], Section 4.2.1). If a //client receives a PUSH_PROMISE that does not include a complete and // valid set of header fields, or the ":method" header field identifies //a method that is not safe, it MUST respond with a stream error //(Section 5.4.2) of type PROTOCOL_ERROR. //Lets think that only GET method is safe for now var method = sequence.Headers.GetValue(CommonHeaders.Method); if (method == null || !method.Equals(Verbs.Get, StringComparison.OrdinalIgnoreCase)) { var frameReceiveStream = GetStream(frame.StreamId); frameReceiveStream.WriteRst(ResetStatusCode.ProtocolError); frameReceiveStream.Close(ResetStatusCode.None); stream = null; return; } stream = GetStream(frame.PromisedStreamId); if (stream.Idle) { stream = CreateStream(sequence); stream.ReservedRemote = true; ValidateHeaders(stream); _lastPromisedId = stream.Id; } else { /* 12 -> 6.6 Similarly, a receiver MUST treat the receipt of a PUSH_PROMISE that promises an illegal stream identifier (that is, an identifier for a stream that is not currently in the "idle" state) as a connection error of type PROTOCOL_ERROR. */ throw new ProtocolError(ResetStatusCode.ProtocolError, "Remote endpoint tried to Promise incorrect Stream id"); } }