I'm trying to build a simple http proxy, which does four really basic things:
Accepts connection from web-browser (using TcpClient/TcpListener).
Reads request from its stream.
Reads hostname and initiates connection with host.
Loads content from webpage and forwards it back to the client.
The troubles i met with:
Sometimes page wouldn't load at all.
Sometimes browser gives me an error 'The content has wrong encryption'(in firefox).
Seldom i can see content corruption(plain text instead of HTML).
What i've done:
HttpListener class that contains methods for listening for incoming requests and invoking event OnNewRequestReceived:
public void Listen()
{
Listener.Start();
while (true)
{
var client = Listener.AcceptTcpClient();
Task.Run(() => StartReceivingData(client));
}
}
public void StartReceivingData(TcpClient client)
{
NetworkStream clientStream = client.GetStream();
var buffer = new byte[16000];
while (true)
{
try
{
if (!clientStream.CanRead)
return;
//connection is closed
if (clientStream.Read(buffer).Equals(0))
return;
OnNewRequestReceived?.Invoke(this, new RequestReceivedEventArgs() { User = client, Request = buffer });
} // when clientStream is disposed, exception is thrown.
catch { return; }
}
}
HttpClient class which basically contains a method that subscribes to event described above:
private void Listener_OnNewConnectionReceived(object sender, RequestReceivedEventArgs e)
{
string hostname = HttpQueryParser.GetHostName(e.Request);
NetworkStream proxyClientStream = e.User.GetStream();
try
{
if (firewall.CheckIfBlocked(hostname))
{
//send error page
e.User.GetStream().Write(Encoding.ASCII.GetBytes("<html><body style=\"padding:0; margin:0;\"><img style=\"padding:0; margin:0; width:100%; height:100%;\" src=\"https://www.hostinger.co.id/tutorial/wp-content/uploads/sites/11/2017/08/what-is-403-forbidden-error-and-how-to-fix-it.jpg\"</body></html>"));
return;
}
var targetServer = new TcpClient(hostname, 80);
NetworkStream targetServerStream = targetServer.GetStream();
targetServerStream.Write(e.Request);
var responseBuffer = new byte[32];
for (int offsetCounter = 0; true; ++offsetCounter)
{
var bytesRead = targetServerStream.Read(responseBuffer, 0, responseBuffer.Length);
// Console.WriteLine($"Read {bytesRead} from {hostname}.");
if (bytesRead.Equals(0))
return;
proxyClientStream.Write(responseBuffer, 0, responseBuffer.Length);
if (offsetCounter.Equals(0))
{
var headers = Encoding.UTF8.GetString(responseBuffer).Split("\r\n");
logger.Log(new HttpRequestEntry()
{
ResponseCode = headers[0].Substring(headers[0].IndexOf(" ") + 1),
Hostname = hostname
});
}
}
}
catch { return; }
finally { proxyClientStream.Dispose(); }
}
So, i'm guessing there's a problem with my buffer size, but changing it to higher values actually doesn't change anything .
Ok so i don't know what's the problem with my byte arrays was, but i made it work, using Stream.CopyTo , which i was quite surprized about - it works on two NetworkStreams.
Here's working method if you are curious:
private void Listener_OnNewConnectionReceived(object sender, RequestReceivedEventArgs e)
{
string hostname = HttpQueryParser.GetHostName(e.Request);
NetworkStream proxyClientStream = e.User.GetStream();
try
{
if (firewall.CheckIfBlocked(hostname))
{
//send error page
e.User.GetStream().Write(Encoding.ASCII.GetBytes("<html><body style=\"padding:0; margin:0;\"><img style=\"padding:0; margin:0; width:100%; height:100%;\" src=\"https://www.hostinger.co.id/tutorial/wp-content/uploads/sites/11/2017/08/what-is-403-forbidden-error-and-how-to-fix-it.jpg\"</body></html>"));
return;
}
var targetServer = new TcpClient(hostname, 80);
NetworkStream targetServerStream = targetServer.GetStream();
targetServerStream.Write(e.Request);
var responseBuffer = new byte[32];
//this is to capture status of http request and log it.
targetServerStream.Read(responseBuffer, 0, responseBuffer.Length);
proxyClientStream.Write(responseBuffer, 0, responseBuffer.Length);
var headers = Encoding.UTF8.GetString(responseBuffer).Split("\r\n");
logger.Log(new HttpRequestEntry()
{
ResponseCode = headers[0].Substring(headers[0].IndexOf(" ") + 1),
Hostname = hostname
});
targetServerStream.CopyTo(proxyClientStream);
}
catch { return; }
finally { proxyClientStream.Dispose(); }
}
Related
I am trying to convert a route from an old controller based api to a new dotnet7 api, however for some reason the temporary endpoint is not being hold. The basic idea is to create a temporary endpoint inside the main endpoint, and if no one calls this endpoint, then it breaks the function. This has been so far experimental but we would like to see if we could put this to work.
Can someone help me converting this code?
old code:
public class EndpointController : ControllerBase
{
private bool endpointCalled = false;
private HttpListener listener;
[HttpGet]
public IActionResult Endpoint1()
{
listener = new HttpListener();
listener.Prefixes.Add("http://localhost:7195/");
listener.Start();
// Create a temporary POST endpoint
listener.BeginGetContext(new AsyncCallback(Endpoint2), listener);
// Close the endpoint after 20 seconds if it has not been called
var timer = new Timer(20000);
timer.Elapsed += CloseEndpoint;
timer.Start();
return Results.Ok(new { message = "Endpoint2 created" });
}
private void Endpoint2(IAsyncResult result)
{
var context = listener.EndGetContext(result);
var request = context.Request;
var response = context.Response;
string responseString = "<HTML><BODY> Hello from Endpoint2!</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
endpointCalled = true;
}
private void CloseEndpoint(object sender, ElapsedEventArgs e)
{
if (!endpointCalled)
{
listener.Stop();
}
}
}
new code:
app.MapPost("/supplier/listen2", () =>
{
bool endpointCalled = false;
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://localhost:7195/testi2/");
listener.Start();
listener.BeginGetContext(new AsyncCallback(Endpoint2), listener);
var timer = new System.Timers.Timer(20000);
timer.Elapsed += CloseEndpoint;
timer.Start();
void Endpoint2(IAsyncResult result)
{
var listener = (HttpListener)result.AsyncState;
var context = listener.EndGetContext(result);
var request = context.Request;
var response = context.Response;
string responseString = "<HTML><BODY> Hello from Endpoint2!</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
endpointCalled = true;
}
void CloseEndpoint(object sender, System.Timers.ElapsedEventArgs e)
{
if (!endpointCalled)
{
listener.Stop();
}
}
return Results.Ok(new { message = "Endpoint2 created" });
// return Results.Ok(new { token = "-1" });
});
UPDATE:
Ok, there was an error before because I wrote the wrong port. Now I get an error because the listener is calling the already open door:
System.Net.HttpListenerException (48): Address already in use
Any tips to solve this?
I have created a simple C# client application. Once it connects to the server it should read the messages sent from the server. It also has the ability to send messages to server too. However I am unable to figure out to correct way to read the data.
I am spawning a thread once it connects to the server. The thread runs in infinite loop and have two interfaces each for reading and writing. Connect() method is called from a ButtonClick event.
My code snippet is as below:
namespace WpfApp1
{
public class TCPClientClass
{
private StreamWriter SwSender;
NetworkStream Sender;
NetworkStream Receiver;
//private StreamReader SrReciever;
private Thread thrMessaging;
TcpClient tcp;
bool connected = false;
public bool Connected { get { return connected; } set { connected = value; } }
//public bool Connect(IPAddress IP, int nPortNo)
public async Task Connect(IPAddress IP, int nPortNo)
{
tcp = new TcpClient();
try
{
//tcp.Connect(strIPAddress.Parse("192.168.137.1"), 2000);
// tcp.Connect(IP , nPortNo);
await tcp.ConnectAsync(IP, nPortNo);
thrMessaging = new Thread(new ThreadStart(ThreadFunction));
thrMessaging.Start();
Connected = true;
}
catch
{
MessageBox.Show("Unable to connect to server");
//return false;
}
//return true;
}
public void Disconnect()
{
Sender?.Close();
Receiver?.Close();
tcp?.Close();
//tcp?.Client.Disconnect(false);
thrMessaging.Abort();
Connected = false;
}
private void ThreadFunction()
{
while (thrMessaging.IsAlive)
DoTasks();
}
private void DoTasks()
{
if (Connected)
{
var a = ReadMessages();
SendMessages();
}
}
private /*void*/async Task ReadMessages()
{
byte[] data = new byte[4096];
//Int32 bytesRead = 0;
//Task<int> bytesReadTask;
String responseData = String.Empty;
Receiver = tcp.GetStream();
try
{
//bytesReadTask = Receiver.ReadAsync(data, 0, data.Length);
//responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytesReadTask.Result);
var response = await Receiver.ReadAsync(data, 0, data.Length);
MessageBox.Show("Server response was " + response);
Thread.Sleep(1000);
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
private void SendMessages()
{
try
{
string strSendData = "Hello from GUI";
Byte[] data = System.Text.Encoding.ASCII.GetBytes(strSendData);
Sender = tcp.GetStream();
Sender.Write(data, 0, data.Length);
Sender.Flush();
Thread.Sleep(1000);
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
}
}
you should change
var response = await Receiver.ReadAsync(data, 0, data.Length);
MessageBox.Show("Server response was " + response);
to
var response = await Receiver.ReadAsync(data, 0, data.Length);
string result = System.Text.Encoding.Default.GetString(data);
MessageBox.Show("Server response was " + result);
if you´re still having problems..my server Code:
public class tcpServer
{
public void method()
{
TcpListener server = new TcpListener(IPAddress.Any, 9999);
server.Start();
TcpClient client = server.AcceptTcpClient();
NetworkStream ns = client.GetStream();
byte[] hello = new byte[100];
hello = Encoding.Default.GetBytes("hello world");
while (client.Connected)
{
ns.Write(hello, 0, hello.Length);
}
}
}
I'm currently developing an UWP app which should have capability to be as a TCP server (using ports) so client can connect to it via other device and send requests and server responds with data.
I followed the Socket example on :Microsoft site, and got sample code working (in which server and client are both in same app)
I changed IP addresses and ports so i could use apps on 2 different machines with direct connection, I also made separate simple client application, using sample code from Here
Now problem is as follows: UWP app can successfully communicate with its own client method provided by Microsoft's sample, but is unable to communicate with console client program I made and was running on other. UWP can indeed connect with client and also send data, but it cannot receive data, the function streamReader.ReadLineAsync(); will wait infinitely long and that's all.
How do i make UWP app get the message client is sending and what i might be doing wrong ?
public sealed partial class MainPage : Page
{
static string PORT_NO = "1300";
const string SERVER_IP = "192.168.0.10";
public MainPage()
{
this.InitializeComponent();
outputText.Text = "Helloo";
StartConnection(SERVER_IP, PORT_NO);
//StartClient();
}
public async void StartConnection(string net_aadress, string port_nr)
{
try
{
var streamSocketListener = new StreamSocketListener();
// The ConnectionReceived event is raised when connections are received.
streamSocketListener.ConnectionReceived += this.StreamSocketListener_ConnectionReceived;
// Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
await streamSocketListener.BindServiceNameAsync(port_nr);
outputText.Text = "server is listening...";
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
outputText.Text = (webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
}
}
private async void StreamSocketListener_ConnectionReceived(Windows.Networking.Sockets.StreamSocketListener sender, Windows.Networking.Sockets.StreamSocketListenerConnectionReceivedEventArgs args)
{
string request = "password";
string second;
/*
using (var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
*/
//await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.serverListBox.Items.Add(string.Format("server received the request: \"{0}\"", request)));
// Echo the request back as the response.
using (Stream outputStream = args.Socket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(request);
await streamWriter.FlushAsync();
}
}
using (var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
second = await streamReader.ReadLineAsync();
}
using (Stream outputStream = args.Socket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(second);
await streamWriter.FlushAsync();
}
}
//await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.serverListBox.Items.Add(string.Format("server sent back the response: \"{0}\"", request)));
sender.Dispose();
//await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.serverListBox.Items.Add("server closed its socket"));
}
private async void StartClient()
{
try
{
// Create the StreamSocket and establish a connection to the echo server.
using (var streamSocket = new Windows.Networking.Sockets.StreamSocket())
{
// The server hostname that we will be establishing a connection to. In this example, the server and client are in the same process.
var hostName = new Windows.Networking.HostName("localhost");
//this.clientListBox.Items.Add("client is trying to connect...");
await streamSocket.ConnectAsync(hostName, PORT_NO);
//this.clientListBox.Items.Add("client connected");
// Send a request to the echo server.
string request = "Hello, World!";
using (Stream outputStream = streamSocket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(request);
await streamWriter.FlushAsync();
}
}
//this.clientListBox.Items.Add(string.Format("client sent the request: \"{0}\"", request));
// Read data from the echo server.
string response;
using (Stream inputStream = streamSocket.InputStream.AsStreamForRead())
{
using (StreamReader streamReader = new StreamReader(inputStream))
{
response = await streamReader.ReadLineAsync();
}
}
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
{
outputText.Text = "Client got back " + response;
}
);
}
//this.clientListBox.Items.Add("client closed its socket");
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
//this.clientListBox.Items.Add(webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
}
}
}
Here is source code for Client application:
{
class Program
{
const int PORT_NUMBER = 1300;
const string SERVER_IP = "192.168.0.10";
static void Main(string[] args)
{
string textToSend = DateTime.Now.ToString();
string password = "Madis on loll";
string receiveddata;
try
{
Console.WriteLine("Client progrm started");
TcpClient client = new TcpClient(SERVER_IP, PORT_NUMBER);
NetworkStream nwStream = client.GetStream();
//System.Threading.Thread.Sleep(500);
//see, how long is packet
byte[] bytesToRead = new byte[client.ReceiveBufferSize];
int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
Console.WriteLine("Received : " + Encoding.ASCII.GetString(bytesToRead, 0, bytesRead));
byte[] password2 = ASCIIEncoding.ASCII.GetBytes(password);
Console.WriteLine("Sending : " + password);
nwStream.Write(password2, 0, password2.Length); //sending packet
byte[] receiveddata2 = new byte[client.ReceiveBufferSize];
int receiveddatalength = nwStream.Read(receiveddata2, 0, client.ReceiveBufferSize);
Console.WriteLine("Received : " + Encoding.ASCII.GetString(receiveddata2, 0, bytesRead));
}
catch (Exception ex)
{
Console.WriteLine("Connection error");
}
}
}
}
Found answer myself: main problem is with ReadLineAsync() in Server program: it waits and collects all the stream until it gets end of line character. In this case end of line was never sent and therefore server kept waiting infinitely.
Simplest fix was on Client side by simply adding end of line at the end of packet, like this:
before:
byte[] password2 = ASCIIEncoding.ASCII.GetBytes(password);
Console.WriteLine("Sending : " + password);
nwStream.Write(password2, 0, password2.Length); //sending packet
after:
byte[] newLine = Encoding.ASCII.GetBytes(Environment.NewLine);
byte[] password2 = ASCIIEncoding.ASCII.GetBytes(password);
Console.WriteLine("Sending : " + password);
nwStream.Write(password2, 0, password2.Length); //sending packet
nwStream.Write(newLine,0,newLine.Length);
Also 1 thing worth mentioning: current StreamSocketListener_ConnectionReceived is able to send only once, then it sets outputStream.CanWrite to false.
This can be solved by removing using() from writing and reading functions, like this:
before:
PS! Manually flushing is also replaced with autoflush.
using (Stream outputStream = args.Socket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(request);
await streamWriter.FlushAsync();
}
}
after:
Stream outputStream = args.Socket.OutputStream.AsStreamForWrite();
var streamWriter = new StreamWriter(outputStream);
streamWriter.AutoFlush = true;
await streamWriter.WriteLineAsync(request);
Hope it helps someone someday.
I'm wanting to run a little socket server in C# to be accessed by a browser. I have a socket listener up and running on a specific port, and am trying to access it via the browser:
class WebSocketServer
{
private static string output = string.Empty;
public void CreateListener()
{
TcpListener tcpListener = null;
var ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
try
{
tcpListener = new TcpListener(ipAddress, 1313);
tcpListener.Start();
output = "Waiting for a connection";
}
catch (Exception e)
{
throw;
}
var socketHelper = new SocketHelper();
while (true)
{
Thread.Sleep(10);
TcpClient tcpClient = tcpListener.AcceptTcpClient();
byte[] bytes = new byte[256];
var stream = tcpClient.GetStream();
stream.Read(bytes, 0, bytes.Length);
socketHelper.ProcessMessage(tcpClient, stream, bytes);
}
}
}
class SocketHelper
{
private static int counter = 0;
public void ProcessMessage(TcpClient tcpClient, NetworkStream stream, byte[] bytesReceived)
{
// handle message received and send response to client
var msg = Encoding.ASCII.GetString(bytesReceived);
string response = string.Empty;
if (msg.Substring(0, 10) == "GET / HTTP")
{
response = "";// html page
}
byte[] bytesToSend = Encoding.ASCII.GetBytes(response);
stream.Write(bytesToSend, 0, bytesToSend.Length);
}
The browser appears to connect to it, but it never seems to display the html - what's wrong? I'm eventually wanting to be able to serve up JSON data via a REST interface. In addition, is there a much easier solution to (I assume) this common problem.
I wrote a simple client-server app in c#. Everything works good, but the server only accepts first call of the client and no more. I tried to put the receive method in the loop too (as acceptTcpSocket method), but it's still the same.
Simplified server code:
public class XMLServer
{
public void start()
{
server = new TcpListener(_serverIP, _serverPort);
try
{
server.Start();
}
catch (SocketException socketError)
{
Console.WriteLine(socketError.Message);
}
}
public void listen()
{
try
{
client = server.AcceptTcpClient();
while (true)
{
receiveFromClient();
}
}
catch (SocketException error)
{
Console.WriteLine(error.Message);
}
}
public void receiveFromClient()
{
byte[] bytes = new byte[client.ReceiveBufferSize];
byte[] send;
int readed;
stream = client.GetStream();
readed = stream.Read(bytes, 0, client.ReceiveBufferSize);
if (readed > 0)
{
string[] request = Encoding.UTF8.GetString(bytes).Split(':');
Console.WriteLine(request[0]);
switch (request[0])
{
case "getFileList":
send = encode(XMLFile.getFileList());
if (stream.CanWrite)
{
stream.Write(send, 0, send.Length);
}
break;
case "getFile":
send = encode(XMLFile.getFile(request[1]));
if (stream.CanWrite)
{
stream.Write(send, 0, send.Length);
stream.Flush();
}
break;
}
}
}
}
Using server code:
XMLServer server = new XMLServer("10.0.0.5", "7777");
server.start();
while (true)
{
server.listen();
}
Client code:
public partial class Client : Form
{
private TcpClient client;
private NetworkStream stream;
public Client(TcpClient parentClient)
{
InitializeComponent();
client = parentClient;
getFileList();
}
private void getFileList()
{
byte[] fileList = Encoding.UTF8.GetBytes("getFileList:null");
byte[] fileListResponse;
string[] files;
int Y = 30;
stream = client.GetStream();
stream.Write(fileList, 0, fileList.Length);
fileListResponse = new byte[client.ReceiveBufferSize];
stream.Read(fileListResponse, 0, client.ReceiveBufferSize);
files = Encoding.UTF8.GetString(fileListResponse).Split(';');
foreach (string file in files)
{
RadioButton radioButton = new RadioButton();
radioButton.Text = file;
radioButton.Location = new Point(10, Y);
groupBoxFiles.Controls.Add(radioButton);
Y += 30;
}
}
private void buttonOpenFile_Click(object sender, EventArgs e)
{
String fileName = groupBoxFiles.Controls.OfType<RadioButton>().FirstOrDefault(x => x.Checked).Text;
byte[] getFile = Encoding.UTF8.GetBytes("getFile:" + fileName);
byte[] getFileResponse;
string fileContent;
stream = client.GetStream();
stream.Write(getFile, 0, getFile.Length);
getFileResponse = new byte[client.ReceiveBufferSize];
stream.Read(getFileResponse, 0, client.ReceiveBufferSize);
fileContent = Encoding.UTF8.GetString(getFileResponse);
textBoxEditor.Enabled = true;
textBoxEditor.Text = fileContent;
}
}
First I call XMLFile.getFileList and Iít works good. Then I want to call XMLFile.getFile, after that the app stops.
What is wrong?
It is hard to tell the single root cause, but at least there is a conceptual problem: the result of the Stream.Read Method call, the actual number of bytes read, is ignored.
The send/receive functions of socket does not guarantee that all the data you provided will be sent/received at one call. The functions return actual number of sent/received bytes.
-- TCP/IP client-server application: exchange with string messages, Sergey Brunov.
Also, please note:
You must close the NetworkStream when you are through sending and receiving data. Closing TcpClient does not release the NetworkStream.
-- TcpClient.GetStream Method, MSDN.