luna

package module
v0.0.0-...-e128b6a Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2019 License: GPL-3.0 Imports: 7 Imported by: 0

README

Luna Made in Nigeria

Create webSocket servers in a minute

Why

Websocket servers are little bit tricky and not very easy to build, we normally fall back to easy alternative BaaS platform like Firebase or services like Pusher to handle realtime data processing in our apps. Luna aim to make creating WebSocket servers in Golang something anyone can do.

How To

It only takes some few steps to use Luna.

Add luna to your project go get github.com/adigunhammedolalekan/luna

  1. create Luna instance
    g := gin.Default()

    // keyExtractor is a function that should return a
    // unique 'string'. It takes a *http.Request pointer.
    // The intention is to extract a unique Id from request's
    // pointer to set as a unique session key for every
    // webSocket request

	keyExtractor := func(req *http.Request) string {
    		return req.Header.Get("Authorization")
    }

    config := &luna.Config{
        KeyExtractor: keyExtractor,
    }

    l := luna.New(config)

Allow luna to handle websocket connection requests

// Example URL - ws://localhost:port/ws/connect

// Using Go default http stack
http.Handle("/ws/connect", func(w http.ResponseWriter, r *http.Request) {
	// This is a websocket connection, allow luna to handle it
	l.HandleHttpRequest(w, r)
})

// Using Gin
g := gin.Default()
g.GET("/ws/connect", func(context *gin.Context) {
	l.HandleHttpRequest(context.Writer, context.Request)
})
  1. Create json data payload from clients(Js frontend, Android, iOS etc) and use any Websocket client lib to send it
// Example payload
{
	"action" : "subscribe",
	"path" : "/rooms/22/message"
}
// This payload simply subscribe the sender's websocket client to path/channel
// `/rooms/22/message`, after subscription, all messages received by `/rooms/22/message`
// would be sent to this client and every other client that subscribed

// Example message payload
messagePayload = {
	"action" : "message",
	"path" : "/rooms/22/message",
	"data" : {
		"sent_by" : "L3kan",
		"text" : "Hey, there. Luna is awesome",
		"time" : "12:30"
	}
}

**action - can be any of the three option(subscribe, message, unsubscribe), what you use depends on what you want to do
**path - path is where you are sending the message to
**data - is the actual message you are sending
// This payload would send `payload.data` to every client that has subscribed to `/rooms/22/message` channel/path. Amazing!

To stop receiving messages on the client side(i.e UnSubscribe from a channel)
{
    "action" : "unsubscribe",
    "path" : "/rooms/22/message"
}

Although, Luna has a feature that automatically removes idle websocket clients.

As you can see, you can set up a websocket server with few lines of code using Luna.


func main() {

	g := gin.Default()

	// setup keyExtractor
	keyExtractor := func(req *http.Request) string {
    		return req.Header.Get("Authorization")
    }

    // create config
    config := &luna.Config{
        KeyExtractor: keyExtractor,
    }

    // start websocket server
	l := luna.New(config)

    // handle websocket connection requests
	g.GET("/ws/connect", func(c *gin.Context) {
		l.HandleHttpRequest(c.Writer, c.Request)
	})

	// OPTIONAL: Handle a message `payload.data` sent to a particular channel/path
	l.Handle("/rooms/{id}/message", func(c *luna.Context) {
		// Message has been sent to appropriate Websocket clients.
		// do other stuffs here, like saving message into a persistence layer?

        m := &Message{}
        err := json.UnMarshall(c.Data, m)
        if err == nil {
		    Db.Save(m)

		    // you can also use the extracted path
		    // parameters
		    vars := c.Vars
            fmt.Println("Id => ", vars["id"] . (string))
            fmt.Println("Got message from path => " +  c.Path)
        }
	})
}

A full example with a Javascript client can be found in example folder

Console

Web

TODO

  • Add support for FCM - Firebase Cloud Messaging
  • Write more tests

I built a small wrapper lib for android. luna-android

Thanks to @olahol for creating melody.

