MCPcopy
hub / github.com/caddyserver/caddy / ServeHTTP

Method ServeHTTP

modules/caddyhttp/reverseproxy/reverseproxy.go:485–597  ·  modules/caddyhttp/reverseproxy/reverseproxy.go::Handler.ServeHTTP
(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler)

Source from the content-addressed store, hash-verified

483}
484
485func (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()

Callers

nothing calls this directly

Calls 15

prepareRequestMethod · 0.95
proxyLoopIterationMethod · 0.95
ErrorFunction · 0.92
SetVarFunction · 0.92
statusErrorFunction · 0.85
DelMethod · 0.80
DurationMethod · 0.80
GetMethod · 0.65
ResetMethod · 0.65
ValueMethod · 0.45
SetMethod · 0.45
ReadMethod · 0.45

Tested by

no test coverage detected