Send back response data while still processing
I wanted to implement a very simple HTTP server to query some large/slow datasets in our buildsystem. This means that generating the data for a GET request can take many seconds.
I noticed that the programming model with all Write-PodeXXXResponsefunctions - most collapsing to Write-PodeTextResponse as far as I can tell - is to collect all the data and then send back the headers and body in one go at the end of the route.
This is quite inconvenient in my case. I would have preferred to be able to send the response data incrementally.
A hypothetical API:
# Do some input validation
...
# Start fetching data
...
# Send response headers (it's fine to do this in one block)
Write-PodeHeaders ...
while (...) {
# Once data available
Write-PodeBodyTextPart $firstPartOfData
...
}
...
I know I could start fiddling with Response.Send() or the underlying request, but it seems this would be very flaky.
Am I maybe missing something?
Use https://badgerati.github.io/Pode/Tutorials/Routes/Utilities/SSE/ for that
Use https://badgerati.github.io/Pode/Tutorials/Routes/Utilities/SSE/ for that
Thanks for the tip.
I note at first glance:
For a request to be convertible, it must have an Accept HTTP request header value of text/event-stream
Did you mean to say SSE would be a drop-in, or rather that the use-case can be adapted to use SSE?
I responded a bit hastily. SSE is a web browser feature. If you need to feed a webpage with data, this is the most technically sound solution to use. However, if your request is not coming from a web page, other methods like a multipart stream can be employed.
Hmm ... isn't multipart/form-data an upload feature?
Hi @bilbothebaggins,
SSE would be the best case, but it depends on how where you're going to call the Route, and how you're going to handle the data as it's streamed back from the server:
- If this is for a website, SSE would work best as you can have the data be streamed back to the client and render it as it coming in.
- If this is an API to be called via CLI, then SSE would "work", but typically
Invoke-RestMethodwill just block until all data is received and the connection closed.
* If this is an API to be called via CLI, then SSE would "work", but typically `Invoke-RestMethod` will just block until all data is received and the connection closed.
It's a mixed bag ... it's an API call to populate a dynamic dropdown on a Jenkins CI Job Parameter. So technically it is an API call that I cannot influence much. But on the user side it is influencing load performance of the site.
Anyways ... since this was even slower than I anticipated, I had to implement some caching anyway.
I might as well outline this for future readers:
- Incoming GET requests on that API endpoint are hashed (json-ify the
$WebEvent.Queryand generate an MD5 hash) to uniquely identify each request. - If a previously generated data set is already here (as keyed by the hash)
- Asynchronously (
Start-ThreadJob) re-start the data generation - return the previous data immediately.
- Asynchronously (
- Otherwise wait for the data and synchronously return it.
A bit involved, but it worked fine for this endpoint.