Skip to content

Instantly share code, notes, and snippets.

@robbertvanginkel
Last active July 19, 2024 18:30
Show Gist options
  • Save robbertvanginkel/d9abba2526fc8cfe5eca700888ed27f3 to your computer and use it in GitHub Desktop.
Save robbertvanginkel/d9abba2526fc8cfe5eca700888ed27f3 to your computer and use it in GitHub Desktop.
gRPC on workers
// Turn on gRPC for domain using
// https://developers.cloudflare.com/support/network/understanding-cloudflare-grpc-support/#enable-grpc, then CF
// rewrites grpc requests to grpc-web (like a reverse envoy filter, see https://blog.cloudflare.com/road-to-grpc/#converting-to-http-1-1)
// which can be handled by workers. Does not work with workers.dev.
export default {
async fetch(request, env, context) {
// Use a stream so CF doesn't add content-lenght
// which would prevent grpc-web -> grpc conversion.
const { readable, writable } = new TransformStream();
let writer = writable.getWriter();
let enc = new TextEncoder();
let responseString = enc.encode(`request content type was ${request.headers.get('content-type')}`);
context.waitUntil(
writer.write(envelope(new Uint8Array([0x0a /* field 1 varint tag */, ...uVarInt(responseString.length), ...responseString])))
);
context.waitUntil(
new Promise(resolve => setTimeout(resolve, 5000)).then(() => {
// Note: no space between header and value, CF grpc-web -> grpc
// conversion inserts one (but really shouldn't).
writer.write(envelope(enc.encode("Grpc-Status:9\r\nGrpc-Message:ohoh\r\n"), true))
}).then(() => writer.close())
);
return new Response(readable, {
status: 200,
headers: new Headers({
'content-type': 'application/grpc-web',
})
})
},
}
// poor mans varint encoding for pb message
function uVarInt(value) {
let buf = [];
while (value > 0x7f) {
buf.push((value & 0x7f) | 0x80);
value = value >>> 7;
}
buf.push(value);
return buf;
}
// write a grpc-web envelope
function envelope(bytes, trailers = false) {
let size = Uint8Array.of(
(bytes.length & 0xff000000) >> 24,
(bytes.length & 0x00ff0000) >> 16,
(bytes.length & 0x0000ff00) >> 8,
(bytes.length & 0x000000ff) >> 0);
return new Uint8Array([trailers ? 0x80 : 0x00, ...size, ...bytes]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment