Skip to content

WebSocket routing

This example demonstrates Grip’s WebSocket capabilities, made simple by the Crystal Programming Language’s core team. It defines a WebSocket controller, custom middleware, and pipeline-based routing to handle WebSocket connections efficiently.

Application Code

# src/application.cr: Defines a Grip application with WebSocket support and middleware.

# Imports the Grip framework for building the web application.
require "grip"

# DemoController handles WebSocket connections and events.
class DemoController < Grip::Controllers::WebSocket
  # Executed when a client opens a WebSocket connection.
  # @param context [HTTP::Server::Context] The HTTP request context.
  # @param socket [Socket] The WebSocket connection.
  def on_open(context : Context, socket : Socket) : Void
    # Handle connection opening logic here.
  end

  # Executed when a client sends a text message.
  # @param context [HTTP::Server::Context] The HTTP request context.
  # @param socket [Socket] The WebSocket connection.
  # @param message [String] The received text message.
  def on_message(context : Context, socket : Socket, message : String) : Void
    # Handle text message logic here.
  end

  # Executed when a client sends a ping message.
  # @param context [HTTP::Server::Context] The HTTP request context.
  # @param socket [Socket] The WebSocket connection.
  # @param message [String] The ping message.
  def on_ping(context : Context, socket : Socket, message : String) : Void
    # Handle ping logic here.
  end

  # Executed when the server receives a pong message.
  # @param context [HTTP::Server::Context] The HTTP request context.
  # @param socket [Socket] The WebSocket connection.
  # @param message [String] The pong message.
  def on_pong(context : Context, socket : Socket, message : String) : Void
    # Handle pong logic here.
  end

  # Executed when a client sends a binary message.
  # @param context [HTTP::Server::Context] The HTTP request context.
  # @param socket [Socket] The WebSocket connection.
  # @param binary [Bytes] The binary message.
  def on_binary(context : Context, socket : Socket, binary : Bytes) : Void
    # Handle binary message logic here.
  end

  # Executed when a client closes the WebSocket connection.
  # @param context [HTTP::Server::Context] The HTTP request context.
  # @param socket [Socket] The WebSocket connection.
  # @param close_code [HTTP::WebSocket::CloseCode | Int?] The close code (optional).
  # @param message [String] The close message.
  def on_close(context : Context, socket : Socket, close_code : HTTP::WebSocket::CloseCode | Int?, message : String) : Void
    # Handle connection closing logic here.
  end
end

# PoweredByHeader adds a Server header to HTTP responses.
class PoweredByHeader
  include HTTP::Handler

  # Adds a Server header to the response.
  # @param context [HTTP::Server::Context] The HTTP request context.
  # @return [HTTP::Server::Context] The modified context with the added header.
  def call(context : HTTP::Server::Context) : HTTP::Server::Context
    context
      .put_resp_header("Server", "grip") # Sets the Server header to "grip".
  end
end

# SecureHeaders adds a Security header to HTTP responses.
class SecureHeaders
  include HTTP::Handler

  # Adds a Security header to the response.
  # @param context [HTTP::Server::Context] The HTTP request context.
  # @return [HTTP::Server::Context] The modified context with the added header.
  def call(context : HTTP::Server::Context) : HTTP::Server::Context
    context
      .put_resp_header("Security", "Absolutely") # Sets a custom Security header.
  end
end

# Application configures and runs a Grip web application.
class Application < Grip::Application
  # Defines the array of HTTP handlers for processing requests.
  property handlers : Array(HTTP::Handler) = [
    Grip::Handlers::Pipeline.new, # Manages the request pipeline for middleware execution.
    Grip::Handlers::WebSocket.new  # Handles WebSocket protocol logic.
    Grip::Handlers::HTTP.new,     # Handles core HTTP protocol logic, it must be the last one as it handles all the requests.
  ] of HTTP::Handler

  # Initializes the application and configures routes.
  def initialize
    routes # Calls the routes method to set up routing.
  end

  # Defines routing structure and pipelines for WebSocket connections.
  def routes
    # Pipeline for API requests, applying the PoweredByHeader middleware.
    pipeline :api, [
      PoweredByHeader.new # Adds a Server header to responses.
    ]

    # Pipeline for web requests, applying the SecureHeaders middleware.
    pipeline :web, [
      SecureHeaders.new # Adds a Security header to responses.
    ]

    # Routes under the root path ("/").
    scope do
      pipe_through :web # Applies the SecureHeaders middleware.
      pipe_through :api # Applies the PoweredByHeader middleware.

      # Maps WebSocket connections to "/:id" to DemoController’s WebSocket methods.
      # The :id parameter can be accessed via context.fetch_path_params.
      ws "/:id", DemoController
    end
  end
end

# Instantiates and starts the Grip application.
app = Application.new
app.run