Symfony kernel.controller event send response - httprequest

as far as I read the Symfony-Documentation i can't find anything about the response handling in the kernel.controller event.
For the kernel.request event the documentation says:
If a Response is returned at this stage, the process skips directly to the kernel.response event.
But what about the kernel.controller event?
If I'm returning a response in the kernel.controller event listener the response is sent to the client but the process isn't canceled and the requested controller action is called.
Is it possible to send a response within the kernel.controller event without proceeding to the requested controller?

Indirectly, you can by changing the controller in the FilterControllerEvent.
You can use any callable as a controller, given that it returns a Response object in the end. In your event listener, you could for example do the following:
$event->setController(function() {
return new Response();
});
Of course you can return any kind of response in your controller.

Related

Ending a response inside custom error handler in expressjs the right way

The official documentation of expressjs at https://expressjs.com/en/guide/error-handling.html states the following:
Notice that when not calling “next” in an error-handling function, you are responsible for writing (and ending) the response. Otherwise those requests will “hang” and will not be eligible for garbage collection.
Their example for the last custom handler is as follows:
...
app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)
function errorHandler (err, req, res, next) {
res.status(500)
res.render('error', { error: err })
}
At this point I am not sure if this example actually ended the response.
Is it already ended when the last custom error handler calls .status() and .render() ?
The documentation further states
res.end([data] [, encoding])
Ends the response process. This method actually comes from Node core, specifically the response.end() method of http.ServerResponse.
Use to quickly end the response without any data. If you need to respond with data, instead use methods such as res.send() and res.json().
This implies to me, that e.g. send() will end the response.
In restify, which is built on express, the documentation states
Unlike other REST frameworks, calling res.send() does not trigger next() automatically. In many applications, work can continue to happen after res.send(), so flushing the response is not synonymous with completion of a request.
This is why unsure, whether just calling send(),json() or status is really enough to release those resources to garbage collection
I would have expected that we either have to
call next() with no parameter that (so that the default handler will eventually end it) or
res.end() to end manually
after we have written a response to the client by
res.status() or
res.status(...).res.send(...) or
res.send(...) or
What is the proper way to end the response in a custom error handler?
res.send internally calls res.end, but res.status only sets the HTTP status and does not call res.end. With res.render, it depends on the callback function. The default callback uses res.send to output the rendering (implying res.end), or next(err) in case of a rendering error.
So res.send, res.end and res.render (with the default callback) will end the response.

use HttpObjectAggregator conditionally

