32

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 !

Gotys
  • 1,371
  • 2
  • 13
  • 22
  • 1
    It's not the client that sends the 200 OK response, but the server. The client makes a request and the server responds, the server doesn't care what is going on with the client (by definition in REST, it is stateless). What are you trying to, could you elaborate? – meouw Feb 03 '10 at 09:28

8 Answers8

25

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.

poke
  • 369,085
  • 72
  • 557
  • 602
  • @Gotys sidenote, if you want to just send a "request" without waiting for a response, look in to the UDP protocol – chris Jul 01 '16 at 22:28
  • 1
    There is the fire and forget "pattern". An asyc request will fire the request. Once executed you can do something else without waiting. In the scenario where you have a reliable network connection UDP is an option but TCP is more reliable in that you know the packets will make it. However, without checking the response you won't know what happened. – Razor Oct 13 '17 at 20:02
  • Can you quote the RFC (or other source) justifying _"You cannot just send data without receiving an answer with HTTP"_ ?. From a TCP standpoint, it is possible to send multiple POST without waiting for the response, so that must be explicitly forbiden somewhere in an HTTP specification. – fgrieu Oct 22 '18 at 09:40
  • 1
    @fgrieu First paragraph in [RFC 2616 section 1.4](https://tools.ietf.org/html/rfc2616#section-1.4): *“The HTTP protocol is a request/response protocol”* – If there’s no response, then you aren’t doing the HTTP right, which is an application protocol and as such can have different requirements than the underlying TCP protocol. Similarly, if you just send a single TCP package over the network but ignore the rest of the protocol, then you are also not automatically doing TCP just because that one package looked like it. – poke Oct 22 '18 at 13:08
14

This solutions is for software minimal recevied package to continue script. If you want don't care about respond and have access to exec than use exec and call script in background. First Recevier File:

recevier.php

ignore_user_abort(true); //continue script if connetions become close by webbrowser(client) within working script

ob_end_clean(); // this 4 lines just extra sending to web about close connect it just in case
header("Connection: close\r\n"); //send to website close connect 
header("Content-Encoding: none\r\n"); 
header("Content-Length: 1"); //

fastcgi_finish_request(); //close nginx,apache connect to php-fpm (php working but nginx or apache stop communication with php)
//continue scripting 
// ...DO HERE WHAT YOU WANT ...
//check test with your mongo or mysql to sure php still keep connection with db

FRONTGROUND by PHP request to HTTP: this solution is better than background and you need wait only 1ms

sender.php:

 curl_setopt($curl, CURLOPT_TIMEOUT_MS, 1); //HERE MAGIC (We wait only 1ms on connection) Script waiting but (processing of send package to $curl is continue up to successful) so after 1ms we continue scripting and in background php continue already package to destiny. This is like apple on tree, we cut and go, but apple still fallow to destiny but we don't care what happened when fall down :) 
 curl_setopt($curl, CURLOPT_NOSIGNAL, 1); // i'dont know just it works together read manual ;)

--------- Check next answer to complete solution ------------

BACKGROUND By Server Request to HTTP: This will execute $cmd in the background (no cmd window) without PHP waiting for it to finish, on both Windows and Unix. @source https://www.php.net/manual/en/function.exec.php

<?php
function execInBackground($cmd) {
    if (substr(php_uname(), 0, 7) == "Windows"){
        pclose(popen("start /B ". $cmd, "r")); 
    }
    else {
        exec($cmd . " > /dev/null &");  
    }
}
?>
Makyen
  • 31,849
  • 12
  • 86
  • 121
Kamil Dąbrowski
  • 984
  • 11
  • 17
  • 1
    I found several solutions that included the output buffer part but it still wasn't working for me until I added those curl options. Thanks! – Charlie Stanard Oct 25 '17 at 21:55
11

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/

Ben D
  • 14,321
  • 3
  • 45
  • 59
  • thankyou. this is best.. i tried using pThreads but need ZTS which i cannot install, i trieed GuzzleHttp (having async function name) but actually syncrhonus.. i can do async with this curl and very fast ;) – david valentino Aug 06 '18 at 06:52
2

http://curl.haxx.se/mail/lib-2002-05/0090.html

libcurl has no asynchronous interface. You can do that yourself either by using threads or by using the non-blocking "multi interface" that libcurl offers. Read up on the multi interface here:

http://curl.haxx.se/libcurl/c/libcurl-multi.html

PHP example of multi interface is here:

http://www.phpied.com/simultaneuos-http-requests-in-php-with-curl/

Tahir Akhtar
  • 11,385
  • 7
  • 42
  • 69
1

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.

NullUserException
  • 83,810
  • 28
  • 209
  • 234
Pekka
  • 442,112
  • 142
  • 972
  • 1,088
1

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";
OndrejC
  • 122
  • 1
  • 3
1

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.

john graham
  • 139
  • 9
0

As other people says when you make a http request you have to wait the response.

In PHP what you can do is make the request using the exec function.

Check this link: php exec command (or similar) to not wait for result

Community
  • 1
  • 1
JesusIniesta
  • 10,412
  • 1
  • 35
  • 28