Server

moqt.Server manages server-side operations for the MoQ protocol. It listens for incoming QUIC connections, establishes MoQ sessions, relays data, and manages their lifecycle.

Overview
func main() {
    server := moqt.Server{
        Addr: "moqt.example.com:9000",
        TLSConfig: &tls.Config{
            NextProtos:         []string{"h3", "moq-00"},
            Certificates:       []tls.Certificate{loadCert()},
            InsecureSkipVerify: false,
        },
        QUICConfig: &quic.Config{
            Allow0RTT:       true,
            EnableDatagrams: true,
        },
        CheckHTTPOrigin: func(r *http.Request) bool {
            return r.Header.Get("Origin") == "https://trusted.example.com"
        },
        Logger: slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
            Level: slog.LevelInfo,
        })),
    }

    path := "/relay"

    // Handle WebTransport connections
    http.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
        err := server.HandleWebTransport(w, r)
        if err != nil {
            slog.Error("Failed to serve MoQ over WebTransport", "error", err)
        }
    })

    // Set up MoQ handler
    moqt.HandleFunc(path, func(w moqt.SetupResponseWriter, r *moqt.SetupRequest) {
        sess, err := moqt.Accept(w, r, nil)
        if err != nil {
            slog.Error("Failed to accept session", "error", err)
            return
        }

        slog.Info("New session established")

        // Handle announcements and tracks...
    })

    err := server.ListenAndServe()
    if err != nil {
        slog.Error("Failed to start server", "error", err)
    }
}

Initialize a Server

There is no dedicated function (such as a constructor) for initializing a server. Users define the struct directly and assign values to its fields as needed.

    server := moqt.Server{
        // Set server options here
    }

Configuration

The following table describes the public fields of the Server struct:

FieldTypeDescription
AddrstringServer address and port
TLSConfig*tls.ConfigTLS configuration for secure connections
QUICConfig*quic.ConfigQUIC protocol configuration
Config*moqt.ConfigMOQ protocol configuration
CheckHTTPOriginfunc(*http.Request) boolValidates the HTTP Origin header for WebTransport connections. If nil, all origins are accepted.
Handlermoqt.HandlerSet-up Request handler for routing
ListenFuncquic.ListenAddrFuncFunction to listen for QUIC connections
NewWebtransportServerFuncfunc(checkOrigin func(*http.Request) bool) webtransport.ServerFunction to create a new WebTransport server
Logger*slog.LoggerLogger for server events and errors

quic-go/quic-go is used internally as the default QUIC implementation when relevant fields which is set for customization are not set or nil.

quic-go/quic-go
Loading...
- -

To use a custom QUIC implementation, you need to provide your own implementation of the gomoqt/quic interfaces and quic.ListenAddrFunc. (moqt.Server).ListenFunc field is set, it is used to listen for incoming QUIC connections instead of the default implementation.

type Server struct {
    // ...
	ListenFunc quic.ListenAddrFunc
    // ...
}

quic-go/webtransport-go is used internally as the default WebTransport implementation when relevant fields which is set for customization are not set or nil.

quic-go/webtransport-go
Loading...
- -

To use a custom WebTransport implementation, you need to provide your own implementation of the webtransport.Server interface and a function to create it. (moqt.Server).NewWebtransportServerFunc field is set, it is used to create a new WebTransport server instead of the default implementation.

type Server struct {
    // ...
    NewWebtransportServerFunc func(checkOrigin func(*http.Request) bool) webtransport.Server
    // ...
}

Accept and Set-Up Sessions

Route Set-Up Requests

Before establishing sessions, servers have to handle incoming set-up requests for a specific path and route them to appropriate handlers. (Server).SetupHandler field is used for this purpose.

type Server struct {
    // ...
    SetupHandler SetupHandler
    // ...
}
type SetupHandler interface {
    ServeMOQ(SetupResponseWriter, *SetupRequest)
}

type SetupHandlerFunc func(SetupResponseWriter, *SetupRequest)

When (Server).SetupHandler is not set and is nil, moqt.DefaultRouter is the default router used by the server.

You can register your handlers to the default router as follows:

    server = &moqt.Server{
        SetupHandler: nil,
        // Other server fields...
    }

    // Register handlers to the default router
    moqt.DefaultRouter.HandleFunc("/path", func(w moqt.SetupResponseWriter, r *moqt.SetupRequest){/* ... */})
    moqt.DefaultRouter.Handle("/path", handler)

    // You can also use global functions
    moqt.HandleFunc("/path", handlerFunc)
    moqt.Handle("/path", handler)

If you need more control over routing, you can create a custom router and set it as the server’s handler:

    var router moqt.SetupHandler

    server = &moqt.Server{
        SetupHandler: router,
        // Other server fields...
    }

Accept Sessions

After a set-up request is routed to a specific handler and is accepted, a session is established.

    var server *moqt.Server
    var mux *moqt.TrackMux

    moqt.HandleFunc("/path", func(w moqt.SetupResponseWriter, r *moqt.SetupRequest) {
        sess, err := moqt.Accept(w, r, mux)
        if err != nil {
            // Handle error
            return
        }

        // Handle the established session
    })

The moqt.Accept function establishes a new MoQ session by accepting the setup request. It takes:

  • w moqt.SetupResponseWriter: Writer to send the server response
  • r *moqt.SetupRequest: The client’s setup request
  • mux *moqt.TrackMux: Multiplexer for track management (can be nil for default handling)

On success, it returns a *moqt.Session for managing the established connection.

Handle WebTransport Connections

For WebTransport-based MoQ sessions, integrate the server with an HTTP server using (moqt.Server).HandleWebTransport method.

Using with net/http:

http.HandleFunc("/moq", func(w http.ResponseWriter, r *http.Request) {
    err := server.HandleWebTransport(w, r)
    if err != nil {
        // Handle error
    }

    // Fallback to another protocol if not WebTransport
})

The (moqt.Server).HandleWebTransport method upgrades the HTTP/3 connection to WebTransport, accepts the session stream, and routes the setup request to the configured (moqt.Server).SetupHandler.

Run the Server

(moqt.Server).ListenAndServe() starts the server listening for incoming connections and setting up sessions.

    server.ListenAndServe()

For more advanced use cases:

  • ListenAndServeTLS(certFile, keyFile string): Starts the server with TLS certificates loaded from files.
  • ServeQUICListener(ln quic.Listener): Serves on an existing QUIC listener.
  • ServeQUICConn(conn quic.Connection): Handles a single QUIC connection directly.

Shutting Down a Server

Servers also support immediate and graceful shutdowns.

Immediate Shutdown

(moqt.Server).Close method terminates all sessions and closes listeners forcefully.

    server.Close() // Immediate shutdown

Graceful Shutdown

(moqt.Server).Shutdown method allows sessions to close gracefully before forcing termination.

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    err := server.Shutdown(ctx)
    if err != nil {
        // Handle forced termination
    }

Note: GOAWAY message

The current implementation does not send a GOAWAY message during shutdown. Immediate session closure occurs when the context is canceled. This will be updated once the GOAWAY message specification is finalized.

📝 Future Work

  • Implement GOAWAY message: (#XXX)