private static void FinishRequest(HTTPRequest req, HTTP2Response resp, FramesAsStreamView dataStream) { if (dataStream != null) { resp.AddData(dataStream); dataStream.Close(); } bool resendRequest; HTTPConnectionStates proposedConnectionStates; KeepAliveHeader keepAliveHeader = null; ConnectionHelper.HandleResponse("HTTP2Stream", req, out resendRequest, out proposedConnectionStates, ref keepAliveHeader); if (resendRequest && !req.IsCancellationRequested) { RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(req, RequestEvents.Resend)); } else if (req.State == HTTPRequestStates.Processing && !req.IsCancellationRequested) { req.State = HTTPRequestStates.Finished; } else { if (req.State == HTTPRequestStates.Processing && req.IsCancellationRequested) { req.State = req.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted; } } }
private static void FinishRequest(HTTP2Stream stream, FramesAsStreamView dataStream) { if (dataStream != null) { try { stream.response.AddData(dataStream); } finally { dataStream.Close(); } } stream.AssignedRequest.Timing.Add(TimingEventNames.Response_Received); bool resendRequest; HTTPConnectionStates proposedConnectionStates; // ignored KeepAliveHeader keepAliveHeader = null; // ignored ConnectionHelper.HandleResponse("HTTP2Stream", stream.AssignedRequest, out resendRequest, out proposedConnectionStates, ref keepAliveHeader, stream.Context, stream.AssignedRequest.Context); if (resendRequest && !stream.AssignedRequest.IsCancellationRequested) { RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(stream.AssignedRequest, RequestEvents.Resend)); } else if (stream.AssignedRequest.State == HTTPRequestStates.Processing && !stream.AssignedRequest.IsCancellationRequested) { stream.AssignedRequest.State = HTTPRequestStates.Finished; } else { if (stream.AssignedRequest.State == HTTPRequestStates.Processing && stream.AssignedRequest.IsCancellationRequested) { stream.AssignedRequest.State = stream.AssignedRequest.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted; } } }
private static void FinishRequest(HTTPRequest req, HTTP2Response resp, FramesAsStreamView dataStream) { if (dataStream != null) { resp.AddData(dataStream); dataStream.Close(); } bool resendRequest; HTTPConnectionStates proposedConnectionStates; KeepAliveHeader keepAliveHeader = null; ConnectionHelper.HandleResponse("HTTP2Stream", req, out resendRequest, out proposedConnectionStates, ref keepAliveHeader); if (resendRequest) { RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(req, RequestEvents.Resend)); } else { req.State = HTTPRequestStates.Finished; } }
private void ProcessIncomingFrames(List <HTTP2FrameHeaderAndPayload> outgoingFrames) { UInt32 windowUpdate = 0; while (this.incomingFrames.Count > 0) { HTTP2FrameHeaderAndPayload frame = this.incomingFrames.Dequeue(); if (this.isRSTFrameSent) { BufferPool.Release(frame.Payload); continue; } if (/*HTTPManager.Logger.Level == Logger.Loglevels.All && */ frame.Type != HTTP2FrameTypes.DATA && frame.Type != HTTP2FrameTypes.WINDOW_UPDATE) { HTTPManager.Logger.Information("HTTP2Stream", string.Format("[{0}] Process - processing frame: {1}", this.Id, frame.ToString())); } switch (frame.Type) { case HTTP2FrameTypes.HEADERS: case HTTP2FrameTypes.CONTINUATION: if (this.State != HTTP2StreamStates.HalfClosedLocal) { // ERROR! continue; } // payload will be released by the view frame.DontUseMemPool = true; if (this.currentFramesView == null) { this.currentFramesView = new FramesAsStreamView(new HeaderFrameView()); } this.currentFramesView.AddFrame(frame); if ((frame.Flags & (byte)HTTP2HeadersFlags.END_HEADERS) != 0) { List <KeyValuePair <string, string> > headers = new List <KeyValuePair <string, string> >(); try { this.encoder.Decode(this, this.currentFramesView, headers); } catch (Exception ex) { HTTPManager.Logger.Exception("HTTP2Stream", string.Format("[{0}] ProcessIncomingFrames", this.Id), ex); } this.response = new HTTP2Response(this.AssignedRequest, false); this.response.AddHeaders(headers); this.AssignedRequest.Response = this.response; this.currentFramesView.Close(); this.currentFramesView = null; if (frame.Type == HTTP2FrameTypes.HEADERS && (frame.Flags & (byte)HTTP2HeadersFlags.END_STREAM) != 0) { PlatformSupport.Threading.ThreadedRunner.RunShortLiving <HTTPRequest, HTTP2Response, FramesAsStreamView>(FinishRequest, this.AssignedRequest, this.response, this.currentFramesView); if (this.State == HTTP2StreamStates.HalfClosedLocal) { this.State = HTTP2StreamStates.Closed; } else { this.State = HTTP2StreamStates.HalfClosedRemote; } } } break; case HTTP2FrameTypes.DATA: if (this.State != HTTP2StreamStates.HalfClosedLocal) { // ERROR! continue; } this.downloaded += frame.PayloadLength; if (this.isStreamedDownload && frame.Payload != null && frame.PayloadLength > 0) { this.response.ProcessData(frame.Payload, (int)frame.PayloadLength); } // frame's buffer will be released by the frames view frame.DontUseMemPool = true; if (this.currentFramesView == null && !this.isStreamedDownload) { this.currentFramesView = new FramesAsStreamView(new DataFrameView()); } if (!this.isStreamedDownload) { this.currentFramesView.AddFrame(frame); } // Track received data, and if necessary(local window getting too low), send a window update frame if (this.localWindow < frame.PayloadLength) { HTTPManager.Logger.Error("HTTP2Stream", string.Format("[{0}] Frame's PayloadLength ({1:N0}) is larger then local window ({2:N0}). Frame: {3}", this.Id, frame.PayloadLength, this.localWindow, frame)); } else { this.localWindow -= frame.PayloadLength; } bool isFinalDataFrame = (frame.Flags & (byte)HTTP2DataFlags.END_STREAM) != 0; // Window update logic. // 1.) We could use a logic to only send window update(s) after a threshold is reached. // When the initial window size is high enough to contain the whole or most of the result, // sending back two window updates (connection and stream) after every data frame is pointless. // 2.) On the other hand, window updates are cheap and works even when initial window size is low. // ( if (isFinalDataFrame || this.localWindow <= this.windowUpdateThreshold) { windowUpdate += this.settings.MySettings[HTTP2Settings.INITIAL_WINDOW_SIZE] - this.localWindow - windowUpdate; } if (isFinalDataFrame) { if (this.isStreamedDownload) { this.response.FinishProcessData(); } HTTPManager.Logger.Information("HTTP2Stream", string.Format("[{0}] All data arrived, data length: {1:N0}", this.Id, this.downloaded)); // create a short living thread to process the downloaded data: PlatformSupport.Threading.ThreadedRunner.RunShortLiving <HTTPRequest, HTTP2Response, FramesAsStreamView>(FinishRequest, this.AssignedRequest, this.response, this.currentFramesView); this.currentFramesView = null; if (this.State == HTTP2StreamStates.HalfClosedLocal) { this.State = HTTP2StreamStates.Closed; } else { this.State = HTTP2StreamStates.HalfClosedRemote; } } else if (this.AssignedRequest.OnDownloadProgress != null) { RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.AssignedRequest, RequestEvents.DownloadProgress, downloaded, this.response.ExpectedContentLength)); } break; case HTTP2FrameTypes.WINDOW_UPDATE: HTTP2WindowUpdateFrame windowUpdateFrame = HTTP2FrameHelper.ReadWindowUpdateFrame(frame); if (HTTPManager.Logger.Level == Logger.Loglevels.All) { HTTPManager.Logger.Information("HTTP2Stream", string.Format("[{0}] Received Window Update: {1:N0}, new remoteWindow: {2:N0}, initial remote window: {3:N0}, total data sent: {4:N0}", this.Id, windowUpdateFrame.WindowSizeIncrement, this.remoteWindow + windowUpdateFrame.WindowSizeIncrement, this.settings.RemoteSettings[HTTP2Settings.INITIAL_WINDOW_SIZE], this.sentData)); } this.remoteWindow += windowUpdateFrame.WindowSizeIncrement; break; case HTTP2FrameTypes.RST_STREAM: // https://httpwg.org/specs/rfc7540.html#RST_STREAM var rstStreamFrame = HTTP2FrameHelper.ReadRST_StreamFrame(frame); HTTPManager.Logger.Error("HTTP2Stream", string.Format("[{0}] RST Stream frame ({1}) received in state {2}!", this.Id, rstStreamFrame, this.State)); Abort(string.Format("RST_STREAM frame received! Error code: {0}({1})", rstStreamFrame.Error.ToString(), rstStreamFrame.ErrorCode)); break; default: HTTPManager.Logger.Warning("HTTP2Stream", string.Format("[{0}] Unexpected frame ({1}) in state {2}!", this.Id, frame, this.State)); break; } if (!frame.DontUseMemPool) { BufferPool.Release(frame.Payload); } } if (windowUpdate > 0) { if (HTTPManager.Logger.Level <= Logger.Loglevels.All) { HTTPManager.Logger.Information("HTTP2Stream", string.Format("[{0}] Sending window update: {1:N0}, current window: {2:N0}, initial window size: {3:N0}", this.Id, windowUpdate, this.localWindow, this.settings.MySettings[HTTP2Settings.INITIAL_WINDOW_SIZE])); } this.localWindow += windowUpdate; outgoingFrames.Add(HTTP2FrameHelper.CreateWindowUpdateFrame(this.Id, windowUpdate)); } }