mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-01 22:15:51 +00:00
15fc6cd966
The netcheck client, when no UDP is available, probes distance using
HTTPS.
Several problems:
* It probes using /derp/latency-check.
* But cmd/derper serves the handler at /derp/probe
* Despite the difference, it work by accident until c8f4dfc8c0
which made netcheck's probe require a 2xx status code.
* in tests, we only use derphttp.Handler, so the cmd/derper-installed
mux routes aren't preesnt, so there's no probe. That breaks
tests in airplane mode. netcheck.Client then reports "unexpected
HTTP status 426" (Upgrade Required)
This makes derp handle both /derp/probe and /derp/latency-check
equivalently, and in both cmd/derper and derphttp.Handler standalone
modes.
I notice this when wgengine/magicsock TestActiveDiscovery was failing
in airplane mode (no wifi). It still doesn't pass, but it gets
further.
Fixes #11989
Change-Id: I45213d4bd137e0f29aac8bd4a9ac92091065113f
82 lines
2.2 KiB
Go
82 lines
2.2 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package derphttp
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"tailscale.com/derp"
|
|
)
|
|
|
|
// fastStartHeader is the header (with value "1") that signals to the HTTP
|
|
// server that the DERP HTTP client does not want the HTTP 101 response
|
|
// headers and it will begin writing & reading the DERP protocol immediately
|
|
// following its HTTP request.
|
|
const fastStartHeader = "Derp-Fast-Start"
|
|
|
|
func Handler(s *derp.Server) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// These are installed both here and in cmd/derper. The check here
|
|
// catches both cmd/derper run with DERP disabled (STUN only mode) as
|
|
// well as DERP being run in tests with derphttp.Handler directly,
|
|
// as netcheck still assumes this replies.
|
|
switch r.URL.Path {
|
|
case "/derp/probe", "/derp/latency-check":
|
|
ProbeHandler(w, r)
|
|
return
|
|
}
|
|
|
|
up := strings.ToLower(r.Header.Get("Upgrade"))
|
|
if up != "websocket" && up != "derp" {
|
|
if up != "" {
|
|
log.Printf("Weird upgrade: %q", up)
|
|
}
|
|
http.Error(w, "DERP requires connection upgrade", http.StatusUpgradeRequired)
|
|
return
|
|
}
|
|
|
|
fastStart := r.Header.Get(fastStartHeader) == "1"
|
|
|
|
h, ok := w.(http.Hijacker)
|
|
if !ok {
|
|
http.Error(w, "HTTP does not support general TCP support", 500)
|
|
return
|
|
}
|
|
|
|
netConn, conn, err := h.Hijack()
|
|
if err != nil {
|
|
log.Printf("Hijack failed: %v", err)
|
|
http.Error(w, "HTTP does not support general TCP support", 500)
|
|
return
|
|
}
|
|
|
|
if !fastStart {
|
|
pubKey := s.PublicKey()
|
|
fmt.Fprintf(conn, "HTTP/1.1 101 Switching Protocols\r\n"+
|
|
"Upgrade: DERP\r\n"+
|
|
"Connection: Upgrade\r\n"+
|
|
"Derp-Version: %v\r\n"+
|
|
"Derp-Public-Key: %s\r\n\r\n",
|
|
derp.ProtocolVersion,
|
|
pubKey.UntypedHexString())
|
|
}
|
|
|
|
s.Accept(r.Context(), netConn, conn, netConn.RemoteAddr().String())
|
|
})
|
|
}
|
|
|
|
// ProbeHandler is the endpoint that clients without UDP access (including js/wasm) hit to measure
|
|
// DERP latency, as a replacement for UDP STUN queries.
|
|
func ProbeHandler(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case "HEAD", "GET":
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
default:
|
|
http.Error(w, "bogus probe method", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|