1

tldr;

  1. very minimal stream socket server in PHP
  2. acts strange since sometimes it successfully serves HTTP request and sometimes fails within the very same process
  3. acts strange across different browsers - almost every time fails in Chrome and never in IE11

code:

$server = stream_socket_server("tcp://0.0.0.0:4444", $errno, $errorMessage);

if ($server === false) 
    throw new UnexpectedValueException("Could not bind to socket: $errorMessage");

$e = "\r\n";
$headers = array(
    "HTTP/1.1 200 OK",
    "Date: " . date('D') . ', ' . date('m') . ' '  . date('M') . ' ' . date('Y') . ' ' . date('H:i:s') . ' GMT' ,
    'Server: MySpeedy',
    'Connection: close',
    'Content-Type: text/plain',
    'Content-Length: 2'
);

$headers = implode($e, $headers) . $e .  $e .'ok';

for (;;) 
{
    $client = stream_socket_accept($server);

    if ($client) 
    {
        echo 'Connection accepted from '.stream_socket_get_name($client, false) . $e;

        fwrite($client, $headers);
        fclose($client);
    }
}

gives me this http response (telnet results):

HTTP/1.1 200 OK
Date: Fri, 11 Nov 2015 20:09:02 GMT
Server: MySpeedy
Connection: close
Content-Type: text/plain
Content-Length: 2

ok

And that leads me to these results:

  • ERR_CONNECTION_RESET in Chrome, almost every time (maybe 1 in 20-30 requests get expected response)
  • The connection was reset in Firefox, approximately 1 in 2-3 requests
  • Correct, expected response in Internet Explorer 11 every time (yay, IE is the best in something).

What am I doing wrong? Is it up to http headers (I couldn't say if I've formatted them incorrectly) or socket loop or..?

Miloš Đakonović
  • 3,751
  • 5
  • 35
  • 55

1 Answers1

2

You don't read the HTTP request from the client but instead simply send your response and close the connection. But closing the socket while there are still data to read will cause a connection reset send back to the client and that's what you will see in Chrome with ERR_CONNECTION_RESET. Other browsers might behave differently and it is also a timing issue if the browser can display the response before handling the reset.

To fix it first read the full request from the client before you close the socket.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • Thanks for answer. How do I do that? – Miloš Đakonović Nov 13 '15 at 21:18
  • @Miloshio: The HTTP request header ends with \r\n\r\n. If this is a GET then you are done after reading the header. For more details see the [specification of HTTP](https://www.ietf.org/rfc/rfc2616.txt) or existing code of [web servers written in PHP](https://www.google.com/search?q=http+server+php&oq=http+server+php) – Steffen Ullrich Nov 13 '15 at 21:36
  • Update for 2022: the `GET` request actually _can_ contain a body now. It's not common to do so; the behaviour of the webserver is not defined by the protocol; but it's also not _illegal_ to do so, and I've seen systems that do it. From a browser, no less. (Whether or not they _should_ do it is a different question) – Vilx- Sep 09 '22 at 15:00
  • 1
    @Vilx- correct, the specification was clarified to allow a body basically with each kind of HTTP request, including GET or HEAD. But [it also states](https://www.rfc-editor.org/rfc/rfc7231#section-4.3.1): *"A payload within a GET request message has __no defined semantics__; sending a payload body on a GET request __might cause some existing implementations to reject the request.__"*. In other words: it makes no sense and might cause problem, but if you still insist lets do it. – Steffen Ullrich Sep 09 '22 at 15:18