private static void ForwardData(object args) { StreamsPair pair = args as StreamsPair; HeyLogger logger = pair.Logger; byte[] buffer = new byte[1024 * 1024]; try { while (pair.StreamA.CanRead) { int bytesRead = pair.StreamA.Read(buffer, 0, buffer.Length); if (bytesRead == 0) { logger.WriteLine(String.Format("No more bytes to read from {0}.", pair.Label)); break; } pair.StreamB.Write(buffer, 0, bytesRead); logger.WriteLine(String.Format("{0} bytes from {1}.", bytesRead, pair.Label)); } } catch (IOException ex) { WriteLine(ConsoleColor.Red, pair.Label); logger.WriteLine(ex.ToString()); } // TODO: Check this is right. // Tell the other side connections is being shutdown. pair.StreamB.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()); } }