discord-api-docs icon indicating copy to clipboard operation
discord-api-docs copied to clipboard

Responding to an interaction received via webhook fails if the response is bigger than 150kb

Open timotejroiko opened this issue 1 year ago • 0 comments

Description

Responding to an interaction received via webhook directly with a multipart/form-data file upload causes "This interaction failed" when uploading a 160kb file, but works when uploading a 150kb file.

I have tested with multiple file types including images, text and binary, with and without content-length, changed keepalive timeout headers, boundary encoding...

I am unsure at which exact size the issue starts, but somewhere between 150kb and 160kb the responses start failing. I believe this is not caused by the 3 second rule, as that would show "This interaction did not respond" instead plus the 160kb test fails 100% of the time while the 150kb test works 99% of the time, which cant be explained by network/upload lag.

ignoring the webhook and calling the /callback endpoint instead works as expected. responding to the webhook with a type 5 (defer response) and then editing or following up also works as expected.

Steps to Reproduce

on interaction webhook received, i respond with the following content:

HTTP/1.1 200 OK
Content-Type: multipart/form-data; boundary=e944dac9857d54f659308490be19fe59
Date: Wed, 27 Jul 2022 12:29:35 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked

--e944dac9857d54f659308490be19fe59
Content-Disposition: form-data; name="files[0]"; filename="test.bin"
Content-Type: application/octet-stream

[bytes here]
--e944dac9857d54f659308490be19fe59
Content-Disposition: form-data; name="payload_json"
Content-Type: application/json

{"type":4,"data":{"embeds":[{"title":"hello","description":"world","color":11908533}]}}
--e944dac9857d54f659308490be19fe59--

if [bytes here] contains 150k bytes the response works as expected, however when i do the exact same response with 160k bytes, it instantly causes "this interaction failed".

if i do not respond to the webhook interaction and instead call the /callback endpoint then it works as expected with both 150kb and 160kb. example /callback call:

POST /api/v10/interactions/[interaction.id]/[interaction.token]/callback HTTP/1.1
Content-Type: multipart/form-data; boundary=151a12fd1c1d2d6f4aa844dc40e4a0be
Host: discord.com
Connection: keep-alive
Transfer-Encoding: chunked

--151a12fd1c1d2d6f4aa844dc40e4a0be
Content-Disposition: form-data; name="files[0]"; filename="test.bin"
Content-Type: application/octet-stream

[bytes here]
--151a12fd1c1d2d6f4aa844dc40e4a0be
Content-Disposition: form-data; name="payload_json"
Content-Type: application/json

{"type":4,"data":{"embeds":[{"title":"hello","description":"world","color":11908533}]}}
--151a12fd1c1d2d6f4aa844dc40e4a0be--

Expected Behavior

Allow larger files like the /callback endpoint (provided the server is capable of uploading them fast enough) or document that responses to webhook interactions have additional size limitations besides the 3 second rule

Current Behavior

responding to a webhook interaction with a 160kb file instantly causes "This interaction failed"

Screenshots/Videos

No response

Client and System Information

node 16.13 on repl.it (for the ssl) using core node modules http.Server for webhook interactions and https.request for the /callback test

sample server code excluding signature validation and ping handling:

onRequest(req, res) {
    req.on("end", () => {
        const boundary = randomBytes(16).toString("hex");
        const files = [{
            name: "test.bin",
            type: "application/octet-stream",
            data: Buffer.from("a".repeat(160000)) // 160000 fails, 150000 works
        }]
        const payload = {
            type: 4,
            data: {
                embeds: [{
                    title: "hello",
                    description: "world",
                    color: 11908533
                }]
            }
        }
        res.writeHead(200, { "Content-Type": `multipart/form-data; boundary=${boundary}` });
        for (let i = 0; i < files.length; i++) {
            const file = files[i];
            const type = typeof file.type === "string" ? `\r\nContent-Type: ${file.type}` : "";
            res.write(`\r\n--${boundary}\r\nContent-Disposition: form-data; name="files[${i}]"; filename="${file.name}"${type}\r\n\r\n`);
            res.write(file.data);
        }
        res.write(`\r\n--${boundary}\r\nContent-Disposition: form-data; name="payload_json"\r\nContent-Type: application/json\r\n\r\n${ JSON.stringify(payload)}`);
        res.end(`\r\n--${boundary}--`);
    });
}

timotejroiko avatar Jul 27 '22 14:07 timotejroiko

Sorry, we don't plan to update support for this anytime soon.

jdongdiscord avatar Aug 29 '22 18:08 jdongdiscord