I have a bunch of events triggered and dispatched to be handled after KernelEvents::TERMINATE. One of my services are dependent on details about the request and I therefor auto inject the RequestStack from Symfony. Sadly it seems that the request is not accessible after the kernel terminate. All functions like getCurrentRequest, getMasterRequest and getParentRequest return null.
I can extract the values I need and send them along in the event but that seems a bit excessive. Is there anyway I can have a service after kernel terminate that can access the request?
do you mean really "after terminate"? If you can include it "in terminate", it is quite easy - main php flow in index.php is following:
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
...
$kernel->terminate($request, $response);
So even though RequestStack is already empty the original $request is still 'alive' during kernel.terminate event and more, it is in the event's argument - so it is easy to reuse it there, e.g.
public function onKernelTerminate(PostResponseEvent $event)
{
$request = $event->getRequest());
}
Hope this helps at least other readers :-)
Related
My middleware need is to:
add an extra query param to requests made by a REST API client derived from GuzzleHttp\Command\Guzzle\GuzzleClient
I cannot do this directly when invoking APIs through the client because GuzzleClient uses an API specification and it only passes on "legal" query parameters. Therefore I must install a middleware to intercept HTTP requests after the API client prepares them.
The track I am currently on:
$apiClient->getHandlerStack()-push($myMiddleware)
The problem:
I cannot figure out the RIGHT way to assemble the functional Russian doll that $myMiddleware must be. This is an insane gazilliardth-order function scenario, and the exact right way the function should be written seems to be different from the extensively documented way of doing things when working with GuzzleHttp\Client directly. No matter what I try, I end up having wrong things passed to some layer of the matryoshka, causing an argument type error, or I end up returning something wrong from a layer, causing a type error in Guzzle code.
I made a carefully weighted decision to give up trying to understand. Please just give me a boilerplate solution for GuzzleHttp\Command\Guzzle\GuzzleClient, as opposed to GuzzleHttp\Client.
The HandlerStack that is used to handle middleware in GuzzleHttp\Command\Guzzle\GuzzleClient can either transform/validate a command before it is serialized or handle the result after it comes back. If you want to modify the command after it has been turned into a request, but before it is actually sent, then you'd use the same method of Middleware as if you weren't using GuzzleClient - create and attach middleware to the GuzzleHttp\Client instance that is passed as the first argument to GuzzleClient.
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Command\Guzzle\GuzzleClient;
use GuzzleHttp\Command\Guzzle\Description;
class MyCustomMiddleware
{
public function __invoke(callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
// ... do something with request
return $handler($request, $options);
}
}
}
$handlerStack = HandlerStack::create();
$handlerStack->push(new MyCustomMiddleware);
$config['handler'] = $handlerStack;
$apiClient = new GuzzleClient(new Client($config), new Description(...));
The boilerplate solution for GuzzleClient is the same as for GuzzleHttp\Client because regardless of using Guzzle Services or not, your request-modifying middleware needs to go on GuzzleHttp\Client.
You can also use
$handler->push(Middleware::mapRequest(function(){...});
Of sorts to manipulate the request. I'm not 100% certain this is the thing you're looking for. But I assume you can add your extra parameter to the Request in there.
private function createAuthStack()
{
$stack = HandlerStack::create();
$stack->push(Middleware::mapRequest(function (RequestInterface $request) {
return $request->withHeader('Authorization', "Bearer " . $this->accessToken);
}));
return $stack;
}
More Examples here: https://hotexamples.com/examples/guzzlehttp/Middleware/mapRequest/php-middleware-maprequest-method-examples.html
I'm working in a section of a Perl module that creates a large CSV response. The server runs on Plack, on which I'm far from expert.
Currently I'm using something like this to send the response:
$res->content_type('text/csv');
my $body = '';
query_data (
parameters => \%query_parameters,
callback => sub {
my $row_object = shift;
$body .= $row_object->to_csv;
},
);
$res->body($body);
return $res->finalize;
However, that query_data function is not a fast one and retrieves a lot of records. In there, I'm just concatenating each row into $body and, after all rows are processed, sending the whole response.
I don't like this for two obvious reasons: First, it takes a lot of RAM until $body is destroyed. Second, the user sees no response activity until that method has finished working and actually sends the response with $res->body($body).
I tried to find an answer to this in the documentation without finding what I need.
I also tried calling $res->body($row_object->to_csv) on my callback section, but seems like that ends up sending only the last call I made to $res->body, overriding all previous ones.
Is there a way to send a Plack response that flushes the content on each row, so the user starts receiving content in real time as the data is gathered and without having to accumulate all data into a veriable first?
Thanks in advance for any comments!
You can't use Plack::Response because that class is intended for representing a complete response, and you'll never have a complete response in memory at one time. What you're trying to do is called streaming, and PSGI supports it even if Plack::Response doesn't.
Here's how you might go about implementing it (adapted from your sample code):
my $env = shift;
if (!$env->{'psgi.streaming'}) {
# do something else...
}
# Immediately start the response and stream the content.
return sub {
my $responder = shift;
my $writer = $responder->([200, ['Content-Type' => 'text/csv']]);
query_data(
parameters => \%query_parameters,
callback => sub {
my $row_object = shift;
$writer->write($row_object->to_csv);
# TODO: Need to call $writer->close() when there is no more data.
},
);
};
Some interesting things about this code:
Instead of returning a Plack::Response object, you can return a sub. This subroutine will be called some time later to get the actual response. PSGI supports this to allow for so-called "delayed" responses.
The subroutine we return gets an argument that is a coderef (in this case, $responder) that should be called and passed the real response. If the real response does not include the "body" (i.e. what is normally the 3rd element of the arrayref), then $responder will return an object that we can write the body to. PSGI supports this to allow for streaming responses.
The $writer object has two methods, write and close which both do exactly as their names suggest. Don't forget to call the close method to complete the response; the above code doesn't show this because how it should be called is dependent on how query_data and your other code works.
Most servers support streaming like this. You can check $env->{'psgi.streaming'} to be sure that yours does.
Plack is middleware. Are you using a web application framework on top of it, like Mojolicious or Dancer2, or something like Apache or Starman server below it? That would affect how the buffering works.
The link above shows an example by Plack's author:
https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/echo-stream-sync.psgi
Or you can do it easily by using Dancer2 on top of Plack and Starman or Apache:
https://metacpan.org/pod/distribution/Dancer2/lib/Dancer2/Manual.pod#Delayed-responses-Async-Streaming
Regards, Peter
Some reading material for you :)
https://metacpan.org/pod/PSGI#Delayed-Response-and-Streaming-Body
https://metacpan.org/pod/Plack::Middleware::BufferedStreaming
https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/echo-stream.psgi
https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/nonblock-hello.psgi
So copy/paste/adapt and report back please
I want the same Controller routines to serve both regular web-based page loads as well as REST calls. I have REST handling set-up in my routes.php:
// Setup REST Handling
Router::mapResources( '<ControllerName>' );
Router::parseExtensions();
Let's take for example, the add() method - how do I distinguish inside this method how the call is being made?
To elaborate on the issue:
public function add() {
$status = array();
if( $this->request->is( 'post' ) ) {
// Read POST body
$entityBody = file_get_contents( 'php://input' );
}
}
As you can see in the above code, I'm detecting POST requests and will deal with the request accordingly. What I need to figure is whether the post data is coming from a REST call of from a web-form. If the data comes from a web-form, it'll be in the request->data array whereas for a REST call, it'll be in XML form.
I'll deal with the data accordingly and dish out an appropriate response.
Thanks.
Use the CakeRequest object, see the documentation, works the same as with post.
$this->request->is('put');
is('get') Check to see if the current request is a GET.
is('put') Check to see if the current request is a PUT.
is('post') Check to see if the current request is a POST.
is('delete') Check to see if the current request is a DELETE.
I am trying to get the output data from a package_state in my IRC bot, which uses POE::Component::IRC as a base. But I just cannot seem to do it.
Basically, in a subroutine outside of the POE session, I wish to get the data from an event subroutine fired by POE when it receives the data from the server.
I've tried saving the data in a global array and even external file, but the outer subroutine will read the old data from it before that data gets updated.
More specifically, I am trying to get this bot to check if someone is 'ison' and if they are, return true (or get all data ( #_ ) from irc_303).
Something like this:
sub check_ison {
my $who = "someguy";
$irc->yield(ison => $who);
$data = (somehow retrieve data from irc_303);
return $data; #or true if $data
}
It sounds like you want a synchronous solution to an asynchronous problem. Due to the asynchronous nature of IRC (and POE, for that matter ...), you'll need to issue your ISON query and handle the numeric response as it comes in.
As far as I know, most client NOTIFY implementations issue an ISON periodically (POE::Component::IRC provides timer sugar via POE::Component::Syndicator), update their state, and tell the user if something changes.
You have options...
You could issue ISONs on a timer, save state appropriately in your numeric response handler, and provide a method to query the state. If your application looks more like a client (the user/something needs to be notified when something changes, that is) your numeric response handler could do some basic list comparison and issue appropriate events for users appearing/disappearing.
Otherwise, you could simply have a 'check_ison' that issues the ISON and yields some sort of 'response received' event from the numeric response handler, letting you know fresh data is available.
How can I write or update session data before a request ends in Perl MVC Catalyst Framework.
I am using Session::State::Cookie and Session::Store::FastMap
I need to ensure that the data is available before the long-running request completes
This is what worked for me.
To ensure the information is updated at the time it is set in the long running request, I do a $c->finalize_session just after updating some importante information related to the session:
$c->session->{important_info} = "new value";
$c->finalize_session;
I verified that the other requests are gathering the right value after that.
I did not observed any side effects calling $c->finalize_session many times during a request just to ensure the session data to be updated, but I am not certained about this.
One of the informations that I am setting in this way is a counter to update a progress bar to feedback the user (because this task takes a long time). I do not know if it is the best way to do that, I will appreciate any suggestion.
You can do some last-second processing just before a request is completed and the response sent to the client by overriding the handle_request method in your application's main module or a plugin.
sub handle_request {
my ($c, #args) = #_;
my $status = $c->next::method(#args);
# Do some last minute processing before the request is completed.
return $status;
}
I've overridden this method before to collect stats about a request or restart a worker process if it uses too much memory. Let me know if this is helpful or if you have more questions about it.