wslis

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: May 7, 2020 License: MIT Imports: 6 Imported by: 0

README

wslis logo

What it does?

Package wslis allows a server to create connections to clients.

wslis allows a server to create connections to clients. For that, clients previously stablish a connection to the server through websockets. Once the websocket connection is stablish, the connection is multiplexed through yamux so several connections can go through the same http stream.

Usage

Client

lis, err := wslis.DialAndListen("ws://localhost:9090", http.Header{"DeviceId": []string{"123"}})
if err != nil {
    panic(err)
}

// Let's have an echo server
conn, err := lis.Accept()
if err != nil {
    panic(err)
}

buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
    panic(err)
}
conn.Write(buf[:n])
conn.Close()
    

Server

log.Fatal(wslis.ListenAndServe("localhost:9090", wslis.EndpointHandlerFunc(func(d *Dialer) {
    id := d.Header.Get("DeviceId")
    log.Printf("Device %s connected\n", id)

    // We open a new connection to the client as soon as we get the connection
    conn, err := d.Dial()
    if err != nil {
        panic(err)
    }

    // We say hi
    conn.Write([]byte("hola"))

    // And we expect a hi back
    n, err := conn.Read(buf)
    if err != nil {
        panic(err)
    }

    if buf[:n] != "hola" {
        panic("That's sad")
    }
})))

Why?

wslis comes from the needs of delegatescreen.com that mainly display webpages in office dashboards. The most common operations are: Set the web page to display, and get a screenshot of the current page. This operations have to be syncronous, and completes as fast as possible.

This lead us to the following desing considerations:

  • Request-Response paradigm.
  • It works over http, so edges, load balancers and other network components work out of the box.

And this are the options considered.

  • ZeroMq, Mqtt or most iot platforms are pub sub based. So even if most of them can work over http, we will need to abstract some kind of request/response ciclye.

  • Signaling where the server sends a signal (kind of request) to the client, and the client initiates an http request. For signaling we considered: longpush, server-sent-events, http2 push . This option was discarded to remove the need of designing a signaling mecanish, and the added complication of matching signals to client/requests.

  • connect the clients to vpns through wireguard. As clients can run on windows/mac/linux this will imply the automation of all the configuraiton in three ooss, and maintain that over time. Also a missconfiguration may brick the computer.

  • ssh over http. This will give the server control over the clients to run any commands. Even if this might be flexible enough, a compromise server could infect a client and access client network.

Documentation

Overview

Package wslis allows a server to create connections to clients.

wslis allows a server to create connections to clients. For that, clients previously stablish a connection to the server through websockets. Once the connection arrives, the server and the clients, multiplex the stream so several connections can go through the same http stream.

History and Design Considerations

wslis comes from the needs of delegatescreen.com that mainly display webpages in office dashboards. The most common operations are: Set the web page to display, and get a screenshot of the current page. This operations have to be syncronous, and completes as fast as possible.

This lead us to the following desing considerations:

- Request-Response paradigm.

- It works over http, so edges, load balancers and other network components work out of the box.

And this are the options considered.

- ZeroMq, Mqtt or most iot platforms are pub sub based. So even if most of them can work over http, we will need to abstract some kind of request/response ciclye.

- Signaling where the server sends a signal (kind of request) to the client, and the client initiates an http request. For signaling we considered: longpush, server-sent-events, http2 push . This option was discarded to remove the need of designing a signaling mecanish, and the added complication of matching signals to client/requests.

- connect the clients to vpns through wireguard. As clients can run on windows/mac/linux this will imply the automation of all the configuraiton in three ooss, and maintain that over time. Also a missconfiguration may brick the computer.

- ssh over http. This will give the server control over the clients to run any commands. Even if this might be flexible enough, a compromise server could infect a client and access client network.

Usage

See Example for usage info.

Example
result := make(chan (string))

// Server
go ListenAndServe("localhost:9090", EndpointHandlerFunc(func(d *Dialer) {
	buf := make([]byte, 1024)

	id := d.Header.Get("DeviceId")
	if id != "123" {
		panic("We don't know you")
	}

	// We open a new connection to the client as soon as we get the connection
	conn, err := d.Dial()
	if err != nil {
		panic(err)
	}

	// We say hi
	conn.Write([]byte("hola"))

	// And we expect a hi back
	n, err := conn.Read(buf)
	if err != nil {
		panic(err)
	}

	result <- string(buf[:n])
}))

