Guzzle 5.3: Unable to POST JSON body if larger than ~1MB - php

I'm using Guzzle 5.3 via Guzzle Services (via https://github.com/ticketevolution/ticketevolution-php) to attempt to POST to an API endpoint with a JSON body that includes a PDF encoded as base64. When the body is less than ~1MB it works fine. When the body is larger it seems that the body is never sent.
I have tested this with and without the Expect: 100 header and it does not seem to make a difference.
I have tested with Transfer-Encoding: chunked, but because the API needs the entire POST body in order to authenticate using chunked does not work.
We have tested with and without the load balance between the client and the appservers.
From everything we can tell the body just doesn't get sent when it is larger than ~1MB.
Does anyone have any ideas on how to get Guzzle 5.3 to send the body even when it is larger than 1MB?
Below is the log output
[2015-09-01 16:15:43] TEvoAPIClientLogger.CRITICAL:
>>>>>>>>
POST /v9/orders/2100732/deliver_etickets HTTP/1.1
Host: api.ticketevolution.com
User-Agent: ticketevolution-php/3.0.0dev Guzzle/5.3.0 curl/7.44.0 PHP/5.5.28
Content-Type: application/json
Content-Length: 1387036
X-Token: b47dsd8c0ab80a1e2bc24sc341415a2f
X-Signature: SwBOkdUOqG3SDtjVwi2etosdP+gppwuV5dCq8yMw9lM=
{"etickets":[{"item_id":1513651,"eticket":"JVBERi0xLjQKJeLjz9MKNCAwIG9iaiBbXQplb… [a whole lot of base64 snipped] …NwolJUVPRgo="}]}
<<<<<<<< --------
cURL error 52: Empty reply from server

Running into the same problem, a bit of debugging resulted in ending up at GuzzleHttp\Ring\Client\CurlFactory::applyBody(), and then this fixed the problem for me:
Setting a default configuration on the client
$client = new \GuzzleHttp\Client([
'defaults' => [
'config' => [
'curl' => [
'body_as_string' => true,
],
],
],
]);
Setting the configuration when issuing the request
$client->post('https://example.com', [
'json' => $json,
'config' => [
'curl' => [
'body_as_string' => true,
],
],
]);
Rewinding the previously fetched actual content stream
Since I'm fetching content from a remote server, this article by Matt Downling helped me in finding out that I need to rewind the actual stream before using it as a part of the multipart/form-data request:
$response->getBody()->seek(0);

Related

Guzzle making a GET request instead of POST

I'm sure this is going to be marked as a duplicate, but I've looked through all the given questions on the same subject and tried many of the suggested solutions, and they haven't worked.
I'm in a laravel project and I have a post request going out through guzzle.
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', $url, [
'headers' => [
'Authorization' => 'Bearer ' . $apiToken,
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'allow_redirects' => false,
// 'allow_redirects'=>['strict'=>true]
],
'json' => json_decode($logText, true)
]);
I keep on getting the response back "message": "The GET method is not supported for this route. Supported methods: POST."
I've checked and indeed, it's sending a GET request to the same $url specified above.
I didn't have those allow_redirects settings at first, but both settings were offered up as potential solutions when I googled around. Unfortunately, both options result in the same error message: The GET method is not supported for this route.
Why in the world is my POST request changing to a GET request?
I've also tried $client->post instead, and THAT became a GET request as well.
I've also double checked that the GET error message isn't actually coming from inside the POST request: it's not. The POST route isn't being hit at all.
PHP version 7.2, Laravel version 6.0.2, Guzzle version 6.5.3
Check for redirects on the server, such as HTTP -> HTTPS. Redirects are always a GET request, which will mess up non-GET routing. Using the correct protocol all the way through (such as always use HTTPS) will bypass the redirect.

Set Content-type header to Elasticsearch-php client

