proxy.go 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build go1.19
  4. // HTTP proxy code
  5. package main
  6. import (
  7. "context"
  8. "io"
  9. "net"
  10. "net/http"
  11. "net/http/httputil"
  12. "strings"
  13. )
  14. // httpProxyHandler returns an HTTP proxy http.Handler using the
  15. // provided backend dialer.
  16. func httpProxyHandler(dialer func(ctx context.Context, netw, addr string) (net.Conn, error)) http.Handler {
  17. rp := &httputil.ReverseProxy{
  18. Director: func(r *http.Request) {}, // no change
  19. Transport: &http.Transport{
  20. DialContext: dialer,
  21. },
  22. }
  23. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  24. if r.Method != "CONNECT" {
  25. backURL := r.RequestURI
  26. if strings.HasPrefix(backURL, "/") || backURL == "*" {
  27. http.Error(w, "bogus RequestURI; must be absolute URL or CONNECT", 400)
  28. return
  29. }
  30. rp.ServeHTTP(w, r)
  31. return
  32. }
  33. // CONNECT support:
  34. dst := r.RequestURI
  35. c, err := dialer(r.Context(), "tcp", dst)
  36. if err != nil {
  37. w.Header().Set("Tailscale-Connect-Error", err.Error())
  38. http.Error(w, err.Error(), 500)
  39. return
  40. }
  41. defer c.Close()
  42. cc, ccbuf, err := w.(http.Hijacker).Hijack()
  43. if err != nil {
  44. http.Error(w, err.Error(), 500)
  45. return
  46. }
  47. defer cc.Close()
  48. io.WriteString(cc, "HTTP/1.1 200 OK\r\n\r\n")
  49. var clientSrc io.Reader = ccbuf
  50. if ccbuf.Reader.Buffered() == 0 {
  51. // In the common case (with no
  52. // buffered data), read directly from
  53. // the underlying client connection to
  54. // save some memory, letting the
  55. // bufio.Reader/Writer get GC'ed.
  56. clientSrc = cc
  57. }
  58. errc := make(chan error, 1)
  59. go func() {
  60. _, err := io.Copy(cc, c)
  61. errc <- err
  62. }()
  63. go func() {
  64. _, err := io.Copy(c, clientSrc)
  65. errc <- err
  66. }()
  67. <-errc
  68. })
  69. }