private async Task <byte[]> GetDecompressedResponseBody(string encodingType, byte[] responseBodyStream) { var decompressionFactory = new DecompressionFactory(); var decompressor = decompressionFactory.Create(encodingType); return(await decompressor.Decompress(responseBodyStream, bufferSize)); }
private static IGZipHandler CreateHandler(CommandType command, string src, string dest) { switch (command) { case CommandType.Compress: var compressionFactory = new CompressionFactory(src, dest); return(new CompressionHandler(compressionFactory)); case CommandType.Decompress: var decompressionFactory = new DecompressionFactory(src, dest); return(new DecompressionHandler(decompressionFactory)); default: return(null); } }
private async Task copyBodyAsync(bool isRequest, bool useOriginalHeaderValues, HttpWriter writer, TransformationMode transformation, Action <byte[], int, int> onCopy, CancellationToken cancellationToken) { var stream = getStreamReader(isRequest); var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response; bool isChunked = useOriginalHeaderValues? requestResponse.OriginalIsChunked : requestResponse.IsChunked; long contentLength = useOriginalHeaderValues ? requestResponse.OriginalContentLength : requestResponse.ContentLength; if (transformation == TransformationMode.None) { await writer.CopyBodyAsync(stream, isChunked, contentLength, onCopy, cancellationToken); return; } LimitedStream limitedStream; Stream decompressStream = null; string contentEncoding = useOriginalHeaderValues ? requestResponse.OriginalContentEncoding : requestResponse.ContentEncoding; Stream s = limitedStream = new LimitedStream(stream, bufferPool, isChunked, contentLength); if (transformation == TransformationMode.Uncompress && contentEncoding != null) { s = decompressStream = DecompressionFactory.Create(contentEncoding, s); } try { using (var bufStream = new CustomBufferedStream(s, bufferPool, bufferSize, true)) { await writer.CopyBodyAsync(bufStream, false, -1, onCopy, cancellationToken); } } finally { decompressStream?.Dispose(); await limitedStream.Finish(); limitedStream.Dispose(); } }
private static async Task copyHttp2FrameAsync(Stream input, Stream output, Http2Settings localSettings, Http2Settings remoteSettings, Func <SessionEventArgs> sessionFactory, ConcurrentDictionary <int, SessionEventArgs> sessions, Func <SessionEventArgs, Task> onBeforeRequestResponse, Guid connectionId, bool isClient, CancellationToken cancellationToken, ExceptionHandler exceptionFunc) { int headerTableSize = 0; Decoder?decoder = null; var frameHeader = new Http2FrameHeader(); var frameHeaderBuffer = new byte[9]; byte[]? buffer = null; while (true) { int read = await forceRead(input, frameHeaderBuffer, 0, 9, cancellationToken); if (read != 9) { return; } int length = (frameHeaderBuffer[0] << 16) + (frameHeaderBuffer[1] << 8) + frameHeaderBuffer[2]; var type = (Http2FrameType)frameHeaderBuffer[3]; var flags = (Http2FrameFlag)frameHeaderBuffer[4]; int streamId = ((frameHeaderBuffer[5] & 0x7f) << 24) + (frameHeaderBuffer[6] << 16) + (frameHeaderBuffer[7] << 8) + frameHeaderBuffer[8]; frameHeader.Length = length; frameHeader.Type = type; frameHeader.Flags = flags; frameHeader.StreamId = streamId; if (buffer == null || buffer.Length < localSettings.MaxFrameSize) { buffer = new byte[localSettings.MaxFrameSize]; } read = await forceRead(input, buffer, 0, length, cancellationToken); if (read != length) { return; } bool sendPacket = true; bool endStream = false; SessionEventArgs? args = null; RequestResponseBase?rr = null; if (type == Http2FrameType.Data || type == Http2FrameType.Headers /* || type == Http2FrameType.PushPromise*/) { if (!sessions.TryGetValue(streamId, out args)) { //if (type == Http2FrameType.Data) //{ // throw new ProxyHttpException("HTTP Body data received before any header frame.", null, args); //} //if (type == Http2FrameType.Headers && !isClient) //{ // throw new ProxyHttpException("HTTP Response received before any Request header frame.", null, args); //} if (type == Http2FrameType.PushPromise && isClient) { throw new ProxyHttpException("HTTP Push promise received from the client.", null, args); } } } //System.Diagnostics.Debug.WriteLine("CONN: " + connectionId + ", CLIENT: " + isClient + ", STREAM: " + streamId + ", TYPE: " + type); if (type == Http2FrameType.Data && args != null) { if (isClient) { args.OnDataSent(buffer, 0, read); } else { args.OnDataReceived(buffer, 0, read); } rr = isClient ? (RequestResponseBase)args.HttpClient.Request : args.HttpClient.Response; bool padded = (flags & Http2FrameFlag.Padded) != 0; bool endStreamFlag = (flags & Http2FrameFlag.EndStream) != 0; if (endStreamFlag) { endStream = true; } if (rr.Http2IgnoreBodyFrames) { sendPacket = false; } if (rr.ReadHttp2BodyTaskCompletionSource != null) { // Get body method was called in the "before" event handler var data = rr.Http2BodyData; int offset = 0; if (padded) { offset++; length--; length -= buffer[0]; } data !.Write(buffer, offset, length); } } else if (type == Http2FrameType.Headers /* || type == Http2FrameType.PushPromise*/) { bool endHeaders = (flags & Http2FrameFlag.EndHeaders) != 0; bool padded = (flags & Http2FrameFlag.Padded) != 0; bool priority = (flags & Http2FrameFlag.Priority) != 0; bool endStreamFlag = (flags & Http2FrameFlag.EndStream) != 0; if (endStreamFlag) { endStream = true; } int offset = 0; if (padded) { offset = 1; breakpoint(); } if (type == Http2FrameType.PushPromise) { int promisedStreamId = (buffer[offset++] << 24) + (buffer[offset++] << 16) + (buffer[offset++] << 8) + buffer[offset++]; if (!sessions.TryGetValue(streamId, out args)) { args = sessionFactory(); args.IsPromise = true; if (!sessions.TryAdd(streamId, args)) { ; } if (!sessions.TryAdd(promisedStreamId, args)) { ; } } System.Diagnostics.Debug.WriteLine("PROMISE STREAM: " + streamId + ", " + promisedStreamId + ", CONN: " + connectionId); rr = args.HttpClient.Request; if (isClient) { // push_promise from client??? breakpoint(); } } else { if (!sessions.TryGetValue(streamId, out args)) { args = sessionFactory(); if (!sessions.TryAdd(streamId, args)) { ; } } rr = isClient ? (RequestResponseBase)args.HttpClient.Request : args.HttpClient.Response; if (priority) { var priorityData = ((long)buffer[offset++] << 32) + ((long)buffer[offset++] << 24) + (buffer[offset++] << 16) + (buffer[offset++] << 8) + buffer[offset++]; rr.Priority = priorityData; } } int dataLength = length - offset; if (padded) { dataLength -= buffer[0]; } var headerListener = new MyHeaderListener( (name, value) => { var headers = isClient ? args.HttpClient.Request.Headers : args.HttpClient.Response.Headers; headers.AddHeader(new HttpHeader(name, value)); }); try { // recreate the decoder when new value is bigger // should we recreate when smaller, too? if (decoder == null || headerTableSize < localSettings.HeaderTableSize) { headerTableSize = localSettings.HeaderTableSize; decoder = new Decoder(8192, headerTableSize); } decoder.Decode(new BinaryReader(new MemoryStream(buffer, offset, dataLength)), headerListener); decoder.EndHeaderBlock(); if (rr is Request request) { var method = headerListener.Method; var path = headerListener.Path; if (method.Length == 0 || path.Length == 0) { throw new Exception("HTTP/2 Missing method or path"); } request.HttpVersion = HttpVersion.Version20; request.Method = method.GetString(); request.IsHttps = headerListener.Scheme == ProxyServer.UriSchemeHttps; request.Authority = headerListener.Authority.GetString(); request.RequestUriString8 = path; //request.RequestUri = headerListener.GetUri(); } else { var response = (Response)rr; response.HttpVersion = HttpVersion.Version20; // todo: avoid string conversion string statusHack = HttpHeader.Encoding.GetString(headerListener.Status.Span); int.TryParse(statusHack, out int statusCode); response.StatusCode = statusCode; response.StatusDescription = string.Empty; } } catch (Exception ex) { exceptionFunc(new ProxyHttpException("Failed to decode HTTP/2 headers", ex, args)); } if (!endHeaders) { breakpoint(); } if (endHeaders) { var tcs = new TaskCompletionSource <bool>(); rr.ReadHttp2BeforeHandlerTaskCompletionSource = tcs; var handler = onBeforeRequestResponse(args); rr.Http2BeforeHandlerTask = handler; if (handler == await Task.WhenAny(tcs.Task, handler)) { rr.ReadHttp2BeforeHandlerTaskCompletionSource = null; tcs.SetResult(true); await sendHeader(remoteSettings, frameHeader, frameHeaderBuffer, rr, endStream, output, args.IsPromise); } else { rr.Http2IgnoreBodyFrames = true; } rr.Locked = true; } sendPacket = false; } else if (type == Http2FrameType.Continuation) { // todo: implementing this type is mandatory for multi-part headers breakpoint(); } else if (type == Http2FrameType.Settings) { if (length % 6 != 0) { // https://httpwg.org/specs/rfc7540.html#SETTINGS // 6.5. SETTINGS // A SETTINGS frame with a length other than a multiple of 6 octets MUST be treated as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR throw new ProxyHttpException("Invalid settings length", null, null); } int pos = 0; while (pos < length) { int identifier = (buffer[pos++] << 8) + buffer[pos++]; int value = (buffer[pos++] << 24) + (buffer[pos++] << 16) + (buffer[pos++] << 8) + buffer[pos++]; if (identifier == 1 /*SETTINGS_HEADER_TABLE_SIZE*/) { //System.Diagnostics.Debug.WriteLine("HEADER SIZE CONN: " + connectionId + ", CLIENT: " + isClient + ", value: " + value); remoteSettings.HeaderTableSize = value; } else if (identifier == 5 /*SETTINGS_MAX_FRAME_SIZE*/) { remoteSettings.MaxFrameSize = value; } } } if (type == Http2FrameType.RstStream) { int errorCode = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; if (streamId == 0) { // connection error exceptionFunc(new ProxyHttpException("HTTP/2 connection error. Error code: " + errorCode, null, args)); return; } else { // stream error sessions.TryRemove(streamId, out _); if (errorCode != 8 /*cancel*/) { exceptionFunc(new ProxyHttpException("HTTP/2 stream error. Error code: " + errorCode, null, args)); } } } if (endStream && rr !.ReadHttp2BodyTaskCompletionSource != null) { if (!rr.BodyAvailable) { var data = rr.Http2BodyData; var body = data !.ToArray(); if (rr.ContentEncoding != null) { using (var ms = new MemoryStream()) { using (var zip = DecompressionFactory.Create(CompressionUtil.CompressionNameToEnum(rr.ContentEncoding), new MemoryStream(body))) { zip.CopyTo(ms); } body = ms.ToArray(); } } if (!rr.BodyAvailable) { rr.Body = body; } } rr.IsBodyRead = true; var tcs = rr.ReadHttp2BodyTaskCompletionSource; rr.ReadHttp2BodyTaskCompletionSource = null; if (!tcs.Task.IsCompleted) { tcs.SetResult(true); } rr.Http2BodyData = null; if (rr.Http2BeforeHandlerTask != null) { await rr.Http2BeforeHandlerTask; } if (args !.IsPromise) { breakpoint(); } await sendBody(remoteSettings, rr, frameHeader, frameHeaderBuffer, buffer, output); } if (!isClient && endStream) { sessions.TryRemove(streamId, out _); System.Diagnostics.Debug.WriteLine("REMOVED CONN: " + connectionId + ", CLIENT: " + isClient + ", STREAM: " + streamId + ", TYPE: " + type); } if (sendPacket) { // do not cancel the write operation frameHeader.CopyToBuffer(frameHeaderBuffer); await output.WriteAsync(frameHeaderBuffer, 0, frameHeaderBuffer.Length /*, cancellationToken*/); await output.WriteAsync(buffer, 0, length /*, cancellationToken*/); } if (cancellationToken.IsCancellationRequested) { return; } /*using (var fs = new System.IO.FileStream($@"c:\temp\{connectionId}.{streamId}.dat", FileMode.Append)) * { * fs.Write(headerBuffer, 0, headerBuffer.Length); * fs.Write(buffer, 0, length); * }*/ } }