phpsocketshttp-status-code-400

I get bad request when I use this string with socket


I am trying to implement Fire and Forget in PHP, and I use this code (found here):

$parts = parse_url($url);
try {
    $socket = $this->openSocket($parts['host'], ($parts['port'] ?? $parts['scheme'] === 'https' ? 443 : 80));
} catch (Exception $e) {
    $socket = null;
}

if (!$socket) {
    return false;
}

$jsPostData = json_encode($postData, JSON_THROW_ON_ERROR);

$contentLength = strlen($jsPostData);

$request = "POST {$parts['path']} HTTP/1.1\r\n";
$request .= "Host: {$parts['host']}\r\n";
$request .= "Authorization: Bearer " . $bearerToken . "\r\n";
$request .= "Content-Length: {$contentLength}\r\n";
$request .= "Content-Type: application/json\r\n\r\n";
$request .= $jsPostData;

fwrite($socket, $request);
fclose($socket);        
    

It results with a request like this:

POST /my_path HTTP/1.1
Host: my_url
Authorization: Bearer my_bearer_token
Content-Length: 263
Content-Type: application/json

{"event":"...."}

I get the error:

HTTP/1.1 400 Bad Request
Server: awselb/2.0
Date: Fri, 06 Oct 2023 09:43:17 GMT
Content-Type: text/html
Content-Length: 220

I do not know if its a really bad request or its a permissions fail.

UPDATE

If I use this code with Guzzle it works:

try {
    $guzzle = new Client();
    $guzzle->post($url, [
        'timeout' => 1,
        'headers' => [
            'Authorization' => "Authorization: Bearer " . $bearerToken,
        ],
        'form_params' => $postData
    ]);
} catch (\GuzzleHttp\Exception\ConnectException $e) {
    // do nothing, the timeout exception is intended
}

Solution

  • In the socket version you are clearly sending 2 extra headers which are not present in the Guzzle version. Also in Guzzle you are using the form_params option to set the data, which - as per the documentation sends the data in form-url-encoded format to the server. Yet in the socket version you are sending JSON instead.

    You should make the socket-based query send form-url-encoded data. This should work:

    //sample data
    $url = "https://www.example.com/postSomeData";
    $postData = ["event" => "xyz", "someOtherField" => "abc"];
    //end of sample
    
    $contentType =  "application/x-www-form-urlencoded";
    $params = http_build_query($postData);
    $parts = parse_url($url);
    
    try {
        $socket = $this->openSocket($parts['host'], ($parts['port'] ?? $parts['scheme'] === 'https' ? 443 : 80));
    } catch (Exception $e) {
        $socket = null;
    }
    
    if (!$socket) {
        return false;
    }
    
    $request = "POST {$parts['path']} HTTP/1.1\r\n";
    $request .= "Host: {$parts['host']}\r\n";
    $request .= "Content-Type: $contentType\r\n\r\n";
    $request .= $params;
    
    //echo($request);
    
    fwrite($socket, $request);
    fclose($socket);
    

    This would generate a request as follows:

    POST /postSomeData HTTP/1.1
    Host: www.example.com
    Content-Type: application/x-www-form-urlencoded
    
    event=xyz&someOtherField=abc
    

    Simulation: https://3v4l.org/SY6cp

    Other reference material: