PHP Guzzle 5 - Post Request Not Asynchronous? - guzzle

I have the following Guzzle 5 request:
$this->guzzle->post("http://endpoint/i/control", array(
'body' => array(
'keyone' => 'valueone',
'keytwo' => 'valuetwo'
),
'future' => true
));
I am under the impression that the above is an async operation because the future key is set to true. I control the endpoint the above request hits, and when I put a 5 second sleep function in the script that serves the endpoint, my request takes 5 seconds longer.
How do I serve a true non-blocking asynchronous request using Guzzle 5?

It turns out, to do true async operations using Guzzle 5, you have to bolt it onto something like ReactPHP - which gives PHP the kind of event loop behavior you see in JavaScript.

Related

Lumen POST Routes not Working

Consider the following:
$router->group([
'prefix' => 'api/v1/group',
'middleware' => 'auth'
], function () use ($router) {
$router->get('/', [
'as' => 'group.list',
'uses' => 'Api\V1\GroupController#list'
]);
$router->post('/', [
'as' => 'group.create',
'uses' =>'Api\V1\GroupController#create'
]);
$router->get('/{groupUUID}', [
'as' => 'group.retrieve',
'uses' =>'Api\V1\GroupController#retrieve'
]);
$router->put('/{groupUUID}', [
'as' => 'group.update',
'uses' => 'Api\V1\GroupController#update'
]);
});
As you can see, a pretty typical route setup. However, I'm seeing some incredibly odd behaviour - in short, the POST route seems to be being interpreted by the app as a GET route. When I make a POST request to api/v1/group (via Postman) I don't see the result of Api\V1\GroupController#create, but the result of Api\V1\GroupController#list.
I wondered if perhaps this was something to do with both routes having the same endpoint (shouldn't matter, but maybe it's different in Lumen? I usually work in full-on Laravel). So I commented out the get route. That made me just see a 404.
I then wondered if perhaps this entire route group was somehow broken. So I made two catchall endpoints:
$router->get('/{any:.*}', function () use ($router) {
return 'I am a get route';
});
$router->post('/{any:.*}', function () use ($router) {
return 'I am a post route';
});
And placed them at the top of the routes file, and commented out all other routes. Regardless of the route I hit or the method used, I always saw the same thing: I am a get route.
What's going on? What could cause my app to understand all POST requests as GET requests?
PS: It's also worth noting that these routes were working, until recently, without any real associated changes. Could something have been updated in a Lumen package that caused this?
PPS: I also tried using Insomnia instead of Postman, just in case it was a problem with Postman. Same result.
$router->get('/api/item/{table}/{id}', "ItemController#itemHandler");
$router->post('/api/item/{table}', "ItemController#itemHandler");
$router->put('/api/item/{table}/{id}', "ItemController#itemHandler");
$router->delete('/api/item/{table}/{id}', "ItemController#itemHandler");
I had pretty much the same issue. In my case - since I use Laravel Valet as development environment - I was able to make a POST request again, after serving the API locally over HTTP by executing valet unsecure my-project. On my production server, I still can use HTTPS, but for my local development environment, this solved the issue. Hope this helps some future readers.
Try api/v1/group/ (with trailing slash).

Sails.js server request timeout

I am using sails.js v0.10.5.
I have long processing controller action which takes more than 2 minutes to complete which causes the sails server to timeout. How can I overwrite this timeout duration and set it to my value say 10 minutes.Please specify in which file and what changes I need to make to increase the sails server timeout.
You can find which action delay the starting process
sails --verbose lift
most of the time, ORM causes this kind of issue.
UPDATE
i think the timeout is provided by Express, if you want to modify it, you can use connect-time
1.install
$ npm install connect-timeout --save
2.add middleware to http.js
var timeout = require('connect-timeout');
......
order: [ 'timeout' ....]
My answer is pretty similar to the provided by Ryan, but I'll expand the example a bit more for clarity. Also I don't see the need for the dependency installation there.
TL;DR Add a custom middleware that amends req to increase the timeout
In /config/http.js
order: [
'startRequestTimer',
'cookieParser',
'session',
'myRequestLogger',
'contentTypeCheck',
'extendTimeout', // <--- The order is somewhat important, added it before the body is parsed without issue
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'$custom',
'router',
'www',
'favicon',
'404',
'500'
],
extendTimeout: function(req, res, next) {
req.setTimeout(300 * 1000); // Increase the request timeout to 5 minutes
return next();
},
// Other custom middleware
Can also set req.setTimeout(0) if you don't want any timeout at all, but could be a bit risky.
This will set the timeout for all routes keep in mind. This can be overridden on a per route basis by simply setting req.setTimeout(<some timeout>) as the first line in the controller handling the route
yjimk, thanks for your idea... it wasn't working for me first, but I found a solution.
Problem was in extendTimeout function, here is the fixed version:
extendTimeout: (function () {
console.log('Initializing `extendTimeout`...');
return function (req, res, next) {
sails.log.info('extendTimeout to 1h');
req.setTimeout(3600000);
return next();
};
})(),

How to serve a 500 page using Sinatra?

