scottoffen / grapevine-legacy

Embedding A REST Server In Your Application Should Be Simple
http://scottoffen.github.io/grapevine-legacy/
Apache License 2.0
209 stars 51 forks source link

Some clients can't connect as 'Route Not Found' due to PatternParser class behavior. #216

Closed longlongago-k closed 3 years ago

longlongago-k commented 3 years ago

I'm trying to connect to Grapevine server from two type clients. 1) curl in linux is successfully connect. 2) Network utility in the certain embedded device.

The client (2) can't connect due to URL filter in Grapevine RoutingTable. As far as I investigated HttpRequest for two clients, I found some differences:

Request URL: http://192.168.14.3:8080/the_endpoint


1) Request {Grapevine.Interfaces.Server.HttpRequest} of client 'curl'

Could you please consider to user can change this adding '^' behavior?

scottoffen commented 3 years ago

@longlongago-k I'm more curious about why the HttpListener implementation on the embedded device puts the protocol, host and port in the RawUrl property. I don't have an embedded system to test this on. Is there an emulator you are aware of that I could use to do some testing with?

scottoffen commented 3 years ago

The documentation for the RawUrl property of the HttpListenerRequest class states:

Gets the URL information (without the host and port) requested by the client.

The difference between the PathInfo property and the RawUrl property is that the latter includes the query string while the former does not. To calculate the PathInfo property, the RawUrl is split by the ? character and the first string in the returned array is assigned to the PathInfo.

Ergo, the problem you are experiencing has something to do with the fact that the RawUrl includes the host and port, which it should not. If you can provide me with a way to duplicate this issue on my end, I'd be happy to take another look, but until such time I am closing this issue.

longlongago-k commented 3 years ago

@scottoffen Hi, I investigated this issue internally. My device expected server as role of both REST HTTP and Proxy server . So that reason, request line included host address like http://192.168.14.3:8080/the_endpoint. As the result, this issue was a wrong usage of Grapevine. I'm sorry for taking your times.

Just for the information, I resolved this issue by following ways: 1) Modified Grapevine in my branch to support proxy style request. 2) Added reverse proxy server between device and Grapevine REST server.

scottoffen commented 3 years ago

Modified Grapevine in my branch to support proxy style request.

Tell me more about this....do you have code I can look at? I can think of a way to fix this, but it's hard to know for sure if I can't test it.

scottoffen commented 3 years ago

@longlongago-k do you have a code sample I can look at?

longlongago-k commented 3 years ago

Hi, According to RFC2616(legacy) or 7235, It sais "When making a request to a proxy, other than a CONNECT or server-wide OPTIONS request (as detailed below), a client MUST send the target URI in absolute-form as the request-target."

a) Normal client requests: GET /the_endpoint HTTP/1.1 b) Clients with proxy requests: (this issue) GET http://192.168.10.2:8080/the_endpoint HTTP/1.1

My device requests b) style (absolute-form). For example, I used following PathInfo

[RestRoute(HttpMethod = HttpMethod.GET, PathInfo = "/the_endpoint/*.*")]
public IHttpContext Exec(IHttpContext context){
}

But, "/the_endpoint/." was converted to "^/the_endpoint/." in Grapevie Routing table. So, this regex pattern rejects proxy style request "GET http://192.168.10.2:8080/the_endpoint HTTP/1.1" In this case, Grapevine outputs: "Route Not Found For GET http://192.168.10.2:8080/the_endpoint" To resolve instantly, I modified following code;

[PatternParser.cs]
//old
//var pattern = new StringBuilder("^");
//modified
var pattern = new StringBuilder(""); // delete ^ char

@scottoffen You can simply test by following sample code.

async void Main()
{
  //Prepare:
  //  add route with your Grapevine server as [RestRoute(HttpMethod = HttpMethod.GET, PathInfo = "/the_endpoint/*.*")]
  var url = @"http://192.168.10.2:8080/the_endpoint";//Set your IP address. do not use localhost string, because Windows ignore proxy
  WebRequestHandler handler = new WebRequestHandler();
  //Add proxy
  handler.Proxy = new WebProxy("http://192.168.10.2:8080"); // do not use localhost, because Windows ignore proxy
  using (HttpClient client = new HttpClient(handler))
  {
    HttpResponseMessage response = client.GetAsync(url).Result;
    var str = await response.Content.ReadAsStringAsync();
    Console.WriteLine(str);
  }
}

This usage is very special case, and generally this case should be used with another (Reverse) proxy.

scottoffen commented 3 years ago

Excellent, thank you. I'll take a look at this.

Edit: Using .NET 5.0 and the HttpRequestHandler does not duplicate this issue. Downloading the .NET Framework 4.8 SDK now so I can test it with the WebRequestHandler.

scottoffen commented 3 years ago

I found the issue. It was already (unintentionally) fixed in Grapevine 5. I'll port that quick fix over to Grapevine 4 and publish a new version. (Because I don't use RawUrl in GV5)

scottoffen commented 3 years ago

The fix for this is in release 4.2.2, and has been uploaded to Nuget.