oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
73.61k stars 2.72k forks source link

Bun.serve HTTP timeout is 10s #13392

Closed mmvsk closed 1 month ago

mmvsk commented 1 month ago

What version of Bun is running?

1.1.24+85a329911

What platform is your computer?

Linux 6.10.5-arch1-1 x86_64 unknown

What steps can reproduce the bug?

Serving HTTP over TLS, with a response being sent more than 10s after the request.

Bun.serve({
    tls: {
        cert: Bun.file(`${import.meta.dir}/cert.pem`),
        key: Bun.file(`${import.meta.dir}/cert.key`),
    },
    async fetch() {
        /* more than the SSL handshake timeout ~10s */
        await Bun.sleep(12_000);
        return new Response("OK\n");
    },
});

Call:

curl -v https://localhost:3000/

What is the expected behavior?

(using node): OK

* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/1.1 200 OK
< content-type: text/plain;charset=utf-8
< Date: Sun, 18 Aug 2024 17:49:01 GMT
< Content-Length: 3
<
OK
* Connection #0 to host localhost left intact

What do you see instead?

(using bun): curl: (52) Empty reply from server

* Request completely sent off
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS alert, close notify (256):
* Empty reply from server
* shutting down connection #0
* TLSv1.3 (OUT), TLS alert, close notify (256):
curl: (52) Empty reply from server

Additional information

No response

cirospaciari commented 1 month ago

the default timeout for uWS (used internally) is 10s if you take more than 10s to send another chunk of data the request will timeout. The solution is adding a option for longer timeouts.


Bun.serve({
  tls,
  port: 8080,
  async fetch() {
    return new Response(
      new ReadableStream(
        {
          async pull(controller) {
            controller.enqueue(new TextEncoder().encode("Hello, "));
            await Bun.sleep(12_000);
            controller.enqueue(new TextEncoder().encode("world!"));
            controller.close();
          },
        },
        { type: "text/plain" }
      )
    );
  },
});
 curl -v -k https://localhost:8080
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:8080...
* Connected to localhost (::1) port 8080
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: C=US; ST=CA; L=San Francisco; O=Oven; OU=Team Bun; CN=server-bun
*  start date: Sep  6 23:27:34 2023 GMT
*  expire date: Sep  5 23:27:34 2025 GMT
*  issuer: C=US; ST=CA; L=San Francisco; O=Oven; OU=Team Bun; CN=server-bun
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.6.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Wed, 21 Aug 2024 18:30:07 GMT
< Transfer-Encoding: chunked
< 
* transfer closed with outstanding read data remaining
* Closing connection
curl: (18) transfer closed with outstanding read data remaining
Hello, ⏎       

https://github.com/oven-sh/bun/blob/fe62a614046948ebba260bed87db96287e67921f/packages/bun-uws/src/HttpResponse.h#L44

cirospaciari commented 1 month ago

Now you should be able to increase the timeout if you wish:

Bun.serve({
  tls,
  port: 8080,
  idleTimeout: 120, //timeout in seconds
  async fetch() {
    return new Response(
      new ReadableStream(
        {
          async pull(controller) {
            controller.enqueue(new TextEncoder().encode("Hello, "));
            await Bun.sleep(12_000);
            controller.enqueue(new TextEncoder().encode("world!"));
            controller.close();
          },
        },
        { type: "text/plain" }
      )
    );
  },
});
codehz commented 1 month ago

@cirospaciari is it possible to disable this feature for some request? like Server-Sent-Event I want to make it keep until client decided to close the connection