DEV Community

WHOAMI V
WHOAMI V

Posted on

Build a WebSocket server easily with Go: EasyWS makes everything simple

Here's an English version of the promotional blog post for EasyWS:


Say Goodbye to Complexity! EasyWS: Building WebSocket Servers in Go Just Got Easier! ๐Ÿš€

Are you tired of grappling with the complexities of connection management, message broadcasting, and concurrency when building WebSocket applications in Go? Good news! Today, I'm thrilled to introduce EasyWSโ€”a lightweight, highly extensible Go library designed to help you effortlessly build high-performance, scalable WebSocket servers.


โœจ Why Choose EasyWS?

EasyWS is engineered with "simplicity and efficiency" at its core. It abstracts away the low-level intricacies of WebSocket handling, allowing you to focus your energy on implementing your core business logic. Whether you're developing real-time chat applications, dynamic dashboards, or multiplayer online games, EasyWS provides the solid foundation you need.

Here are the highlights of EasyWS:

  • Ultimate Simplicity: Get a fully functional WebSocket server up and running with just a few lines of code.
  • High Flexibility: Easily plug in custom logic to handle connections, disconnections, messages, and even user authentication.
  • Concurrency-Safe: Built with Go's native concurrency primitives (like sync.RWMutex and channels) to ensure all operations are thread-safe.
  • Excellent Scalability: Designed for efficient management of numerous client connections and message broadcasting.
  • Comprehensive Customization: From client ID generation to cross-origin checks, and even integration with existing HTTP routersโ€”everything is under your control.

๐Ÿ“ฆ Installation, Effortlessly Done!

Getting started with EasyWS is incredibly straightforward; just one command:

go get github.com/whoamixl/easyws
Enter fullscreen mode Exit fullscreen mode

EasyWS relies on github.com/gorilla/websocket, which will be automatically fetched when you run go mod tidy.


๐Ÿš€ Quick Start & Demo

Let's demonstrate the power of EasyWS with a simple "echo server" example. This server not only echoes messages but also handles client authentication and broadcasts messages from all authenticated clients to others!

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/whoamixl/easyws" // Import the EasyWS library
)

func main() {
    // Create a new WebSocket server instance
    server := easyws.NewWebSocketServer()

    // Configure Hub callbacks โ€“ this is where your application logic goes!
    server.Hub.OnConnect = func(client *easyws.Client) error {
        log.Printf("New client connected: %s", client.ID)
        // Send a welcome message to the newly connected client
        client.SendText("Welcome to the EasyWS echo server! Please send 'AUTH <your_secret_key>' to authenticate.")
        return nil // Return nil if connection should proceed
    }

    server.Hub.OnDisconnect = func(client *easyws.Client, err error) {
        if err != nil {
            log.Printf("Client %s disconnected with error: %v", client.ID, err)
        } else {
            log.Printf("Client %s disconnected cleanly.", client.ID)
        }
        // You can notify other clients here that someone disconnected
    }

    server.Hub.OnMessage = func(client *easyws.Client, messageType int, data []byte) error {
        msg := string(data)
        log.Printf("Received message from %s: %s (Type: %d)", client.ID, msg, messageType)

        // Echo the message back to the sender
        client.SendText("You said: " + msg)

        // Broadcast the message to all other *authenticated* clients
        // Note: The authentication check happens *before* OnMessage if OnAuth is set.
        // We still check here for clarity of what's being broadcast.
        if client.GetAuth() {
            server.Hub.BroadcastText(fmt.Sprintf("Client %s (authenticated) says: %s", client.ID, msg))
        } else {
            client.SendText("Please authenticate first to participate in broadcast!")
        }
        return nil // Return nil if message was processed successfully
    }

    // Implement a simple authentication mechanism
    server.Hub.OnAuth = func(client *easyws.Client, messageType int, data []byte) (bool, error) {
        authMsg := string(data)
        log.Printf("Client %s attempting to authenticate with: %s", client.ID, authMsg)

        // Simple secret key authentication
        if authMsg == "AUTH mysecretkey123" {
            client.SendText("Authentication successful!")
            log.Printf("Client %s authenticated successfully.", client.ID)
            return true, nil // Return true for successful authentication
        }
        client.SendText("Authentication failed! Please send 'AUTH <your_secret_key>'")
        log.Printf("Client %s authentication failed.", client.ID)
        return false, nil // Return false if authentication fails
    }

    // --- Advanced Customization (Optional) ---

    // Customize client ID generation (e.g., from a URL query parameter)
    server.SetGenerateClientID(func(r *http.Request) string {
        if id := r.URL.Query().Get("user_id"); id != "" {
            return "user_" + id
        }
        // Fallback to a timestamp-based ID if no user_id is provided
        return fmt.Sprintf("guest_%d", time.Now().UnixNano())
    })

    // Set custom CORS check (e.g., allow specific origins for security)
    server.SetCheckOrigin(func(r *http.Request) bool {
        origin := r.Header.Get("Origin")
        // Allow requests from specific origins
        return origin == "http://localhost:3000" || origin == "https://your-frontend-domain.com"
        // For local development, `return true` is often used, but be cautious in production.
    })

    // Start the server with default options (:8080/ws)
    log.Println("Starting EasyWS server on :8080/ws")
    if err := server.StartWithDefaults(); err != nil {
        log.Fatalf("Server failed to start: %v", err)
    }
}
Enter fullscreen mode Exit fullscreen mode

To run this demo:

  1. Save the code as main.go in your project.

  2. Make sure you have your go.mod file correctly set up.

  3. Run go mod tidy to ensure all dependencies are fetched.

  4. Execute go run main.go from your terminal.

  5. Open your browser's developer console and connect using JavaScript, or use a WebSocket client tool:

    const ws = new WebSocket("ws://localhost:8080/ws?user_id=johndoe");
    
    ws.onopen = () => {
        console.log("Connected to WebSocket server!");
        ws.send("Hello there!"); // This won't be processed by OnMessage until authenticated
        setTimeout(() => {
            ws.send("AUTH mysecretkey123"); // Authenticate after a short delay
        }, 1000);
    };
    
    ws.onmessage = (event) => {
        console.log("Received message:", event.data);
    };
    
    ws.onclose = () => {
        console.log("Disconnected from WebSocket server.");
    };
    
    ws.onerror = (error) => {
        console.error("WebSocket error:", error);
    };
    

๐Ÿ› ๏ธ Core Components of EasyWS

EasyWS is built around a few key abstractions that make managing WebSockets intuitive:

Hub: Your WebSocket Central Hub

The Hub is the central orchestrator of your WebSocket application. It's responsible for managing all connected clients, handling their registration and unregistration, and facilitating message broadcasting. You'll primarily interact with the Hub by setting its powerful event-driven callback functions.

  • OnConnect(client *Client) error: Invoked after a new client successfully establishes a WebSocket connection.
  • OnDisconnect(client *Client, err error): Called when a client's connection is closed.
  • OnMessage(client *Client, messageType int, data []byte) error: Executed when a client sends a message to the server. For unauthenticated clients, this is only called after OnAuth (if configured) has successfully authenticated them.
  • OnAuth(client *Client, messageType int, data []byte) (bool, error): (Optional) If set, this callback is the first to process any incoming message from an unauthenticated client.
  • BroadcastText(text string): Sends a string message to all currently connected clients.

Client: The Individual Connection Representative

Each Client object represents an individual, active WebSocket connection. It encapsulates the underlying *websocket.Conn and provides convenient methods for interacting with that specific client.

  • ID string: A unique identifier assigned to the client.
  • IsAuth bool: A flag indicating whether the client has been authenticated.
  • UserData map[string]interface{}: A flexible, thread-safe map where you can store any custom data pertinent to this client's session.
  • SendText(text string) error: Queues a string message to be sent to this client.
  • SetAuth(isAuth bool) / GetAuth() bool: Safely update and retrieve the client's authentication status.

WebSocketServer: Launch Your WebSocket Service

The WebSocketServer is your entry point for setting up and running the WebSocket listener within your HTTP server. It handles the initial HTTP upgrade request to a WebSocket connection.

  • NewWebSocketServer(): Creates a new WebSocketServer instance, including a default Hub.
  • SetCheckOrigin(checkOrigin func(r *http.Request) bool): Allows you to override the default CheckOrigin function used by gorilla/websocket. This is crucial for controlling Cross-Origin Resource Sharing (CORS) in production environments.
  • SetGenerateClientID(generateClientID func(r *http.Request) string): Provides a powerful hook to define your own logic for how client IDs are generated.
  • StartWithDefaults() error: A convenient helper to start the server on the default address ":8080" and WebSocket path prefix "/ws".

๐Ÿค Contribution & Collaboration

EasyWS is still evolving, and we enthusiastically welcome contributions of all forms! Whether you have ideas for new features, bug fixes, or improvements, please feel free to visit our GitHub repository: https://github.com/whoamixl/easyws to open an issue or submit a pull request.


๐Ÿ“„ License

EasyWS is open-source software licensed under the MIT License. See the LICENSE file for more details.


What are you waiting for? Experience EasyWS now and make your Go WebSocket development simpler and more enjoyable than ever before! We believe EasyWS will become an invaluable tool for building powerful real-time applications.


What other features would you like to see in EasyWS, or what improvements do you envision for future versions? Feel free to share your thoughts in the comments!

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.