Files
teleport/api/testhelpers/proxy.go
T
STeve (Xin) Huang e677aadb9f Fix an issue ALPN handshake test does not respect "HTTPS_PROXY" (#27583)
* Fix an issue ALPN handshake test does not respect "HTTPS_PROXY"

* address review comment

* remove simplehttsproxy

* Add context to IsALPNConnUpgradeRequired in ten thousand places

* add goc and dial with context
2023-06-13 15:30:48 +00:00

101 lines
2.6 KiB
Go

// Copyright 2023 Gravitational, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testhelpers
import (
"io"
"net"
"net/http"
"sync"
"time"
"github.com/gravitational/trace"
)
// ProxyHandler is a http.Handler that implements a simple HTTP proxy server.
type ProxyHandler struct {
sync.Mutex
count int
}
// ServeHTTP only accepts the CONNECT verb and will tunnel your connection to
// the specified host. Also tracks the number of connections that it proxies for
// debugging purposes.
func (p *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Validate http connect parameters.
if r.Method != http.MethodConnect {
trace.WriteError(w, trace.BadParameter("%v not supported", r.Method))
return
}
if r.Host == "" {
trace.WriteError(w, trace.BadParameter("host not set"))
return
}
// Dial to the target host, this is done before hijacking the connection to
// ensure the target host is accessible.
dialer := net.Dialer{}
dconn, err := dialer.DialContext(r.Context(), "tcp", r.Host)
if err != nil {
trace.WriteError(w, err)
return
}
defer dconn.Close()
// Once the client receives 200 OK, the rest of the data will no longer be
// http, but whatever protocol is being tunneled.
w.WriteHeader(http.StatusOK)
// Hijack request so we can get underlying connection.
hj, ok := w.(http.Hijacker)
if !ok {
trace.WriteError(w, trace.AccessDenied("unable to hijack connection"))
return
}
sconn, _, err := hj.Hijack()
if err != nil {
trace.WriteError(w, err)
return
}
defer sconn.Close()
// Success, we're proxying data now.
p.Lock()
p.count++
p.Unlock()
// Copy from src to dst and dst to src.
errc := make(chan error, 2)
replicate := func(dst io.Writer, src io.Reader) {
_, err := io.Copy(dst, src)
errc <- err
}
go replicate(sconn, dconn)
go replicate(dconn, sconn)
// Wait until done, error, or 10 second.
select {
case <-time.After(10 * time.Second):
case <-errc:
}
}
// Count returns the number of requests that have been proxied.
func (p *ProxyHandler) Count() int {
p.Lock()
defer p.Unlock()
return p.count
}