Send gzip request with Guzzle - php

I have to make a HTTP call to send data compressed data. I'm developing in Symfony2. For HTTP calls I'm using Guzzle client (version 3.8.1). Also, I am using Guzzle Service Descriptions to decribe the operations allowed on each command.
I know that I have to add the header "Content-Encoding: gzip" in the request, but the request body is not compressed.
Is there a way to specify in the Guzzle client that the request needs to be compressed? (maybe specify this in the Service Description)
Thank you!

To tell the server to give you compressed version, you have to inform it that you understand how to decompress the data.
For that purpose, you send a header during request which is called Accept-Encoding.
Example of accept-encoding header and values (those are the compression schemes that your client knows to use):
accept-encoding:gzip, deflate, sdch, br
The RESPONSE header Content-Encoding is sent by the server. IF that header is set, then your client asserts that the content is compressed and uses the algorithm that the server sent as value of Content-Encoding.
The server doesn't have to respond with compressed page.
Therefore, these are the steps:
Tell the server you know how to deal with compressed pages. You send accept-encoding header and then you specify which compression algorithms your client knows how to deal with.
Inspect whether the server sent Content-Encoding header. If not, content isn't compressed
If yes, check the value of the header. That tells you which algorithm was used for compression, it doesn't have to be gzip, but usually is.
The server doesn't have to respond with compressed page. You are merely informing the server you understand how to deal with compressed pages.
So, for you, what you should do is verify that your server sends gzipped responses and then you should set the request header accept-encoding. You got it the wrong way around.

I found a solution to send data compressed using Guzzle client with Operation Command and Service Description.
In the JSON file containing the service description I have specified that data sent in body is a string:
{
...
"operations": {
"sendCompressedData": {
"httpMethod": "POST",
"uri": ...,
"parameters": {
"Content-Type": {
"location": "header",
"required": true,
"type": "string",
"default": "application/json"
},
"Content-Encoding": {
"location": "header",
"required": true,
"type": "string",
"default": "gzip"
},
"data": {
"location": "body",
"required": true,
"type": "string"
}
}
}
}
}
As mentioned by #Mjh, Guzzle doesn't compress data automatically if "Content-Encoding" header is set, so data needs to be compressed before sending it to the Guzzle client to execute the command. I have serialized the object and used "gzencode($string)" for compressions.
$serializedData = SerializerBuilder::create()->build()->serialize($request, 'json');
$compressedData = gzencode($serializedData);
...
$command = $this->client->getCommand('sendCompressedData', array('data' => $compressedData));
$result = $command->execute();

Related

FCM HTTP v1 Batch request won't work in PHP

I'm using currently the Firebase messaging with PHP. I was able to make it work with a single notification with PHP and cURL. I've read the documentation about making batch request and I've constructed the request string as follow:
--subrequest_boundary
Content-Type: application/http
Content-Transfer-Encoding: binary
Authorization: Bearer ya29.xxxxxnY
POST /v1/projects/xxxxxxxx/messages:send
Content-Type: application/json
accept: application/json
{
"message":{
"token":"cxxxxx3",
"data":{
"typeNoti":"paiement",
"idcompte":"admin",
"typecompte":"paiement"
},
"notification":{
"title":"Test1",
"body":"Notification de test 1"
}
}
}
--subrequest_boundary
Content-Type: application/http
Content-Transfer-Encoding: binary
Authorization: Bearer yaxxxxxxY
POST /v1/projects/xxxxxx/messages:send
Content-Type: application/json
accept: application/json
{
"message":{
"token":"eoxxxxxU1",
"data":{
"typeNoti":"paiement",
"idcompte":"compte2",
"typecompte":"paiement"
},
"notification":{
"title":"Test1",
"body":"Notification de test 1"
}
}
}
--subrequest_boundary--
This string is generated in a file called batch_request.txt as the documentation said. When trying to send the request with cURL I receive this error:
{
"error": {
"code": 400,
"message": "Failed to parse batch request, error: 0 items. Received batch body: (0) bytes redacted",
"status": "INVALID_ARGUMENT"
}
}
I've experienced a strange behavior also, which is when I construct that string manually (in PHP I use HEREDOC), the batch request does occurs with 200ok response. I've tried to find some hidden character but couldn't.
I know there is a library called firebase-php. The problem with it is that I cannot find a straight foreward example like this:
//authenticate with json file
//create new token if it doesn't exist or skip
//create notification array
//create data array
//send multi notifications to multiple devices
I actually use a for loop and mono notification (one device token, one message at a time). The problem with this is that not all notifications are sent.
I appreciate your help thanks!
I gathered the code from firebase-php docs. After installing the library with composer I get a lot of errors and a lot of missing files in Firebase directory inside vendor like Factory.php etc
My solution was to NOT USE firebase-php library due to the numerous bugs I've faced (or maybe misuses) neither to construct the REST request text file for batch request. I used instead the NodeJS SDK which worked like a charm. I'm running a javascript file from my php code using exec.

