Version: v0.29.0 Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Dec 1, 2021 License: Apache-2.0 Imports: 18 Imported by: 0



SendIt is a tool for setting up a reverse proxy to localhost that's accessible externally. It's similar to tools like ngrok and serveo.

It was created to aid in running/testing GoAlert integrations.


Usage: sendit-server -secret <SECRET_STRING>

If exposing publicly, it's recommended to start in secure mode with the -secret flag set. This will require clients to provide a token generated with the sendit-token command.



Example: sendit -token xxxxxx http://localhost:3030

Make sure GoAlert is started with the appropriate prefix. For the above example: make start GOALERT_HTTP_PREFIX=/foobar

If you are testing Twilio functionality, you will also need to set your General.PublicURL config to the source URL (example above: Don't forget to update configuration within Twilio, GitHub, etc... to match.

Generate Tokens

Usage: sendit-token -secret <SECRET_STRING>

The sendit-token command will generate a token that will work with a server that has the provided SECRET_STRING set as it's -secret parameter.

How It Works

A session is established on the open endpoint reserving a desired prefix, and providing the client with a token to establish two HTTP streams.

  • A POST request is made to read which the client will use by reading the response body
  • A POST request is made to write which the client will use by writing the request body

With both read and write the two requests are used together to provide a bi-directional stream. On top of said stream, a multiplexing library is used that allows multiple "streams" to be emulated within a single ReadWriteCloser. New HTTP requests on the server end can then be proxied back to the client, and from the client to the target local URL.

After the max-ttl is reached, the client will initiate new read and write requests to the server, using the existing token. A 4-way handshake is used to transition traffic from the old requests to the new. Afterwards the old requests complete normally and the new ones carry all traffic. Only one request is used for each half of the "pipe" at a time.

The max-ttl can be adjusted lower, for environments that have a shorter request timeouts than the default of 15 seconds.




View Source
const (
	TokenIssuer          = "sendit"
	TokenAudienceAuth    = "auth"
	TokenAudienceConnect = "connect"

Token values


This section is empty.


func ConnectAndServe

func ConnectAndServe(urlStr, dstURLStr, token string, ttl time.Duration) error

ConnectAndServe will connect to the server at `urlStr` and start serving/routing traffic to the local `addr`. If ttl > 0 then connections in each direction will be refreshed at the specified interval.

func FlushWriter

func FlushWriter(w io.Writer) io.Writer

FlushWriter will call .Flush() immediately after every call to .Write() on the returned io.Writer.

If w does not implement http.Flusher, it panics.

func GenerateToken

func GenerateToken(secret []byte, aud, sub string) (string, error)

GenerateToken will create a new token string with the given audience and subject, signed with the provided secret.

func TokenSubject

func TokenSubject(secret []byte, aud, token string) (string, error)

TokenSubject will return the subject of a token, after verifying the signature and audience.

func ValidPath

func ValidPath(p string) bool

ValidPath will return true if `p` is a valid prefix path.


type Server

type Server struct {
	// contains filtered or unexported fields

Server is an http.Handler that can multiplex reverse-proxied requests over 2 POST requests from a client.

func NewServer

func NewServer(authSecret []byte, prefix string) *Server

NewServer will create a new Server with a global pathPrefix and using the provided `authSecret` to require valid tokens for any connecting clients.

func (*Server) DialContext

func (s *Server) DialContext(ctx context.Context, network, addr string) (net.Conn, error)

DialContext will return a new `net.Conn` for the given `addr`. `network` is ignored.

It will only connect to "hosts" that match an active session ID.

type Stream

type Stream struct {
	// contains filtered or unexported fields

Stream is a ReadWriteCloser that can have it's read/write pipe replaced safely while actively sending data.

func NewStream

func NewStream() *Stream

NewStream initializes a new stream. SetPipe must be called before data can be transferred.

func (*Stream) Close

func (s *Stream) Close() (err error)

Close will shutdown the stream, and close the underlying Writer.

func (*Stream) Read

func (s *Stream) Read(p []byte) (int, error)

func (*Stream) Ready

func (s *Stream) Ready() <-chan struct{}

Ready will return a channel indicating when the stream is ready for reading and writing.

func (*Stream) SetPipe

func (s *Stream) SetPipe(r io.ReadCloser, wc io.WriteCloser) error

SetPipe will swap the io.ReadCloser and WriteCloser with the underlying Stream safely.

func (*Stream) Write

func (s *Stream) Write(p []byte) (int, error)


Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL