dotnet convert controller route api to minimal web api - c#

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?

Related

Unable to read data from tcp server

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);
}
}
}

The way of building a simple http proxy

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(); }
}

Second call to HttpClient's PostAsJsonAsync stucks

I'm trying to post some data to a web service and receive response with HttpClient in a console application. If I try to reuse a HttpClient instance, the first call to PostAsJsonAsync works as expected, but the second just waits forever. If i create a new HttpClient for every call then everything is OK.
public class DirectHandler
{
HttpClient httpClient;
public string SendToPlayer(object message)
{
if (httpClient == null)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("..url...");
}
HttpResponseMessage response = httpClient.PostAsJsonAsync("", message).Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsByteArrayAsync().Result;
return System.Text.Encoding.Default.GetString(result);
}
else
{
throw new HttpRequestException("Error code " + response.StatusCode + ", reason: " + response.ReasonPhrase);
}
}
}
The code using this class:
DirectHandler dh = new DirectHandler();
var resp = dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);
Thread.Sleep(500);
resp = dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);
The "server" is a HttpListener:
public class HTTPServer
{
void StartListener()
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("my prefix");
listener.Start();
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}
private void OnRequestReceive(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerResponse response = context.Response;
HttpListenerRequest request = context.Request;
using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
Console.WriteLine(reader.ReadToEnd());
}
string responseString = "{'a': \"b\"}";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}
}
How can i reuse HttpClient?
Update:
I changed .Result to async/await, now the SendToPlayer method looks like this:
public async Task<string> SendToPlayer(object message)
{
if (httpClient == null)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("..url...");
}
HttpResponseMessage response = await httpClient.PostAsJsonAsync("", message).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsByteArrayAsync();
return System.Text.Encoding.Default.GetString(result);
}
else
{
throw new HttpRequestException("Error code " + response.StatusCode + ", reason: " + response.ReasonPhrase);
}
}
It still waits forever in PostAsJsonAsync when called more than once.
Update2:
The test code, hangs on the second SendToPlayer line:
public static void Main(string[] args)
{
Task.Run(async () =>
{
DirectHandler dh = new DirectHandler();
var resp = await dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + str);
Thread.Sleep(500);
resp = await dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + str);
}).Wait();
}
I can't help but notice that the code you provided doesn't even compile. Like where does myObj in your main method come from? And how do you even start the http listener since the method StartListener is declared private?
These kind of issues make me suspect that the code you've posted is not the actual code that has the described problem.
Also, what do you put into myObj?
I copied and adjusted your code to make it work on my machine. Please take a look to see if it runs on your machine as well in a new console app (you'll need administrator permissions to run it)
public static void Main(string[] args)
{
var s = new HTTPServer();
s.StartListener();
Task.Run(async () =>
{
var myObj = 8;
DirectHandler dh = new DirectHandler();
var resp = await dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);
Thread.Sleep(500);
resp = await dh.SendToPlayer(myObj);
Console.WriteLine("Received: " + resp);
}).Wait();
}
public class HTTPServer
{
public void StartListener()
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:8080/");
listener.Start();
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}
private void OnRequestReceive(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerResponse response = context.Response;
HttpListenerRequest request = context.Request;
using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
Console.WriteLine(reader.ReadToEnd());
}
string responseString = "{'a': \"b\"}";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
listener.BeginGetContext(new AsyncCallback(OnRequestReceive), listener);
}
}
public class DirectHandler
{
HttpClient httpClient;
public async Task<string> SendToPlayer(object message)
{
if (httpClient == null)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:8080");
}
HttpResponseMessage response = await httpClient.PostAsJsonAsync("", message).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsByteArrayAsync();
return System.Text.Encoding.Default.GetString(result);
}
else
{
throw new HttpRequestException("Error code " + response.StatusCode + ", reason: " + response.ReasonPhrase);
}
}
}
this outputs
8
Received: {'a': "b"}
8
Received: {'a': "b"}
Now, you might have simplified your code posted here. If there is any code you've not posted here it will be hard to solve.
Is your HttpListener running in a separate (console) app? Maybe the server process is terminated after the first call, due to an error for example.
If the above code works in a new console app try to find the differences with your code.
As I see it, there are two issues:
1) your server needs to wait around to process more than only one event, and
2) your client and server code should reside in separate execution threads.
Based on the way you've written it, the server will shut down shortly after starting up, which is why maybe the first request works but the second one hangs.
I've created an example:
https://github.com/paulsbruce/StackOverflowExamples/tree/master/SOF40934218
You have to kick the server project off first, which waits for incoming requests (StartListening doesn't hold the server thread open, so there is a while(IsRunning) { Thread.Sleep(100); } loop as an example.
https://github.com/paulsbruce/StackOverflowExamples/blob/master/SOF40934218/SOF40934218_Server/Server.cs
Then kick off the client as a separate process and watch it's console output. It does in fact get a second response (why there is a 5 second delay to allow you to see this).
https://github.com/paulsbruce/StackOverflowExamples/blob/master/SOF40934218/SOF40934218_Client/Client.cs
Hope these two suggestions help!

How can I use HttpListener when the url doesn't have ending by "/"?

I want to get listen to a url for getting some information. so my code is this:
public static void SimpleListenerExample(string[] prefixes)
{
HttpListener listener = new HttpListener();
// Add the prefixes.
foreach (string s in prefixes)
{
listener.Prefixes.Add(s);
}
listener.Start();
//Console.WriteLine("Listening...");
// Note: The GetContext method blocks while waiting for a request.
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
listener.Stop();
}
private void button2_Click(object sender, EventArgs e)
{
string[] test = { "http://xxx.xxxx.xxx.xxx:8086/sms.html" };
SimpleListenerExample(test);
}
The URL that I want to add as a prefix, has not ending by "/". and if I add it at the end of url, its not valid and doesn't work.
so how can I listen to a url that it doesn't have ending by "/" ??
The url you supply is a prefix, so a handler for prefix http://xxx:8086 will match any requests that begin with that string, including http://xxx:8086/sms.html. This is the reason the prefixes have to end with "/", and it's described here: https://msdn.microsoft.com/en-us/library/system.net.httplistener(v=vs.110).aspx
If you want your listener to only listen to specific paths, you have to write some logic, and inspect what the URL in the request. To do that per request, you have to provide a callback to the httplistener, and inspect the request uri.
Here's a small program that returns 200 OK for http://localhost:8086/nisse.html and http://localhost:8086/kalle.html, but 404 for all other urls with the prefix
http://localhost:8086/.
class Program
{
private static HttpListener listener;
static string[] uris =
{
"http://localhost:8086/nisse.html",
"http://localhost:8086/kalle.html",
};
private static void ListenerCallback(IAsyncResult result)
{
var context = listener.EndGetContext(result);
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
//Maybe not use exact string matching here
if (uris.Contains(request.Url.ToString()))
{
context.Response.StatusCode = 200;
context.Response.StatusDescription = "OK";
string responseString = "<HTML><BODY> YOU ASKED FOR:" + request.Url + "</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();
context.Response.Close();
}
else
{
context.Response.StatusCode = 404;
context.Response.StatusDescription = "NOT FOUND";
context.Response.Close();
}
}
private static void Main()
{
listener = new HttpListener();
//Add the distinct prefixes, you may want ot parse this in a more elegant way
foreach (string s in uris.Select(u=>u.Substring(0,u.LastIndexOf("/")+1)).Distinct())
{
listener.Prefixes.Add(s);
}
listener.Start();
while (true)
{
var result = listener.BeginGetContext(ListenerCallback, listener);
result.AsyncWaitHandle.WaitOne();
}
}
}

Sending the server time as a response to HTTP GET via c# service

Alright, I think I have it working 100% now!
Here's the code, any critique is welcome, this was my first attempt at c#, coming from a mostly JS background.
Ended up using thread.abort, not sure if that is the best way to end this. I put in a _shouldStop bool as well.
public partial class TimeReporterService : ServiceBase
{
private Thread worker = null;
private bool _shouldStop = false;
public TimeReporterService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
_shouldStop = false;
worker = new Thread(SimpleListenerExample);
worker.Name = "Time Reporter";
worker.IsBackground = false;
worker.Start();
}
protected override void OnStop()
{
_shouldStop = true;
worker.Abort();
}
void SimpleListenerExample()
{
string[] prefixes = new[] { "http://*:12227/" };
// URI prefixes are required,
// for example "http://contoso.com:8080/index/".
if (prefixes == null || prefixes.Length == 0)
throw new ArgumentException("prefixes");
// Create a listener.
HttpListener listener = new HttpListener();
// Add the prefixes.
foreach (string s in prefixes)
{
listener.Prefixes.Add(s);
}
listener.Start();
while (!_shouldStop)
{
// Note: The GetContext method blocks while waiting for a request.
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
string responseString = "{\"systemtime\":\"" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\"}";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
}
listener.Stop();
}
}
Something you could use in the HTTPListener request method.
public void HttpListenerCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerResponse Response = context.Response;
String dateAsString = DateTime.Now.ToString(#"MM\/dd\/yyyy h\:mm tt");
byte[] bOutput = System.Text.Encoding.UTF8.GetBytes(dateAsString);
Response.ContentType = "text/plain";
Response.ContentLength64 = bOutput.Length;
Stream OutputStream = Response.OutputStream;
OutputStream.Write(bOutput, 0, bOutput.Length);
OutputStream.Close();
}
For this you should use HTTPListener in asynchronous (non-blocking) mode.
Ex:
public void NonblockingListener()
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:8081/");
listener.Start();
IAsyncResult result = listener.BeginGetContext(
new AsyncCallback(HttpListenerCallback), listener);
Console.WriteLine("Waiting for request to be processed asyncronously.");
result.AsyncWaitHandle.WaitOne(); //just needed to don't close this thread, you can do other work or run in a loop
Console.WriteLine("Request processed asyncronously.");
listener.Close();
}
More info: http://msdn.microsoft.com/en-us//library/system.net.httplistener.aspx

Categories

Resources