API endpoint accepting changeable set of data

One endpoint of my API must have a changeable data set depending on the context (some option, let's say a domain). How (apart from API documentation, of course) can we inform frontend that we are expecting such a data set? It comes to my mind to write an endpoint returning the fields that I expect in response (more specifically - the entire form with particular types of inputs, their placeholders, default values, etc.).
The potential self-describing API you are looking for is OPTIONS HTTP request.
The OPTIONS method requests information about the communication
options available for the target resource, at either the origin
server or an intervening intermediary. This method allows a client
to determine the options and/or requirements associated with a
resource, or the capabilities of a server, without implying a
resource action.
http://zacstewart.com/2012/04/14/http-options-method.html
For different possible datasets you can use custom Content-Type header like: Content-type: application/vnd+some.payload+json.
The good example of using those types is GitHub API: https://developer.github.com/v3/media/
Return Http 400 Bad Request with JSON response containing URL to JSON Schema to which payload should comply.
Separate endpoint returning JSON Schema is also an option.
I don't know your case, but I dislike a idea of "changeable set of data". Maybe you can consider redesigning your API or wrapping your data sets into kind of container, so all of them can be described by single JSON Schema? (like in sample below)
{
"oneOf": [
{
"type": "object",
"required": [ "domain", "content" ],
"properties": {
"domain":{"type": "string", "enum": ["domain1"]},
"content": {"type": "string"}
}
},
{
"type": "object",
"required": [ "domain", "content" ],
"properties": {
"domain":{"type": "string", "enum": ["domain2"]},
"content": {"type": "number"}
}
}
]
}
depending on "domain" value, content can be either string or number

Error 422 on uploading rackspace cloudfiles static large object

In reference to this: http://docs.rackspace.com/files/api/v1/cf-devguide/content/Uploading_the_Manifest-d1e2227.html
so I uploaded the following JSON via PUT command on Rackspace:
Body:
[
{
"path": "/archive5/8b98fb0bc6f8694d07a1bc851b58f72a",
"etag": "8b98fb0bc6f8694d07a1bc851b58f72a",
"size_bytes": 20971520
},
{
"path": "/archive5/c234d0f0204f67340fb4741bdf9f5e92",
"etag": "c234d0f0204f67340fb4741bdf9f5e92",
"size_bytes": 8382711
}
]
URL: https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_hashhash/archive5/606f95dbf0a17bd7d5de202f3aab98c7?multipart-manifest=put
Method: PUT
Headers
ETag: 606f95dbf0a17bd7d5de202f3aab98c7
Content-Type: application/octet-stream
Content-Length: 266
X-Auth-Token: theAuthToken
X-Auth-Project-Id: theProjectId
but then Cloudfiles would then return
Problem saving/updating object
[https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_hashhash/archive5/606f95dbf0a17bd7d5de202f3aab98c7]
HTTP status [422] response [Unprocessable
EntityUnable to process the contained instructions]
What did I do wrong? I've verified that the files in the JSON body indeed exist and that they have the correct path/etag(md5) and filesize...
Have you tried using php-opencloud to do this? There's an upload-large-object.php that takes care of all of the details for you.

Sending to RESTful service JSON with file pointer resource

I'm trying to figure out how to send this kind of data, via PHP, to a RESTful service:
{
"api_version": 1.0,
"project_id" : 123,
"batch_id" : 111,
"accesskey" : "abc123",
"job": {
"file": file,
"word_count": 111,
"title": "FAQ",
"cms_id": "page_123_en_sp",
"url" : "http://original_post_url.com",
"is_update": false,
"translator_id": 123,
"note": "Translate these words",
"source_language": "en",
"target_language": "it"
}
}
First of all, I cannot use curl, as who's going to use this code might not have it installed or might be not allowed to install it.
That's not a 'normal' JSON object.
The job->file property is an actual file (not even an url to a file).
That's the actual code I'm using to send all requests and it worked, until I've met this specific case: http://pastebin.com/6pEjhAkg
The 'file' property is created as such:
$file = generate_file( $content );
protected static function generate_file($content) {
$file = fopen('php://temp','r+');
fwrite($file, $content);
rewind($file);
return $file;
}
Now, when sending data, with $params argument properly set on PHP side, the RESTful returns an error about missing 'api_version' and 'project_id', but they are present.
I'm pretty sure the problem is that he's not receiving data as JSON, but how can I convert to JSON an object that, in his properties, contains a file pointer resource?
The code that sends data and builds the file has been created by a former developer, and I can't get in touch with him.
I tried to understand what is wrong there and the only thing I managed to fix so far, for another unrelated issue, is to actually send JSON data (when $multipart==false), as you can see in lines 16-19 of the linked code, rather than sending urlencoded data.
Any hint?
JSON does not have a "file" concept. You can either load the file content, encode it using Base64 and then stuff it into a JSON string - or you can use multipart format for sending the file in one part and the complete JSON in another part. The multipart solution should be the best performing as it doesn't have to base64 encode the file content.
Here is a similar example message format from Mason that tries to formalize the json/multipart usage:
POST /projects/2/issues HTTP/1.1
Accept: application/vnd.mason+json
Content-Type: multipart/form-data; boundary=d636dfda-b79f-4f29-aaf6-4b6687baebeb
--d636dfda-b79f-4f29-aaf6-4b6687baebeb
Content-Disposition: form-data; name="attachment"; filename="hogweed.jpg"
... binary data for attached image ...
--d636dfda-b79f-4f29-aaf6-4b6687baebeb
Content-Disposition: form-data; name="args"; filename="args"
Content-Type: application/json
{
"Title": "Hogweeds on the plaza",
"Description": "Could you please remove the hogweeds growing at the plaza?",
"Severity": 5,
"Attachment":
{
"Title": "Hogweed",
"Description": "Photo of the hogweeds."
}
}

Modifying User-Attribute on SharePoint data item using HTTP

I'm using the (JSON) HTTP interface to communicate with SharePoint. The communication itself is done via cURL and a convinience-wrapper in PHP. Problems arise, when I want to push data to SP.
Since I'm no Microsoft / SharePoint guy, I'm missing the proper vocabulary to explain my needs. I'll therefore demonstrate using data I received from SharePoint.
GET http://example.org/mytest/_vti_bin/listdata.svc/Aufgaben(2) returns the following (truncated by me) data:
{
"d" : {
"__metadata": {
"uri": "http://example.org/mytest/_vti_bin/listdata.svc/Aufgaben(2)",
"etag": "W/\"5\"",
"type": "Microsoft.SharePoint.DataService.AufgabenItem"
},
"ID": 2,
"InhaltstypID": "0x010800821BC29B80192B4C960A688416597526",
"Inhaltstyp": "Aufgabe",
"Titel": "Neuer Titel",
"ZugewiesenAn": {
"__deferred": {
"uri": "http://example.org/mytest/_vti_bin/listdata.svc/Aufgaben(2)/ZugewiesenAn"
}
},
"ZugewiesenAnId": 29,
"F\u00e4lligkeitsdatum": "\/Date(1323993600000)\/"
}
}
"ZugewiesenAn" is a user. If I query the deferred values, I get (truncated by me, again)
{
"d" : {
"__metadata": {
"uri": "http://example.org/mytest/_vti_bin/listdata.svc/Benutzerinformationsliste(29)",
"etag": "W/\"1\"",
"type": "Microsoft.SharePoint.DataService.BenutzerinformationslisteItem"
},
"InhaltstypID": "0x010A000719C31710976A48867763D86F6586E0",
"Name": "Rehm Rodney",
"Konto": "EXT\\rodney.rehm",
"ID": 29,
"Inhaltstyp": "Person",
}
}
So I can see that the value of "ZugewiesenAn" should be "EXT\rodney.rehm" (as I need the username). Thus far, no problem.
The question is how I create a new or update an existing object with a different user for "ZugewiesenAn" (a User/Group field)?
I've tried:
Sending the username as the value of "ZugewiesenAn" or "ZugewiesenAnId" results in a Bad Request.
Querying http://example.org/_vti_bin/People.asmx (SOAP: SearchPrincipals) only yields numeric IDs for people that have actually worked with the list. If I query a username that hasn't logged into that SharePoint list before, I get ID -1.
I could not find out how to add users to the userlist via REST. You can, however, use the SOAP ResolvePrincipal request (example) - which does the job!
I am not a SharePoint guy and focus mostly on REST and OData. But think that REST OData API for SharePoint follows common rules for REST OData.
Common rules for REST and OData is to use different HTTP verbs for different operations. Read, create, update, and delete operations are mapped directly to GET, POST, PUT, and DELETE HTTP verbs.
So, you are getting your user by GET HTTP verb on URI http://example.org/mytest/_vti_bin/listdata.svc/Benutzerinformationsliste(29)
To delete this user use verb DELETE on the same URI and user id with empty HTTP message body.
To create user HTTP verb POST, same URI and json in message body. Also while creating ID shouldn't be specified (except situations when ID isn't auto-incremented in databases). Content-Type for HTTP message should be set: application/json for JSON.
The same situation with update - PUT, same URI
http://example.org/mytest/_vti_bin/listdata.svc/Benutzerinformationsliste(29)
and json in HTTP message body with content-type:application/json.
Format of json should be the same as you've received.
{
"InhaltstypID": "0x010A000719C31710976A48867763D86F6586E0",
"Name": "Rehm Rodney",
"Konto": "EXT\\rodney.rehm",
"ID": 29,
"Inhaltstyp": "Person",
}

Categories