Skip to content

Conversation

@inconshreveable
Copy link
Contributor

@inconshreveable inconshreveable commented May 4, 2025

v2 API for ngrok-go

  • Dramatically simplified the API to remove many overlapping options, options that are now deprecated, unnecessary convenience functions, etc.
  • Simplified the API by creating a unified API by removing all protocol-specific behaviors (which have all been moved to Traffic Policy).
  • Removed the config package. All of its options are now folded into the top-level package or removed because they were migrated into Traffic Policy.
  • Updates the API to use new ngrok terminology of Agents, Endpoints and Traffic Policy.
  • Removes unnecessary functionality that has been moved to Traffic Policy.
  • Removes functionality that is now deprecated (like labeled tunnels).
  • Added support for agent-based TLS termination and Mutual TLS termination.
  • Added support for full TLS control over forwarding to the upstream
  • Removed the prototype policy package that was not well supported.
  • Separated out a concept of an Agent from its Session which were previously co-mingled.

Summarized new API:

package ngrok // import "golang.ngrok.com/ngrok/v2"

var DefaultAgent = NewAgent(WithAuthtoken(os.Getenv("NGROK_AUTHTOKEN")))
func Forward(ctx context.Context, upstream *Upstream, opts ...EndpointOption) (EndpointForwarder, error)
func Listen(ctx context.Context, opts ...EndpointOption) (EndpointListener, error)
func NewAgent(agentOpts ...AgentOption) (Agent, error)
type Agent interface{ ... }
type AgentOption func(*agentOpts)
    func WithAgentConnectCAs(pool *x509.CertPool) AgentOption
    func WithAgentConnectURL(addr string) AgentOption
    func WithAgentDescription(desc string) AgentOption
    func WithAgentMetadata(meta string) AgentOption
    func WithAuthtoken(token string) AgentOption
    func WithAutoSessionManagement(auto bool) AgentOption
    func WithClientInfo(clientType, version string, comments ...string) AgentOption
    func WithDialer(dialer Dialer) AgentOption
    func WithEventHandler(handler EventHandler) AgentOption
    func WithHeartbeatInterval(interval time.Duration) AgentOption
    func WithHeartbeatTolerance(tolerance time.Duration) AgentOption
    func WithLogger(logger log.Logger) AgentOption
    func WithMultiLeg(enable bool) AgentOption
    func WithProxyURL(urlSpec string) AgentOption
    func WithRPCHandler(handler RPCHandler) AgentOption
    func WithTLSConfig(tlsCustomizer func(*tls.Config)) AgentOption
type AgentSession interface{ ... }
type Dialer interface{ ... }
type Endpoint interface{ ... }
type EndpointForwarder interface{ ... }
type EndpointListener interface{ ... }
type EndpointOption func(*endpointOpts)
    func WithAgentTLSTermination(config *tls.Config) EndpointOption
    func WithBindings(bindings ...string) EndpointOption
    func WithDescription(desc string) EndpointOption
    func WithMetadata(meta string) EndpointOption
    func WithPoolingEnabled(pool bool) EndpointOption
    func WithProxyProtocol(proxyProtocol string) EndpointOption
    func WithTrafficPolicy(policy string) EndpointOption
    func WithURL(urlSpec string) EndpointOption
type Error interface{ ... }
type Event interface{ ... }
type EventAgentConnectSucceeded struct{ ... }
type EventAgentDisconnected struct{ ... }
type EventAgentHeartbeatReceived struct{ ... }
type EventHandler func(Event)
type EventType int
    const EventTypeAgentConnectSucceeded EventType = iota ...
type RPCHandler func(context.Context, AgentSession, rpc.Request) ([]byte, error)
type Upstream struct{ ... }
    func WithUpstream(addr string, opts ...UpstreamOption) *Upstream
type UpstreamOption func(*Upstream)
    func WithUpstreamProtocol(proto string) UpstreamOption
    func WithUpstreamTLSClientConfig(config *tls.Config) UpstreamOption

fixes #202 #193 #178

