Search code examples
phpprotocol-buffers

Error "Unexpected wire type" in a protobuf response in PHP


I am executing a POST request to a server, which responds "properly", but when trying to call the mergeFromString() function I receive the following error:

Google\Protobuf\Internal\GPBDecodeException: Error occurred during parsing: Unexpected wire type. in Google\Protobuf\Internal\Message.php on line 353

I am using CURL with PHP:

$handle = curl_init($url);

$curl_options = [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $body,
    CURLOPT_HEADER => true,
    CURLOPT_VERBOSE => true
];

curl_setopt_array($handle, $curl_options);

$curlResponse = curl_exec($handle);

$responseMessage = new MyMessage();
$responseMessage->mergeFromString($curlResponse);

curl_close($handle);

The result of var_dump($curlResponse) is as follows:

╔╔eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI1MTQwMTAsInVzZXJuYW1lIjoiNTM1MTAyODA2MCIsInZlcnNpb24iOiIyMTgwNiJ9.Li-bp3bIPdIrsRhuTWEWToS0ds62VCG-a2PCGaKSrigڲօ═"

In plain text it should look something like this:

1: 1
2: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI1MTQwMTAsInVzZXJuYW1lIjoiNTM1MTAyODA2MCIsInZlcnNpb24iOiIyMTgwNiJ9.Li-bp3bIPdIrsRhuTWEWToS0ds62VCG-a2PCGaKSrig
3: 1619765852

My .proto file for the response is as follows:

message MyMessage{
    // I'm only interested in property 2
    string prop = 2;
}

Environment:

"protobuf-php / protobuf": "^ 0.1.3",
"google / protobuf": "^ 3.16"

However, tests using Protoman (A Postman-like API client for protobuf-based messages) are successful. I did the tests with the same proto files that I use in PHP.


Solution

  • In this case the error is because the string is invalid.
    And it is invalid because the value (string) returned by CURL includes all the data from the HTTP request, for example:

    HTTP/2 200 
    content-type: application/x-protobuf
    date: Wed, 02 Jun 2021 23:12:37 GMT
    content-length: 172
    
    ╔╔eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI1MTQwMTAsInVzZXJuYW1lIjoiNTM1MTAyODA2MCIsInZlcnNpb24iOiIyMTgwNiJ9.Li-bp3bIPdIrsRhuTWEWToS0ds62VCG-a2PCGaKSrigڲօ═"
    

    I didn't notice this because I thought those details were due to the CURLOPT_VERBOSE => true option.

    Now, how to extract the body from the value returned by CURL is a bit complex, in this question you will see why.

    My solution is as follows (I repeat, read the question I mentioned, maybe a simpler solution will work for you):

    // I can't trust the Content-Length header (it may or may not be present).
    // However, I can reliably calculate the length of the headers and use it instead.
    
    $responseHeadersLength = 0;
    
    $handle = curl_init($url);
    
    $curl_options = [
        // ...
        CURLOPT_HEADERFUNCTION => function ($curl, $header) use (&$responseHeadersLength) {
                $len = strlen($header);
                $responseHeadersLength += $len;
                return $len;
        }
    ];
    
    curl_setopt_array($handle, $curl_options);
    
    $response = curl_exec($handle);
    
    $body = trim(substr($response, $responseHeadersLength));