in pkg/hbone/hbonec.go [103:245]
func (hc *Endpoint) Proxy(ctx context.Context, stdin io.Reader, stdout io.WriteCloser) error {
if hc.SNIGate != "" {
return hc.sniProxy(ctx, stdin, stdout)
}
t0 := time.Now()
// It is usually possible to pass stdin directly to NewRequest.
// Using a pipe allows getting stats.
i, o := io.Pipe()
defer stdout.Close()
r, err := http.NewRequest("POST", hc.URL, i)
if err != nil {
return err
}
var rt = hc.rt
if hc.hb.TokenCallback != nil {
h := r.URL.Host
if strings.Contains(h, ":") && h[0] != '[' {
hn, _, _ := net.SplitHostPort(h)
h = hn
}
t, err := hc.hb.TokenCallback(ctx, "https://"+h)
if err != nil {
log.Println("Failed to get token, attempt unauthenticated", err)
} else {
r.Header.Set("Authorization", "Bearer "+t)
}
}
if hc.rt == nil {
/* Alternative, using http.Client.
ug = &http.Client{
Transport: &http2.Transport{
// So http2.Transport doesn't complain the URL scheme isn't 'https'
AllowHTTP: true,
// Pretend we are dialing a TLS endpoint.
// Note, we ignore the passed tls.Config
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
},
},
}
*/
h := r.URL.Host
if hc.H2Gate != "" {
h = hc.H2Gate
}
if Debug {
rd, _ := httputil.DumpRequest(r, false)
log.Println("HB req: ", h, string(rd))
}
host, port, _ := net.SplitHostPort(h)
if r.URL.Scheme == "http" {
d := &net.Dialer{}
dialHost := r.URL.Host
if port == "" {
dialHost = net.JoinHostPort(dialHost, "80")
}
nConn, err := d.DialContext(ctx, "tcp", dialHost)
if err != nil {
return err
}
hc.tlsCon = nConn
} else {
// Expect system certificates.
d := tls.Dialer{
Config: &tls.Config{
NextProtos: []string{"h2"},
},
NetDialer: &net.Dialer{},
}
dialHost := r.URL.Host
if port == "" {
dialHost = net.JoinHostPort(dialHost, "443")
}
if hc.H2Gate != "" {
dialHost = hc.H2Gate
}
nConn, err := d.DialContext(ctx, "tcp", dialHost)
if err != nil {
return err
}
tlsCon := nConn.(*tls.Conn)
tlsCon.VerifyHostname(host)
if err != nil {
return err
}
if tlsCon.ConnectionState().NegotiatedProtocol != "h2" {
log.Println("Failed to negotiate h2", tlsCon.ConnectionState().NegotiatedProtocol)
return errors.New("invalid ALPN protocol")
}
hc.tlsCon = tlsCon
}
rt, err = hc.hb.h2t.NewClientConn(hc.tlsCon)
if err != nil {
return err
}
hc.rt = rt
}
res, err := rt.RoundTrip(r)
if err != nil {
return err
}
t1 := time.Now()
ch := make(chan int)
var s1, s2 Stream
s1 = Stream{
ID: "client-o",
Dst: o,
Src: stdin,
}
go s1.CopyBuffered(ch, true)
s2 = Stream{
ID: "client-i",
Dst: stdout,
Src: res.Body,
}
s2.CopyBuffered(nil, true)
<-ch
log.Println("HBoneC-done", "url", r.URL, "status", res.Status, "conTime", t1.Sub(t0), "dur", time.Since(t1))
if s2.Err != nil || s1.Err != nil || s2.InError || s1.InError {
log.Println("HboneC close errors", s2.Err, s1.Err, s2.InError, s1.InError)
}
return s2.Err
}