mirror of
https://github.com/zap-proto/zip.git
synced 2026-10-19 15:38:57 +00:00
A socket op's handshake is decided before its handler runs, so a handler with effects runs only for a connection it will get: 426 for another protocol version, 400 without a key, and 403 for a browser on an origin the op does not admit — its own, plus zip.Origins(...). The origin policy is the op's, not the answer's, so Socket carries only Serve, Subprotocols and Compression, and wsx's own refusals are back to the library's. The in-process CLI refuses a socket op before invoking it. An op that answers JSON or bytes keeps JSON's media for its JSON when Produces names the bytes'. 204, 304 and a redirecting op's 3xx carry no body in the document; any other declared status, 3xx included, carries the Out and the doc comment's example, as before. An MCP read of a Reader is bounded in time as a stream is, and the reader closed. TestConnMemory counts the goroutines holding connections added, as it counts the heap they added, so goroutines other tests leave winding down are not charged to the server.
74 lines
2.5 KiB
Go
74 lines
2.5 KiB
Go
// Package wsx provides Fiber-v3-compatible WebSocket support via
|
|
// fasthttp/websocket. The public surface is intentionally small:
|
|
//
|
|
// app.Get("/ws", wsx.Upgrade(func(c *wsx.Conn) error {
|
|
// for {
|
|
// _, msg, err := c.ReadMessage()
|
|
// if err != nil { return err }
|
|
// _ = c.WriteMessage(wsx.TextMessage, msg)
|
|
// }
|
|
// }))
|
|
//
|
|
// A typed op that answers a WebSocket returns a [zip.Socket] instead; both run
|
|
// the same upgrade (internal/ws).
|
|
package wsx
|
|
|
|
import (
|
|
"github.com/fasthttp/websocket"
|
|
"github.com/valyala/fasthttp"
|
|
|
|
"github.com/zap-proto/zip"
|
|
"github.com/zap-proto/zip/internal/ws"
|
|
)
|
|
|
|
// Conn is the WebSocket connection passed to wsx handlers.
|
|
type Conn = ws.Conn
|
|
|
|
// Message-type constants re-exported for convenience.
|
|
const (
|
|
TextMessage = websocket.TextMessage
|
|
BinaryMessage = websocket.BinaryMessage
|
|
CloseMessage = websocket.CloseMessage
|
|
PingMessage = websocket.PingMessage
|
|
PongMessage = websocket.PongMessage
|
|
)
|
|
|
|
// Handler is the wsx handler signature.
|
|
type Handler func(c *Conn) error
|
|
|
|
// Config configures the WebSocket upgrade. It is the type [zip.Socket]'s
|
|
// Config field holds.
|
|
type Config = ws.Config
|
|
|
|
// Upgrade returns a zip.Handler that upgrades the HTTP connection to a
|
|
// WebSocket and calls fn with the established *Conn.
|
|
func Upgrade(fn Handler, opts ...Config) zip.Handler {
|
|
var cfg Config
|
|
if len(opts) > 0 {
|
|
cfg = opts[0]
|
|
}
|
|
// An untyped upgrade admits every origin unless told otherwise: zip is
|
|
// multi-tenant and such a route gates on its own. A typed [zip.Socket]
|
|
// keeps the library's same-origin default.
|
|
if cfg.CheckOrigin == nil {
|
|
cfg.CheckOrigin = func(*fasthttp.RequestCtx) bool { return true }
|
|
}
|
|
// TERMINAL: the upgrade takes over the connection, so this answers the
|
|
// address it is registered at and never yields to anything after it.
|
|
// zip.Terminal is what makes app.Use(wsx.Upgrade(…)) a build error instead
|
|
// of a server that tries to upgrade every request it receives.
|
|
return zip.Terminal("wsx.Upgrade", func(c *zip.Ctx) error {
|
|
rc := c.Fiber().RequestCtx()
|
|
// A plain request to a WebSocket address is the client's mistake — a
|
|
// crawler, a health probe, a browser tab opened on the URL. Answered 426
|
|
// (RFC 9110 §15.5.22) with the protocol to switch to, as a client error,
|
|
// rather than handed to the upgrader, whose handshake error is reported
|
|
// as a server fault.
|
|
if !ws.Is(rc) {
|
|
c.SetHeader("Upgrade", "websocket")
|
|
return zip.Errorf(fasthttp.StatusUpgradeRequired, ws.Refusal)
|
|
}
|
|
return ws.Upgrade(rc, cfg, func(conn *Conn) { _ = fn(conn) })
|
|
})
|
|
}
|