Last active
May 11, 2022 18:47
-
-
Save drewwiens/41bda35616817849b7ac683fc05a03fc to your computer and use it in GitHub Desktop.
Work around Azure App Gateway timeout by returning a response as a stream that sends newline character periodically and then the response JSON. The response body to the client is a bunch of newlines followed by the JSON, which is still valid JSON. Only use if you for some reason can't get the gateway timeout limit increased.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { finalize, firstValueFrom, timeout } from 'rxjs'; | |
const KEEP_ALIVE_INTERVAL = 3 * 1000; | |
const KEEP_ALIVE_TIMEOUT = 2 * 60 * 1000; | |
export interface ErrorInHttpResponse { | |
errorMsg: string; | |
status: number; | |
name?: string; | |
} | |
// app is the ExpressJS app object. You have to create it elsewhere. | |
app.post('my-endpoint', (_req, res) => { | |
res.writeHead(200); | |
// Send something periodically to prevent Azure App Gateway from terminating | |
// the request with 504 Gateway Time-out after 20 seconds. Extra whitespace | |
// before JSON doesn't change the parsed result, and newline was chosen over | |
// space because it is printed immediately by cURL when testing. This uses | |
// chunked transfer encoding which is built into NodeJS and browsers. | |
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding | |
// https://nodejs.org/api/http.html#responsewritechunk-encoding-callback | |
const handle = setInterval(() => res.write('\n'), KEEP_ALIVE_INTERVAL); | |
let result: unknown[] | ErrorInHttpResponse; | |
try { | |
result = await firstValueFrom( | |
someSlowObservable$.pipe( | |
timeout(KEEP_ALIVE_TIMEOUT), | |
finalize(() => clearInterval(handle)), | |
), | |
); | |
} catch (e) { | |
// Cannot change the HTTP response code since the stream already started | |
// as 200 OK, so put any error inside the response body: | |
if (e instanceof Error) { | |
result = { errorMsg: e.message, status: 500, name: e.name }; | |
} else { | |
result = { errorMsg: String(e), status: 500 }; | |
} | |
} | |
res.write(JSON.stringify(result)); | |
res.end(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment