Skip to content

sockudo/sockudo-http-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

303 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sockudo-http-go

Official Go server SDK for Sockudo — a fast, self-hosted WebSocket server with full Pusher HTTP API compatibility.

Supported Platforms

  • Go 1.21 or greater

Table of Contents

Installation

go get github.com/sockudo/sockudo-http-go/v5

Getting Started

package main

import (
    sockudo "github.com/sockudo/sockudo-http-go/v5"
)

func main() {
    client := sockudo.Client{
        AppID:  "your-app-id",
        Key:    "your-app-key",
        Secret: "your-app-secret",
        Host:   "127.0.0.1",
        Port:   "6001",
    }

    data := map[string]string{"message": "hello world"}

    err := client.Trigger("my-channel", "my-event", data)
    if err != nil {
        panic(err)
    }
}

Configuration

Direct struct initialization

client := sockudo.Client{
    AppID:  "your-app-id",
    Key:    "your-app-key",
    Secret: "your-app-secret",
    Host:   "127.0.0.1",
    Port:   "6001",
    Secure: false,
}

From URL

client := sockudo.ClientFromURL("http://key:secret@127.0.0.1:6001/apps/app-id")

From Environment Variable

// Reads SOCKUDO_URL, expected format: http://key:secret@host:port/apps/app_id
client := sockudo.ClientFromEnv("SOCKUDO_URL")

HTTPS

client.Secure = true

Custom HTTP Client (timeouts, keep-alive, etc.)

import "net/http"
import "time"

client.HTTPClient = &http.Client{Timeout: time.Second * 5}

If no HTTP client is set, a default is created with a 5-second timeout.

Triggering Events

Single Channel

data := map[string]string{"hello": "world"}
err := client.Trigger("my-channel", "my-event", data)

With Parameters (excluding a socket, requesting channel info)

socketID := "1234.12"
params := sockudo.TriggerParams{SocketID: &socketID}
channels, err := client.TriggerWithParams("my-channel", "my-event", data, params)

Multiple Channels

err := client.TriggerMulti([]string{"channel-1", "channel-2"}, "my-event", data)

Batch Events

batch := []sockudo.Event{
    {Channel: "channel-1", Name: "event-1", Data: "hello"},
    {Channel: "channel-2", Name: "event-2", Data: "world"},
}
response, err := client.TriggerBatch(batch)

The Event struct:

type Event struct {
    Channel  string
    Name     string
    Data     interface{}
    SocketID *string
}

Idempotent Publishing

Pass an IdempotencyKey in TriggerParams to safely retry publishes without causing duplicate deliveries:

key := "order-shipped-order-789"
params := sockudo.TriggerParams{IdempotencyKey: &key}
err := client.TriggerWithParams("my-channel", "my-event", data, params)

The server deduplicates publishes with the same key within the configured window.

Authenticating Users

Use AuthenticateUser to generate an authentication response for user connections. The userData map must contain at least an "id" key.

func sockudoUserAuth(res http.ResponseWriter, req *http.Request) {
    params, _ := io.ReadAll(req.Body)
    userData := map[string]interface{}{
        "id":   "user-123",
        "name": "Jane Doe",
    }
    response, err := client.AuthenticateUser(params, userData)
    if err != nil {
        http.Error(res, "unauthorized", http.StatusUnauthorized)
        return
    }
    fmt.Fprint(res, string(response))
}

func main() {
    http.HandleFunc("/sockudo/user-auth", sockudoUserAuth)
    http.ListenAndServe(":5000", nil)
}

Authorizing Channels

Private Channel

func sockudoAuth(res http.ResponseWriter, req *http.Request) {
    params, _ := io.ReadAll(req.Body)
    response, err := client.AuthorizePrivateChannel(params)
    if err != nil {
        http.Error(res, "unauthorized", http.StatusUnauthorized)
        return
    }
    fmt.Fprint(res, string(response))
}

func main() {
    http.HandleFunc("/sockudo/auth", sockudoAuth)
    http.ListenAndServe(":5000", nil)
}

Presence Channel

params, _ := io.ReadAll(req.Body)

member := sockudo.MemberData{
    UserID: "user-123",
    UserInfo: map[string]string{
        "name": "Jane Doe",
    },
}

response, err := client.AuthorizePresenceChannel(params, member)
if err != nil {
    panic(err)
}

fmt.Fprint(res, string(response))

The MemberData struct:

type MemberData struct {
    UserID   string
    UserInfo map[string]string
}

Application State

List Channels

prefix := "presence-"
attribute := "user_count"
params := sockudo.ChannelsParams{FilterByPrefix: &prefix, Info: &attribute}
channels, err := client.Channels(params)

// channels => &{Channels:map[presence-room:{UserCount:4}]}

Get a Single Channel

attribute := "user_count,subscription_count"
params := sockudo.ChannelParams{Info: &attribute}
channel, err := client.Channel("presence-room", params)

// channel => &{Name:presence-room Occupied:true UserCount:42 SubscriptionCount:42}

Get Users in a Presence Channel

users, err := client.GetChannelUsers("presence-room")

// users => &{List:[{ID:13} {ID:90}]}

Webhook Validation

func sockudoWebhook(res http.ResponseWriter, req *http.Request) {
    body, _ := io.ReadAll(req.Body)
    webhook, err := client.Webhook(req.Header, body)
    if err != nil {
        fmt.Println("Invalid webhook:", err)
        http.Error(res, "invalid", http.StatusUnauthorized)
        return
    }

    for _, event := range webhook.Events {
        fmt.Printf("event: %s, channel: %s\n", event.Name, event.Channel)
    }

    res.WriteHeader(http.StatusOK)
}

The Webhook struct:

type Webhook struct {
    TimeMs int
    Events []WebhookEvent
}

type WebhookEvent struct {
    Name     string
    Channel  string
    Event    string
    Data     string
    SocketID string
}

End-to-End Encryption

Sockudo supports end-to-end encryption for private-encrypted- prefixed channels. Only your server and connected clients can read the messages.

  1. Generate a 32-byte master key:

    openssl rand -base64 32
  2. Pass it when constructing the client:

    client := sockudo.Client{
        AppID:                    "your-app-id",
        Key:                      "your-app-key",
        Secret:                   "your-app-secret",
        Host:                     "127.0.0.1",
        Port:                     "6001",
        EncryptionMasterKeyBase64: "<output from command above>",
    }
  3. Use channels prefixed with private-encrypted-.

Google App Engine

This library is compatible with Google App Engine's urlfetch library. Pass the urlfetch.Client as the HTTP client:

package main

import (
    "appengine"
    "appengine/urlfetch"
    "fmt"
    "net/http"
    sockudo "github.com/sockudo/sockudo-http-go/v5"
)

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)

    client := sockudo.Client{
        AppID:      "your-app-id",
        Key:        "your-app-key",
        Secret:     "your-app-secret",
        Host:       "127.0.0.1",
        Port:       "6001",
        HTTPClient: urlfetch.Client(c),
    }

    client.Trigger("my-channel", "my-event", map[string]string{"message": "hello"})

    fmt.Fprint(w, "OK")
}

Testing

go test ./...

Channel History

limit := 50
direction := "newest_first"
page, err := client.ChannelHistory("my-channel", sockudo.HistoryParams{
	Limit: &limit,
	Direction: &direction,
})

cursor := "opaque-cursor-from-previous-page"
nextPage, err := client.ChannelHistory("my-channel", sockudo.HistoryParams{
	Cursor: &cursor,
})

Pusher SDK Compatibility

Sockudo implements the full Pusher HTTP API. If you prefer to use the official github.com/pusher/pusher-http-go/v5 package or are migrating from Pusher, point it at your Sockudo instance:

import pusher "github.com/pusher/pusher-http-go/v5"

client := pusher.Client{
    AppID:  "your-app-id",
    Key:    "your-app-key",
    Secret: "your-app-secret",
    Host:   "127.0.0.1",
    Port:   "6001",
}

All standard Pusher SDK calls work against a self-hosted Sockudo server without modification.

License

MIT

About

Sockudo Go HTTP server SDK

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages