I'm trying to make asynchronous HTTP POST requests from PHP server to an Nginx server. I read this very informative blog post about the issue, decided to use the technique with pfsockopen
function, and imported the code doing this written by the blog author. I include the key sections below:
private function createSocket() {
if ($this->socket_failed)
return false;
$protocol = $this->ssl() ? "ssl" : "tcp";
$host = $this->options["host"];
$port = $this->ssl() ? 443 : 80;
$timeout = $this->options["timeout"];
try {
# Open our socket to the API Server.
# Since we're try catch'ing prevent PHP logs.
$socket = @pfsockopen($protocol . "://" . $host, $port, $errno,
$errstr, $timeout);
# If we couldn't open the socket, handle the error.
if (false === $socket) {
...
return false;
}
return $socket;
} catch (Exception $e) {
...
return false;
}
}
private function makeRequest($socket, $req, $retry = true) {
$bytes_written = 0;
$bytes_total = strlen($req);
$closed = false;
# Write the request
while (!$closed && $bytes_written < $bytes_total) {
try {
# Since we're try catch'ing prevent PHP logs.
$written = @fwrite($socket, substr($req, $bytes_written));
} catch (Exception $e) {
...
$closed = true;
}
if (!isset($written) || !$written) {
$closed = true;
} else {
$bytes_written += $written;
}
}
# If the socket has been closed, attempt to retry a single time.
if ($closed) {
fclose($socket);
if ($retry) {
$socket = $this->createSocket();
if ($socket) return $this->makeRequest($socket, $req, false);
}
return false;
}
$success = true;
...
return $success;
}
It mostly works very well, but I'm observing the following problem. Usually, after about 30 seconds of inactivity, when I send a request this way, Nginx responds with a TCP RST (reset) packet, which I read indicates that the socket has been closed on the remote end. I can also see the connection statuses with netstat -na | grep 8080
and by that time, the connection PHP -> Nginx is in state CLOSE_WAIT
, while Nginx -> PHP is in FIN_WAIT2
. This guide confirms that this means the remote server wants to close the connection.
I know that the issue is caused by the connection timeout settings on the remote end, because when I increase it, connections shown by netstat
remain in state ESTABLISHED
for longer, and if PHP server decides to reuse such a connection, it works fine. However, I don't want to set connection timeout to a very high value, as I'm worried of running out of ports.
Is there any way I can detect in PHP that a connection went into a CLOSE_WAIT
state? I tried the following and none of it works:
- Checking if handle returned by
pfsockopen
is false. - Checking
$errno
variable written bypfsockopen
. - Try-catching exceptions from
pfsockopen
andfwrite
. - Checking if
fwrite($socket)
returns false. - Checking if
fflush($socket)
returns false. - Checking
stream_get_meta_data($socket)['timed_out']
.