Closed SaricVr closed 4 years ago
Hello @SaricVr, thanks for using EmbedIO!
As far as I know, there is no specific restriction on the use of relative URLs for redirection in HTTPS.
Let's rule out some possible external causes first:
As for the chance of this being caused by a bug in EmbedIO, the only possible malfunction I can think of at the moment would be EmbedIO closing the connection without properly announcing so to the client via a Connection: close
header. I can't figure out why it would be happening, much less why it would depend on relative vs. absolute URL in a response's Location
header, but you may want to give it a shot by changing your Page
method as follows:
[Route(HttpVerbs.Get, "/")]
public void Page()
{
HttpContext.Redirect("/page2");
HttpContext.Response.KeepAlive = false;
}
This will force EmbedIO to close the connection and send a Connection: close
header.
If this makes relative redirection work, then we'll have to look closely at how EmbedIO manages secure connections.
Hi @rdeago, thank you for your advice.
I tried another browser and the problem remains. I also disabled the antivirus and nothing changed. Also setting KeepAlive = false
doesn't change anything.
Maybe the issue is related to the certificate I'm using? I created it with the powershell command New-SelfSignedCertificate -certstorelocation cert:\currentuser\my -friendlyname "Test certificate" -DnsName "localhost"
It would be useful if someone could try to reproduce the issue, so to exclude it's a problem related to my machine (I also tried to connect to the web server on my machine from my phone (LAN) and the error persists).
Thanks
I'll try to reproduce the issue. Can you post your server initialization code, so I can be sure to use your same options?
Here is the code:
public static void Start()
{
var urls = new string[] {"https://localhost:9696"};
using (var server = CreateWebServer(urls))
server.RunAsync();
}
private static WebServer CreateWebServer(string[] urls)
{
var cert = new X509Certificate2("cert.pfx", "12345678", X509KeyStorageFlags.DefaultKeySet);
var server = new WebServer(o => o
.WithUrlPrefixes(urls)
.WithMode(HttpListenerMode.EmbedIO).WithCertificate(cert))
.WithLocalSessionManager().WithWebApi("/", m => m
.WithController<WebController>());
return server;
}
As I said, the certificate has been created with the powershell command reported in my previous post, manually exported from the Windows certificate store as a .pfx file.
Thank you
Hello, a small update on the problem... this is the log I observe when requesting "page1:
[hXVXgRXzZk60UZhWBueAzw] GET /page1: "302 Found" sent in 39ms (0 bytes)
Eccezione generata: 'System.IO.IOException' in System.dll
Eccezione generata: 'System.IO.IOException' in System.dll
Eccezione generata: 'System.IO.IOException' in System.dll
Eccezione generata: 'System.IO.IOException' in EmbedIO.dll
Eccezione generata: 'System.IO.IOException' in System.dll
Eccezione generata: 'System.IO.IOException' in System.dll
Eccezione generata: 'System.IO.IOException' in System.dll
Eccezione generata: 'System.IO.IOException' in EmbedIO.dll
Eccezione generata: 'System.IO.IOException' in System.dll
Eccezione generata: 'System.IO.IOException' in System.dll
Eccezione generata: 'System.IO.IOException' in System.dll
Eccezione generata: 'System.IO.IOException' in EmbedIO.dll
Hope this helps a bit.
P.S. "Eccezione generata" is just "Exception thrown".
Hello @SaricVr, sorry for not coming back to you in the meantime. I've been busy with work. Good to see you've been further investigating the issue.
(Also, thanks for the translation on behalf of the rest of the team. As for me, I'm Italian too. 😁)
Back to the issue. We see from the log that a 302 response is actually generated and sent to the client (the first log entry in your comment was generated after flushing the response stream.)
Then all hell breaks loose. 😱
To me, those "Exception thrown" messages look more like they come straight from .NET Framework (or Core) rather than being part of the log. You can verify this by logging to a file:
using Swan.Logging;
// ...
Logger.RegisterLogger(new FileLogger("PATH_TO_LOG_FILE"));
// ... then initialize and run your server as usual.
If those messages are not written to your log file, it obviously means that they were written directly to the console - not by EmbedIO, of course, but rather from the .NET runtime. This distinction is important to start understanding where the exceptions come from.
Another possible test is requesting "/" from a non-browser client, such as curl
, that does not follow redirections. This way you can see whether the exceptions are thrown while closing the first response or accepting the second request.
If you use Linux, you either have curl
already installed on your machine, or available through your distro's package manager (e.g. sudo apt-get install curl
on Debian and derivatives.) For Windows, you can download it from here (no installation required, just extract the contents of the bin
folder from the ZIP somewhere handy.)
What do you see in the log file when requesting with curl
?
curl https://127.0.0.1/
Finally, you can re-enable write exceptions in the HTTP listener:
Logger.RegisterLogger(new FileLogger("PATH_TO_LOG_FILE"));
using (var server = CreateWebServer())
{
server.Listener.IgnoreWriteExceptions = false;
server.RunAsync();
}
The differences between logs with IgnoreWriteExceptions
set to false
and set to true
could be revealing.
(The default value is true
for HttpListenerMode.EmbedIO
, false
for HttpListenerMode.Microsoft
- oops, my fault!)
Hi @rdeago, thanks for the help.
So, I did all the tests you suggested and:
HTTP/1.0 302 Found
Expires: Sat, 26 Jul 1997 05:00:00 GMT
Last-Modified: Thu, 05 Mar 2020 09:39:46 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: http://localhost:9696/page2
Content-Type: application/octet-stream; charset=utf-8
Server: EmbedIO/3.4.2
Date: Thu, 05 Mar 2020 09:39:46 GMT
Content-Length: 0
Connection: close
curl: (56) Recv failure: Connection was reset
So it seems to me that the redirect is followed, but the connection is closed (probably due to internal exception of EmbedIO).
Just to help me with the debugging... How can I make EmbedIO throw the exception so I can handle it in the code instead of logging it only? I really can't figure it out from the code or documentation.
Thanks again
Unfortunately, there's currently no official way to debug EmbedIO.
Try the following:
clone the https://github.com/unosquare/embedio.git
repository in a folder of your choice;
check out the v3.X
branch;
build EmbedIO.sln
in Debug mode;
remove the package reference to EmbedIO in your project and replace it with a library reference to the EmbedIO.dll
you just built;
proceed to debug your project. You should be able to get meaningful exception stack traces and even step through EmbedIO code.
Version 4 will have SourceLink and symbol packages, I'm currently experimenting with them.
Here I am again. I found the root cause of the problem. So, first of all, being able to debug the code... actually helps you debugging the code!
Essentially, in file HttpListenerRequest.cs
, line 279-280 (I guess there's a way to create a link to that line on github but I don't know how) I found this (I'm referring to the 3.X branch):
// var baseUri = $"{(IsSecureConnection ? "https" : "http")}://{host}:{LocalEndPoint.Port}";
var baseUri = $"http://{host}:{LocalEndPoint.Port}";
As you can see, the baseUri is fixed to "http". The funny thing is that the correct line (the previous one) is commented out :) By keeping only line 279, redirects work also with "https".
Also, in the output of curl in my previous post you could notice Location: http://localhost:9696/page2
which I didn't notice at first.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
I also see a similar issue, maybe worth adding an overload to HttpContext.Redirect to be able to do relative redirection.
A work around for now could be:
public static Task RelativeRedirect(IHttpContext ctx, string url){
ctx.Response.StatusCode = 303;
// Relative redirection if url is relative
ctx.Response.Headers.Add(HttpResponseHeader.Location, url);
ctx.SetHandled();
return Task.CompletedTask;
}
Hello,
I'm facing a strange behavior with a simple page redirection inside a controller. This is my code:
Now, the following redirection from "/" to "/page2" works with HTTP, but fails with HTTPS. If instead i change the redirect to a fully qualified url such as
HttpContext.Redirect("https://localhost:9696/page2")
the code works also with HTTPS. I'm using a self-signed certificate for testing purposes and accessing the page with the Google Chrome (the specific error isERR_CONNECTION_RESET
.Now, I may be lacking a fundamental concept behind HTTPS that I'm not aware of (although I did research the issue), but if you could clarify the situation to me it would be great.
Thank you