// Client
go func() {
	time.Sleep(time.Millisecond) // Lets give time to the server to start
	lis, err := DialAndListen("ws://localhost:9090", http.Header{"DeviceId": []string{"123"}})
	if err != nil {
		panic(err)
	}

	// Let's have an echo server
	conn, err := lis.Accept()
	if err != nil {
		panic(err)
	}

	buf := make([]byte, 1024)
	n, err := conn.Read(buf)
	if err != nil {
		panic(err)
	}
	conn.Write(buf[:n])
	conn.Close()
}()

fmt.Println(<-result)
Output:

hola

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DialAndListen

func DialAndListen(url string, header http.Header) (net.Listener, error)

DialAndListen creates a client and connects to the given url

func ListenAndServe

func ListenAndServe(addr string, handler EndpointHandler) error

ListenAndServe is a convenient function that starts a server in the given address and send all new clients to handler

func ListenAndServeTLS

func ListenAndServeTLS(addr string, certFile, keyFile string, handler EndpointHandler) error

ListenAndServeTLS is the TLS version of ListenAndServe

Example
result := make(chan (string))

// Server
go ListenAndServeTLS("localhost:9443", "ssl.crt", "ssl.key", EndpointHandlerFunc(func(d *Dialer) {
	buf := make([]byte, 1024)

	id := d.Header.Get("DeviceId")
	if id != "123" {
		panic("We don't know you")
	}

	// We open a new connection to the client as soon as we get the connection
	conn, err := d.Dial()
	if err != nil {
		panic(err)
	}

	// We say hi
	conn.Write([]byte("hola"))

	// And we expect a hi back
	n, err := conn.Read(buf)
	if err != nil {
		panic(err)
	}

	result <- string(buf[:n])
}))

// Client
go func() {
	time.Sleep(time.Second * 2) // Wait for the server to start
	lis, err := DialAndListen("wss://localhost:9443", http.Header{"DeviceId": []string{"123"}})
	if err != nil {
		panic(err)
	}

	// Let's have an echo server
	conn, err := lis.Accept()
	if err != nil {
		panic(err)
	}

	buf := make([]byte, 1024)
	n, err := conn.Read(buf)
	if err != nil {
		panic(err)
	}
	conn.Write(buf[:n])
	conn.Close()
}()

fmt.Println(<-result)
Output:

hola

Types

type Client

type Client struct {
	// URL of the server to connect.
	// It should be ws:// or wss:// protocol
	URL string

	// Headers to send to the server while opening a connection
	Header http.Header

	// HandshakeTimeout specifies the duration for the handshake to complete.
	HandshakeTimeout time.Duration
}

A Client calls a server through websocket and waits for connections.

func (*Client) DialAndListen

func (c *Client) DialAndListen() (net.Listener, error)

DialAndListen does a websocket

type Dialer

type Dialer struct {
	Header http.Header
	// contains filtered or unexported fields
}

Dialer represent an active connection with a client, ready to be open

func (*Dialer) Dial

func (d *Dialer) Dial() (net.Conn, error)

Dial opens a new connection to the client

type EndpointHandler

type EndpointHandler interface {
	HandleEndpoint(*Dialer)
}

EndpointHandler responds to a new connection from a client

type EndpointHandlerFunc

type EndpointHandlerFunc func(*Dialer)

The EndpointHandlerFunc type is an adapter to allow the use of ordinary functions as Endpoint Handlers.

func (EndpointHandlerFunc) HandleEndpoint

func (f EndpointHandlerFunc) HandleEndpoint(d *Dialer)

HandleEndpoint calls f(d)

type Server

type Server struct {
	// Addr to listen (only needed if started with ListenAndServe)
	Addr string

	// Handler called when a new connection is called.
	Handler EndpointHandler
	// contains filtered or unexported fields
}

Server will waits for connections to arrives and then calls EndpointHandler It can be use directly with the ListenAndServe functions or indirectly as an http.Handler

func (*Server) ListenAndServe

func (s *Server) ListenAndServe() error

ListenAndServe will create a basic http server listening in the given address. If you need more control over the server, you can use the hole server as an http.Handler

func (*Server) ListenAndServeTLS

func (s *Server) ListenAndServeTLS(certFile, keyFile string) error

ListenAndServeTLS is the TLS version of ListenAndServe

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP establish the connection with the client and calls the EndpointHandler

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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