private void SendGZipResponse(HeyHttpResponse response) { using (MemoryStream compressedStream = new MemoryStream()) { using (GZipStream gzipStream = new GZipStream(compressedStream, CompressionMode.Compress)) { response.ContentStream.CopyTo(gzipStream); // Not sure why, but gzip stream must be close before getting the data. gzipStream.Close(); byte[] bytes = compressedStream.ToArray(); // Send response headers. response.Headers.Add("Content-Encoding: gzip"); response.Headers.Add("Content-Length: " + bytes.Length); response.Headers.Add("Content-Type: " + GetMediaType(request.Path)); response.CopyHeadersTo(networkStream); //compressedStream.CopyTo(networkStream); //networkStream.Flush(); // For debugging. networkStream.Write(bytes, 0, bytes.Length); logger.WriteBodyLine(BitConverter.ToString(bytes)); } } }
private static void Reply407AndClose(HeyLogger logger, NetworkStream clientStream) { HeyHttpResponse response = new HeyHttpResponse(logger); response.Version = "HTTP/1.0"; response.Status = "407 Proxy Authentication Required"; response.Headers.Add("Proxy-agent: Netscape-Proxy/1.1"); response.Headers.Add("Proxy-Authenticate: Basic realm=\"WallyWorld\""); response.Headers.Add("Content-Length: 0"); response.Headers.Add("Connection: close"); response.CopyHeadersTo(clientStream); clientStream.Close(); }
private static void OnAccept(IAsyncResult asyncResult) { HeyLogger logger = new HeyLogger(); Socket socketListener = asyncResult.AsyncState as Socket; Socket client = socketListener.EndAccept(asyncResult); socketListener.BeginAccept(OnAccept, asyncResult.AsyncState); try { HeyHttpRequest request = new HeyHttpRequest(logger); // Client got connected. logger.WriteLine(String.Format("Connected: {0}", client.RemoteEndPoint)); // Read HTTP headers. NetworkStream clientStream = new NetworkStream(client); MemoryStream tempStream = new MemoryStream(); request.ReadHeaders(clientStream, tempStream); // Authentication. if (settings.AuthenticationRequired && !IsAuthenticated(request)) { Reply407AndClose(logger, clientStream); return; } // Find server host name. logger.WriteLine(String.Format("Trying to connect to {0}", request.Host)); // Get IP address for the given server hostname. IPHostEntry hostEntry = Dns.GetHostEntry(request.Host); if (hostEntry.AddressList.Length == 0) { throw new Exception("Unknow server hostname."); } IPAddress address = hostEntry.AddressList[0]; // Connect to server. Socket server = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); server.Connect(new IPEndPoint(address, request.Port)); logger.WriteLine(String.Format("Connected to {0}", request.Host)); // Send cached data to server. NetworkStream serverStream = new NetworkStream(server); // When using the CONNECT method, proxy must send a HTTP response to client. // See "Tunneling TCP based protocols through Web proxy servers" internet-draft. if (request.Method == "CONNECT") { HeyHttpResponse response = new HeyHttpResponse(logger); response.Status = "200 Connection established"; response.Headers.Add("Proxy-agent: Happy-Sockets-Proxy/1.0"); response.CopyHeadersTo(clientStream); } else { // Only forward headers when it is not a CONNECT request. tempStream.Seek(0, SeekOrigin.Begin); tempStream.CopyTo(serverStream); } // Forward data. ParameterizedThreadStart serverToClientStart = new ParameterizedThreadStart(ForwardData); Thread serverToClientThread = new Thread(serverToClientStart); serverToClientThread.Start( new StreamsPair { StreamA = serverStream, StreamB = clientStream, Label = "server to client", Logger = logger }); ParameterizedThreadStart clientToServerStart = new ParameterizedThreadStart(ForwardData); Thread clientToServerThread = new Thread(clientToServerStart); clientToServerThread.Start( new StreamsPair { StreamA = clientStream, StreamB = serverStream, Label = "client to server", Logger = logger }); //serverToClientThread.Join(); //clientToServerThread.Join(); // TODO: make sure streams do not go out of scope. // TODO: wait until threads end and ensure connections are close. } catch (SocketException ex) { WriteLine(ConsoleColor.Red, ex.Message); logger.WriteLine(ex.ToString()); // We couldn't connect to the server, terminate connection with the client. client.Close(); } catch (IOException ex) { WriteLine(ConsoleColor.Red, ex.Message); logger.WriteLine(ex.ToString()); // The client closed the connection, terminate the connection with the server. client.Close(); } catch (Exception ex) { WriteLine(ConsoleColor.Red, ex.Message); logger.WriteLine(ex.ToString()); } }
private void SendChunkedResponse(HeyHttpResponse response) { Stream stream = response.ContentStream; // Send headers. response.Headers.Add("Transfer-Encoding: chunked"); response.Headers.Add("Trailer: \"Foo\""); response.Headers.Add("Content-Type: " + GetMediaType(request.Path)); response.CopyHeadersTo(networkStream); // Send a random amount of bytes. Random random = new Random(); int bytesRead = 0; while (bytesRead < stream.Length) { byte[] buffer; int chunkSize = random.Next(1, MaxChunckSize); if (stream.Length - bytesRead < chunkSize) { chunkSize = (int)stream.Length - bytesRead; } // Send chunck size and CRLF. string chunkSizeString = String.Format("{0:X}\r\n", chunkSize); buffer = Encoding.ASCII.GetBytes(chunkSizeString); networkStream.Write(buffer, 0, buffer.Length); // Send chunck. buffer = new byte[chunkSize]; int localBytesRead = stream.Read(buffer, 0, buffer.Length); networkStream.Write(buffer, 0, localBytesRead); // Send CRLF. buffer = Encoding.ASCII.GetBytes("\r\n"); networkStream.Write(buffer, 0, buffer.Length); networkStream.Flush(); bytesRead += localBytesRead; logger.WriteBodyLine( String.Format("Chunk of {0,13:N0} bytes, {1,13:N0} of {2,13:N0} bytes sent.", chunkSize, bytesRead, stream.Length)); Thread.Sleep(100); } StringBuilder theEnd = new StringBuilder(); // Add last chunk. theEnd.Append("0\r\n"); // Add trailer. theEnd.Append("Foo: Bar\r\n"); // Add last CRLF. theEnd.Append("\r\n"); byte[] lastChunkBuffer = Encoding.ASCII.GetBytes(theEnd.ToString()); networkStream.Write(lastChunkBuffer, 0, lastChunkBuffer.Length); }
private void SendSlowResponse( HeyHttpResponse response, int delayInMilliseconds, HttpResponseOption option) { Stream stream = response.ContentStream; long idleLength; if (!request.QueryStringHas("idleLength", out idleLength)) { idleLength = -1; } // Calculate positions. long totalBytesRead = 0; long firstPosition = 0; if (response.FirstPosition > 0) { firstPosition = response.FirstPosition; stream.Position = firstPosition; } long length = response.ContentStream.Length; if (response.LastPosition > 0) { length = response.LastPosition - firstPosition + 1; } // Send headers. response.Headers.Add("Content-Length: " + length); response.Headers.Add("Content-Type: " + GetMediaType(request.Path)); response.CopyHeadersTo(networkStream); // Server must not send a message body when using HEAD method (RFC 7231 4.3.2 HEAD). if (request.IsHeadMethod) { return; } // Calulate buffer size. int bufferLength; if (!request.QueryStringHas("bufferLength", out bufferLength)) { bufferLength = 1000000; // 1 MB } byte[] buffer = new byte[bufferLength]; while (totalBytesRead < length) { long remainingBytes = length - totalBytesRead; int loopLength = (int)Math.Min(bufferLength, remainingBytes); int localBytesRead = stream.Read(buffer, 0, loopLength); if (option == HttpResponseOption.Pause) { BlockThreadUntilEnterIsPressed(); } if (option == HttpResponseOption.Slow) { Thread.Sleep(delayInMilliseconds); } networkStream.Write(buffer, 0, localBytesRead); networkStream.Flush(); totalBytesRead += localBytesRead; logger.WriteBodyLine(String.Format( "{0,13:N0} of {1,13:N0} bytes sent.", totalBytesRead, length)); if (idleLength >= 0 && totalBytesRead >= idleLength) { BlockThreadUntilClientIsDisconnected(); } } logger.WriteBodyLine("Response completed!\r\n"); }