/// <summary> /// Pipeline step: tunnel the request from the client to the remove /// server, and schedule the next step to be <c>ReadResponse</c> /// </summary> protected virtual void SendRequest() { // Transmit the request to the server RequestLine.SendTo(RemoteSocket); RequestHeaders.SendTo(RemoteSocket); if (State.RequestHasMessage) { // Tunnel the request message if (State.RequestMessageChunked) { BrowserSocket.TunnelChunkedDataTo(RemoteSocket); } else { Debug.Assert(State.RequestMessageLength > 0); BrowserSocket.TunnelDataTo(TunnelRemoteSocket, State.RequestMessageLength); } } State.NextStep = ReadResponse; }
/// <summary> /// Pipeline: tunnel the HTTP response from the remote server to the /// local client, and end the request processing /// </summary> protected virtual void SendResponse() { if ((ResponseHeaders.TransferEncoding == null && ResponseHeaders.ContentLength == null) == false) { // Transmit the response header to the client SendResponseStatusAndHeaders(); } // Find out if there is a message body // (RFC 2616, section 4.4) int sc = ResponseStatusLine.StatusCode; if (RequestLine.Method.Equals("HEAD") || sc == 204 || sc == 304 || (sc >= 100 && sc <= 199)) { SendResponseStatusAndHeaders(); CallOnReceiveResponse(ResponseHeaders.ContentEncoding); return; } bool responseMessageChunked = false; uint responseMessageLength = 0; if (ResponseHeaders.TransferEncoding != null) { responseMessageChunked = Array.IndexOf(ResponseHeaders.TransferEncoding, "chunked") >= 0; Debug.Assert(responseMessageChunked); } else if (ResponseHeaders.ContentLength != null) { responseMessageLength = (uint)ResponseHeaders.ContentLength; if (responseMessageLength == 0) { CallOnReceiveResponse(ResponseHeaders.ContentEncoding); return; } } else { // We really should have been given a response // length. It appears that some popular websites // send small files without a transfer-encoding // or length. // It seems that all of the browsers handle this // case so we need to as well. var buffer = new byte[512]; RemoteSocket.TunnelDataTo(ref buffer); // Transmit the response header to the client ResponseHeaders.ContentLength = (uint)buffer.Length; ResponseStatusLine.SendTo(BrowserSocket); ResponseHeaders.SendTo(BrowserSocket); BrowserSocket.TunnelDataTo(TunnelBrowserSocket, buffer); State.NextStep = null; return; } if (State.OnResponseMessagePacket != null) { if (State.PersistConnectionRemoteSocket == false) { // Pipeline until the connection is closed RemoteSocket.TunnelDataTo(State.OnResponseMessagePacket); } else { if (responseMessageChunked) { RemoteSocket.TunnelChunkedDataTo(State.OnResponseMessagePacket); } else { RemoteSocket.TunnelDataTo(State.OnResponseMessagePacket, responseMessageLength); } } State.OnResponseMessagePacket(null, 0, 0); } else { if (State.PersistConnectionRemoteSocket == false) { // Pipeline until the connection is closed RemoteSocket.TunnelDataTo(TunnelBrowserSocket); } else { if (responseMessageChunked) { RemoteSocket.TunnelChunkedDataTo(BrowserSocket); } else { RemoteSocket.TunnelDataTo(TunnelBrowserSocket, responseMessageLength); } } } CallOnReceiveResponse(ResponseHeaders.ContentEncoding); }