Closed
Description
Repro steps
-
Using programming model v4, create an HTTP-triggered function where:
- The handler function provides a
ReadableStream
in the return object'sbody
field. - The
ReadableStream
enqueues data with a time delay. - The
ReadableStream
continues to enqueue data after the handler function has returned. - While enqueuing data,
context.log()
gets called.
- The handler function provides a
-
Call the function via HTTP.
(I recommend to check the source code below; I guess it's more telling than my description here.)
Expected behavior
When the function is called:
- When new data is enqueued, it should directly be sent to the HTTP client as a chunk (using
Transfer-Encoding: chunked
). - The
context.log()
calls should log "as usual" and should not raise warnings/errors.
Actual behavior
- While
Transfer-Encoding: chunked
is set in the HTTP response, newly enqueued data is not directly sent when it becomes available. Instead, the completion of the stream is awaited before any data is sent. All data is then sent in a single chunk. - While the
context.log()
calls (that happen when data is enqueued after the handler function has returned) do output the log message, they also output a warning:Unexpected call to 'log' on the context object after function execution has completed. Please check for asynchronous calls that are not awaited. Function name: streamingTest. Invocation Id: [removed].
Known workarounds
n/a
Related information
- Programming language used: TypeScript
Source
import { ReadableStream } from "stream/web";
import { HttpRequest, HttpResponse, InvocationContext, app } from "@azure/functions";
// Test function that returns a ReadableStream as body.
export async function streamingTest(request: HttpRequest, context: InvocationContext): Promise<HttpResponse> {
context.log(`Http function processed request for url "${request.url}"`);
const encoder = new TextEncoder();
let times = 0;
const stream = new ReadableStream({
type: 'bytes',
async start(controller) {
function onTimer() {
if (times >= 1000) {
controller.close();
return;
}
context.log(`${times}`);
// This raises a warning:
// Unexpected call to 'log' on the context object after function execution
// has completed. Please check for asynchronous calls that are not awaited.
// Function name: streamingTest. Invocation Id: [removed].
times++;
const data = encoder.encode(`${times}\r\n`);
controller.enqueue(data);
setTimeout(onTimer, 10);
}
onTimer();
}
}, {
highWaterMark: 1
});
return new HttpResponse({
status: 200,
headers: {
"Content-Type": "text/plain"
},
body: stream
});
};
app.http('streamingTest', {
methods: ['GET'],
authLevel: 'anonymous',
handler: streamingTest
});