Slide 1

Slide 1 text

ASYNCHRONOUS NETWORKING GOPHERCON, DENVER — 28 AUGUST 2018 Filippo Valsorda Google @FiloSottile

Slide 2

Slide 2 text

Threading servers Thread Request 2 Request 1 Thread Thread Request 3

Slide 3

Slide 3 text

The event loop Request 1 Thread Request 1 Request 2 Request 3 Request 3 Request 2

Slide 4

Slide 4 text

The event loop Request 1 Thread Request 1 Request 2 Request 3 Request 3 Pop from event queue Monitor for events Request 2

Slide 5

Slide 5 text

Goroutines go Request 2 Request 1 go Request 1 go Request 3 Block Block Request 3 Request 2 Block

Slide 6

Slide 6 text

Accepting connections SECTION ONE

Slide 7

Slide 7 text

Let's write code!

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

ACCEPTING CONNECTIONS — RECAP 01. ACCEPTING CONNECTIONS l, err := net.Listen("tcp", "localhost:4242") if err != nil { log.Fatal(err) } Open a net.Listener. Never block in the Accept loop. for { conn, err := l.Accept() if err != nil { log.Fatal(err) } go serviceConn(conn) } Call Accept in a loop, and start new goroutines for each Conn.

Slide 10

Slide 10 text

A simple proxy SECTION TWO

Slide 11

Slide 11 text

Proxying a connection 02. A SIMPLE PROXY Proxy gophercon.com:443 localhost:4242 Browser

Slide 12

Slide 12 text

"splice(2) moves data between two file descriptors without copying between kernel address space and user address space."

Slide 13

Slide 13 text

A SIMPLE PROXY — RECAP 02. A SIMPLE PROXY upstream, err := net.Dial("tcp", addr) if err != nil { log.Println(err) return } defer upstream.Close() Connect to upstream. go io.Copy(upstream, conn) _, err = io.Copy(conn, upstream) Copy both directions. Close cancels both io.Copy.

Slide 14

Slide 14 text

Parsing TLS SECTION THREE

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

PARSING A TLS CLIENT HELLO — RECAP 03. PARSING TLS conn.SetDeadline(time.Now().Add(30 * time.Second)) var buf bytes.Buffer if _, err := io.CopyN(&buf, conn, 1+2+2); err != nil { return } length := binary.BigEndian.Uint16(buf.Bytes()[3:5]) if _, err := io.CopyN(&buf, conn, int64(length)); err != nil { return } Set a timeout and
 read the message. ch, ok := ParseClientHello(buf.Bytes()) if ok { log.Printf("Received connection for %q!", ch.SNI) } Parse TLS.

Slide 20

Slide 20 text

Pre-read connection 03. PARSING TLS Client Hello Already Read() up to here rest of the connection Would only proxy this part In bytes.Buffer

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

PROXYING A PRE-READ CONNECTION — RECAP 03. PARSING TLS type prefixConn struct { io.Reader net.Conn } func (c prefixConn) Read(b []byte) (int, error) { return c.Reader.Read(b) } Make a net.Conn wrapper. proxyConn(prefixConn{ Reader: io.MultiReader(&buf, conn), Conn: conn, }, "gophercon.com:https") Use MultiReader.

Slide 23

Slide 23 text

Serving TLS SECTION FOUR

Slide 24

Slide 24 text

! ! tls.Conn is a net.Conn wrapper 04. SERVING TLS

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

SERVING A TLS CONNECTION — RECAP 04. SERVING TLS c := tls.Server(prefixConn{ Reader: io.MultiReader(&buf, conn), Conn: conn, }, config) Wrap the net.Conn with tls.Server. proxyConn(c, "gophercon.com:http") Service the wrapped plaintext connection. tls.Conn is a net.Conn wrapper. It takes care of handshake and de/encryption.

Slide 27

Slide 27 text

Thank you! https://git.io/fAYGy Filippo Valsorda Google @FiloSottile