private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame) { string originalPath = stream.Headers.GetValue(CommonHeaders.Path.ToLower()); //If user sets the empty file in get command we return notFound webpage string fileName = string.IsNullOrEmpty(Path.GetFileName(originalPath)) ? Index : Path.GetFileName(originalPath); string path = Path.Combine(AssemblyPath, fileName); try { _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count, path, stream.ReceivedDataAmount != 0); } catch (IOException) { Http2Logger.LogError("File is still downloading. Repeat request later"); stream.Close(ResetStatusCode.InternalError); return; } stream.ReceivedDataAmount += dataFrame.Data.Count; if (dataFrame.IsEndStream) { if (stream.HalfClosedRemote) { //send terminator stream.WriteDataFrame(new ArraySegment<byte>(new byte[0]), true); Http2Logger.LogConsole("Terminator was sent"); } _fileHelper.RemoveStream(path); Http2Logger.LogConsole("Bytes received: " + stream.ReceivedDataAmount); } }
private void HandleRstFrame(RstStreamFrame resetFrame, out Http2Stream stream) { Http2Logger.LogDebug("RST_STREAM frame: stream id={0}, status code={1}", resetFrame.StreamId, resetFrame.StatusCode); /* 12 -> 6.4 * RST_STREAM frames MUST be associated with a stream. If a RST_STREAM * frame is received with a stream identifier of 0x0, the recipient MUST * treat this as a connection error of type PROTOCOL_ERROR. */ if (resetFrame.StreamId == 0) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Rst frame with stream id=0"); } stream = GetStream(resetFrame.StreamId); if (stream.Closed) { /* 12 -> 5.4.2 * An endpoint MUST NOT send a RST_STREAM in response to an RST_STREAM * frame, to avoid looping. */ if (!stream.WasRstSent) { throw new Http2StreamNotFoundException(resetFrame.StreamId); } return; } if (!(stream.ReservedRemote || stream.Opened || stream.HalfClosedLocal)) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Rst for non opened or reserved stream"); } stream.Close(ResetStatusCode.None); }
private void ValidateHeaders(Http2Stream stream) { /* 12 -> 8.1.3 * Header field names MUST be converted to lowercase prior to their * encoding in HTTP/2.0. A request or response containing uppercase * header field names MUST be treated as malformed. */ foreach (var header in stream.Headers) { string key = header.Key; if (!_matcher.IsMatch(key) || key == ":") { stream.WriteRst(ResetStatusCode.RefusedStream); stream.Close(ResetStatusCode.RefusedStream); break; } } }
private void ValidateHeaders(Http2Stream stream) { /* 12 -> 8.1.3 Header field names MUST be converted to lowercase prior to their encoding in HTTP/2.0. A request or response containing uppercase header field names MUST be treated as malformed. */ foreach (var header in stream.Headers) { string key = header.Key; if (!_matcher.IsMatch(key) || key == ":") { stream.WriteRst(ResetStatusCode.RefusedStream); stream.Close(ResetStatusCode.RefusedStream); break; } } }
protected override void ProcessRequest(Http2Stream stream, Frame frame) { //spec 09 -> 8.1.3.2. Response Header Fields //A single ":status" header field is defined that carries the HTTP //status code field (see [HTTP-p2], Section 6). This header field MUST //be included in all responses, otherwise the response is malformed if (stream.Headers.GetValue(CommonHeaders.Status) == null) { throw new ProtocolError(ResetStatusCode.ProtocolError, "no status header in response. StreamId = " + stream.Id); } int code; if (!int.TryParse(stream.Headers.GetValue(CommonHeaders.Status), out code)) { stream.WriteRst(ResetStatusCode.ProtocolError); //Got something strange in the status field stream.Close(ResetStatusCode.ProtocolError); } //got some king of error if (code != StatusCode.Code200Ok) { //Close server's stream stream.Close(ResetStatusCode.Cancel); //will dispose client's stream and close server's one. } //Do nothing. Client may not process requests for now }
private void HandleRstFrame(RstStreamFrame resetFrame, out Http2Stream stream) { Http2Logger.LogDebug("RST_STREAM frame: stream id={0}, status code={1}", resetFrame.StreamId, resetFrame.StatusCode); /* 12 -> 6.4 RST_STREAM frames MUST be associated with a stream. If a RST_STREAM frame is received with a stream identifier of 0x0, the recipient MUST treat this as a connection error of type PROTOCOL_ERROR. */ if (resetFrame.StreamId == 0) throw new ProtocolError(ResetStatusCode.ProtocolError, "Rst frame with stream id=0"); stream = GetStream(resetFrame.StreamId); if (stream.Closed) { /* 12 -> 5.4.2 An endpoint MUST NOT send a RST_STREAM in response to an RST_STREAM frame, to avoid looping. */ if (!stream.WasRstSent) { throw new Http2StreamNotFoundException(resetFrame.StreamId); } return; } if (!(stream.ReservedRemote || stream.Opened || stream.HalfClosedLocal)) throw new ProtocolError(ResetStatusCode.ProtocolError, "Rst for non opened or reserved stream"); stream.Close(ResetStatusCode.None); }
/// <summary> /// Overrides request processing logic. /// </summary> /// <param name="stream">The stream.</param> /// <param name="frame">The request header frame.</param> /// <returns></returns> protected override void ProcessRequest(Http2Stream stream, Frame frame) { /* 12 -> 8.1.3.1 All HTTP/2 requests MUST include exactly one valid value for the ":method", ":scheme", and ":path" header fields, unless this is a CONNECT request (Section 8.3). An HTTP request that omits mandatory header fields is malformed (Section 8.1.3.5). */ if (stream.Headers.GetValue(CommonHeaders.Method) == null || stream.Headers.GetValue(CommonHeaders.Path) == null || stream.Headers.GetValue(CommonHeaders.Scheme) == null) { stream.WriteRst(ResetStatusCode.ProtocolError); stream.Close(ResetStatusCode.ProtocolError); return; } Task.Factory.StartNew(async () => { try { var context = new Http2OwinMessageContext(stream); var contextEnv = context.OwinContext.Environment; PushFunc pushDelegate = null; pushDelegate = async pairs => { var promisedStream = CreateStream(); //assume that we have already received endStream promisedStream.HalfClosedLocal = true; stream.WritePushPromise(pairs, promisedStream.Id); var headers = new HeadersList(pairs); promisedStream.Headers.AddRange(headers); var http2MsgCtx = new Http2OwinMessageContext(promisedStream); var http2PushCtx = http2MsgCtx.OwinContext; http2PushCtx.Set(CommonOwinKeys.ServerPushFunc, pushDelegate); //pass add info from parent to child context. This info can store //reference table for example or something els that should be passed from //client request into child push requests. if (contextEnv.ContainsKey(CommonOwinKeys.AdditionalInfo)) http2PushCtx.Set(CommonOwinKeys.AdditionalInfo, contextEnv[CommonOwinKeys.AdditionalInfo]); await _next(http2PushCtx); http2MsgCtx.FinishResponse(); }; context.OwinContext.Set(CommonOwinKeys.ServerPushFunc, pushDelegate); context.OwinContext.Set(CommonOwinKeys.EnableServerPush, _isPushEnabled); await _next(context.OwinContext); context.FinishResponse(); } catch (Exception ex) { EndResponse(stream, ex); } }); }