Since Sintra is a Rack app, I think it could use Rack way:
get '/' do
result = true
if result
['200', {'Content-Type' => 'application/json'}, [{result:true}.to_ json]]
else
binding.pry
['500', {'Content-Type' => 'application/json'}, [{result:false}.to _json]]
end
end
It doesn't work, it alway return a 500 page, whether the variable is true or false
If you actually have this literal code in your app:
{result:true}.to_ json # <<< whitespace that shouldn't be there
Then your code will have a runtime exception (probably missing method to_ or unknown variable or method json), and that's why you get a 500 error every time.
You can return a Rack-like array as a response like this, but the first item of the array needs to be a Fixnumspecifically, rather than just something that will result in a number when parsed with to_i. Sinatra checks the type of the entry to determine how to handle the response.
Your response would be valid in a “pure” Rack app, but not in Sinatra. Arguably this is a bug in Sinatra, or at least the docs which says any valid Rack response is valid.
Sinatra tries to return the whole array as the body of the reponse (since it reponds to each), but then fails when trying to process the array since the contents aren’t all Strings.
To fix it simply change the status codes in your responses from Strings to Fixnums:
[200, {'Content-Type' => 'application/json'}, [{result:true}.to_json]]

Testing a JSON PUT request in mojolicious

I am creating test cases for my app developed using Mojolicious framework. I am testing the routes for appropriate responses for the REST calls that are made. I use JSON as a means of communication between the server and client. To test GET, POST and DELETE requests I use the following functions
GET : get_ok()POST: post_json_ok() DELETE: delete_ok()
but for PUT I am not able to use put_ok(). And example of my usage of put_ok() would be my $t = Test::Mojo->new;$t->put_ok('this/is/url/to/resource'=>{ "element" => "modified value"})->status_is(204)
I get a bad request status every time(400) and so the test fails. I use Test::Mojo for testing my application.
Additional information:
$t->put_ok('http://localhost:3000/application/instances/system/test'=>{"model" => "testing put"})->status_is(204);
This is the exact test for PUT request that is attempted. model is a property of resource test that is being attempted to be changed to testing put and the expected status response is 204.
The mapping to the controller is done by
$r->route('/application/instances/system/:id, id => qr/[A-Za-z0-9 ]+/ -> via('put')
->to(controller => 'system', action => 'update_sys');
This route calls the update_sys method in the system controller and that is how the resource is located.
Actually, after discussions with SRI on IRC, he pointed me to an example almost identical you your needs in the documentation.
# Test custom transaction
my $tx = $t->ua->build_json_tx('/user/99' => {name => 'sri'});
$tx->req->method('PUT');
$t->tx($t->ua->start($tx))
->status_is(200)
->json_is('/message' => 'User has been replaced.');
So this should work for you!
Postscript:
You might be interested to know that this discussion has brought some progress: the next release of Mojolicious (version 3.66) will now have a cleaner syntax for this purpose, the new request_ok method. The above example can then be rewritten as
my $tx = $t->ua->build_json_tx('/user/99' => {name => 'sri'});
$tx->req->method('PUT');
$t->request_ok($tx)
->status_is(200)
->json_is('/message' => 'User has been replaced.');
Can you share the definition of your route for 'this/is/url/to/resource' ? the server is returning 400, so it appears Mojo::Controller in your app does not understand what you are sending ...

Is it possible to do batch POST API calls?

I'm writing an app that makes a daily post as a user, and having benchmarked the PHP code that does this, it seems to take about two seconds per user. I'm dividing the work up in to chunks, and using multiple cron jobs to do each chunk. I'd like to scale to many thousands of users one day, but this kind of work load is just too much. It would take my server literally all day to post to each user one at a time using this method.
How do people normally do this? I've seen other apps that do it. Is there some way of sending all these posts off at once using just one API call? Using individual API calls per user is crazy slow.
Thanks.
On one hand, this is entirely dependent on the API.
However, you could use a multi-threaded or pseudo-parallel approach to this, such that your program sends, say, 100 HTTP POST requests at a time, rather than generating one request after another in series.
Since you're using PHP, multi-threading is out (I think) but this question is very similar to others. For example, see how these folks recommend curl_multi.
You can use batch query to achieve what you need.
The code for batch query is mentioned below. You can refer more about Facebook batch query at : http://25labs.com/tutorial-post-to-multiple-facebook-wall-or-timeline-in-one-go-using-graph-api-batch-request/
$body = array(
'message' => $_POST['message'],
'link' => $_POST['link'],
'picture' => $_POST['picture'],
'name' => $_POST['name'],
'caption' => $_POST['caption'],
'description' => $_POST['description'],
);
$batchPost[] = array(
'method' => 'POST',
'relative_url' => "/{ID1}/feed",
'body' => http_build_query($body) );
$batchPost[] = array(
'method' => 'POST',
'relative_url' => "/{ID2}/feed",
'body' => http_build_query($body) );
$batchPost[] = array(
'method' => 'POST',
'relative_url' => "/{ID3}/feed",
'body' => http_build_query($body) );
$multiPostResponse = $facebook->api('?batch='.urlencode(json_encode($batchPost)), 'POST');