| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- //go:build go1.19
- // HTTP proxy code
- package main
- import (
- "context"
- "io"
- "net"
- "net/http"
- "net/http/httputil"
- "strings"
- )
- // httpProxyHandler returns an HTTP proxy http.Handler using the
- // provided backend dialer.
- func httpProxyHandler(dialer func(ctx context.Context, netw, addr string) (net.Conn, error)) http.Handler {
- rp := &httputil.ReverseProxy{
- Director: func(r *http.Request) {}, // no change
- Transport: &http.Transport{
- DialContext: dialer,
- },
- }
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Method != "CONNECT" {
- backURL := r.RequestURI
- if strings.HasPrefix(backURL, "/") || backURL == "*" {
- http.Error(w, "bogus RequestURI; must be absolute URL or CONNECT", 400)
- return
- }
- rp.ServeHTTP(w, r)
- return
- }
- // CONNECT support:
- dst := r.RequestURI
- c, err := dialer(r.Context(), "tcp", dst)
- if err != nil {
- w.Header().Set("Tailscale-Connect-Error", err.Error())
- http.Error(w, err.Error(), 500)
- return
- }
- defer c.Close()
- cc, ccbuf, err := w.(http.Hijacker).Hijack()
- if err != nil {
- http.Error(w, err.Error(), 500)
- return
- }
- defer cc.Close()
- io.WriteString(cc, "HTTP/1.1 200 OK\r\n\r\n")
- var clientSrc io.Reader = ccbuf
- if ccbuf.Reader.Buffered() == 0 {
- // In the common case (with no
- // buffered data), read directly from
- // the underlying client connection to
- // save some memory, letting the
- // bufio.Reader/Writer get GC'ed.
- clientSrc = cc
- }
- errc := make(chan error, 1)
- go func() {
- _, err := io.Copy(cc, c)
- errc <- err
- }()
- go func() {
- _, err := io.Copy(c, clientSrc)
- errc <- err
- }()
- <-errc
- })
- }
|