@inconshreveable inconshreveable marked this pull request as draft May 4, 2025 20:18
@inconshreveable inconshreveable force-pushed the alan/v2 branch 12 times, most recently from 12d562d to 17b7c1c Compare May 6, 2025 06:21
@inconshreveable
Copy link
Contributor Author

This is ready for review. A few remaining items I'd like to get through myself before merge:

  1. Some additional test cases
  2. A thorough read through to audit for anything I've missed in the authoring process
  3. A git tag for the 2.0.0 release

@inconshreveable inconshreveable force-pushed the alan/v2 branch 2 times, most recently from 63c7aa9 to 59a7df9 Compare May 6, 2025 06:26
@inconshreveable inconshreveable marked this pull request as ready for review May 8, 2025 01:51
@inconshreveable
Copy link
Contributor Author

additional tests have been added as well as some codebase cleanups and small modifications to the API based on review and suggestions

before merging it needs a rebase/squash and a git tag of v2.0.0

Copy link

@nijikokun nijikokun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this feels really nice so far. Tests and formatting passed locally on macOS and Windows for me and no major issues in the code from what I read / tested. I had one note on one of the options.

Copy link
Member

@Alice-Lilith Alice-Lilith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 to @bmpngrok's nits, otherwise looks clean to me.

Copy link
Contributor

@euank euank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new package is way simpler, and I overall like the UX!

The only wart I've found so far is that waiting for a session to shutdown (i.e. from clicking 'stop' in the dashboard) doesn't seem possible with this API. I could be missing something, and I'd be happy to know if I am

I also have a small set of code nits

@inconshreveable
Copy link
Contributor Author

The only wart I've found so far is that waiting for a session to shutdown (i.e. from clicking 'stop' in the dashboard) doesn't seem possible with this API. I could be missing something, and I'd be happy to know if I am

Hmmm, that's true and I agree that it's a miss. On the other hand, it also was not possible with the previous version of the API. I'd like to advocate that we don't require that in this PR because this PR is already large enough and it can be safely added later without a breaking change. If that's amenable, I'll add it to the TODO

@andrewryno
Copy link
Member

I'm fairly certain this happens with v1 as well but this is my first time using the SDK so far so it was a fairly rough edge that would be great to address: if you don't have your authtoken env variable (or it's wrong), the SDK will just fail in the background and attempt to reconnect every 30 seconds and hang fover. I was only able to find out the issue by adding my own logger because it didn't even print it anywhere by default (I was running doing the connection in a go test).

@inconshreveable
Copy link
Contributor Author

I'm fairly certain this happens with v1 as well but this is my first time using the SDK so far so it was a fairly rough edge that would be great to address: if you don't have your authtoken env variable (or it's wrong), the SDK will just fail in the background and attempt to reconnect every 30 seconds and hang fover. I was only able to find out the issue by adding my own logger because it didn't even print it anywhere by default (I was running doing the connection in a go test).

indeed, that's this issue: #176

since it's not (meaningfully) API-breaking, i'd like to fix that post-merge to try to get this PR out without stacking on additional changes

Copy link
Contributor

@euank euank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another round of review!

I think the RPC bit needs another round of iteration, and I agree with ryno that the error code stuff should also be properly plumbed.

The Agent vs AgentSession also feels quite confusing, since a lot of the methods I'd expect to be on the AgentSession (like Disconnect) are actually on the Agent.

I get that the abstraction is "One agent has an automatically reconnecting session, so it can have multiple sessions over time meaning creating an endpoint should then re-create the endpoint, so it's tied to the agent not the session", but it still feels slightly off somehow.

Copy link
Contributor

@bmpngrok bmpngrok left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have anything to add, so approval conditional on responding to Euan and Ryno's concerns

See CHANGELOG.md for details.
@inconshreveable inconshreveable merged commit bfa15fa into main Jun 2, 2025
2 of 4 checks passed
@inconshreveable inconshreveable deleted the alan/v2 branch June 2, 2025 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using the option config.WithURL should infer the scheme

7 participants