Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #60471: Solution for "Invalid request (Unexpected EOF)" message in built-in web server #2442

Closed
wants to merge 1 commit into from

Conversation

SammyK
Copy link
Contributor

@SammyK SammyK commented Mar 29, 2017

Possible solution to bug #60471.

The built-in web server generates a lot of confusing Invalid request (Unexpected EOF) error messages when using a browser that supports preconnect like Firefox or Chrome.

screen shot 2017-03-29 at 9 43 46 am

In addition, Chrome uses a predictive TCP preconnect technique to speed up the request time. This means that for nearly every connection to a web server, Chrome will open more than one TCP connection as you can see here.

screen shot 2017-03-29 at 9 00 25 am

In this case, the first connection (50685) was used for the actual request while the other one (50686) waits for a request for some other resource (like a favicon or whatever). You can see it hanging open when looking at the TCP connections with nettop (on Mac).

screen shot 2017-03-29 at 9 00 15 am

After a timeout (usually about 10 seconds) the Invalid request (Unexpected EOF) message is output from the PHP CLI web server. This error message is confusing to developers everywhere. So developers think they did something wrong and spend a lot of time looking for answers only to be greeted with lots of misinformation like "just add a favicon".

Since preconnecting is starting to become a common practice with modern web browsers, this PR offers a possible solution by changing the behavior of the built-in web server in the following ways:

  1. Display the useful debug messages about opening and closing the TCP connections by checking PHP_DEBUG instead of DEBUG so we see them when using the --enable-debug flag. (I don't know where DEBUG came from).
  2. Change the unhelpful Invalid request (Unexpected EOF) message to Closed without sending a request; it was probably just a preconnect so that a quick Google search for "preconnect" will explain all the things.
  3. Move the new message to display only at the debug level since it's an expected behavior from modern browsers. This will prevent any future confusion since it's not an error.

The new output for closed TCP preconnections in debug mode would look like this.

screen shot 2017-03-29 at 9 00 52 am

Phew. I hope this clarifies things for everyone who has been pulling their hair out with this! :)

@sgolemon
Copy link
Contributor

sgolemon commented Apr 4, 2017

Repeating what I just told you in person. :D

This PR incorrectly assumes that a request will always come in as a single packet. However, if a client connects to the webserver, sends a packet, THEN disconnects (see repro below) then the same recv==0 state will be triggered and mistaken for an ababdoned optimistic connection.

Try taking a look at client->parser.state to verify that it's in s_start_req state. If that is true AND recv == 0, then we can safely call it an abandoned optimistic connection. Otherwise we still want to raise a warning that a partial connection was abandoned.

$sock = fsockopen('127.0.0.1', $whateverport);
fwrite($sock, "HEAD / HTTP/1.0\r\n");
fclose($sock);

* Correctly identify unused speculative preconnections from browsers
  like Chrome and Firefox
* Add a new message to the debug level that is emitted when a TCP
  connection is closed without sending any request (a preconnection)
* Fix an issue where the existing debug messages were not being
  displayed even when debug mode was enabled
@SammyK SammyK force-pushed the fix-web-server-debug-messages branch from 4c7f7c1 to 6b74984 Compare April 5, 2017 14:15
@SammyK
Copy link
Contributor Author

SammyK commented Apr 5, 2017

Done & done! :)

The web server now correctly identifies the difference between an actual unexpected EOF vs a preconnection and shows the correct message. I didn't see any infrastructure to add automated tests to the web server, but here's the code I used to test it:

<?php

const HOSTNAME = 'localhost';
const PORT = '8888';
const BASE_URL = 'http://'.HOSTNAME.':'.PORT;

echo "PHP's built-in webserver test...\n";

# Normal request
file_get_contents(BASE_URL.'/phpinfo.php');

# Partial HTTP request
$sock = fsockopen(HOSTNAME, PORT);
fwrite($sock, "HEAD / HTTP/1.0\r\n");
fclose($sock);

# Non-HTTP request
$sock = fsockopen(HOSTNAME, PORT);
fwrite($sock, "POOP!");
fclose($sock);

# Mimmik a preconnect
$sock = fsockopen(HOSTNAME, PORT);
fclose($sock);

I received the following results as expected:

screen shot 2017-04-05 at 9 05 13 am

@php-pulls
Copy link

Comment on behalf of pollita at php.net:

Pushed as fd0e71d

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants