From aab402cabb9f77796d7dcce75c32f28ef94fb367 Mon Sep 17 00:00:00 2001 From: Josh Robson Chase Date: Tue, 21 Nov 2023 15:49:16 -0500 Subject: [PATCH 1/5] ngrok: add ForwardsProto to Bind --- internal/tunnel/proto/msg.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/internal/tunnel/proto/msg.go b/internal/tunnel/proto/msg.go index 2b706136..9e26e606 100644 --- a/internal/tunnel/proto/msg.go +++ b/internal/tunnel/proto/msg.go @@ -200,12 +200,13 @@ type AuthRespExtra struct { // A client sends this message to the server over a new stream // to request the server bind a remote port/hostname on the client's behalf. type Bind struct { - ID string `json:"-"` - ClientID string `json:"Id"` // a session-unique bind ID generated by the client, if empty, one is generated - Proto string // the protocol to bind (one of 'http', 'https', 'tcp', 'tls', 'ssh') - ForwardsTo string // the address of the upstream service the ngrok agent will forward to - Opts any // options for the bind - protocol dependent - Extra BindExtra // anything extra the application wants to send + ID string `json:"-"` + ClientID string `json:"Id"` // a session-unique bind ID generated by the client, if empty, one is generated + Proto string // the protocol to bind (one of 'http', 'https', 'tcp', 'tls', 'ssh') + ForwardsTo string // the address of the upstream service the ngrok agent will forward to + ForwardsProto string // the L7 protocol the upstream service is expecting (one of '', 'h1', 'h2c') + Opts any // options for the bind - protocol dependent + Extra BindExtra // anything extra the application wants to send } type BindExtra struct { From ce644eff5d8bb4026031bd6f17910fd7e221ca67 Mon Sep 17 00:00:00 2001 From: Josh Robson Chase Date: Wed, 22 Nov 2023 12:37:04 -0500 Subject: [PATCH 2/5] ngrok: plumb configuration through all of the internal Listen calls --- config/common.go | 5 +++ config/http.go | 4 ++ config/labeled.go | 4 ++ config/tcp.go | 4 ++ config/tls.go | 4 ++ config/tunnel_config.go | 1 + internal/tunnel/client/raw_session.go | 26 ++++++------ internal/tunnel/client/reconnecting.go | 12 +++--- internal/tunnel/client/session.go | 34 +++++++-------- internal/tunnel/client/tunnel.go | 59 +++++++++++++++----------- internal/tunnel/proto/msg.go | 7 +-- session.go | 4 +- tunnel_config.go | 1 + 13 files changed, 100 insertions(+), 65 deletions(-) diff --git a/config/common.go b/config/common.go index 8fe0fdd2..91e0eb27 100644 --- a/config/common.go +++ b/config/common.go @@ -12,6 +12,11 @@ type commonOpts struct { // bearing on tunnel behavior. // If not set, defaults to a URI in the format `app://hostname/path/to/executable?pid=12345` ForwardsTo string + + // The protocol that's forwarded from the ngrok edge. + // Currently only relevant for HTTP/1.1 vs HTTP/2, since there's a potential + // change-of-protocol happening at our edge. + ForwardsProto string } type CommonOptionsFunc func(cfg *commonOpts) diff --git a/config/http.go b/config/http.go index ced22b83..f6581b53 100644 --- a/config/http.go +++ b/config/http.go @@ -128,6 +128,10 @@ func (cfg *httpOptions) toProtoConfig() *proto.HTTPEndpoint { return opts } +func (cfg httpOptions) ForwardsProto() string { + return cfg.commonOpts.ForwardsProto +} + func (cfg httpOptions) ForwardsTo() string { return cfg.commonOpts.getForwardsTo() } diff --git a/config/labeled.go b/config/labeled.go index 55911b7e..5e9259a3 100644 --- a/config/labeled.go +++ b/config/labeled.go @@ -52,6 +52,10 @@ func WithLabel(label, value string) LabeledTunnelOption { }) } +func (cfg labeledOptions) ForwardsProto() string { + return cfg.commonOpts.ForwardsProto +} + func (cfg labeledOptions) ForwardsTo() string { return cfg.commonOpts.getForwardsTo() } diff --git a/config/tcp.go b/config/tcp.go index af8cf1ce..cf90e6b8 100644 --- a/config/tcp.go +++ b/config/tcp.go @@ -58,6 +58,10 @@ func (cfg tcpOptions) ForwardsTo() string { return cfg.commonOpts.getForwardsTo() } +func (cfg tcpOptions) ForwardsProto() string { + return "" // Not supported for TCP +} + func (cfg *tcpOptions) WithForwardsTo(url *url.URL) { cfg.commonOpts.ForwardsTo = url.Host } diff --git a/config/tls.go b/config/tls.go index 36fc39a5..b2bde572 100644 --- a/config/tls.go +++ b/config/tls.go @@ -84,6 +84,10 @@ func (cfg *tlsOptions) toProtoConfig() *proto.TLSEndpoint { return opts } +func (cfg tlsOptions) ForwardsProto() string { + return "" // Not supported for TLS +} + func (cfg tlsOptions) ForwardsTo() string { return cfg.commonOpts.getForwardsTo() } diff --git a/config/tunnel_config.go b/config/tunnel_config.go index 3b200394..02a3682d 100644 --- a/config/tunnel_config.go +++ b/config/tunnel_config.go @@ -18,6 +18,7 @@ type Tunnel interface { // the public interface with internal details. type tunnelConfigPrivate interface { ForwardsTo() string + ForwardsProto() string Extra() proto.BindExtra Proto() string Opts() any diff --git a/internal/tunnel/client/raw_session.go b/internal/tunnel/client/raw_session.go index c9bc6e7f..81384f54 100644 --- a/internal/tunnel/client/raw_session.go +++ b/internal/tunnel/client/raw_session.go @@ -19,8 +19,8 @@ import ( type RawSession interface { Auth(id string, extra proto.AuthExtra) (proto.AuthResp, error) - Listen(proto string, opts any, extra proto.BindExtra, id string, forwardsTo string) (proto.BindResp, error) - ListenLabel(labels map[string]string, metadata string, forwardsTo string) (proto.StartTunnelWithLabelResp, error) + Listen(proto string, opts any, extra proto.BindExtra, id string, forwardsTo string, forwardsProto string) (proto.BindResp, error) + ListenLabel(labels map[string]string, metadata string, forwardsTo string, forwardsProto string) (proto.StartTunnelWithLabelResp, error) Unlisten(id string) (proto.UnbindResp, error) Accept() (netx.LoggedConn, error) @@ -95,13 +95,14 @@ func (s *rawSession) Auth(id string, extra proto.AuthExtra) (resp proto.AuthResp // opts are protocol-specific options for listening. // extra is an opaque struct useful for passing application-specific data. // id is an session-unique identifier, if empty it will be assigned for you -func (s *rawSession) Listen(protocol string, opts any, extra proto.BindExtra, id string, forwardsTo string) (resp proto.BindResp, err error) { +func (s *rawSession) Listen(protocol string, opts any, extra proto.BindExtra, id string, forwardsTo string, forwardsProto string) (resp proto.BindResp, err error) { req := proto.Bind{ - ClientID: id, - Proto: protocol, - Opts: opts, - Extra: extra, - ForwardsTo: forwardsTo, + ClientID: id, + Proto: protocol, + Opts: opts, + Extra: extra, + ForwardsTo: forwardsTo, + ForwardsProto: forwardsProto, } err = s.rpc(proto.BindReq, &req, &resp) if err != nil { @@ -115,11 +116,12 @@ func (s *rawSession) Listen(protocol string, opts any, extra proto.BindExtra, id } // ListenLabel sends a listen message to the server and returns the server's response -func (s *rawSession) ListenLabel(labels map[string]string, metadata string, forwardsTo string) (resp proto.StartTunnelWithLabelResp, err error) { +func (s *rawSession) ListenLabel(labels map[string]string, metadata string, forwardsTo string, forwardsProto string) (resp proto.StartTunnelWithLabelResp, err error) { req := proto.StartTunnelWithLabel{ - Labels: labels, - Metadata: metadata, - ForwardsTo: forwardsTo, + Labels: labels, + Metadata: metadata, + ForwardsTo: forwardsTo, + ForwardsProto: forwardsProto, } err = s.rpc(proto.StartTunnelWithLabelReq, &req, &resp) return diff --git a/internal/tunnel/client/reconnecting.go b/internal/tunnel/client/reconnecting.go index edc5b8ad..1ca13256 100644 --- a/internal/tunnel/client/reconnecting.go +++ b/internal/tunnel/client/reconnecting.go @@ -39,16 +39,16 @@ func (s *swapRaw) Auth(id string, extra proto.AuthExtra) (resp proto.AuthResp, e return proto.AuthResp{}, ErrSessionNotReady } -func (s *swapRaw) Listen(protocol string, opts any, extra proto.BindExtra, id string, forwardsTo string) (resp proto.BindResp, err error) { +func (s *swapRaw) Listen(protocol string, opts any, extra proto.BindExtra, id string, forwardsTo string, forwardsProto string) (resp proto.BindResp, err error) { if raw := s.get(); raw != nil { - return raw.Listen(protocol, opts, extra, id, forwardsTo) + return raw.Listen(protocol, opts, extra, id, forwardsTo, forwardsProto) } return proto.BindResp{}, ErrSessionNotReady } -func (s *swapRaw) ListenLabel(labels map[string]string, metadata string, forwardsTo string) (resp proto.StartTunnelWithLabelResp, err error) { +func (s *swapRaw) ListenLabel(labels map[string]string, metadata string, forwardsTo string, forwardsProto string) (resp proto.StartTunnelWithLabelResp, err error) { if raw := s.get(); raw != nil { - return raw.ListenLabel(labels, metadata, forwardsTo) + return raw.ListenLabel(labels, metadata, forwardsTo, forwardsProto) } return proto.StartTunnelWithLabelResp{}, ErrSessionNotReady } @@ -236,7 +236,7 @@ func (s *reconnectingSession) connect(acceptErr error) error { var respErr string if tCfg.Labels != nil { - resp, err := raw.ListenLabel(tCfg.Labels, tCfg.Metadata, t.ForwardsTo()) + resp, err := raw.ListenLabel(tCfg.Labels, tCfg.Metadata, t.ForwardsTo(), t.ForwardsProto()) if err != nil { return err } @@ -250,7 +250,7 @@ func (s *reconnectingSession) connect(acceptErr error) error { newTunnels[oldID] = t } } else { - resp, err := raw.Listen(tCfg.ConfigProto, tCfg.Opts, t.bindExtra, t.ID(), t.ForwardsTo()) + resp, err := raw.Listen(tCfg.ConfigProto, tCfg.Opts, t.bindExtra, t.ID(), t.ForwardsTo(), t.ForwardsProto()) if err != nil { return err } diff --git a/internal/tunnel/client/session.go b/internal/tunnel/client/session.go index 54b3bcbb..d5bdc883 100644 --- a/internal/tunnel/client/session.go +++ b/internal/tunnel/client/session.go @@ -38,20 +38,20 @@ type Session interface { // // Applications will typically prefer to call the protocol-specific methods like // ListenHTTP, ListenTCP, etc. - Listen(protocol string, opts any, extra proto.BindExtra, forwardsTo string) (Tunnel, error) + Listen(protocol string, opts any, extra proto.BindExtra, forwardsTo string, forwardsProto string) (Tunnel, error) // Listen negotiates with the server to create a new remote listen for the // given labels. It returns a *Tunnel on success from which the caller can // accept new connections over the listen. - ListenLabel(labels map[string]string, metadata string, forwardsTo string) (Tunnel, error) + ListenLabel(labels map[string]string, metadata string, forwardsTo string, forwardsProto string) (Tunnel, error) // Convenience methods // ListenHTTP listens on a new HTTP endpoint - ListenHTTP(opts *proto.HTTPEndpoint, extra proto.BindExtra, forwardsTo string) (Tunnel, error) + ListenHTTP(opts *proto.HTTPEndpoint, extra proto.BindExtra, forwardsTo string, forwardsProto string) (Tunnel, error) // ListenHTTP listens on a new HTTPS endpoint - ListenHTTPS(opts *proto.HTTPEndpoint, extra proto.BindExtra, forwardsTo string) (Tunnel, error) + ListenHTTPS(opts *proto.HTTPEndpoint, extra proto.BindExtra, forwardsTo string, forwardsProto string) (Tunnel, error) // ListenTCP listens on a remote TCP endpoint ListenTCP(opts *proto.TCPEndpoint, extra proto.BindExtra, forwardsTo string) (Tunnel, error) @@ -116,8 +116,8 @@ func (s *session) Heartbeat() (time.Duration, error) { return s.raw.Heartbeat() } -func (s *session) Listen(protocol string, opts any, extra proto.BindExtra, forwardsTo string) (Tunnel, error) { - resp, err := s.raw.Listen(protocol, opts, extra, "", forwardsTo) +func (s *session) Listen(protocol string, opts any, extra proto.BindExtra, forwardsTo string, forwardsProto string) (Tunnel, error) { + resp, err := s.raw.Listen(protocol, opts, extra, "", forwardsTo, forwardsProto) if err != nil { return nil, err } @@ -128,7 +128,7 @@ func (s *session) Listen(protocol string, opts any, extra proto.BindExtra, forwa } // make tunnel - t := newTunnel(resp, extra, s, forwardsTo) + t := newTunnel(resp, extra, s, forwardsTo, forwardsProto) // add to tunnel registry s.addTunnel(resp.ClientID, t) @@ -136,8 +136,8 @@ func (s *session) Listen(protocol string, opts any, extra proto.BindExtra, forwa return t, nil } -func (s *session) ListenLabel(labels map[string]string, metadata string, forwardsTo string) (Tunnel, error) { - resp, err := s.raw.ListenLabel(labels, metadata, forwardsTo) +func (s *session) ListenLabel(labels map[string]string, metadata string, forwardsTo string, forwardsProto string) (Tunnel, error) { + resp, err := s.raw.ListenLabel(labels, metadata, forwardsTo, forwardsProto) if err != nil { return nil, err } @@ -148,7 +148,7 @@ func (s *session) ListenLabel(labels map[string]string, metadata string, forward } // make tunnel - t := newTunnelLabel(resp, metadata, labels, s, forwardsTo) + t := newTunnelLabel(resp, metadata, labels, s, forwardsTo, forwardsProto) // add to tunnel registry s.addTunnel(resp.ID, t) @@ -156,24 +156,24 @@ func (s *session) ListenLabel(labels map[string]string, metadata string, forward return t, nil } -func (s *session) ListenHTTP(opts *proto.HTTPEndpoint, extra proto.BindExtra, forwardsTo string) (Tunnel, error) { - return s.Listen("http", opts, extra, forwardsTo) +func (s *session) ListenHTTP(opts *proto.HTTPEndpoint, extra proto.BindExtra, forwardsTo string, forwardsProto string) (Tunnel, error) { + return s.Listen("http", opts, extra, forwardsTo, forwardsProto) } -func (s *session) ListenHTTPS(opts *proto.HTTPEndpoint, extra proto.BindExtra, forwardsTo string) (Tunnel, error) { - return s.Listen("https", opts, extra, forwardsTo) +func (s *session) ListenHTTPS(opts *proto.HTTPEndpoint, extra proto.BindExtra, forwardsTo string, forwardsProto string) (Tunnel, error) { + return s.Listen("https", opts, extra, forwardsTo, forwardsProto) } func (s *session) ListenTCP(opts *proto.TCPEndpoint, extra proto.BindExtra, forwardsTo string) (Tunnel, error) { - return s.Listen("tcp", opts, extra, forwardsTo) + return s.Listen("tcp", opts, extra, forwardsTo, "") } func (s *session) ListenTLS(opts *proto.TLSEndpoint, extra proto.BindExtra, forwardsTo string) (Tunnel, error) { - return s.Listen("tls", opts, extra, forwardsTo) + return s.Listen("tls", opts, extra, forwardsTo, "") } func (s *session) ListenSSH(opts *proto.SSHOptions, extra proto.BindExtra, forwardsTo string) (Tunnel, error) { - return s.Listen("ssh", opts, extra, forwardsTo) + return s.Listen("ssh", opts, extra, forwardsTo, "") } func (s *session) SrvInfo() (proto.SrvInfoResp, error) { diff --git a/internal/tunnel/client/tunnel.go b/internal/tunnel/client/tunnel.go index 7871493a..a297f477 100644 --- a/internal/tunnel/client/tunnel.go +++ b/internal/tunnel/client/tunnel.go @@ -26,14 +26,15 @@ type ProxyConn struct { // A Tunnel is a net.Listener that Accept()'s connections from a // remote machine. type tunnel struct { - id atomic.Value - configProto string - url string - opts any - token string - bindExtra proto.BindExtra - labels map[string]string - forwardsTo string + id atomic.Value + configProto string + url string + opts any + token string + bindExtra proto.BindExtra + labels map[string]string + forwardsTo string + forwardsProto string accept chan *ProxyConn // new connections come on this channel unlisten func() error // call this function to close the tunnel @@ -42,24 +43,25 @@ type tunnel struct { shut shutdown // for clean shutdowns } -func newTunnel(resp proto.BindResp, extra proto.BindExtra, s *session, forwardsTo string) *tunnel { +func newTunnel(resp proto.BindResp, extra proto.BindExtra, s *session, forwardsTo string, forwardsProto string) *tunnel { id := atomic.Value{} id.Store(resp.ClientID) return &tunnel{ - id: id, - configProto: resp.Proto, - url: resp.URL, - opts: resp.Opts, - token: resp.Extra.Token, - bindExtra: extra, // this makes the reconnecting session a little easier - accept: make(chan *ProxyConn), - unlisten: func() error { return s.unlisten(resp.ClientID) }, - forwardsTo: forwardsTo, - closeError: errors.New("Listener closed"), + id: id, + configProto: resp.Proto, + url: resp.URL, + opts: resp.Opts, + token: resp.Extra.Token, + bindExtra: extra, // this makes the reconnecting session a little easier + accept: make(chan *ProxyConn), + unlisten: func() error { return s.unlisten(resp.ClientID) }, + forwardsTo: forwardsTo, + forwardsProto: forwardsProto, + closeError: errors.New("Listener closed"), } } -func newTunnelLabel(resp proto.StartTunnelWithLabelResp, metadata string, labels map[string]string, s *session, forwardsTo string) *tunnel { +func newTunnelLabel(resp proto.StartTunnelWithLabelResp, metadata string, labels map[string]string, s *session, forwardsTo string, forwardsProto string) *tunnel { id := atomic.Value{} id.Store(resp.ID) return &tunnel{ @@ -67,11 +69,12 @@ func newTunnelLabel(resp proto.StartTunnelWithLabelResp, metadata string, labels bindExtra: proto.BindExtra{ Metadata: metadata, }, // this makes the reconnecting session a little easier - labels: labels, - accept: make(chan *ProxyConn), - unlisten: func() error { return s.unlisten(resp.ID) }, - forwardsTo: forwardsTo, - closeError: errors.New("Listener closed"), + labels: labels, + accept: make(chan *ProxyConn), + unlisten: func() error { return s.unlisten(resp.ID) }, + forwardsTo: forwardsTo, + forwardsProto: forwardsProto, + closeError: errors.New("Listener closed"), } } @@ -115,6 +118,12 @@ func (t *tunnel) Addr() net.Addr { return t.RemoteBindConfig() } +// ForwardsProto returns the protocol of the upstream that the ngrok agent +// adverstises to the edge. +func (t *tunnel) ForwardsProto() string { + return t.forwardsProto +} + // ForwardsTo returns the address of the upstream the ngrok agent will // forward proxied connections to func (t *tunnel) ForwardsTo() string { diff --git a/internal/tunnel/proto/msg.go b/internal/tunnel/proto/msg.go index 9e26e606..55db443e 100644 --- a/internal/tunnel/proto/msg.go +++ b/internal/tunnel/proto/msg.go @@ -234,9 +234,10 @@ type BindRespExtra struct { // to request the server start a new tunnel with the given labels on the client's behalf. type StartTunnelWithLabel struct { // ID string `json:"-"` // a session-unique bind ID generated by the client, if empty, one is generated - Labels map[string]string // labels for tunnel group membership - ForwardsTo string // the address of the upstream service the ngrok agent will forward to - Metadata string + Labels map[string]string // labels for tunnel group membership + ForwardsTo string // the address of the upstream service the ngrok agent will forward to + ForwardsProto string // the L7 protocol the upstream service is expecting (one of '', 'h1', 'h2c') + Metadata string } // The server responds with a StartTunnelWithLabelResp message to notify the client of the diff --git a/session.go b/session.go index a0603944..a83aecc4 100644 --- a/session.go +++ b/session.go @@ -800,9 +800,9 @@ func (s *sessionImpl) Listen(ctx context.Context, cfg config.Tunnel) (Tunnel, er extra := tunnelCfg.Extra() if tunnelCfg.Proto() != "" { - tunnel, err = s.inner().Listen(tunnelCfg.Proto(), tunnelCfg.Opts(), extra, tunnelCfg.ForwardsTo()) + tunnel, err = s.inner().Listen(tunnelCfg.Proto(), tunnelCfg.Opts(), extra, tunnelCfg.ForwardsTo(), tunnelCfg.ForwardsProto()) } else { - tunnel, err = s.inner().ListenLabel(tunnelCfg.Labels(), extra.Metadata, tunnelCfg.ForwardsTo()) + tunnel, err = s.inner().ListenLabel(tunnelCfg.Labels(), extra.Metadata, tunnelCfg.ForwardsTo(), tunnelCfg.ForwardsProto()) } impl := &tunnelImpl{ diff --git a/tunnel_config.go b/tunnel_config.go index 3747b208..2ed8c51a 100644 --- a/tunnel_config.go +++ b/tunnel_config.go @@ -13,6 +13,7 @@ import ( // Duplicated from config/tunnel_config.go type tunnelConfigPrivate interface { ForwardsTo() string + ForwardsProto() string Extra() proto.BindExtra Proto() string Opts() any From 81ab9fc97959aa7107adac4da018e0788ac2dc87 Mon Sep 17 00:00:00 2001 From: Josh Robson Chase Date: Wed, 22 Nov 2023 12:37:35 -0500 Subject: [PATCH 3/5] ngrok/config: add WithAppProtocol config options --- config/app_protocol.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 config/app_protocol.go diff --git a/config/app_protocol.go b/config/app_protocol.go new file mode 100644 index 00000000..93aa2e4f --- /dev/null +++ b/config/app_protocol.go @@ -0,0 +1,18 @@ +package config + +type appProtocol string + +func (ap appProtocol) ApplyHTTP(cfg *httpOptions) { + cfg.commonOpts.ForwardsProto = string(ap) +} + +func (ap appProtocol) ApplyLabeled(cfg *labeledOptions) { + cfg.commonOpts.ForwardsProto = string(ap) +} + +func WithAppProtocol(proto string) interface { + HTTPEndpointOption + LabeledTunnelOption +} { + return appProtocol(proto) +} From 2e66c55f31c5d1f637a21c40cc548d3370dd1b96 Mon Sep 17 00:00:00 2001 From: Josh Robson Chase Date: Wed, 22 Nov 2023 14:45:19 -0500 Subject: [PATCH 4/5] document WithAppProtocol --- config/app_protocol.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/app_protocol.go b/config/app_protocol.go index 93aa2e4f..a0714875 100644 --- a/config/app_protocol.go +++ b/config/app_protocol.go @@ -10,6 +10,12 @@ func (ap appProtocol) ApplyLabeled(cfg *labeledOptions) { cfg.commonOpts.ForwardsProto = string(ap) } +// WithAppProtocol declares the protocol that the upstream service speaks. +// This may be used by the ngrok edge to make decisions regarding protocol +// upgrades or downgrades. +// +// Currently, `http2` is the only valid string, and will cause connections +// received from HTTP endpoints to always use HTTP/2. func WithAppProtocol(proto string) interface { HTTPEndpointOption LabeledTunnelOption From 78bff0a014b99be0995527a57c8c50ebf48a872b Mon Sep 17 00:00:00 2001 From: Josh Robson Chase Date: Tue, 28 Nov 2023 11:51:03 -0500 Subject: [PATCH 5/5] fix ForwardsProto comment --- internal/tunnel/proto/msg.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/tunnel/proto/msg.go b/internal/tunnel/proto/msg.go index 55db443e..9484621e 100644 --- a/internal/tunnel/proto/msg.go +++ b/internal/tunnel/proto/msg.go @@ -204,7 +204,7 @@ type Bind struct { ClientID string `json:"Id"` // a session-unique bind ID generated by the client, if empty, one is generated Proto string // the protocol to bind (one of 'http', 'https', 'tcp', 'tls', 'ssh') ForwardsTo string // the address of the upstream service the ngrok agent will forward to - ForwardsProto string // the L7 protocol the upstream service is expecting (one of '', 'h1', 'h2c') + ForwardsProto string // the L7 protocol the upstream service is expecting (one of '', 'http1', 'http2') Opts any // options for the bind - protocol dependent Extra BindExtra // anything extra the application wants to send } @@ -236,7 +236,7 @@ type StartTunnelWithLabel struct { // ID string `json:"-"` // a session-unique bind ID generated by the client, if empty, one is generated Labels map[string]string // labels for tunnel group membership ForwardsTo string // the address of the upstream service the ngrok agent will forward to - ForwardsProto string // the L7 protocol the upstream service is expecting (one of '', 'h1', 'h2c') + ForwardsProto string // the L7 protocol the upstream service is expecting (one of '', 'http1', 'http2') Metadata string }