Last active
April 12, 2025 15:18
-
-
Save robbertvanginkel/d9abba2526fc8cfe5eca700888ed27f3 to your computer and use it in GitHub Desktop.
gRPC on workers
This file contains hidden or 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
// 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
Will this work if my worker is using the
workers.dev
domain?