I'm trying to use Elasticsearch-php version 5.0 to send search queries to Elasticsearch 6.4.2.
One of the breaking changes from 5.0 to 6.0 is that there is the "strict content type validation"
which means that requests to Elasticsearch must sent with "Content-type: application/json" header.
In order to add this header, I tried to use polyfractal's suggestion from this thread:
$params = [
'index' => $index,
'type' => $mapping,
'body' => $query,
'client' => [
'curl' => [CURLOPT_HTTPHEADER => array('Content-type: text/plain')]
]
];
$res = $this->mESClient->search($params); // this is Elasticsearch/Client
return $res;
but for some reason, I keep getting "Notice: Array to string conversion" when the code tries to do curl_setopt_array(), and the request is net sent.
Please note: that when I remove the 'client' part of the $params array the request is being received in the Elasticsearch.
According to Version Matrix, you should use elasticsearch-php 6.0 when dealing with ES >=6.
elasticsearch-php 5.0 is not compatible with ElasticSearch 6.
The thread you mentioned, relates to ES-PHP 1.x/2.x, which may have different syntax for options. It's not relevant for your situation, except that one of comments says the same that I just did above.
FYI, if you're using Elasticsearch 6.0+, you need to upgrade your ES-PHP client to the 6.0 branch too. ES-PHP 6.0+ sets the content-type headers automatically: fd3b0f1
Found the problem. It seems that there is a buggy functionality when trying to set HTTP headers using curl and specifying authorization details in the URL.
for example:
https://username:password#host:port
For some reason the client copies the curl http headers into other curl options (that requires string and not array) and, therefore, the exception of Array to string conversion thrown from curl_setopt_array.
When I removed the authorization details from the host URL and used the curl authorization header everything worked.

Is it possible to make a Guzzle request to an endpoint that has CORS restriction to one domain origin?

I'm trying to get some data from an endpoint. And the endpoint allows only requests that originate from one specific domain (which is not mine)
Is it possible to make a request with Guzzle and make it "pretend" as if it was coming from the allowed origin?
Currently I am trying to set some headers to achieve that and get the response back but it is always returning me code 200 with content-length: 0
You can set Origin header to what you want as long as you are making a request out of browser's control. In Guzzle it could be set like this:
$client->request('GET', '/data', [
'headers' => [
'Origin' => 'http://foo.bar',
]
]);
If targeting host is satisfied with this only header then you are fine otherwise you won't get your expected response.

Rest API get Content-Length of Body

I got a Problem. Im working on a Datatransfer to a Restful API at the Moment. All the arrays and everything and json objects should be fine, but i just do not understand how to determine the Content-Length of my Body?
$http = new Client([
'headers' => ['Authorization' => 'Bearer ' . $token["access_token"], 'Transfer-Encoding' => 'chunked', 'Content-Encoding' => 'chunked']
]);
This is my code at the moment. But the Answer thats coming back is always
HTTP Error 400. There is an invalid content length or chunk length in the request.
Can you help me to determine the right Content-Length?
Cheers!
There are at least 2 ways to do it.
If you have content in variable then add to your headers array 'Content-Length' => strlen($var)
You can also use output buffering and get length with function ob_get_length()
'Content-Length' => ob_get_length()
Here you will find example

Cakephp hanging after post request via httpsocket containing json data

at the moment I am fighting with a http post request within cakePHP.
I am using the code below to communicate with an external API that requires Authorization.
The problem is, as soon as I add the body with the JSON context, the whole application is hanging. It's loading forever in the browser, not coming back with any results. Have to stop and start Apache to come back to normal. Tested on different machines, same effect.
If I don't send the (JSON) body, I am getting obviously a 500 server error, but it's telling me that the authorization is working otherwise I would get an unauthorized.
If I do a "GET" request to the same API, it's working fine and coming back with a result.
The JSON syntax is clean, if I process the variable and the other information using an API tester it's working (https://apigee.com/console).
UPDATE: It is working if the request is valid. However, on the API tester it's giving me a "400 Bad Request". I would need to handle that within my application but in this case the situation described above is happening. Expectation would be that it's returning the error.
$options = array(
'body' => $json_order,
'header' => array(
'Authorization' =>'123456',
'Content-Type' => 'application/json; charset=utf-8',
),
);
$result=$HttpSocket->post('https://<address>',null,$options);
debug($HttpSocket->request);
same effect if I would do it the other way
$options = array(
'header' => array(
'Authorization' =>'123456',
'Content-Type' => 'application/json; charset=utf-8',
),
);
$result=$HttpSocket->post('https://<address>',$json_order,$options);
debug($HttpSocket->request);
Any help would be more than appreciated!
To clear that up: The problem was on the far end not responding correctly with an error code. =

Categories