/// <summary> /// Set the tcp connection to server used by this webclient /// </summary> /// <param name="Connection"></param> internal void SetConnection(TcpConnectionCache Connection) { Connection.LastAccess = DateTime.Now; ServerConnection = Connection; }
/// <summary> /// This is the core request handler method for a particular connection from client /// </summary> /// <param name="client"></param> /// <param name="httpCmd"></param> /// <param name="clientStream"></param> /// <param name="clientStreamReader"></param> /// <param name="clientStreamWriter"></param> /// <param name="isHttps"></param> /// <returns></returns> private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool isHttps) { TcpConnectionCache connection = null; //Loop through each subsequest request on this particular client connection //(assuming HTTP connection is kept alive by client) while (true) { if (string.IsNullOrEmpty(httpCmd)) { Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); break; } var args = new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse); args.ProxyClient.TcpClient = client; try { //break up the line into three components (method, remote URL & Http Version) var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3); var httpMethod = httpCmdSplit[0]; //find the request HTTP version Version version = new Version(1, 1); if (httpCmdSplit.Length == 3) { var httpVersion = httpCmdSplit[2].ToLower().Trim(); if (httpVersion == "http/1.0") { version = new Version(1, 0); } } args.WebSession.Request.RequestHeaders = new List <HttpHeader>(); //Read the request headers string tmpLine; while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync())) { var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); args.WebSession.Request.RequestHeaders.Add(new HttpHeader(header[0], header[1])); } var httpRemoteUri = new Uri(!isHttps ? httpCmdSplit[1] : (string.Concat("https://", args.WebSession.Request.Host, httpCmdSplit[1]))); #if DEBUG //Just ignore local requests while Debugging //Its annoying if (httpRemoteUri.Host.Contains("localhost")) { Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); break; } #endif args.WebSession.Request.RequestUri = httpRemoteUri; args.WebSession.Request.Method = httpMethod; args.WebSession.Request.HttpVersion = version; args.ProxyClient.ClientStream = clientStream; args.ProxyClient.ClientStreamReader = clientStreamReader; args.ProxyClient.ClientStreamWriter = clientStreamWriter; PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession); args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority; //If user requested interception do it if (BeforeRequest != null) { Delegate[] invocationList = BeforeRequest.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ((Func <object, SessionEventArgs, Task>)invocationList[i])(null, args); } await Task.WhenAll(handlerTasks); } //if upgrading to websocket then relay the requet without reading the contents if (args.WebSession.Request.UpgradeToWebSocket) { await TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHeaders, httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps, SupportedSslProtocols); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); return; } //construct the web request that we are going to issue on behalf of the client. connection = await tcpConnectionCacheManager.GetClient(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version, UpStreamHttpProxy, UpStreamHttpsProxy, BUFFER_SIZE, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate), new LocalCertificateSelectionCallback(SelectClientCertificate)); args.WebSession.Request.RequestLocked = true; //If request was cancelled by user then dispose the client if (args.WebSession.Request.CancelRequest) { Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); break; } //if expect continue is enabled then send the headers first //and see if server would return 100 conitinue if (args.WebSession.Request.ExpectContinue) { args.WebSession.SetConnection(connection); await args.WebSession.SendRequest(Enable100ContinueBehaviour); } //If 100 continue was the response inform that to the client if (Enable100ContinueBehaviour) { if (args.WebSession.Request.Is100Continue) { await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", "Continue", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); } else if (args.WebSession.Request.ExpectationFailed) { await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", "Expectation Failed", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); } } //If expect continue is not enabled then set the connectio and send request headers if (!args.WebSession.Request.ExpectContinue) { args.WebSession.SetConnection(connection); await args.WebSession.SendRequest(Enable100ContinueBehaviour); } //If request was modified by user if (args.WebSession.Request.RequestBodyRead) { if (args.WebSession.Request.ContentEncoding != null) { args.WebSession.Request.RequestBody = await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.RequestBody); } //chunked send is not supported as of now args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length; var newStream = args.WebSession.ServerConnection.Stream; await newStream.WriteAsync(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length); } else { if (!args.WebSession.Request.ExpectationFailed) { //If its a post/put request, then read the client html body and send it to server if (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT") { await SendClientRequestBody(args); } } } //If not expectation failed response was returned by server then parse response if (!args.WebSession.Request.ExpectationFailed) { await HandleHttpSessionResponse(args); } //if connection is closing exit if (args.WebSession.Response.ResponseKeepAlive == false) { Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); return; } //send the tcp connection to server back to connection cache for reuse await tcpConnectionCacheManager.ReleaseClient(connection); // read the next request httpCmd = await clientStreamReader.ReadLineAsync(); } catch { Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); break; } } }