PageSourceSearch

https://docs.gofiber.io/assets/js/9885ae42.20a192ca.js

js gofiber.io collected 2026-10-03 19:32:23 UTC 61,896 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkfiber_docs=self.webpackChunkfiber_docs||[]).push([["12766"],{58112(e,t,n){n.r(t),n.d(t,{metadata:()=>s,default:()=>h,frontMatter:()=>r,contentTitle:()=>c,toc:()=>o,assets:()=>d});var s=JSON.parse('{"id":"socketio/socketio","title":"Socket.io","description":"Release","source":"@site/contrib_versioned_docs/version-v3_loadshed_v0.x.x/socketio/README.md","sourceDirName":"socketio","slug":"/socketio/","permalink":"/contrib/v3_loadshed_v0.x.x/socketio/","draft":false,"unlisted":false,"editUrl":"https://github.com/gofiber/contrib/edit/main/v3/socketio/README.md","tags":[],"version":"v3_loadshed_v0.x.x","lastUpdatedAt":1791052031000,"frontMatter":{"id":"socketio"},"sidebar":"left_sidebar","previous":{"title":"Sentry","permalink":"/contrib/v3_loadshed_v0.x.x/sentry/"},"next":{"title":"SocketIO Legacy Event Shim","permalink":"/contrib/v3_loadshed_v0.x.x/socketio/legacy/"}}'),i=n(474848),l=n(28453);let r={id:"socketio"},c="Socket.io",d={},o=[{value:"Features",id:"features",level:2},{value:"Known limitations",id:"known-limitations",level:2},{value:"Production hardening notes",id:"production-hardening-notes",level:4},{value:"Configuration",id:"configuration",level:2},{value:"Go version support",id:"go-version-support",level:2},{value:"Install",id:"install",level:2},{value:"Protocol compatibility",id:"protocol-compatibility",level:2},{value:"Required client configuration",id:"required-client-configuration",level:3},{value:"Polling pitfalls",id:"polling-pitfalls",level:4},{value:"Tunable globals",id:"tunable-globals",level:3},{value:"Message format",id:"message-format",level:3},{value:"Acks, namespaces, handshake auth",id:"acks-namespaces-handshake-auth",level:3},{value:"Multi-argument emits",id:"multi-argument-emits",level:4},{value:"Server-initiated acks",id:"server-initiated-acks",level:4},{value:"Client-initiated acks",id:"client-initiated-acks",level:4},{value:"Namespaces",id:"namespaces",level:4},{value:"Handshake auth",id:"handshake-auth",level:4},{value:"Signatures",id:"signatures",level:2},{value:"Example",id:"example",level:2},{value:"Go server",id:"go-server",level:3},{value:"TypeScript / JavaScript client",id:"typescript--javascript-client",level:3},{value:"Supported events",id:"supported-events",level:2},{value:"Event Payload object",id:"event-payload-object",level:2},{value:"Socket instance functions",id:"socket-instance-functions",level:2}];function a(e){let t={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",hr:"hr",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,l.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.header,{children:(0,i.jsx)(t.h1,{id:"socketio",children:"Socket.io"})}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.img,{src:"https://img.shields.io/github/v/tag/gofiber/contrib?filter=*socketio*",alt:"Release"}),"\n",(0,i.jsx)(t.a,{href:"https://gofiber.io/discord",children:(0,i.jsx)(t.img,{src:"https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7",alt:"Discord"})}),"\n",(0,i.jsx)(t.img,{src:"https://github.com/gofiber/contrib/workflows/Test%20Socket.io/badge.svg",alt:"Test"})]}),"\n",(0,i.jsxs)(t.p,{children:["WebSocket wrapper for ",(0,i.jsx)(t.a,{href:"https://github.com/gofiber/fiber",children:"Fiber"})," that implements the ",(0,i.jsx)(t.a,{href:"https://github.com/socketio/engine.io-protocol",children:"Engine.IO v4"})," / ",(0,i.jsx)(t.a,{href:"https://github.com/socketio/socket.io-protocol",children:"Socket.IO v5"})," wire protocol, making it fully compatible with the official ",(0,i.jsx)(t.a,{href:"https://socket.io/docs/v4/client-api/",children:(0,i.jsx)(t.code,{children:"socket.io-client"})})," library."]}),"\n",(0,i.jsxs)(t.p,{children:["For applications that used older ",(0,i.jsx)(t.code,{children:"socketio"})," releases as a plain WebSocket event bus, migrate to ",(0,i.jsx)(t.code,{children:"github.com/gofiber/contrib/v3/websocket/event"}),". A deprecated compatibility shim is available at ",(0,i.jsx)(t.code,{children:"github.com/gofiber/contrib/v3/socketio/legacy"}),"."]}),"\n",(0,i.jsx)(t.p,{children:(0,i.jsx)(t.strong,{children:"Compatible with Fiber v3."})}),"\n",(0,i.jsx)(t.h2,{id:"features",children:"Features"}),"\n",(0,i.jsx)(t.p,{children:"This middleware implements the full Engine.IO v4 / Socket.IO v5 wire protocol. Highlights:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Synchronous handshake."})," The Engine.IO OPEN / Socket.IO CONNECT exchange completes before the user ",(0,i.jsx)(t.code,{children:"New()"})," callback returns, so emits issued inside the callback are ordered after the handshake reply."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"HTTP long-polling fallback (opt-in)."})," Set ",(0,i.jsx)(t.code,{children:"socketio.EnablePolling = true"})," and mount the same handler for ",(0,i.jsx)(t.code,{children:"GET"})," and ",(0,i.jsx)(t.code,{children:"POST"})," to accept ",(0,i.jsx)(t.code,{children:"transport=polling"})," clients. Polling sessions speak the same Engine.IO v4 / Socket.IO v5 wire protocol over HTTP and route through the same listener API (Emit, Ack, Close, Broadcast). Polling-to-WebSocket transport upgrade is not yet implemented; sessions that connect via polling stay on polling."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Namespaces and handshake auth."})," The negotiated namespace is honoured for inbound and outbound packets; the client's connect-time ",(0,i.jsx)(t.code,{children:"auth"})," payload is exposed via ",(0,i.jsx)(t.code,{children:"Websocket.HandshakeAuth()"})," and ",(0,i.jsx)(t.code,{children:"EventPayload.HandshakeAuth"}),"."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Inbound acks."})," Client-initiated callbacks surface as ",(0,i.jsx)(t.code,{children:"EventPayload.HasAck"})," / ",(0,i.jsx)(t.code,{children:"AckID"}),"; reply once with ",(0,i.jsx)(t.code,{children:"payload.Ack(args...)"}),"."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Outbound acks."})," Server-initiated ",(0,i.jsx)(t.code,{children:"EmitWithAck"}),", ",(0,i.jsx)(t.code,{children:"EmitWithAckTimeout"}),", and ",(0,i.jsx)(t.code,{children:"EmitWithAckArgs"})," round-trip a callback id and invoke the supplied callback when the client acks (or on timeout/disconnect)."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Multi-arg events."})," Inbound events expose every argument tuple as ",(0,i.jsx)(t.code,{children:"EventPayload.Args [][]byte"}),"; outbound ",(0,i.jsx)(t.code,{children:"EmitArgs"})," / ",(0,i.jsx)(t.code,{children:"EmitWithAckArgs"})," send pre-encoded JSON tuples."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Deterministic heartbeat."})," Server PINGs every ",(0,i.jsx)(t.code,{children:"PingInterval"}),"; the connection is torn down if no PONG arrives within ",(0,i.jsx)(t.code,{children:"PingTimeout"}),"."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"EIO 0x1E batched frames."})," Multi-packet WebSocket frames separated by ASCII RS (",(0,i.jsx)(t.code,{children:"0x1E"}),") are parsed correctly, with a hard cap (",(0,i.jsx)(t.code,{children:"MaxBatchPackets"}),") to prevent slice-header amplification."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Reserved-event-name guard."}
1)," User code cannot register or emit names reserved by the protocol (e.g. ",(0,i.jsx)(t.code,{children:"connect"}),", ",(0,i.jsx)(t.code,{children:"disconnect"}),")."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"EIO version validation."})," Handshakes that advertise an unsupported ",(0,i.jsx)(t.code,{children:"EIO"})," version are rejected."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Auth payload validation."})," The auth blob must be a JSON object and is bounded by ",(0,i.jsx)(t.code,{children:"MaxAuthPayload"}),"; oversize or malformed payloads are answered with CONNECT_ERROR."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"DoS hardening."})," ",(0,i.jsx)(t.code,{children:"MaxPayload"}),", ",(0,i.jsx)(t.code,{children:"MaxBatchPackets"}),", ",(0,i.jsx)(t.code,{children:"MaxEventNameLength"}),", and ",(0,i.jsx)(t.code,{children:"MaxAuthPayload"})," bound every attacker-controlled length."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Lock-free listener registry"})," plus ",(0,i.jsx)(t.code,{children:"atomic.Bool isAlive"}),", removing the per-event mutex from the hot path."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Optional drop-frames-on-overflow."})," When ",(0,i.jsx)(t.code,{children:"DropFramesOnOverflow"})," is true, a saturated send queue drops the offending frame and fires ",(0,i.jsx)(t.code,{children:"EventError"})," instead of tearing down the connection."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Graceful drain."})," The package-level ",(0,i.jsx)(t.code,{children:"Shutdown(ctx)"})," closes every active socket and waits for each worker to exit (or until ",(0,i.jsx)(t.code,{children:"ctx"})," is cancelled)."]}),"\n"]}),"\n",(0,i.jsx)(t.h2,{id:"known-limitations",children:"Known limitations"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"One namespace per Engine.IO connection."})," Each WebSocket binds the namespace negotiated during the SIO CONNECT packet; multiplexing several namespaces over one EIO connection is not supported."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"No BINARY_EVENT (5) / BINARY_ACK (6)."})," Binary Socket.IO frames are passed through as raw ",(0,i.jsx)(t.code,{children:"EventMessage"})," data; attachment reassembly is not implemented."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"No connection-state recovery."})," Resume-on-reconnect (Socket.IO's ",(0,i.jsx)(t.code,{children:"connectionStateRecovery"})," feature) is not implemented; reconnects always start a fresh session."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"No polling-to-WebSocket transport upgrade."})," When polling is enabled, sessions that open with ",(0,i.jsx)(t.code,{children:"transport=polling"})," advertise an empty ",(0,i.jsx)(t.code,{children:"upgrades"})," array and stay on polling for the session lifetime. Clients that need WebSocket from the start should configure ",(0,i.jsx)(t.code,{children:"transports: ['websocket']"}),"."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"No JSONP polling fallback."})," JSONP requests (",(0,i.jsx)(t.code,{children:"?j=N"}),") are rejected with engine.io error code 3. Modern browsers use XHR2/fetch; JSONP support is not planned."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"CORS is not handled by the middleware."})," Mount ",(0,i.jsx)(t.code,{children:"github.com/gofiber/fiber/v3/middleware/cors"})," (or your preferred CORS middleware) upstream of the polling route to control the policy. Long-poll holds connections open for up to ~25s by default, so reverse-proxy timeouts must accommodate (e.g. nginx ",(0,i.jsx)(t.code,{children:"proxy_read_timeout >= 60s"})," and ",(0,i.jsx)(t.code,{children:"proxy_buffering off"}),")."]}),"\n"]}),"\n",(0,i.jsx)(t.h4,{id:"production-hardening-notes",children:"Production hardening notes"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Rate limiting"}),". Each polling open allocates a ",(0,i.jsx)(t.code,{children:"*Websocket"}
1)," plus 2 short-lived goroutines. With ",(0,i.jsx)(t.code,{children:"EnablePolling = true"})," an unauthenticated client can create sessions until ",(0,i.jsx)(t.code,{children:"HandshakeTimeout"})," reaps idle ones (10s default). Mount ",(0,i.jsx)(t.code,{children:"github.com/gofiber/fiber/v3/middleware/limiter"})," upstream of the route to bound concurrent session creation."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Write timeout"}),". A long-poll GET response that the client never reads pins a fasthttp worker on TCP backpressure. Configure ",(0,i.jsx)(t.code,{children:"fiber.Config{WriteTimeout: ...}"})," (a few seconds is typically appropriate) so abandoned reads do not strand workers."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Burst sizing"}),". ",(0,i.jsx)(t.code,{children:"PollQueueMaxFrames"})," (default ",(0,i.jsx)(t.code,{children:"1024"}),") bounds the per-session outbound buffer. With the default ",(0,i.jsx)(t.code,{children:"DropFramesOnOverflow = false"})," a synchronous burst of more than 1024 emits inside a single listener call disconnects the session with ",(0,i.jsx)(t.code,{children:"ErrSendQueueClosed"}),". Either pace large bursts across drains, raise ",(0,i.jsx)(t.code,{children:"PollQueueMaxFrames"}),", or set ",(0,i.jsx)(t.code,{children:"DropFramesOnOverflow = true"})," to tolerate overflow at the cost of dropped frames + ",(0,i.jsx)(t.code,{children:"EventError"}),"."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Listener panics"}),". Both transports recover panics inside the ",(0,i.jsx)(t.code,{children:"New()"})," callback and inside event listeners; the panic value is logged via the package ",(0,i.jsx)(t.code,{children:"Logger"})," hook. Avoid ",(0,i.jsx)(t.code,{children:"panic(string(attackerControlledBytes))"})," to prevent log injection in downstream consumers."]}),"\n"]}),"\n",(0,i.jsx)(t.h2,{id:"configuration",children:"Configuration"}),"\n",(0,i.jsx)(t.p,{children:"All tunables are package-level variables; override before the first connection is accepted."}),"\n",(0,i.jsxs)(t.table,{children:[(0,i.jsx)(t.thead,{children:(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Variable"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Default"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Meaning"})]})}),(0,i.jsxs)(t.tbody,{children:[(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"PingInterval"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"25s"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"How often the server emits Engine.IO PING."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"PingTimeout"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"20s"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Grace window for the client PONG before the connection is killed."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"HandshakeTimeout"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"10s"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Hard deadline for completing EIO OPEN + SIO CONNECT."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxPayload"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:[(0,i.jsx)(t.code,{children:"1_000_000"})," (1 MB)"]}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Max bytes per inbound WebSocket frame; advertised to the client."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxAuthPayload"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"8 KiB"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Max bytes for the SIO CONNECT auth JSON."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxBatchPackets"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"256"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Max EIO packets in a single ",(0,i.jsx)(t.code,{children:"0x1E"}),"-batched frame."]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxEventNameLength"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"256"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"}
1,children:"Max length of an inbound SIO event name."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"OutboundAckTimeout"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"30s"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Default ack deadline for ",(0,i.jsx)(t.code,{children:"EmitWithAck"}),"."]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"SendQueueSize"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"100"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Capacity of the per-connection outbound queue."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"DropFramesOnOverflow"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"false"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["If true, drop the offending frame on overflow (fires ",(0,i.jsx)(t.code,{children:"EventError"}),")."]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"RetrySendTimeout"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"20ms"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Back-off between send retries."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxSendRetry"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"5"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Max send retries before a frame is dropped."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"ReadTimeout"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"10ms"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Deprecated: no longer consulted by the read loop; kept for backward compatibility."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"EnablePolling"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"false"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["If true, the handler returned from ",(0,i.jsx)(t.code,{children:"New"})," also serves Engine.IO HTTP long-polling on ",(0,i.jsx)(t.code,{children:"GET"}),"/",(0,i.jsx)(t.code,{children:"POST"}),"."]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"PollingMaxBufferSize"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"1_000_000"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Cap on a single polling HTTP body (request POST or response GET drain)."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxPollWait"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"30s"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Maximum time a long-poll GET blocks waiting for outbound frames."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"PollQueueMaxFrames"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"1024"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Cap on buffered outbound frames per polling session; overflow honors ",(0,i.jsx)(t.code,{children:"DropFramesOnOverflow"}),"."]})]})]})]}),"\n",(0,i.jsxs)(t.p,{children:["Use ",(0,i.jsx)(t.code,{children:"socketio.Shutdown(ctx)"})," from ",(0,i.jsx)(t.code,{children:"fiber.App.ShutdownWithContext"})," for a deterministic drain."]}),"\n",(0,i.jsx)(t.h2,{id:"go-version-support",children:"Go version support"}),"\n",(0,i.jsxs)(t.p,{children:["We only support the latest two versions of Go. Visit ",(0,i.jsx)(t.a,{href:"https://go.dev/doc/devel/release",children:"https://go.dev/doc/devel/release"})," for more information."]}),"\n",(0,i.jsx)(t.h2,{id:"install",children:"Install"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-sh",children:"go get -u github.com/gofiber/fiber/v3\ngo get -u github.com/gofiber/contrib/v3/socketio\n"})}),"\n",(0,i.jsx)(t.h2,{id:"protocol-compatibility",children:"Protocol compatibility"}),"\n",(0,i.jsxs)(t.p,{children:["The middleware automatically handles the Engine.IO / Socket.IO handshake so you do ",(0,i.jsx)(t.strong,{children:"not"})," need any special server-side code; just point your ",(0,i.jsx)(t.code,{children:"socket.io-client"})," at the WebSocket endpoint."]}
1),"\n",(0,i.jsx)(t.h3,{id:"required-client-configuration",children:"Required client configuration"}),"\n",(0,i.jsxs)(t.p,{children:["The default ",(0,i.jsx)(t.code,{children:"socket.io-client"})," transport order is ",(0,i.jsx)(t.code,{children:"['polling', 'websocket']"}),". The middleware supports both, but polling is ",(0,i.jsx)(t.strong,{children:"opt-in"}),":"]}),"\n",(0,i.jsx)(t.p,{children:(0,i.jsx)(t.strong,{children:"WebSocket only (default):"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:'import { io } from "socket.io-client";\n\nconst socket = io("http://localhost:3000", {\n  path: "/ws",                 // match the Fiber route\n  transports: ["websocket"],   // skip polling\n});\n'})}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.strong,{children:"Polling (or polling + websocket fallback):"})," enable ",(0,i.jsx)(t.code,{children:"EnablePolling"})," server-side and mount the handler for both ",(0,i.jsx)(t.code,{children:"GET"})," and ",(0,i.jsx)(t.code,{children:"POST"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'socketio.EnablePolling = true\nh := socketio.New(func(kws *socketio.Websocket) { /* ... */ })\napp.Get("/ws", h)\napp.Post("/ws", h)\n// Optionally allow CORS preflight:\n// app.Options("/ws", h)\n'})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:'import { io } from "socket.io-client";\n\n// Default transport order: polling first, then upgrade attempt. Since this\n// implementation does not yet upgrade polling sessions to WebSocket, the\n// session stays on polling. For a forced WebSocket connect use\n// transports: ["websocket"]; for polling-only use transports: ["polling"].\nconst socket = io("http://localhost:3000", {\n  path: "/ws",\n});\n'})}),"\n",(0,i.jsxs)(t.blockquote,{children:["\n",(0,i.jsxs)(t.p,{children:["CORS is not handled by the middleware. If your client connects from a different origin, mount your preferred CORS middleware (e.g. ",(0,i.jsx)(t.code,{children:"github.com/gofiber/fiber/v3/middleware/cors"}),") upstream of the route. Long-polling holds a request open for up to ~25s by default, so reverse-proxy timeouts must accommodate (",(0,i.jsx)(t.code,{children:"proxy_read_timeout"})," >= 60s on nginx, ",(0,i.jsx)(t.code,{children:"proxy_buffering off"}),")."]}),"\n"]}),"\n",(0,i.jsx)(t.h4,{id:"polling-pitfalls",children:"Polling pitfalls"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Forgot to mount POST."})," Polling clients send packets via POST; without ",(0,i.jsx)(t.code,{children:"app.Post(path, h)"})," (or ",(0,i.jsx)(t.code,{children:"app.All(...)"}),") the server returns 404 and the client loops with ",(0,i.jsx)(t.code,{children:"transport error"}),". Always mount both ",(0,i.jsx)(t.code,{children:"GET"})," and ",(0,i.jsx)(t.code,{children:"POST"})," for polling routes."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsxs)(t.strong,{children:[(0,i.jsx)(t.code,{children:"kws.Conn"})," is nil on polling."]})," Use ",(0,i.jsx)(t.code,{children:"kws.IsPolling()"})," to branch, or stick to the transport-agnostic ",(0,i.jsx)(t.code,{children:"Emit"}),", ",(0,i.jsx)(t.code,{children:"EmitEvent"}),", ",(0,i.jsx)(t.code,{children:"EmitArgs"}),", ",(0,i.jsx)(t.code,{children:"EmitWithAck*"}),", ",(0,i.jsx)(t.code,{children:"Broadcast"}),", ",(0,i.jsx)(t.code,{children:"Ack"}),", and ",(0,i.jsx)(t.code,{children:"Close"})," methods. They all work identically on both transports."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Snapshot vs live request state."})," ",(0,i.jsx)(t.code,{children:"kws.Locals"}),", ",(0,i.jsx)(t.code,{children:"kws.Params"}),", ",(0,i.jsx)(t.code,{children:"kws.Query"}),", ",(0,i.jsx)(t.code,{children:"kws.Cookies"})," are captured at session-open time on polling sessions (because fasthttp recycles the request context after the OPEN handler returns). Store mutable per-connection data via ",(0,i.jsx)(t.code,{children:"kws.SetAttribute"})," instead."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsxs)(t.strong,{children:["Burst bigger than ",(0,i.jsx)(t.code,{children:"PollQueueMaxFrames"}),"."]})," With the default ",(0,i.jsx)(t.code,{children:"DropFramesOnOverflow = false"}),", emitting more than ",(0,i.jsx)(t.code,{children:"PollQueueMaxFrames"})," (1024) frames before any GET drains them tears the session down with ",(0,i.jsx)(t.code,{children:"ErrSendQueueClosed"}),". Either pace bursts, raise ",(0,i.jsx)(t.code,{children:"PollQueueMaxFrames"}),", or set ",(0,i.jsx)(t.code,{children:"DropFramesOnOverflow = true"})," to drop the offending frames + fire ",(0,i.jsx)(t.code,{children:"EventError(ErrSendQueueOverflow)"})," instead."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.strong,{children:"Body limit collision."})," If your Fiber app sets ",(0,i.jsx)(t.code,{children:"BodyLimit"})," lower than ",(0,i.jsx)(t.code,{children:"PollingMaxBufferSize"}),", fasthttp rejects the POST before our handler runs. Keep ",(0,i.jsx)(t.code,{children:"BodyLimit"})," >= ",(0,i.jsx)(t.code,{children:"PollingMaxBufferSize"}),"."]}),"\n"]}),"\n",(0,i.jsx)(t.h3,{id:"tunable-globals",children:"Tunable globals"}),"\n",(0,i.jsxs)(t.p,{children:["These package-level variables can be overridden before the first connection is accepted (typically in ",(0,i.jsx)(t.code,{children:"init()"})," or early in ",(0,i.jsx)(t.code,{children:"main"}),"). They control timing and limits for the Engine.IO / Socket.IO transport."]}
1),"\n",(0,i.jsxs)(t.table,{children:[(0,i.jsx)(t.thead,{children:(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Variable"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Default"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Description"})]})}),(0,i.jsxs)(t.tbody,{children:[(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"PingInterval"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"25 * time.Second"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Interval between Engine.IO PING frames sent by the server to keep the connection alive."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"PingTimeout"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"20 * time.Second"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"How long the server waits for the client's PONG before considering the connection dead."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"HandshakeTimeout"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"10 * time.Second"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Maximum time allowed for the Engine.IO / Socket.IO handshake (including namespace CONNECT) to complete."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxPayload"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:[(0,i.jsx)(t.code,{children:"1 << 20"})," (1 MiB)"]}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Maximum size in bytes for a single inbound WebSocket frame; oversize messages close the socket."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxAuthPayload"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:[(0,i.jsx)(t.code,{children:"8 << 10"})," (8 KiB)"]}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Maximum size in bytes for the Socket.IO CONNECT auth JSON."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxBatchPackets"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"256"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Maximum number of Engine.IO packets accepted in a single ",(0,i.jsx)(t.code,{children:"0x1E"}),"-batched frame."]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxEventNameLength"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"256"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"}
1,children:"Maximum length of an inbound Socket.IO event name."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"OutboundAckTimeout"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"30 * time.Second"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Default timeout used by ",(0,i.jsx)(t.code,{children:"EmitWithAck"})," when no per-call timeout is supplied."]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"DropFramesOnOverflow"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"false"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["If true, saturated outbound queues drop the offending frame and fire ",(0,i.jsx)(t.code,{children:"EventError"}),"."]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"RetrySendTimeout"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"20 * time.Millisecond"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Back-off between WebSocket send retries."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxSendRetry"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"5"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Maximum number of WebSocket send retries before a frame is dropped."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"EnablePolling"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"false"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["If true, the handler also accepts Engine.IO HTTP long-polling on ",(0,i.jsx)(t.code,{children:"GET"}),"/",(0,i.jsx)(t.code,{children:"POST"})," (opt-in fallback)."]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"PollingMaxBufferSize"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"1_000_000"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Cap on a single polling HTTP body (POST request body or GET drain response body), in bytes."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"MaxPollWait"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"30 * time.Second"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Maximum time a long-poll GET blocks waiting for outbound frames before returning an empty 200."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"PollQueueMaxFrames"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"1024"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Maximum buffered outbound frames per polling session before overflow handling applies."})]})]})]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"func init() {\n    socketio.PingInterval = 15 * time.Second\n    socketio.PingTimeout  = 10 * time.Second\n    socketio.MaxPayload   = 4 << 20 // 4 MiB\n}\n"})}),"\n",(0,i.jsx)(t.h3,{id:"message-format",children:"Message format"}),"\n",(0,i.jsx)(t.p,{children:"All messages are exchanged as Socket.IO events."}),"\n",(0,i.jsxs)(t.table,{children:[(0,i.jsx)(t.thead,{children:(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Side"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"API call"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Wire format"})]})}),(0,i.jsxs)(t.tbody,{children:[(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Server \u2192 Client"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:'kws.Emit([]byte("hello"))'})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:'42["message","hello"]'})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Server \u2192 Client"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:'kws.EmitEvent("greet", data)'})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:'42["greet",<data>]'})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Client \u2192 Server"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:'socket.emit("message", obj)'})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["fires ",(0,i.jsx)(t.code,{children:"EventMessage"})," with ",(0,i.jsx)(t.code,{children:"obj"})]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Client \u2192 Server"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:'socket.emit("custom", obj)'})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["fires the ",(0,i.jsx)(t.code,{children:'"custom"'})," event"]})]})]})]}),"\n",(0,i.jsxs)(t.blockquote,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.strong,{children:"Note:"})," ",(0,i.jsx)(t.code,{children:"Emit"}),", ",(0,i.jsx)(t.code,{children:"EmitEvent"}),", ",(0,i.jsx)(t.code,{children:"EmitArgs"}),", and ack-emitting variants pass valid JSON through unchanged. Raw text bytes are encoded as JSON strings for compatibility with older examples."]}),"\n"]}),"\n",(0,i.jsx)(t.h3,{id:"acks-namespaces-handshake-auth",children:"Acks, namespaces, handshake auth"}),"\n",(0,i.jsx)(t.p,{children:"The middleware implements the full Socket.IO v5 ack flow and forwards the client's connect-time auth payload to your handlers."}),"\n",(0,i.jsx)(t.h4,{id:"multi-argument-emits",children:"Multi-argument emits"}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"EmitArgs"})," and ",(0,i.jsx)(t.code,{children:"EmitWithAckArgs"})," accept a variadic list of values, so you can send richer event tuples without manually concatenating arrays. Valid JSON is passed through unchanged; raw text is encoded as a JSON string."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'// 42["greet","hi",{"id":1}]\nkws.EmitArgs("greet", []byte(`"hi"`), []byte(`{"id":1}`))\n'})}),"\n",(0,i.jsx)(t.h4,{id:"server-initiated-acks",children:"Server-initiated acks"}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"EmitWithAck"})," (and ",(0,i.jsx)(t.code,{children:"EmitWithAckTimeout"}),") emit an event with an ack id and invoke the supplied callback once the client acks, or with an error when the timeout expires. ",(0,i.jsx)(t.code,{children:"EmitWithAck"})," uses ",(0,i.jsx)(t.code,{children:"OutboundAckTimeout"}),"; ",(0,i.jsx)(t.code,{children:"EmitWithAckTimeout"})," takes a per-call duration plus a structured ",(0,i.jsx)(t.code,{children:"AckCallback"})," that distinguishes timeout from disconnect."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'kws.EmitWithAckTimeout("ping", []byte(`"hello"`), 3*time.Second, func(ack []byte, err error) {\n    if err != nil {\n        log.Printf("ack failed: %v", err)\n        return\n    }\n    // ack is the raw JSON the client passed to its callback (single value\n    // or a JSON-array literal for multi-arg acks).\n})\n'})}),"\n",(0,i.jsx)(t.h4,{id:"client-initiated-acks",children:"Client-initiated acks"}),"\n",(0,i.jsxs)(t.p,{children:["When the client emits with a callback, the inbound event payload carries an ack id. Use ",(0,i.jsx)(t.code,{children:"HasAck"})," and ",(0,i.jsx)(t.code,{children:"AckID"})," to detect it, then send a single ack reply via ",(0,i.jsx)(t.code,{children:"EventPayload.Ack"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'socketio.On("greet", func(ep *socketio.EventPayload) {\n    if ep.HasAck {\n        // ep.Args holds the raw JSON arguments the client sent.\n        _ = ep.Ack([]byte(`"ok"`))\n    }\n})\n'})}),"\n",(0,i.jsx)(t.h4,{id:"namespaces",children:"Namespaces"}),"\n",(0,i.jsx)(t.p,{children:"The middleware honours the namespace negotiated during the Socket.IO CONNECT packet. Events emitted from the server are routed back on the same namespace the client joined; no extra configuration is required on the Go side."}),"\n",(0,i.jsx)(t.h4,{id:"handshake-auth",children:"Handshake auth"}),"\n",(0,i.jsxs)(t.p,{children:["The client's ",(0,i.jsx)(t.code,{children:"auth"})," payload must be a JSON object. It is parsed during the Socket.IO handshake and exposed to handlers as ",(0,i.jsx)(t.code,{children:"EventPayload.HandshakeAuth"})," (raw JSON bytes). It is most commonly inspected on ",(0,i.jsx)(t.code,{children:"EventConnect"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:'// client\nconst socket = io("http://localhost:3000", {\n  path: "/ws",\n  transports: ["websocket"],\n  auth: { token: "secret" },\n});\n'})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'socketio.On(socketio.EventConnect, func(ep *socketio.EventPayload) {\n    // ep.HandshakeAuth == []byte(`{"token":"secret"}`)\n    var auth struct{ Token string `json:"token"` }\n    _ = json.Unmarshal(ep.HandshakeAuth, &auth)\n})\n'})}),"\n",(0,i.jsx)(t.h2,{id:"signatures",children:"Signatures"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Initialize new socketio in the callback this will\n// execute a callback that expects kws *Websocket Object\n// and optional config websocket.Config\nfunc New(callback func(kws *Websocket), config ...websocket.Config) func(fiber.Ctx) error\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Add listener callback for an event into the listeners list\nfunc On(event string, callback func(payload *EventPayload))\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Emit the message to a specific socket uuids list\n// Ignores all errors\nfunc EmitToList(uuids []string, message []byte, mType ...int)\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Emit to a specific socket connection\nfunc EmitTo(uuid string, message []byte, mType ...int) error\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Broadcast to all the active connections\nfunc Broadcast(message []byte, mType ...int)\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Fire custom event on all connections\nfunc Fire(event string, data []byte)\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'// Emit a named event with multiple arguments\n// (e.g. EmitArgs("greet", []byte(`"hi"`), []byte(`{"id":1}`)))\nfunc (kws *Websocket) EmitArgs(event string, args ...[]byte)\n'})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Emit a named event and invoke cb when the client acks (or on timeout /\n// disconnect). The default deadline is OutboundAckTimeout. The callback\n// receives the raw JSON ack value (or nil on timeout/disconnect).\nfunc (kws *Websocket) EmitWithAck(event string, data []byte, cb func(ack []byte))\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Like EmitWithAck but with a per-call timeout and a structured AckCallback\n// that distinguishes ErrAckTimeout from ErrAckDisconnected. Pass timeout = 0\n// to disable the timeout.\nfunc (kws *Websocket) EmitWithAckTimeout(event string, data []byte, timeout time.Duration, cb AckCallback)\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Multi-argument variant of EmitWithAck. The callback receives the slice of\n// raw ack arguments the client supplied (or an error on timeout /\n// disconnect). Uses OutboundAckTimeout.\nfunc (kws *Websocket) EmitWithAckArgs(event string, args [][]byte, cb func([][]byte, error))\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// HandshakeAuth returns the raw JSON auth payload sent by the client at\n/
1/ connect time (nil if the client did not provide one).\nfunc (kws *Websocket) HandshakeAuth() json.RawMessage\n"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"// Ack sends a Socket.IO ACK frame back to the client for the inbound event\n// represented by this payload. Idempotent: only the first invocation\n// produces a wire frame; later calls return ErrAckAlreadySent.\nfunc (ep *EventPayload) Ack(args ...[]byte) error\n"})}),"\n",(0,i.jsx)(t.h2,{id:"example",children:"Example"}),"\n",(0,i.jsx)(t.h3,{id:"go-server",children:"Go server"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:'package main\n\nimport (\n    "encoding/json"\n    "fmt"\n    "log"\n\n    "github.com/gofiber/contrib/v3/socketio"\n    "github.com/gofiber/contrib/v3/websocket"\n    "github.com/gofiber/fiber/v3"\n)\n\n// MessageObject Basic chat message object\ntype MessageObject struct {\n    Data  string `json:"data"`\n    From  string `json:"from"`\n    Event string `json:"event"`\n    To    string `json:"to"`\n}\n\nfunc main() {\n\n    // The key for the map is message.to\n    clients := make(map[string]string)\n\n    // Start a new Fiber application\n    app := fiber.New()\n\n    // Setup the middleware to retrieve the data sent in first GET request\n    app.Use(func(c fiber.Ctx) error {\n        // IsWebSocketUpgrade returns true if the client\n        // requested upgrade to the WebSocket protocol.\n        if websocket.IsWebSocketUpgrade(c) {\n            c.Locals("allowed", true)\n            return c.Next()\n        }\n        return fiber.ErrUpgradeRequired\n    })\n\n    // Multiple event handling supported\n    socketio.On(socketio.EventConnect, func(ep *socketio.EventPayload) {\n        fmt.Printf("Connection event 1 - User: %s", ep.Kws.GetStringAttribute("user_id"))\n    })\n\n    // Custom event handling supported\n    socketio.On("CUSTOM_EVENT", func(ep *socketio.EventPayload) {\n        fmt.Printf("Custom event - User: %s", ep.Kws.GetStringAttribute("user_id"))\n        // ---\x3e\n\n        // DO YOUR BUSINESS HERE\n\n        // ---\x3e\n    })\n\n    // On message event\n    socketio.On(socketio.EventMessage, func(ep *socketio.EventPayload) {\n\n        fmt.Printf("Message event - User: %s - Message: %s", ep.Kws.GetStringAttribute("user_id"), string(ep.Data))\n\n        message := MessageObject{}\n\n        // Unmarshal the json message\n        // {\n        //  "from": "<user-id>",\n        //  "to": "<recipient-user-id>",\n        //  "event": "CUSTOM_EVENT",\n        //  "data": "hello"\n        //}\n        err := json.Unmarshal(ep.Data, &message)\n        if err != nil {\n            fmt.Println(err)\n            return\n        }\n\n        // Fire custom event based on some\n        // business logic\n        if message.Event != "" {\n            ep.Kws.Fire(message.Event, []byte(message.Data))\n        }\n\n        // Emit the message directly to specified user\n        err = ep.Kws.EmitTo(clients[message.To], ep.Data, socketio.TextMessage)\n        if err != nil {\n            fmt.Println(err)\n        }\n    })\n\n    // On disconnect event\n    socketio.On(socketio.EventDisconnect, func(ep *socketio.EventPayload) {\n        // Remove the user from the local clients\n        delete(clients, ep.Kws.GetStringAttribute("user_id"))\n        fmt.Printf("Disconnection event - User: %s", ep.Kws.GetStringAttribute("user_id"))\n    })\n\n    // On close event\n    // This event is called when the server disconnects the user actively with .Close() method\n    socketio.On(socketio.EventClose, func(ep *socketio.EventPayload) {\n        // Remove the user from the local clients\n        delete(clients, ep.Kws.GetStringAttribute("user_id"))\n        fmt.Printf("Close event - User: %s", ep.Kws.GetStringAttribute("user_id"))\n    })\n\n    // On error event\n    socketio.On(socketio.EventError, func(ep *socketio.EventPayload) {\n        fmt.Printf("Error event - User: %s", ep.Kws.GetStringAttribute("user_id"))\n    })\n\n    app.Get("/ws/:id", socketio.New(func(kws *socketio.Websocket) {\n\n        // Retrieve the user id from endpoint\n        userId := kws.Params("id")\n\n        // Add the connection to the list of the connected clients\n        // The UUID is generated randomly and is the key that allow\n        // socketio to manage Emit/EmitTo/Broadcast\n        clients[userId] = kws.UUID\n\n        // Every websocket connection has an optional session key => value storage\n        kws.SetAttribute("user_id", userId)\n\n        // Broadcast to all the connected users the newcomer\n        newUserMsg, _ := json.Marshal(fmt.Sprintf("New user connected: %s and UUID: %s", userId, kws.UUID))\n        kws.Broadcast(newUserMsg, true, socketio.TextMessage)\n\n        // Write welcome message. Raw text is encoded as a JSON string.\n        welcomeMsg, _ := json.Marshal(fmt.Sprintf("Hello user: %s with UUID: %s", userId, kws.UUID))\n        kws.Emit(welcomeMsg, socketio.TextMessage)\n    }))\n\n    log.Fatal(app.Listen(":3000"))\n}\n'})}),"\n",(0,i.jsx)(t.h3,{id:"typescript--javascript-client",children:"TypeScript / JavaScript client"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-ts",children:'import { io } from "socket.io-client";\n\nconst socket = io("http://localhost:3000", {\n  path: "/ws",\n  transports: ["websocket"],\n});\n\nsocket.on("connect", () => {\n  console.log("connected, sid =", socket.id);\n\n  // Send a message to the server\n  socket.emit("message", {\n    from: "user1",\n    to:   "user2",\n    event: "",\n    data: "hello",\n  });\n});\n\nsocket.on("message", (data: unknown) =>
1 {\n  console.log("received message:", data);\n});\n\nsocket.on("disconnect", (reason) => {\n  console.log("disconnected:", reason);\n});\n'})}),"\n",(0,i.jsx)(t.hr,{}),"\n",(0,i.jsx)(t.h2,{id:"supported-events",children:"Supported events"}),"\n",(0,i.jsxs)(t.table,{children:[(0,i.jsx)(t.thead,{children:(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Const"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Event"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Description"})]})}),(0,i.jsxs)(t.tbody,{children:[(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EventMessage"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"message"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Fired when a ",(0,i.jsx)(t.code,{children:'socket.emit("message", \u2026)'})," event is received from the client"]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EventPing"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"ping"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Fired when a WebSocket PING control frame is received (RFC 6455). Engine.IO PING is server-originated and not surfaced via this event."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EventPong"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"pong"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Fired when an Engine.IO PONG (",(0,i.jsx)(t.code,{children:'"3"'}),") replies to the server's heartbeat or when a WebSocket PONG control frame is received."]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EventDisconnect"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"disconnect"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Fired on disconnection. The error provided in disconnection event as defined in RFC 6455, section 11.7."})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EventConnect"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"connect"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Fired after the Engine.IO / Socket.IO handshake completes; ",(0,i.jsx)(t.code,{children:"ep.HandshakeAuth"})," is populated with the client's ",(0,i.jsx)(t.code,{children:"auth"})," payload (raw JSON, nil if not provided)"]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EventClose"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"close"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Fired when the connection is actively closed from the server. Different from client disconnection"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EventError"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"error"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Fired when some error appears useful also for debugging websockets"})]})]})]}),"\n",(0,i.jsxs)(t.p,{children:["Custom events map directly to the event name used in ",(0,i.jsx)(t.code,{children:'socket.emit("myEvent", \u2026)'})," on the client and ",(0,i.jsx)(t.code,{children:'kws.EmitEvent("myEvent", data)'})," on the server."]}),"\n",(0,i.jsx)(t.h2,{id:"event-payload-object",children:"Event Payload object"}),"\n",(0,i.jsxs)(t.table,{children:[(0,i.jsx)(t.thead,{children:(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Variable"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Type"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Description"})]})}),(0,i.jsxs)(t.tbody,{children:[(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Kws"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"*Websocket"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"The connection object"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Name"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"string"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"The name of the event"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"SocketUUID"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"string"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Unique connection UUID"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"SocketAttributes"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"map[string]any"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Optional websocket attributes"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Error"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"error"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"(optional) Fired from disconnection or error events"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Data"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"[]byte"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Raw JSON of the event payload (first argument of ",(0,i.jsx)(t.code,{children:"socket.emit"}),")"]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Args"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"[][]byte"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"All raw JSON arguments after the event name; useful when the client emits multiple values"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"AckID"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"uint64"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Ack id assigned by the client when it emitted with a callback (0 if ",(0,i.jsx)(t.code,{children:"HasAck"})," is false)"]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"HasAck"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"bool"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["True when the inbound event expects an ack reply; respond via ",(0,i.jsx)(t.code,{children:"EventPayload.Ack(args...)"})]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"HandshakeAuth"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"json.RawMessage"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Raw JSON auth payload from the Socket.IO handshake; populated on ",(0,i.jsx)(t.code,{children:"EventConnect"})," listeners (use ",(0,i.jsx)(t.code,{children:"Kws.HandshakeAuth()"})," elsewhere)"]})]})]})]}),"\n",(0,i.jsx)(t.h2,{id:"socket-instance-functions",children:"Socket instance functions"}),"\n",(0,i.jsxs)(t.table,{children:[(0,i.jsx)(t.thead,{children:(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Name"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Type"}),(0,i.jsx)(t.th,{style:{textAlign:"left"},children:"Description"})]})}),(0,i.jsxs)(t.tbody,{children:[(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"SetAttribute"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Set a specific attribute for the specific socket connection"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"GetUUID"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"string"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Get socket connection UUID"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"SetUUID"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"error"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Set socket connection UUID"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"GetAttribute"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"string"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Get a specific attribute from the socket attributes"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EmitToList"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Emit the message to a specific socket uuids list"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EmitTo"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"error"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Emit to a specific socket connection"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Broadcast"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Broadcast to all the active connections except broadcasting the message to itself"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Fire"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Fire custom event"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Emit"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Send data as a ",(0,i.jsx)(t.code,{children:'"message"'})," socket.io event; valid JSON is passed through, raw text is JSON-encoded"]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EmitEvent"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Send a named socket.io event; valid JSON is passed through, raw text is JSON-encoded"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EmitArgs"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Emit a named event with multiple arguments; valid JSON is passed through, raw text is JSON-encoded"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EmitWithAck"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Emit an event and invoke ",(0,i.jsx)(t.code,{children:"cb(ack)"})," when the client acks (uses ",(0,i.jsx)(t.code,{children:"OutboundAckTimeout"}),")"]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EmitWithAckTimeout"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Like ",(0,i.jsx)(t.code,{children:"EmitWithAck"})," but with a per-call timeout and a structured ",(0,i.jsx)(t.code,{children:"AckCallback"})]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"EmitWithAckArgs"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Multi-arg variant; ",(0,i.jsx)(t.code,{children:"cb([][]byte, error)"})," receives the ack tuple (uses ",(0,i.jsx)(t.code,{children:"OutboundAckTimeout"}),")"]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"HandshakeAuth"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"json.RawMessage"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Raw JSON auth payload sent by the client at connect time (nil if absent)"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"IsAlive"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"bool"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Reports whether the underlying connection is still open and the heartbeat loop is running"})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"IsPolling"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"bool"})}),(0,i.jsxs)(t.td,{style:{textAlign:"left"},children:["Reports whether the session is bound to HTTP long-polling rather than WebSocket; when true, ",(0,i.jsx)(t.code,{children:"Conn"})," is nil"]})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Close"}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:(0,i.jsx)(t.code,{children:"void"})}),(0,i.jsx)(t.td,{style:{textAlign:"left"},children:"Actively close the connection from the server"})]})]})]}),"\n",(0,i.jsx)(t.p,{children:(0,i.jsx)(t.strong,{children:"Note: the FastHTTP connection can be accessed directly from the instance"})}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-go",children:"kws.Conn\n"})}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"kws.Conn"})," is ",(0,i.jsx)(t.code,{children:"nil"})," for HTTP long-polling sessions. Code that touches the underlying WebSocket directly should guard with ",(0,i.jsx)(t.code,{children:"if kws.Conn != nil"})," or check the transport via the absence of ",(0,i.jsx)(t.code,{children:"kws.Conn"}),". Listener APIs (",(0,i.jsx)(t.code,{children:"Emit"}),", ",(0,i.jsx)(t.code,{children:"Ack"}),", ",(0,i.jsx)(t.code,{children:"Close"}),", ",(0,i.jsx)(t.code,{children:"Broadcast"}),", ",(0,i.jsx)(t.code,{children:"EmitWithAck"}
1),", etc.) work transparently on both transports."]})]})}function h(e={}){let{wrapper:t}={...(0,l.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(a,{...e})}):a(e)}},28453(e,t,n){n.d(t,{R:()=>r,x:()=>c});var s=n(296540);let i={},l=s.createContext(i);function r(e){let t=s.useContext(l);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),s.createElement(l.Provider,{value:t},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.