For a few months this year, a lot of your Internet requests might have gone through Go. That's because at Cloudflare we picked crypto/tls to build our TLS 1.3 implementation.
case recordTypeAlert: c.in.setErrorLocked(&net.OpError{ Op: "remote error", Err: alert(data[1])}) case recordTypeApplicationData: c.input = b b = nil case recordTypeHandshake: c.hand.Write(data) }
switch data[0] { case typeClientHello: m = new(clientHelloMsg) case typeServerHello: m = new(serverHelloMsg) case typeNewSessionTicket: m = new(newSessionTicketMsg) case typeCertificate: m = new(certificateMsg) ... m.unmarshal(data)
actually know which part exactly is 0-RTT, but you know if what you read so far is safe, and can wait otherwise. (Sadly, it must buffer in the slow path.)
crypto, you currently need assembly implementations. On amd64: • crypto/elliptic.P256 https://blog.cloudflare.com/go-crypto-bridging-the-performance-gap/ • golang.org/x/crypto/curve25519 • crypto/aes (especially fast with GCM) • golang.org/x/crypto/chacha20poly1305
Go's default ciphersuite preferences, // which are tuned to avoid attacks. Does nothing on clients. PreferServerCipherSuites: true, // Only use curves which have assembly implementations CurvePreferences: []tls.CurveID{ tls.CurveP256, tls.X25519, // Go 1.8 only }, }
d := c.server.ReadTimeout; d != 0 { c.rwc.SetReadDeadline(time.Now().Add(d)) } if d := c.server.WriteTimeout; d != 0 { defer func() { c.rwc.SetWriteDeadline(time.Now().Add(d)) }() }
1.9. • fixed ReadTimeout never resetting in H/2 #16450 • neutered WriteTimeout never resetting in H/2 #18437 (reintroduced and fixed in Go 1.9) • introduced a proper IdleTimeout #14204
Not a deadline for completion, just a requirement for the other party to answer the ping • Useless against malicious DoS (“slowloris”) • Enabled only by ListenAndServe[TLS], hard-coded at 3m What about TCP keep-alives?
[...] if tlsConn, ok := c.rwc.(*tls.Conn); ok { if d := c.server.ReadTimeout; d != 0 { c.rwc.SetReadDeadline(time.Now().Add(d)) } if d := c.server.WriteTimeout; d != 0 { c.rwc.SetWriteDeadline(time.Now().Add(d)) } First, it sets the timeouts.
[...] if tlsConn, ok := c.rwc.(*tls.Conn); ok { // [...] if err := tlsConn.Handshake(); err != nil { c.server.logf("http: TLS handshake error from %s: %v”, c.rwc.RemoteAddr(), err) return } Then, it runs Handshake and logs the error if any. … so, how do we drop a connection? Well…
[...] if tlsConn, ok := c.rwc.(*tls.Conn); ok { // [...] if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) { if fn := c.server.TLSNextProto[proto]; fn != nil { h := initNPNRequest{tlsConn, serverHandler{c.server}} fn(c.server, tlsConn, h) } return } And finally it invokes the HTTP/2 handler!
is set and ListenAndServeTLS is used or tls.Config.NextProtos includes "h2" Server.TLSNextProto is automatically set (and used if the connection is HTTPS and the client offered H/2). When is HTTP/2 auto-enabled?
used a modified httputil.ReverseProxy: • Dial overridden to always open a connection to nginx • A sizeable pool of connections to nginx • Websockets • H/2 Push • 0-RTT with confirmation httputil.ReverseProxy
i, ph := range derivePushHints(linkHeader) { po := &http.PushOptions{ Header: make(http.Header) } for _, k := range pushPromiseHeaders { v := res.Request.Header.Get(k) if v != "" { po.Header.Set(k, v) } } pu.Push(ph.uri, po) }