Reputation: 1146
I use the code below to get an oAuth code using a browser login, it works just fine ie. auth code is returned BUT there is some timing issue between responseOutput.WriteAsync() and http.stop().
When in debug (breakpoint on **** below), then the response is returned to the browser as expected.
If I the comment out the line http.stop() (**** below), then the response is returned to the browser as expected.
BUT If I run the code as usual then the browser shows "page cannot be found", so, it looks as though responseOutput.WriteAsync() is not actually completing (not begin 'awaited'). Do I need to do anything else to ensure that the response is completely sent before stopping the listener?
await GetAuthorizationCode();
public async Task GetAuthorizationCode() {
// Creates an Listener
string redirectUri = "http://127.0.0.1:12345";
HttpListener http = new HttpListener();
http.Prefixes.Add(redirectUri);
http.Start();
// Open auth page in browser
string authUri = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?........";
var authorizationRequest = authUri;
Process.Start(authorizationRequest);
// Wait for auth response.
HttpListenerContext context = await http.GetContextAsync();
var sCode = context.Request.QueryString.Get("code");
//Send response to the browser.
HttpListenerResponse response = context.Response;
string responseString = string.Format("<html><head></head><body>Auth OK.</body></html>");
var buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
using (Stream responseOutput = response.OutputStream)
{
await responseOutput.WriteAsync(buffer, 0, buffer.Length);
responseOutput.Close();
}
//****** this line causes problems
http.Stop();
AuthorizationCode = sCode;
}
Upvotes: 1
Views: 1306
Reputation: 530
From an old http handler I made some time ago, I have this code:
protected static void WriteString(HttpListenerResponse response, HttpContentType contentType, Encoding encoding, string content)
{
byte[] outputBuffer = encoding.GetBytes(content);
response.ContentType = contentType.Value + "; charset=" + encoding.BodyName;
response.ContentLength64 = outputBuffer.Length;
response.Close(outputBuffer, true);
}
This code have been active on a program that usually stayed days if not weeks serving few thousand requests each day and never had any kind of issues regarding memory leaks.
From docs:
You can customize the response by setting various properties, such as StatusCode, StatusDescription, and Cookies. Use the HttpListenerResponse.OutputStream property to obtain a Stream instance to which response data can be written. Finally, send the response data to the client by calling the Close method.
https://learn.microsoft.com/en-us/dotnet/api/system.net.httplistenerresponse?view=net-6.0
Oposed to my though, the HttpListener docs states to close the output stream:
https://learn.microsoft.com/en-us/dotnet/api/system.net.httplistener?view=net-6.0
Which is quite confusing:
System.IO.Stream output = response.OutputStream;
output.Write(buffer,0,buffer.Length);
// You must close the output stream.
output.Close();
listener.Stop();
I would suggest to try the follwing code:
using (Stream responseOutput = response.OutputStream)
{
await responseOutput.WriteAsync(buffer, 0, buffer.Length);
//The following two lines are possibly unnecesary in an unsing statement
//responseOutput.Flush(); //Ensure content placed on the right place
//responseOutput.Close();
}
response.Close();
http.Stop();
--- Update ---
I could run the following code under .Net6.0 without any issue and got the response content on the browser:
class Program
{
static async Task Main(string[] args)
{
await RunUntilServeOneRequest();
}
private static async Task RunUntilServeOneRequest()
{
Console.WriteLine("Starting...");
// Creates an Listener
string listenUri = "http://127.0.0.1:12346/";
HttpListener http = new HttpListener();
http.Prefixes.Add(listenUri);
http.Start();
// Wait for request.
Console.WriteLine("Awaiting request to " + listenUri);
HttpListenerContext context = await http.GetContextAsync();
//Send response to the browser.
HttpListenerResponse response = context.Response;
string responseString = string.Format($"<html><head></head><body>Hello world: {DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}</body></html>");
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
using (Stream responseOutput = response.OutputStream)
{
await responseOutput.WriteAsync(buffer, 0, buffer.Length);
responseOutput.Close();
}
response.Close();
Console.WriteLine("Request answered");
http.Stop();
}
}
Upvotes: 2
Reputation: 1146
It seems that I have to set KeepAlive prior to http.stop()
response.KeepAlive = False
Somehow even with calling response.close and/or with a 'using' block around the response it still needed this setting.
Upvotes: 3