Netty version: 4.0.37
I have a requirement to have a netty server which handles both simple JSON requests and also large file uploads. HttpObjectAggregator has a limit of 2 GB for a request size, so I would prefer to use the HttpUploadServer example available here.
So, I want the pipeline to conditionally change depending on the type of request coming in. If it's a POST request, and it's a Multipart type of request, I want the request to be handled by the Upload handler and I want to skip all the rest of the handlers. If not, I want it to pass through the HttpObjectAggregator and then be handled by the Default handler.
I thought of creating one single pipeline looking like this:
HttpRequestDecoder
HttpContentDecompressor
FileUploadHandler <--- My handler to handle file uploads
HttpObjectAggregator
DefaultHandler <---- My handler to handle normal requests, without file body
And inside the "FileUploadHandler", I added the if else logic like this:
private boolean uploadURL(HttpObject object) {
HttpRequest request = (HttpRequest) object;
boolean isMultipart = HttpPostRequestDecoder.isMultipart(request);
if (request.getMethod().equals(HttpMethod.POST) && isMultipart) {
// To be handled by file upload handler
return true;
}
return false;
}
public void channelRead0(ChannelHandlerContext channelHandlerContext,
HttpObject object) throws Exception {
if (!uploadURL(object)) {
ReferenceCountUtil.retain(object);
channelHandlerContext.fireChannelRead(object);
} else {
// Handle the File Upload
....
My objective was to make the UploadHandler "pass on" the message to HttpObjectAggregator IF it's anything other than a POST Multipart request with file body. However, this isn't working for a GET request as the request times out after sometime for lack of a response.
I don't entirely understand why this is happening, but my guess is that HttpObjectAggregator is not receiving the initial HttpRequest object from my UploadHandler at all? And that in turn, isn't delivering it to the Default Handler either.
Is my approach wrong? Is there a different way of handling this conditional routing, outside of my Upload Handler?
Can I have any handler before HttpObjectAggregator or should all custom/user handlers come AFTER the HttpObjectAggregator?
I did this by using a Decoder before HttpObjectAggregator. The pipeline looks like:
HttpRequestDecoder
HttpContentDecompressor
RequestURLDecoder <--- New decoder to route requests.
FileUploadHandler <--- My handler to handle file uploads
HttpObjectAggregator
DefaultHandler <---- My handler to handle normal requests, without file body
The new decoder looks at the request and if it's a POST multipart, dynamically modifies the pipeline to remove the Object aggregator and the default handler. If it's not, then it removes the file upload handler.
(list.add(ReferenceCountUtil.retain(object)) is very important!)

Do I need to call next() after res.send()?

If I called res.send(), but didn't call next():
Any middleware after this middleware won't be executed.
If this middleware is not the last middleware, then the request will "hang" forever, and will not be garbage collected, because It is still waiting for the next() to be called.
The above is my attempt to argue that I should always call next(), is it true?
You don't need to call next() to finish sending the response. res.send() or res.json() should end all writing to the response stream and send the response.
However, you absolutely can call next() if you want to do further processing after the response is sent, just make sure you don't write to the response stream after you call res.send().
Simply
All the middlewares use same request and response objects, so if you send the response via any middleware all next middlewares will be skipped
You can still execute further operations by calling next(); but you can't do res.send() or res.json() further

Prevent getting old updates from Telegram Bot API using a web hook

I'm writing a Telegram bot and I'm using the official bot API. I've got a webhook server that handles requests and sends a 200 OK response for every request.
Before the server stops, the webhook is detached so Telegram does not send updates anymore. However, whenever I turn the bot on and set the webhook URL again, Telegram starts flooding the webhook server with old updates.
Is there any way I can prevent this without requesting /getUpdates repeatedly until I reach the last update?
Here's a heavily simplified version of how my code looks like:
var http = require('http'),
unirest = require('unirest'),
token = '***';
// Attach the webhook
unirest.post('https://api.telegram.org/bot' + token + '/setWebhook')
.field('url', 'https://example.com/api/update')
.end();
process.on('exit', function() {
// Detach the webhook
unirest.post('https://api.telegram.org/bot' + token + '/setWebhook')
.field('url', '')
.end();
});
// Handle requests
var server = http.createServer(function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('Thanks!');
});
server.listen(80);
Thanks in advance.
The best way is to use update_id which is a specific number that increases on every new request (i.e. update). How to implement it?
First off, let's start with the following anonymous class (using PHP7):
$lastUpdateId = new class()
{
const FILE_PATH = "last-update-id.txt";
private $value = 1;
public function __construct()
{
$this->ensureFileExists();
$this->value = filesize(self::FILE_PATH) == 0
? 0 : (int)(file_get_contents(self::FILE_PATH));
}
public function set(int $lastUpdateId)
{
$this->ensureFileExists();
file_put_contents(self::FILE_PATH, $lastUpdateId);
$this->value = $lastUpdateId;
}
public function get(): int
{
return $this->value;
}
public function isNewRequest(int $updateId): bool
{
return $updateId > $this->value;
}
private function ensureFileExists()
{
if (!file_exists(self::FILE_PATH)) {
touch(self::FILE_PATH);
}
}
};
What the class does is clear: Handling the last update_id via a plain file.
Note: The class is tried to be as short as possible. It does not provide error-checking. Use your custom implementation (e.g. use SplFileObject instead of file_{get|put}_contents() functions) instead.
Now, there are two methods of getting updates: Long Polling xor WebHooks (check Telegram bot API for more details on each methods and all JSON properties). The above code (or similar) should be used in both cases.
Note: Currently, it is impossible to use both methods at the same time.
Long Polling Method (default)
This way, you send HTTPS requests to Telegram bot API, and you'd get updates as response in a JSON-formatted object. So, the following work can be done to get new updates (API, why using offset):
$botToken = "<token>";
$updates = json_decode(file_get_contents("https://api.telegram.org/bot{$botToken}/getUpdates?offset={$lastUpdateId->get()}"), true);
// Split updates from each other in $updates
// It is considered that one sample update is stored in $update
// See the section below
parseUpdate($update);
WebHook Method (preferred)
Requiring support for HTTPS POST method from your server, the best way of getting updates at-the-moment.
Initially, you must enable WebHooks for your bot, using the following request (more details):
https://api.telegram.org/bot<token>/setWebhook?url=<file>
Replace <token> with you bot token, and <file> with the address of your file which is going to accept new requests. Again, it must be HTTPS.
OK, the last step is creating your file at the specified URL:
// The update is sent
$update = $_POST;
// See the section below
parseUpdate($update);
From now, all requests and updates your bot will be directly sent to the file.
Implementation of parseUpdate()
Its implementation is totally up to you. However, to show how to use the class above in the implementation, this is a sample and short implementation for it:
function parseUpdate($update)
{
// Validate $update, first
// Actually, you should have a validation class for it
// Here, we suppose that: $update["update_id"] !== null
if ($lastUpdateId->isNewRequest($update["update_id"])) {
$lastUpdateId->set($update["update_id"]);
// New request, go on
} else {
// Old request (or possible file error)
// You may throw exceptions here
}
}
Enjoy!
Edit: Thanks to #Amir for suggesting editions made this answer more complete and useful.
When you server starts up you can record the timestamp and then use this to compare against incoming message date values. If the date is >= the timestamp when you started...the message is ok to be processed.
I am not sure if there is a way you can tell Telegram you are only interested in new updates, their retry mechanism is a feature so that messages aren't missed...even if your bot is offline.
In the webhook mode, Telegram servers send updates every minute until receives an OK response from the webhook program.
so I recommend these steps:
Check your webhook program that you specified its address as url parameter of the setWebhook method. Call its address in a browser. It does not produce an output to view, but clears that probably there is no error in your program.
Include a command that produces a '200 OK Status' header output in your program to assure that the program sends this header to the Telegram server.
I have the same issue, then I tried to reset the default webhook with
https://api.telegram.org/bot[mybotuniqueID]/setWebhook?url=
after that, i verified the current getUpdates query were the same old updates but I sent new requests through the telegram's bot chat
https://api.telegram.org/bot[mybotuniqueID]/getUpdates
when I set up my webhook again the webhook read the same old updates. Maybe the getUpdates method is not refreshing the JSON content.
NOTE:
in my case, it was working fine until I decided to change /set privacy bot settings from botfather

Pass response body from IDispatchMessageInspector to Application_EndRequest

I want to log the request parameters and response body that traffics thru my WCF REST service . I can access full response in IDispatchMessageInspector. And I can access request headers and other items that I store in Context.Items during the operations in Application_EndRequest.
During my debugging, I see the operations goes thru IDispatchMessageInspector and then thru Application_EndRequest. My idea is to store the response somewhere in IDispatchMessageInspector and then in Application_EndRequest, I'll retrieve the response and log it together with other request parameters.
So my question is: Where should I store the response so it's accessible in Application_EndRequest?
I'm currently trying to do something similar. I am logging an incoming request, storing it in the database and would then like to pass the log ID to my endpoint for use later. In your AfterReceiveRequest call, simply add whatever you need to the IncomingMessageProperties attribute of the current operationcontext:
Edit: Fixed the code below
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
int LogRequestID = Logging.LogIncomingRequest(buffer.CreateMessage());
OperationContext.Current.IncomingMessageProperties.Add("LogRequestID", LogRequestID);
return null;
}
I can then read the LogRequestID in my endpoint with the following code:
OperationContext.Current.IncomingMessageProperties["LogRequestID"]
You can also pass a more complex if you need to. Hope that helps.