[Solved] Subsequent Hyper requests to Nickel not working as expected


#1

So, I’m having some really extrange issues when interacting Nickel and Hyper. I have a Nickel server, using the latest Git code (need HTTPS, even though it’s currently disabled in my test environment), and the latest Hyper 0.9.10 code.

The thing is I have a method in the client, that calls two times to the nickel server, with the same information. My main script calls that method twice, so, in total, 4 requests are sent from the client to the server. The curious issue is that in the first two, I receive an Status::OK response, while in the other two I receive a Not found response. If I put more or less requests in the method, in the first call to the method all will work and in the second all will fail.

Curl is working well with the Nickel server (even though I don’t know how to perform multiple requests at once, or create the same behaviour. And the thing is that I debugged the Nickel server using a custom 404 handler, and printing the headers received in the 404 and in the correct request seems that everything is the same. In fact, the URL received in the requests that fail is a correct URL and request method.

So, I guess that something is going wrong at lower level? are requests not being sent correctly? or not received? Maybe some sort of KeepAlive thing? I really don’t have much clue on what’s going on :confused:

Thanks for the help in advance, anyway!


#2

Do you have publicly available code to reproduce the problem? I tried to make a minimal server/client, but couldn’t make the client misbehave.


#3

I made this repo with a minimal working example so that anybody can test it. It contains two folders. The server folder is a crate that can be run with cargo run, and then use the client crate (also with cargo run to check what happens.

https://github.com/Razican/nickel_test

Edit: I wiresharked the connection and I see that in the first 3 requests, the origin ports are diferent, while in the second two, it reuses the ports from the first requests. Could be related to the issue?


#4

It seems that Nickel doesn’t deal well with connection reuse. If you add Connection: close to your headers, both calls to test_method() will return OK.

In the first call to test_method(), each let mut response... initiates a new connection to the server. In the subsequent call, hyper will try to reuse one of connections (which you’ve seen in the Wireshark trace), but Nickel won’t like that and will return 404.


#5

Wow, Thanks!! I had a really hard time trying to figure this out. Should I fill a bug against Nickel?


#6

Oops… I blamed Nickel unjustly. The real problem is that the server doesn’t read the body of the request sent by the client (“grant_type=client_credentials”), so the next request on the same connection first reads the unread part and glues it to the method name, which ends up being the nonsensical grant_type=client_credentialsPOST instead of just POST. Nickel quite correctly fails to recognize the method and returns 404.

Put the following in the server’s request handler:

let mut body = vec![];
request.origin.read_to_end(&mut body).unwrap();

Then you can remove Connection: close from the headers, and both calls to test_method() will still work.


#7

Or just discard the data instead of allocating a huge Vec:

std::io::copy(&mut request.origin, &mut std::io::sink());

#8

Wow! true. Thanks for the catch, now everything is working perfectly! :smiley:


#9

404 isn’t really the correct error code though… Should be a generic 400 IMO.