Sending POST Requests without waiting for response?

Viewed 66087

I am writing a simple REST service, which responds to requests from clients. All in PHP.

My concern is, that when my server responds to a request, it could end up tying up resources if the client side is too slow in sending back "ok" response.

How do I send a POST request via lib_curl setting it to not wait for any responses, but rather quit immidiately after the POST data have been sent?

Is this even possible? Thank you !

8 Answers

You cannot just send data without receiving an answer with HTTP. HTTP always goes request -> response. Even if the response is just very short (like a simple 200 with no text), there needs to be a response. And every HTTP socket will wait for that response.

If you don't care about the response, you could add a process to the server that makes your requests, and you just push your request data to it (like a service that is running in the background, checking a request database, and always starting the request whenever a new entry was added). That way you would make the request asynchronously and could quit as soon as you added that request to the stack.

Also as meouw said, the client is not part of any communication you are doing with php. Php is a server-side language, so when the client requests a webpage (the php file), the server executes that file (and does all requests the php file states) and then returns the result to the client.

If you really don't care about the response you're probably best off exec-ing a wget command. This is mentioned in passing in some of the other answers, but here's a super easy function for sending a _POST package via this approach (which is asynchronous and takes 1-2ms):

function wget_request($url, $post_array, $check_ssl=true) {

  $cmd = "curl -X POST -H 'Content-Type: application/json'";
  $cmd.= " -d '" . json_encode($post_array) . "' '" . $url . "'";

  if (!$check_ssl){
    $cmd.= "'  --insecure"; // this can speed things up, though it's not secure
  }
  $cmd .= " > /dev/null 2>&1 &"; //just dismiss the response

  exec($cmd, $output, $exit);
  return $exit == 0;
}

Credits: Function was adapted from https://segment.com/blog/how-to-make-async-requests-in-php/

I have never tried this, but setting the CURLOPT_TIMEOUT to a very low value might do the trick. Try 0 or 0.1.

However, I don't know how cURL and the client will behave with this, whether the connection will be actively cancelled when the connection is already established, and the timeout is reached. You would have to try out. If you're calling PHP scripts, maybe ignore_user_abort() can make sure your scripts run through either way.

If you have 2 PHP servers communicating with each other, e.g. server 1 wants to send JSON data to a server 2, the server 2 is doing some heavy work and terminates the connection right after it receives the data so the server 1 doesn't have to wait for the result. You can do it like this:

Server 1 (client creating POST request with JSON data):

Use CURL, don't use file_get_contents() because in my experience, file_get_contents() doesn't handle Connection: close HTTP header correctly and doesn't terminate the connection as it should.

    $curl = curl_init('http://server2.com/');
    curl_setopt($curl, CURLOPT_HEADER, false);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_HTTPHEADER, ["Content-type: application/json"]);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode(['some data']));

    $response = curl_exec($curl);
    $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);

    if ($status !== 200) {
        exit("Failed with status {$status}, response {$response}, curl_error " . curl_error($curl) . ", curl_errno " . curl_errno($curl));
    }
    curl_close($curl);

    echo $response;

Server 2:

Modified code from bubba-h57 used.

// Cause we are clever and don't want the rest of the script to be bound by a timeout.
// Set to zero so no time limit is imposed from here on out.
set_time_limit(0);
// Client disconnect should NOT abort our script execution
ignore_user_abort(true);

// Clean (erase) the output buffer and turn off output buffering
// in case there was anything up in there to begin with.
ob_end_clean();
// Turn on output buffering, because ... we just turned it off ...
// if it was on.
ob_start();
echo 'I received the data, closing connection now, bye.';
// Return the length of the output buffer
$size = ob_get_length();
// Send headers to tell the browser to close the connection
// Remember, the headers must be called prior to any actual
// input being sent via our flush(es) below.
header("Connection: close");
// Hack how to turn off mod deflate in Apache (gzip compression).
header("Content-Encoding: none");
header("Content-Length: {$size}");
// Set the HTTP response code
http_response_code(200);
// Flush (send) the output buffer and turn off output buffering
ob_end_flush();
// Flush (send) the output buffer
// This looks like overkill, but trust me. I know, you really don't need this
// unless you do need it, in which case, you will be glad you had it!
@ob_flush();
// Flush system output buffer
// I know, more over kill looking stuff, but this
// Flushes the system write buffers of PHP and whatever backend PHP is using
// (CGI, a web server, etc). This attempts to push current output all the way
// to the browser with a few caveats.
flush();

// Close current session.
session_write_close();

// Here, you can proceed with some heavy work.

echo "This won't be sent, the connection should be already closed";

In Laravel

use Illuminate\Support\Facades\Http;


...Some Code here

$prom = Http::timeout(1)->async()->post($URL_STRING, $ARRAY_DATA)->wait();

... Some more important code here

return "Request sent"; //OR whatever you need to return

This works for me as I don't need to know the response always.

Related