Follow me on Twitter @L3kanAdigun if you have any question.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	COMMAND_SUBSCRIBE   = "subscribe"
	COMMAND_MESSAGE     = "message"
	COMMAND_UNSUBSCRIBE = "unsubscribe"
)
View Source
var DefaultConfig = &Config{BufferSize: 512 * 10, MaxMessageSize: 512 * 10}

Functions

func ExtractParams

func ExtractParams(template, path string) (map[string]interface{}, error)

ExtractParams extracts data from path parameter.

func MatchRoute

func MatchRoute(pattern, path string) bool

MatchRoute checks if path matches the patten supplied. E.g pattern=/rooms/{id}/messages will match path=/rooms/12/messages

Types

type Channel

type Channel struct {
	Id      string
	Mtx     sync.Mutex
	Clients map[*Client]bool
}

A message channel. Multiple clients can subscribe to a channel, message will be broadcast to all clients anytime Channel.Send(data) is called

func (*Channel) Lock

func (ch *Channel) Lock()

func (*Channel) Send

func (ch *Channel) Send(data interface{}) error

Send broadcast message to all connected clients and update their last activity time

func (*Channel) Subscribe

func (ch *Channel) Subscribe(session *melody.Session)

Subscribe subscribes a session to a client.

func (*Channel) UnLock

func (ch *Channel) UnLock()

func (*Channel) UnSubscribe

func (ch *Channel) UnSubscribe(session *melody.Session)

UnSubscribe removes a session from a channel

type Client

type Client struct {
	Session  *melody.Session
	LastSeen time.Time
}

type Config

type Config struct {
	BufferSize     int
	MaxMessageSize int64
	KeyExtractor   ExtractKeyFunc
}

Config holds luna configurations

type Context

type Context struct {
	Path string
	Vars map[string]interface{}
	Data interface{}
}

type ExtractKeyFunc

type ExtractKeyFunc func(*http.Request) string

ExtractKeyFunc to assign a unique key for each session can be used like :-

keyFunc := func(r *http.Request) string {
		return r.Header.Get("x-auth-token")
}

type Hub

type Hub struct {
	Channels []*Channel //Slice of channels in this Hub
}

Hub holds all channels in a slice. Clients can subscribe and send message to channels through the Hub

func (*Hub) ClientsCount

func (h *Hub) ClientsCount(id string) int

ClientsCount returns no of connected clients in a channel

func (*Hub) Count

func (h *Hub) Count() int

Count returns number of connected channels

func (*Hub) EnsureClean

func (h *Hub) EnsureClean()

EnsureClean keep clients slice clean. Remove all clients that has been idle for more than 10minutes

func (*Hub) GetChannel

func (h *Hub) GetChannel(id string) *Channel

GetChannel returns a Channel identified by @param id

func (*Hub) Send

func (h *Hub) Send(channel string, data interface{}) error

Send sends data payload to a channel. This broadcast @params data to all connected clients

func (*Hub) Subscribe

func (h *Hub) Subscribe(id string, session *melody.Session) *Channel

Subscribe subscribes a client to a channel. Create channel if it does not exists before

func (*Hub) UnSubscribe

func (h *Hub) UnSubscribe(id string, session *melody.Session)

type Luna

type Luna struct {
	// contains filtered or unexported fields
}

func New

func New(config *Config) *Luna

New creates a new Luna instance

func (*Luna) Handle

func (l *Luna) Handle(path string, f OnMessageHandler)

Handle registers a new Route

func (*Luna) HandleHttpRequest

func (l *Luna) HandleHttpRequest(wr http.ResponseWriter, req *http.Request) error

func (*Luna) Publish

func (l *Luna) Publish(channel string, data interface{}) error

Publish sends @param data to channel @param channel

type OnMessageHandler

type OnMessageHandler func(context *Context)

type Route

type Route struct {
	Path         string
	OnNewMessage OnMessageHandler
}

type WsMessage

type WsMessage struct {
	Path   string      `json:"path"`
	Action string      `json:"action"`
	Data   interface{} `json:"data"`
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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