(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler)
| 483 | } |
| 484 | |
| 485 | func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { |
| 486 | repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) |
| 487 | |
| 488 | // prepare the request for proxying; this is needed only once |
| 489 | clonedReq, err := h.prepareRequest(r, repl) |
| 490 | if err != nil { |
| 491 | return caddyhttp.Error(http.StatusInternalServerError, |
| 492 | fmt.Errorf("preparing request for upstream round-trip: %v", err)) |
| 493 | } |
| 494 | |
| 495 | // websocket over http2 or http3 if extended connect is enabled, assuming backend doesn't support this, the request will be modified to http1.1 upgrade |
| 496 | // Both use the same upgrade mechanism: server advertizes extended connect support, and client sends the pseudo header :protocol in a CONNECT request |
| 497 | // The quic-go http3 implementation also puts :protocol in r.Proto for CONNECT requests (quic-go/http3/headers.go@70-72,185,203) |
| 498 | // TODO: once we can reliably detect backend support this, it can be removed for those backends |
| 499 | if (r.ProtoMajor == 2 && r.Method == http.MethodConnect && r.Header.Get(":protocol") == "websocket") || |
| 500 | (r.ProtoMajor == 3 && r.Method == http.MethodConnect && r.Proto == "websocket") { |
| 501 | clonedReq.Header.Del(":protocol") |
| 502 | // keep the body for later use. http1.1 upgrade uses http.NoBody |
| 503 | caddyhttp.SetVar(clonedReq.Context(), "extended_connect_websocket_body", clonedReq.Body) |
| 504 | clonedReq.Body = http.NoBody |
| 505 | clonedReq.Method = http.MethodGet |
| 506 | clonedReq.Header.Set("Upgrade", "websocket") |
| 507 | clonedReq.Header.Set("Connection", "Upgrade") |
| 508 | key := make([]byte, 16) |
| 509 | _, randErr := rand.Read(key) |
| 510 | if randErr != nil { |
| 511 | return randErr |
| 512 | } |
| 513 | clonedReq.Header["Sec-WebSocket-Key"] = []string{base64.StdEncoding.EncodeToString(key)} |
| 514 | } |
| 515 | |
| 516 | // we will need the original headers and Host value if |
| 517 | // header operations are configured; this is so that each |
| 518 | // retry can apply the modifications, because placeholders |
| 519 | // may be used which depend on the selected upstream for |
| 520 | // their values |
| 521 | reqHost := clonedReq.Host |
| 522 | reqHeader := clonedReq.Header |
| 523 | |
| 524 | // If the request contained a body, wrap it in io.NopCloser |
| 525 | // to prevent Go's transport from closing it on dial errors. |
| 526 | // cloneRequest does a shallow copy, so clonedReq.Body and |
| 527 | // r.Body share the same io.ReadCloser — a dial-failure Close() |
| 528 | // would kill the original body for all subsequent retry |
| 529 | // attempts or subsequent handlers. The real body is closed by |
| 530 | // the HTTP server when the handler returns. |
| 531 | // |
| 532 | // If the body was already fully buffered (via request_buffers), |
| 533 | // we also extract the buffer so the retry loop can replay it |
| 534 | // from the beginning on each attempt. (see #6259, #7546, #7713) |
| 535 | var bufferedReqBody *bytes.Buffer |
| 536 | if clonedReq.Body != nil { |
| 537 | if reqBodyBuf, ok := clonedReq.Body.(bodyReadCloser); ok && reqBodyBuf.body == nil && reqBodyBuf.buf != nil { |
| 538 | bufferedReqBody = reqBodyBuf.buf |
| 539 | reqBodyBuf.buf = nil |
| 540 | clonedReq.Body = io.NopCloser(bytes.NewReader(bufferedReqBody.Bytes())) |
| 541 | defer func() { |
| 542 | bufferedReqBody.Reset() |
nothing calls this directly
no test coverage detected