Documentation
¶
Overview ¶
Package jaws provides a mechanism to create dynamic webpages using JavaScript and WebSockets.
It integrates well with Go's html/template package, but can be used without it. It can be used with any router that supports the standard http.Handler interface.
This package holds the core engine and the UI interfaces. The standard widgets (Span, Button, Select, Text, and so on) and the RequestWriter helper methods live in github.com/linkdata/jaws/lib/ui, and value binding lives in github.com/linkdata/jaws/lib/bind.
Locking ¶
The package uses a single, acyclic lock hierarchy. When more than one of these locks is held at once they must be acquired in this order, outermost first:
Jaws.mu -> Request.mu -> Session.mu
Request.muQueue and per-Element state are leaf locks taken below all of the above. Blocking work (channel sends, user callbacks) is always performed after snapshotting the needed state and releasing the relevant lock; see Session.Broadcast and Session.Close for the canonical pattern.
UI value and widget types in the subpackages carry their own leaf locks that guard the bound value: the binders in github.com/linkdata/jaws/lib/bind, the JsVar in github.com/linkdata/jaws/lib/ui and the named values in github.com/linkdata/jaws/lib/named. These are leaves with respect to each other, acquired containing-before-contained (for example a named BoolArray's mutex is taken before a member Bool's). They sit below the three core locks above, but with one deliberate reverse edge: marking an Element dirty or broadcasting a change ultimately takes the outermost Jaws.mu, and a value type may run that side effect while still holding its own value lock (lib/named does this; lib/bind and lib/ui release the value lock first). That is the reverse of the "outermost first" rule and is safe only because the edge never closes a cycle: no code path holding Jaws.mu, Request.mu or Session.mu ever calls into a UI value's Get/Set/Dirty methods, which are the only callers that take a value lock. Code holding any of the three core locks must therefore never invoke a UI value method. New value types should prefer the lib/bind / lib/ui pattern (mutate under the value lock, release it, then mark dirty) so they do not depend on this invariant.
A second deliberate reverse edge lives in github.com/linkdata/jaws/lib/ui: ContainerHelper.reconcile holds its own widget mutex while calling Request.NewElement, which takes Request.mu — again leaf-before-core. It is safe for the same reason: no code path holding any of the three core locks ever invokes a widget's render or update method (the Serve loop calls JawsRender and JawsUpdate only after releasing Request.mu). Code holding Jaws.mu, Request.mu or Session.mu must therefore never call a container's render/update entry points. Note the widget mutex is a plain sync.Mutex, so deadlock.Debug cannot observe this inversion; the invariant is maintained by convention.
Element handlers are an intentional exception to the locking rules: they are populated only while an Element is rendered and are then read without a lock on the event goroutine. This is safe solely because rendering completes before any event for that Element can be processed, so handlers must not be added after Element.JawsRender returns (or after Element.Freeze for update-only registrations). All builds enforce this: every handler mutator funnels through an internal chokepoint guarded by a lockless atomic flag that drops late additions; debug builds panic instead.
Testing ¶
Always run the tests with the -race flag (or -tags debug). Both set deadlock.Debug, which enables the debug-gated runtime invariant checks: the lock-order verification described above, the late-handler panic, and the tag-comparability check in github.com/linkdata/jaws/lib/tag. Those branches are compile-time dead in normal builds, so a plain "go test" neither exercises them nor reports their statement coverage. CI builds with -tags debug -race.
Index ¶
- Constants
- Variables
- func AppendID(b []byte) []byte
- func CallEventHandlers(ui any, elem *Element, wht what.What, value string) (err error)
- func MakeID() string
- func NextID() int64
- func ParseParams(params []any) (tags []any, handlers []any, attrs []string)
- type Auth
- type Click
- type ClickHandler
- type ConnectFn
- type Container
- type ContextMenuHandler
- type DefaultAuth
- type Element
- func (elem *Element) AddHandlers(h ...any)
- func (elem *Element) Append(htmlCode template.HTML)
- func (elem *Element) ApplyGetter(getter any) (tagValue any, attrs []template.HTMLAttr, err error)
- func (elem *Element) ApplyParams(params []any) (attrs []template.HTMLAttr)
- func (elem *Element) Freeze()
- func (elem *Element) HasTag(tagValue any) bool
- func (elem *Element) JawsRender(w io.Writer, params []any) (err error)
- func (elem *Element) JawsUpdate()
- func (elem *Element) Jid() jid.Jid
- func (elem *Element) Order(jidList []jid.Jid)
- func (elem *Element) Remove(htmlID string)
- func (elem *Element) RemoveAttr(attr string)
- func (elem *Element) RemoveClass(cls string)
- func (elem *Element) Replace(htmlCode template.HTML)
- func (elem *Element) SetAttr(attr, value string)
- func (elem *Element) SetClass(cls string)
- func (elem *Element) SetInner(innerHTML template.HTML)
- func (elem *Element) SetValue(value string)
- func (elem *Element) String() string
- func (elem *Element) Tag(tags ...any)
- func (elem *Element) UI() UI
- type HandleFunc
- type InitHandler
- type InitialHTMLAttrHandler
- type InputFn
- type InputHandler
- type Jaws
- func (jw *Jaws) AddTemplateLookuper(tl TemplateLookuper) (err error)
- func (jw *Jaws) Alert(level, msg string)
- func (jw *Jaws) Append(target any, html template.HTML)
- func (jw *Jaws) Broadcast(msg wire.Message)
- func (jw *Jaws) Close()
- func (jw *Jaws) ContentSecurityPolicy() (s string)
- func (jw *Jaws) Delete(target any)
- func (jw *Jaws) Dirty(dirtyTags ...any)
- func (jw *Jaws) Done() <-chan struct{}
- func (jw *Jaws) FaviconURL() (s string)
- func (jw *Jaws) GenerateHeadHTML(extra ...string) (err error)
- func (jw *Jaws) GetSession(r *http.Request) (sess *Session)
- func (jw *Jaws) Insert(target any, where string, html template.HTML)
- func (jw *Jaws) JsCall(target any, jsfunc, jsonstr string)
- func (jw *Jaws) Log(err error) error
- func (jw *Jaws) LookupTemplate(name string) *template.Template
- func (jw *Jaws) MustLog(err error)
- func (jw *Jaws) NewRequest(r *http.Request) (rq *Request)
- func (jw *Jaws) NewSession(w http.ResponseWriter, r *http.Request) (sess *Session)
- func (jw *Jaws) Pending() (n int)
- func (jw *Jaws) Redirect(url string)
- func (jw *Jaws) Reload()
- func (jw *Jaws) RemoveAttr(target any, attr string)
- func (jw *Jaws) RemoveClass(target any, cls string)
- func (jw *Jaws) RemoveTemplateLookuper(tl TemplateLookuper) (err error)
- func (jw *Jaws) Replace(target any, html template.HTML)
- func (jw *Jaws) RequestCount() (n int)
- func (jw *Jaws) RequestCounts() (total, active int)
- func (jw *Jaws) SecureHeadersMiddleware(next http.Handler) http.Handler
- func (jw *Jaws) Serve()
- func (jw *Jaws) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (jw *Jaws) ServeWithTimeout(requestTimeout time.Duration)
- func (jw *Jaws) Session(h http.Handler) http.Handler
- func (jw *Jaws) SessionCount() (n int)
- func (jw *Jaws) Sessions() (sessions []*Session)
- func (jw *Jaws) SetAttr(target any, attr, value string)
- func (jw *Jaws) SetClass(target any, cls string)
- func (jw *Jaws) SetInner(target any, innerHTML template.HTML)
- func (jw *Jaws) SetValue(target any, value string)
- func (jw *Jaws) Setup(handleFn HandleFunc, prefix string, extras ...any) (err error)
- func (jw *Jaws) TestServe(rq *Request, onPanic func(recovered any)) (inCh chan wire.WsMsg, outCh chan wire.WsMsg, bcastCh chan wire.Message, ...)
- func (jw *Jaws) UseRequest(jawsKey uint64, r *http.Request) (rq *Request)
- type Jid
- type Logger
- type MakeAuthFn
- type Renderer
- type Request
- func (rq *Request) Alert(level, msg string)
- func (rq *Request) AlertError(err error)
- func (rq *Request) Cancel(err error)
- func (rq *Request) Context() (ctx context.Context)
- func (rq *Request) DeleteElement(elem *Element)
- func (rq *Request) Dirty(dirtyTags ...any)
- func (rq *Request) Get(key string) any
- func (rq *Request) GetConnectFn() (fn ConnectFn)
- func (rq *Request) GetElementByJid(jid Jid) (elem *Element)
- func (rq *Request) GetElements(tagValue any) (elems []*Element)
- func (rq *Request) HasTag(elem *Element, tagValue any) (yes bool)
- func (rq *Request) HeadHTML(w io.Writer) (err error)
- func (rq *Request) Initial() (r *http.Request)
- func (rq *Request) JawsKeyString() string
- func (rq *Request) Log(err error) error
- func (rq *Request) MustLog(err error)
- func (rq *Request) NewElement(ui UI) *Element
- func (rq *Request) Redirect(url string)
- func (rq *Request) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (rq *Request) Session() (sess *Session)
- func (rq *Request) Set(key string, value any)
- func (rq *Request) SetConnectFn(fn ConnectFn)
- func (rq *Request) SetContext(fn func(oldCtx context.Context) (newCtx context.Context))
- func (rq *Request) String() string
- func (rq *Request) Tag(elem *Element, tagItems ...any)
- func (rq *Request) TagExpanded(elem *Element, expandedTags []any)
- func (rq *Request) TagsOf(elem *Element) (tags []any)
- func (rq *Request) TailHTML(w io.Writer) (err error)
- type Session
- func (sess *Session) Broadcast(msg wire.Message)
- func (sess *Session) Clear()
- func (sess *Session) Close() (cookie *http.Cookie)
- func (sess *Session) Cookie() (cookie *http.Cookie)
- func (sess *Session) CookieValue() (s string)
- func (sess *Session) Get(key string) (value any)
- func (sess *Session) ID() (id uint64)
- func (sess *Session) IP() (ip netip.Addr)
- func (sess *Session) Jaws() (jw *Jaws)
- func (sess *Session) Reload()
- func (sess *Session) Requests() (requests []*Request)
- func (sess *Session) Set(key string, value any)
- type SetupFunc
- type TemplateLookuper
- type UI
- type Updater
Constants ¶
const ( // DefaultUpdateInterval is the default browser update interval. DefaultUpdateInterval = time.Millisecond * 100 // DefaultWebSocketPingInterval is the default WebSocket keepalive ping interval. DefaultWebSocketPingInterval = time.Minute // DefaultWebSocketTimeout is the default time allowed for WebSocket connect and ping responses. DefaultWebSocketTimeout = time.Second * 10 // DefaultMaxPendingRequestsPerIP is the default maximum number of unclaimed // Requests allowed for each client IP. DefaultMaxPendingRequestsPerIP = 100 )
Variables ¶
var ( // ErrWebsocketOriginMissing is returned when a WebSocket request has no Origin header. ErrWebsocketOriginMissing = errors.New("websocket request missing Origin header") // ErrWebsocketOriginWrongScheme is returned when a WebSocket Origin is not HTTP or HTTPS. ErrWebsocketOriginWrongScheme = errors.New("websocket Origin not http or https") // ErrWebsocketOriginWrongHost is returned when a WebSocket Origin host does not match the initial request host. ErrWebsocketOriginWrongHost = errors.New("websocket Origin host mismatch") // ErrWebsocketOriginNoInitial is returned when origin validation cannot run // because the [Request] has no initial HTTP request to compare against. The // check fails closed rather than accepting an unverified Origin. ErrWebsocketOriginNoInitial = errors.New("websocket Origin cannot be validated: no initial request") // ErrRequestAlreadyClaimed is returned when [Jaws.UseRequest] is called more than once for a [Request]. ErrRequestAlreadyClaimed = errors.New("request already claimed") // ErrJavascriptDisabled is returned when the noscript probe indicates JavaScript is disabled. ErrJavascriptDisabled = errors.New("javascript is disabled") )
var ErrEventHandlerPanic errEventHandlerPanic
ErrEventHandlerPanic is returned when an event handler panics.
var ErrEventUnhandled = errEventUnhandled{}
ErrEventUnhandled returned by [InputHandler.JawsInput], [ClickHandler.JawsClick] or [ContextMenuHandler.JawsContextMenu] causes the next available handler to be invoked.
var ErrNoWebSocketRequest errNoWebSocketRequest
ErrNoWebSocketRequest is returned when the WebSocket callback was not received within the timeout period. The most common reason is that the client is not using JavaScript.
var ErrRequestCancelled errRequestCancelled
ErrRequestCancelled indicates a Request was cancelled. Use Unwrap() to see the underlying cause.
var ErrTooManyPendingRequests errTooManyPendingRequests
ErrTooManyPendingRequests indicates an older pending Request was evicted because its client IP had reached [Jaws.MaxPendingRequestsPerIP].
var ErrValueUnchanged = errors.New("value unchanged")
ErrValueUnchanged can be returned from JawsSet methods to indicate that while there was no error, the underlying value was already the desired value.
Functions ¶
func AppendID ¶ added in v0.31.0
AppendID appends the result of NextID() in text form to the given slice.
func CallEventHandlers ¶ added in v0.300.0
CallEventHandlers calls the event handlers for the given Element. Recovers from panics in user-provided handlers, returning them as errors.
func MakeID ¶ added in v0.24.0
func MakeID() string
MakeID returns a string in the form "jaws.X" where X is unique within the lifetime of the program.
func NextID ¶ added in v0.31.0
func NextID() int64
NextID returns an int64 unique within the lifetime of the program.
func ParseParams ¶ added in v0.60.0
ParseParams parses the parameters passed to UI helpers when creating a new Element, returning UI tags, event handlers and HTML attributes.
Unlike Element.ApplyGetter, which is given the primary getter, ParseParams only recognizes InputFn, InputHandler, ClickHandler and ContextMenuHandler. A param implementing InitHandler or InitialHTMLAttrHandler is treated only as a tag here; its JawsInit / JawsInitialHTMLAttr are intentionally invoked only for the primary getter.
Types ¶
type Auth ¶ added in v0.85.0
type Auth interface {
Data() map[string]any // returns authenticated user data, or nil
Email() string // returns authenticated user email, or an empty string
IsAdmin() bool // return true if admins are defined and current user is one, or if no admins are defined
}
Auth describes authentication data available to templates through ui.With.
type Click ¶ added in v0.400.0
type Click struct {
Name string
X float64 // X is the browser clientX coordinate in CSS pixels.
Y float64 // Y is the browser clientY coordinate in CSS pixels.
Shift bool
Control bool
Alt bool
}
Click identifies a browser click-like event, pointer location and modifier state.
type ClickHandler ¶ added in v0.31.0
type ClickHandler interface {
// JawsClick is called when an [Element]'s HTML element or something within it
// is clicked in the browser.
//
// [Click.Name] is taken from the first name HTML attribute or HTML
// 'button' textContent found when traversing the DOM. It may be empty.
JawsClick(elem *Element, click Click) (err error)
}
ClickHandler handles click events sent from the browser.
type ConnectFn ¶
ConnectFn can be used to interact with a Request before message processing starts. Returning an error causes the Request to abort, and the WebSocket connection to close.
type Container ¶ added in v0.31.0
type Container interface {
// JawsContains must return a slice of comparable [UI] objects (they are used
// as map keys; see [UI] for the comparability requirement). The slice contents
// must not be modified after returning it.
JawsContains(elem *Element) (contents []UI)
}
Container is implemented by UI values that render a dynamic list of child UI values.
type ContextMenuHandler ¶ added in v0.400.0
type ContextMenuHandler interface {
// JawsContextMenu is called when an [Element]'s HTML element or something
// within it receives a context menu event in the browser.
JawsContextMenu(elem *Element, click Click) (err error)
}
ContextMenuHandler handles context-menu events sent from the browser.
type DefaultAuth ¶ added in v0.300.0
DefaultAuth is the permissive default Auth implementation used for templates when [Jaws.MakeAuth] is nil.
SECURITY: DefaultAuth.IsAdmin always returns true. Because it is substituted whenever [Jaws.MakeAuth] is unset, a template that gates privileged UI on {{if .Auth.IsAdmin}} will render that UI to EVERY visitor on any instance that forgot to set [Jaws.MakeAuth]. Data and Email are fail-safe (nil / empty); only IsAdmin is fail-open. Always set [Jaws.MakeAuth] in production, and treat a nil MakeAuth as "no authorization configured", not "deny".
func (*DefaultAuth) Data ¶ added in v0.300.0
func (*DefaultAuth) Data() map[string]any
func (*DefaultAuth) Email ¶ added in v0.300.0
func (*DefaultAuth) Email() string
func (*DefaultAuth) IsAdmin ¶ added in v0.300.0
func (da *DefaultAuth) IsAdmin() bool
type Element ¶ added in v0.31.0
type Element struct {
*Request // (read-only) the Request the Element belongs to
// contains filtered or unexported fields
}
Element is an instance of a Request, a UI object and a Jid.
func (*Element) AddHandlers ¶ added in v0.300.0
AddHandlers adds the given handlers to the Element.
It must be called while the Element is being rendered, before any event can be processed for it; see the package "Locking" documentation. Handlers added after Element.JawsRender has returned (or Element.Freeze has been called) are dropped; debug builds panic.
func (*Element) Append ¶ added in v0.31.0
Append appends a new HTML element as a child to the current one.
Call this only during JawsRender() or JawsUpdate() processing.
func (*Element) ApplyGetter ¶ added in v0.75.0
ApplyGetter examines getter, and if it is not nil, either adds it as a tag, or, if it is a tag.TagGetter, adds the result of that as a tag.
If getter is an InputHandler, ClickHandler, ContextMenuHandler or InitialHTMLAttrHandler, relevant values are added to the Element.
Finally, if getter is an InitHandler, its JawsInit function is called.
Returns the Tag(s) added (or nil if getter was nil), any initial HTML attrs provided by InitialHTMLAttrHandler, and any error returned from JawsInit() if it was called.
If the Element is already frozen, an event-handler getter is not added to the handler list (debug builds panic); tag and init processing still occur.
func (*Element) ApplyParams ¶ added in v0.60.0
ApplyParams parses the parameters passed to UI() when creating a new Element, adding UI tags, adding any additional event handlers found.
Returns the list of HTML attributes found, if any.
Handlers found in params are added only while the Element is mutable; after it is frozen (Element.JawsRender returning or Element.Freeze) they are dropped (debug builds panic), though tags and HTML attributes are still processed.
func (*Element) Freeze ¶ added in v0.500.0
func (elem *Element) Freeze()
Freeze marks the Element's handlers as final, as Element.JawsRender does on return. After Freeze, the handler-mutating methods (AddHandlers, ApplyParams, ApplyGetter) no longer add handlers (debug builds panic). Use this for elements registered for updates without being rendered.
func (*Element) JawsRender ¶ added in v0.55.0
JawsRender calls [Renderer.JawsRender] for this Element.
Do not call this yourself unless it is from within another JawsRender implementation.
func (*Element) JawsUpdate ¶ added in v0.55.0
func (elem *Element) JawsUpdate()
JawsUpdate calls [Updater.JawsUpdate] for this Element.
Do not call this yourself unless it is from within another JawsUpdate implementation.
func (*Element) Jid ¶ added in v0.31.0
Jid returns the JaWS ID for this Element, unique within its Request.
func (*Element) Order ¶ added in v0.31.0
Order reorders the HTML elements.
Call this only during JawsRender() or JawsUpdate() processing.
func (*Element) Remove ¶ added in v0.31.0
Remove requests that the HTML child with the given HTML ID of this Element is removed from the Request and its HTML element from the browser.
Call this only during JawsRender() or JawsUpdate() processing.
func (*Element) RemoveAttr ¶ added in v0.31.0
RemoveAttr queues sending a request to remove an attribute to the browser for the Element.
Call this only during JawsRender() or JawsUpdate() processing.
func (*Element) RemoveClass ¶ added in v0.31.0
RemoveClass queues sending a request to remove a class to the browser for the Element.
Call this only during JawsRender() or JawsUpdate() processing.
func (*Element) Replace ¶ added in v0.31.0
Replace replaces the Element's entire HTML DOM node with new HTML code.
The HTML code must contain the element's own HTML id; if it does not, the call is a programming error: debug builds panic and production builds report it via Jaws.MustLog and skip the replacement.
Call this only during JawsRender() or JawsUpdate() processing.
func (*Element) SetAttr ¶ added in v0.31.0
SetAttr queues sending a new attribute value to the browser for the Element.
The value parameter must be the unescaped logical attribute value. It is sent to the browser DOM and used as the value argument to setAttribute().
Call this only during JawsRender() or JawsUpdate() processing.
func (*Element) SetClass ¶ added in v0.31.0
SetClass queues sending a class to the browser for the Element.
Call this only during JawsRender() or JawsUpdate() processing.
func (*Element) SetInner ¶ added in v0.31.0
SetInner queues sending new inner HTML content to the browser for the Element.
Call this only during JawsRender() or JawsUpdate() processing.
func (*Element) SetValue ¶ added in v0.31.0
SetValue queues sending a new current input value in textual form to the browser for the Element.
Call this only during JawsRender() or JawsUpdate() processing.
type HandleFunc ¶ added in v0.111.6
HandleFunc matches the signature of http.ServeMux.Handle.
type InitHandler ¶ added in v0.110.0
InitHandler allows initializing UI getters and setters before their use.
You can of course initialize them in the call from the template engine, but at that point you don't have access to the Element, [Element.Context] or [Element.Session].
type InitialHTMLAttrHandler ¶ added in v0.400.0
type InitialHTMLAttrHandler interface {
// JawsInitialHTMLAttr is called when an [Element] is initially rendered,
// and may return an initial HTML attribute string to write out.
JawsInitialHTMLAttr(elem *Element) (s template.HTMLAttr)
}
InitialHTMLAttrHandler can add attributes during initial Element rendering.
type InputFn ¶ added in v0.401.0
InputFn is the signature of an input handling function to be called when JaWS receives an input, hook or set message from JavaScript via the WebSocket connection.
type InputHandler ¶ added in v0.401.0
type InputHandler interface {
// JawsInput is called when an [Element] receives a browser input event.
JawsInput(elem *Element, value string) (err error)
}
InputHandler handles input events sent from the browser.
type Jaws ¶
type Jaws struct {
CookieName string // Name for session cookies, defaults to "jaws"
AutoSession bool // Create a session during WebSocket upgrade when a Request has none. Defaults to false.
TrustForwardedHeaders bool // Trust X-Forwarded-* headers: governs the session cookie Secure flag (X-Forwarded-Proto) and the client IP used for session/request binding (X-Forwarded-For/X-Real-IP). Defaults to false; only enable behind a single reverse proxy you control that sets these headers.
Logger Logger // Optional logger to use
Debug bool // Set to true to enable debug info in generated HTML code. Call GenerateHeadHTML after changing it.
MakeAuth MakeAuthFn // Function to create ui.With.Auth for Templates. If nil, templates get the fail-open DefaultAuth (IsAdmin()==true for everyone); set it to enforce authorization. See DefaultAuth.
BaseContext context.Context // Non-nil base context for Requests, set to context.Background() in New()
WebSocketPingInterval time.Duration // Interval between keepalive pings on active WebSocket connections. Defaults to DefaultWebSocketPingInterval. Set <=0 to disable keepalive pings.
MaxPendingRequestsPerIP int // Maximum number of unclaimed Requests per client IP. Defaults to DefaultMaxPendingRequestsPerIP. Set <=0 to disable the cap.
// contains filtered or unexported fields
}
Jaws holds the server-side state and configuration for a JaWS instance.
A single Jaws value coordinates template lookup, session handling and the request lifecycle that keeps the browser and backend synchronized via WebSockets. The zero value is not ready for use; construct instances with New to ensure the helper goroutines and static assets are prepared.
The exported configuration fields are ordinary fields, not live synchronized settings. Set them before exposing handlers, creating Requests, or starting Jaws.Serve / Jaws.ServeWithTimeout. Methods document their own concurrency behavior and may be called concurrently when stated.
func New ¶
New allocates a JaWS instance with the default configuration.
The returned Jaws value is ready for use: static assets are embedded, the broadcast channels and update ticker are allocated and the request pool is primed. You must still start the processing loop with Jaws.Serve or Jaws.ServeWithTimeout on its own goroutine before broadcasting. Call Jaws.Close when the instance is no longer needed to free associated resources.
func (*Jaws) AddTemplateLookuper ¶ added in v0.45.0
func (jw *Jaws) AddTemplateLookuper(tl TemplateLookuper) (err error)
AddTemplateLookuper adds a TemplateLookuper.
The lookuper must be comparable so it can be removed with Jaws.RemoveTemplateLookuper.
func (*Jaws) Alert ¶
Alert sends an alert to all Request values.
The level argument should be one of Bootstrap's alert levels: primary, secondary, success, danger, warning, info, light or dark.
The level and msg are HTML-escaped before being sent, so it is safe to pass untrusted text; do not pre-escape it.
func (*Jaws) Append ¶
Append calls the JavaScript appendChild method on all HTML elements matching target.
func (*Jaws) Broadcast ¶
Broadcast sends a message to all Request values.
It must not be called before the JaWS processing loop (Jaws.Serve or Jaws.ServeWithTimeout) is running. Otherwise this call may block once the internal broadcast channel fills.
All convenience helpers on Jaws that call Broadcast inherit this requirement.
func (*Jaws) Close ¶
func (jw *Jaws) Close()
Close frees resources associated with the Jaws object, and closes the completion channel if the Jaws was created with New. Once the completion channel is closed, broadcasts and sends may be discarded. Subsequent calls to Close() have no effect.
func (*Jaws) ContentSecurityPolicy ¶ added in v0.300.0
ContentSecurityPolicy returns the generated Content-Security-Policy header value.
func (*Jaws) Dirty ¶ added in v0.31.0
Dirty marks all Element values that have one or more of the given tags as dirty.
Note that if any of the tags implement tag.TagGetter, it will be called with a nil Request. Prefer using Request.Dirty which avoids this.
func (*Jaws) Done ¶
func (jw *Jaws) Done() <-chan struct{}
Done returns the channel that is closed when Jaws.Close has been called.
func (*Jaws) FaviconURL ¶ added in v0.111.6
FaviconURL returns the favicon URL discovered by Jaws.GenerateHeadHTML.
func (*Jaws) GenerateHeadHTML ¶ added in v0.5.0
GenerateHeadHTML regenerates the HTML code that goes in the HEAD section, ensuring that the provided URL resources in extra are loaded, along with the JaWS JavaScript.
If one of the resources is named "favicon", its URL will be stored and can be retrieved using Jaws.FaviconURL.
You only need to call this if you add your own images, scripts and stylesheets.
func (*Jaws) GetSession ¶ added in v0.11.0
GetSession returns the Session associated with the given http.Request, or nil.
Sessions are bound to the client IP (see the clientIP method). Behind a reverse proxy that connects over loopback, every request appears to come from loopback and IP binding is effectively disabled unless [Jaws.TrustForwardedHeaders] is enabled so the forwarded client IP is used instead.
func (*Jaws) Insert ¶
Insert calls the JavaScript 'insertBefore()' method on all HTML elements matching target.
The position parameter 'where' may be either an HTML ID, a child index or the text "null". html is trusted HTML, matching Jaws.SetInner and Jaws.Append.
func (*Jaws) JsCall ¶ added in v0.114.0
JsCall calls the JavaScript function jsfunc with the argument jsonstr on all HTML elements matching target.
func (*Jaws) Log ¶
Log sends an error to the [Jaws.Logger] if set. Has no effect if err is nil or the Logger is nil. Returns err.
func (*Jaws) LookupTemplate ¶ added in v0.66.0
LookupTemplate queries the known TemplateLookuper values in the order they were added and returns the first found.
func (*Jaws) MustLog ¶ added in v0.1.1
MustLog sends an error to the [Jaws.Logger] if set, or panics with the given error if the Logger is nil. Has no effect if err is nil.
Some update-time paths cannot return errors to their caller and report them through MustLog. Set [Jaws.Logger] when those errors should be logged instead of treated as fatal programming errors.
func (*Jaws) NewRequest ¶
NewRequest returns a new pending JaWS request.
Call this as soon as you start processing an HTML request, and store the returned Request pointer so it can be used while constructing the HTML response in order to register the JaWS IDs you use in the response, and use its [Request.JawsKey] when sending the JavaScript portion of the reply.
Automatic timeout handling is performed by Jaws.ServeWithTimeout. The default Jaws.Serve helper uses a 10-second timeout.
func (*Jaws) NewSession ¶ added in v0.26.0
NewSession creates a new Session.
Any pre-existing Session will be cleared and closed. This may call Session.Close on an existing session and therefore requires the JaWS processing loop (Jaws.Serve or Jaws.ServeWithTimeout) to be running.
Subsequent Request values created with Jaws.NewRequest that have the cookie set and originate from the same IP will be able to access the Session. The IP comparison is the same loopback-aware, optionally forwarded-header-based match used everywhere else; see Jaws.GetSession and [Jaws.TrustForwardedHeaders] for the reverse-proxy caveat.
func (*Jaws) Pending ¶
Pending returns the number of requests waiting for their WebSocket callbacks.
func (*Jaws) Redirect ¶
Redirect requests all Request values to navigate to the given URL.
The URL is validated to be a relative path or an http/https URL; script-bearing schemes such as javascript: and protocol-relative ("//host") URLs are refused and logged rather than sent to the browser.
func (*Jaws) Reload ¶
func (jw *Jaws) Reload()
Reload requests all Request values to reload their current page.
func (*Jaws) RemoveAttr ¶
RemoveAttr sends a request to remove the given attribute from all HTML elements matching target.
func (*Jaws) RemoveClass ¶ added in v0.31.0
RemoveClass sends a request to remove the given class from all HTML elements matching target.
func (*Jaws) RemoveTemplateLookuper ¶ added in v0.45.0
func (jw *Jaws) RemoveTemplateLookuper(tl TemplateLookuper) (err error)
RemoveTemplateLookuper removes the given TemplateLookuper.
func (*Jaws) Replace ¶
Replace replaces HTML on all HTML elements matching target.
html is trusted HTML, matching Jaws.SetInner and Jaws.Append.
func (*Jaws) RequestCount ¶ added in v0.25.0
RequestCount returns the total number of Request values, equal to the total returned by Jaws.RequestCounts (see it for what the count includes).
func (*Jaws) RequestCounts ¶ added in v0.407.0
RequestCounts returns the number of Request values.
The total count includes all Requests, including those being rendered, those waiting for the WebSocket callback and those active. The active count includes Requests whose WebSocket Request.ServeHTTP loop is running.
func (*Jaws) SecureHeadersMiddleware ¶ added in v0.300.0
SecureHeadersMiddleware wraps next with security headers that match the current JaWS configuration.
It clones secureheaders.DefaultHeaders(), replacing the Content-Security-Policy value with Jaws.ContentSecurityPolicy so responses allow the resources configured by Jaws.GenerateHeadHTML.
The returned middleware does not trust forwarded HTTPS headers. Note that the session cookie Secure flag is governed separately by [Jaws.TrustForwardedHeaders] (also false by default), so the two stay consistent unless you opt in. The next handler must be non-nil.
func (*Jaws) Serve ¶
func (jw *Jaws) Serve()
Serve calls ServeWithTimeout(DefaultWebSocketTimeout). It is intended to run on its own goroutine. It returns when Jaws.Close is called.
func (*Jaws) ServeHTTP ¶ added in v0.19.0
func (jw *Jaws) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP can handle the required JaWS endpoints, which all start with "/jaws/".
func (*Jaws) ServeWithTimeout ¶
ServeWithTimeout begins processing requests with the given timeout. It is intended to run on its own goroutine. It returns when Jaws.Close is called.
func (*Jaws) Session ¶ added in v0.77.0
Session returns an http.Handler that ensures a JaWS Session exists before invoking h.
func (*Jaws) SessionCount ¶ added in v0.11.0
SessionCount returns the number of active sessions.
func (*Jaws) Sessions ¶ added in v0.11.0
Sessions returns a list of all active sessions, which may be nil.
func (*Jaws) SetAttr ¶
SetAttr sends a request to replace the given attribute value in all HTML elements matching target.
The value parameter must be the unescaped logical attribute value. It is sent to the browser DOM and used as the value argument to setAttribute().
func (*Jaws) SetClass ¶ added in v0.31.0
SetClass sends a request to set the given class in all HTML elements matching target.
func (*Jaws) SetInner ¶
SetInner sends a request to replace the inner HTML of all HTML elements matching target.
func (*Jaws) SetValue ¶
SetValue sends a request to set the current input value (in textual form) of all HTML elements matching target. It sets the live DOM value/state, not the HTML "value" attribute.
func (*Jaws) Setup ¶ added in v0.111.6
func (jw *Jaws) Setup(handleFn HandleFunc, prefix string, extras ...any) (err error)
Setup configures Jaws with extra functionality and resources.
The list of extras can be strings, *url.URL or *staticserve.StaticServe URL resources, or a setup function matching SetupFunc such as jawsboot.Setup.
It calls Jaws.GenerateHeadHTML with the final list of URLs, with any relative URL paths prefixed with prefix.
func (*Jaws) TestServe ¶ added in v0.500.0
func (jw *Jaws) TestServe(rq *Request, onPanic func(recovered any)) (inCh chan wire.WsMsg, outCh chan wire.WsMsg, bcastCh chan wire.Message, readyCh, doneCh chan struct{})
TestServe runs rq's WebSocket message-processing loop for test harnesses, including the out-of-package harness in github.com/linkdata/jaws/jawstest.
It subscribes rq to broadcasts, waits for the running Serve loop to process the subscription, then runs rq.process in a new goroutine using freshly created inbound/outbound channels, recycling rq when the loop stops. It panics if the Jaws processing loop (Jaws.Serve or Jaws.ServeWithTimeout) is not running.
TestServe is exported solely to let test harnesses outside package jaws drive a request loop without access to unexported internals. It is not intended for production use; it does not import any testing-only packages, so it does not pull net/http/httptest into the production build.
onPanic must be non-nil; it is called with the recovered value (nil if the loop exited normally) when the loop goroutine stops, before doneCh is closed, so a harness can publish captured panic state before any <-doneCh waiter observes it. A harness that does not expect panics should re-panic when the value is non-nil so unexpected loop panics still surface.
func (*Jaws) UseRequest ¶
UseRequest extracts the JaWS Request with the given key from the request map if it exists and the HTTP request remote IP matches.
Call it when receiving the WebSocket connection on "/jaws/:key" to get the associated Request, and then call its Request.ServeHTTP method to process the WebSocket messages.
Returns nil if the key was not found or the IP doesn't match, in which case you should return an HTTP "404 Not Found" status.
type Jid ¶ added in v0.31.0
Jid is the identifier type used for HTML elements managed by JaWS.
It is provided as a convenience alias to the value defined in the jid subpackage so applications do not have to import that package directly when working with element IDs.
type Logger ¶ added in v0.110.1
type Logger interface {
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
}
Logger is satisfied by a *log/slog.Logger via its Info, Warn and Error methods.
type MakeAuthFn ¶ added in v0.85.0
MakeAuthFn constructs an Auth value for a Request.
Set [Jaws.MakeAuth] to your implementation to enforce real authorization. If [Jaws.MakeAuth] is left nil, templates receive DefaultAuth, which is fail-open: see its documentation.
It is a type alias so a bare func value can be assigned without conversion, matching the sibling callback types ConnectFn, InputFn and HandleFunc.
type Renderer ¶ added in v0.60.0
type Renderer interface {
// JawsRender is called once per [Element] when rendering the initial webpage.
// Do not call this yourself unless it is from within another JawsRender implementation.
JawsRender(elem *Element, w io.Writer, params []any) error
}
Renderer renders the initial HTML for a UI object.
type Request ¶
type Request struct {
Jaws *Jaws // (read-only) the JaWS instance the Request belongs to
JawsKey uint64 // (read-only) a random number used in the WebSocket URI to identify this Request
Rendering atomic.Bool // set to true by RequestWriter.Write()
// contains filtered or unexported fields
}
Request maintains the state for a JaWS WebSocket connection, and handles processing of events and broadcasts.
Note that we have to store the context inside the struct because there is no call chain between the Request being created and it being used once the WebSocket is created.
func (*Request) Alert ¶
Alert attempts to show an alert message on the current request webpage if it has an HTML element with the id "jaws-alert". The level argument should be one of Bootstrap's alert levels: primary, secondary, success, danger, warning, info, light or dark.
The level and msg are HTML-escaped before being sent, so it is safe to pass untrusted text; do not pre-escape it.
The default JaWS JavaScript only supports Bootstrap dismissible alerts. See Jaws.Broadcast for processing-loop requirements.
func (*Request) AlertError ¶
AlertError calls Request.Alert if the given error is not nil.
func (*Request) Cancel ¶ added in v0.500.0
Cancel aborts the Request.
It cancels the Request's context with the given cause (logged via [Jaws.Logger]) and tears down its WebSocket processing loop. It is safe to call from UI code, for example to terminate a connection that violates a server-side limit. A nil err cancels without a specific cause, and calling Cancel on an already-finished or already-cancelled Request has no effect.
func (*Request) Context ¶
Context returns the Request's context, which is by default derived from [Jaws.BaseContext].
func (*Request) DeleteElement ¶ added in v0.300.0
DeleteElement removes elem from the Request element registry.
This is primarily intended for UI implementations that manage dynamic child element sets and need to drop stale elements after issuing a corresponding DOM remove operation.
func (*Request) Dirty ¶ added in v0.31.0
Dirty marks all Element values that have one or more of the given tags as dirty.
func (*Request) Get ¶ added in v0.11.0
Get is shorthand for Session().Get and returns the session value associated with the key, or nil. If no session is associated with the Request, it returns nil.
func (*Request) GetConnectFn ¶ added in v0.7.0
GetConnectFn returns the currently set ConnectFn. That function will be called before starting the WebSocket tunnel if not nil.
func (*Request) GetElementByJid ¶ added in v0.300.0
GetElementByJid returns the element with jid, or nil if it is not known.
func (*Request) GetElements ¶ added in v0.31.0
GetElements returns a list of the UI elements in the Request that have the given tags.
func (*Request) Initial ¶ added in v0.8.0
Initial returns the Request's initial HTTP request, or nil.
func (*Request) JawsKeyString ¶
JawsKeyString returns the request key in the text form used by JaWS URLs.
func (*Request) Log ¶ added in v0.300.0
Log sends an error to the [Jaws.Logger] if set. Has no effect if err is nil or the Logger is nil. Returns err.
func (*Request) MustLog ¶ added in v0.300.0
MustLog sends an error to the [Jaws.Logger] if set, or panics with the given error if the Logger is nil. Has no effect if err is nil.
Some update-time paths cannot return errors to their caller and report them through MustLog. Set [Jaws.Logger] when those errors should be logged instead of treated as fatal programming errors.
func (*Request) NewElement ¶ added in v0.31.0
NewElement creates a new Element using the given UI object.
Panics if the build tag "debug" is set and the UI object doesn't satisfy all requirements.
func (*Request) Redirect ¶
Redirect requests the current Request to navigate to the given URL.
The URL is validated to be a relative path or an http/https URL; script-bearing schemes such as javascript: and protocol-relative ("//host") URLs are refused and logged rather than sent to the browser. See Jaws.Broadcast for processing-loop requirements.
func (*Request) ServeHTTP ¶
func (rq *Request) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements http.Handler.
Requires Jaws.UseRequest to have been successfully called for the Request.
func (*Request) Set ¶ added in v0.11.0
Set is shorthand for Session().Set and sets a session value to be associated with the key. If value is nil, the key is removed from the session. Does nothing if no session is associated with the Request.
func (*Request) SetConnectFn ¶ added in v0.7.0
SetConnectFn sets the ConnectFn. That function will be called before starting the WebSocket tunnel if not nil.
func (*Request) SetContext ¶ added in v0.110.0
SetContext atomically replaces the Request's context with the function return value. The function is given the current context and must return a non-nil context. The returned context must be derived from oldCtx so cancellation and deadlines continue to propagate to Request.Context.
Returning a nil context is a programming error: debug builds panic and production builds report it via Jaws.MustLog and keep the existing context.
func (*Request) TagExpanded ¶ added in v0.300.0
TagExpanded adds already-expanded tags to the given Element.
func (*Request) TagsOf ¶ added in v0.31.0
TagsOf returns the tags currently associated with elem in this Request, or nil if elem is nil. The returned slice is a snapshot and must not be modified.
func (*Request) TailHTML ¶ added in v0.79.0
TailHTML writes optional HTML code at the end of the page's BODY section that will immediately apply HTML attribute and class updates made during initial rendering, which minimizes flicker without having to write the correct value in templates or during [Renderer.JawsRender].
It also adds a <noscript> tag that warns of reduced functionality.
type Session ¶ added in v0.11.0
type Session struct {
// contains filtered or unexported fields
}
Session stores server-side per-user state shared by one or more requests.
A Session is bound to the remote IP that created it. Its exported methods are safe to call on a nil *Session; those calls return the documented zero value or do nothing.
func (*Session) Broadcast ¶ added in v0.26.0
Broadcast attempts to send a message to all Request values using this session.
It must not be called before the JaWS processing loop (Jaws.Serve or Jaws.ServeWithTimeout) is running. Otherwise this call may block. It is safe to call on a nil Session.
func (*Session) Clear ¶ added in v0.16.0
func (sess *Session) Clear()
Clear removes all key/value pairs from the session. It is safe to call on a nil Session.
func (*Session) Close ¶ added in v0.17.0
Close invalidates and expires the Session. Future Request values won't be able to associate with it, and Session.Cookie will return a deletion cookie.
Existing Request values already associated with the Session will ask the browser to reload the pages. Key/value pairs in the Session are left unmodified; use Session.Clear to remove all of them.
It must not be called before the JaWS processing loop (Jaws.Serve or Jaws.ServeWithTimeout) is running, because reload broadcasts may block.
Returns a cookie to be sent to the client browser that will delete the browser cookie. Returns nil if the session was not found. It is safe to call on a nil Session.
func (*Session) Cookie ¶ added in v0.11.0
Cookie returns a cookie for the Session. Returns a delete cookie if the Session is expired. It is safe to call on a nil Session, in which case it returns nil.
func (*Session) CookieValue ¶ added in v0.11.0
CookieValue returns the session cookie value. It is safe to call on a nil Session, in which case it returns an empty string.
func (*Session) Get ¶ added in v0.11.0
Get returns the value associated with the key, or nil. It is safe to call on a nil Session.
func (*Session) ID ¶ added in v0.11.0
ID returns the session ID, a 64-bit random value. It is safe to call on a nil Session, in which case it returns zero.
func (*Session) IP ¶ added in v0.11.0
IP returns the remote IP the session is bound to, or the zero netip.Addr if unset. It is safe to call on a nil Session, in which case it returns the zero netip.Addr.
func (*Session) Jaws ¶ added in v0.81.0
Jaws returns the Jaws instance of the Session, or nil. It is safe to call on a nil Session.
func (*Session) Reload ¶ added in v0.17.0
func (sess *Session) Reload()
Reload calls Session.Broadcast with a message asking browsers to reload the page. See Session.Broadcast for the processing-loop requirement.
type SetupFunc ¶ added in v0.111.6
SetupFunc is called by Jaws.Setup and allows setting up addons for JaWS.
The URLs returned will be used in a call to Jaws.GenerateHeadHTML.
type TemplateLookuper ¶ added in v0.45.0
TemplateLookuper resolves a name to a *template.Template.
type UI ¶ added in v0.31.0
UI defines the required methods on JaWS UI objects.
In addition, all UI objects must be comparable so they can be used as map keys. The compile-time type must be comparable; debug builds additionally perform a runtime value-level check in Request.NewElement and panic on a value that is statically comparable but not comparable at runtime (for example a comparable struct holding a func in an interface field). Production builds rely on the static check alone, so such a value is accepted and instead panics when first used as a map key; callers must therefore ensure UI values are genuinely comparable.
type Updater ¶ added in v0.60.0
type Updater interface {
// JawsUpdate is called for an [Element] that has been marked dirty to update its HTML.
// Do not call this yourself unless it is from within another JawsUpdate implementation.
JawsUpdate(elem *Element)
}
Updater updates browser-side DOM for a dirty Element.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
minesweeper
command
|
|
|
Package jawsboot provides embedded Bootstrap assets for JaWS applications.
|
Package jawsboot provides embedded Bootstrap assets for JaWS applications. |
|
Package jawstest provides an importable harness for driving a jaws.Request's WebSocket message-processing loop in tests.
|
Package jawstest provides an importable harness for driving a jaws.Request's WebSocket message-processing loop in tests. |
|
Package jawstree provides a JaWS widget and embedded assets for the Quercus.js treeview library.
|
Package jawstree provides a JaWS widget and embedded assets for the Quercus.js treeview library. |
|
lib
|
|
|
assets
Package assets contains the embedded client assets and helpers used by JaWS setup code.
|
Package assets contains the embedded client assets and helpers used by JaWS setup code. |
|
bind
Package bind adapts Go values to JaWS getter, setter, HTML and tag interfaces.
|
Package bind adapts Go values to JaWS getter, setter, HTML and tag interfaces. |
|
htmlio
Package htmlio writes the small HTML fragments used by standard JaWS widgets.
|
Package htmlio writes the small HTML fragments used by standard JaWS widgets. |
|
jid
Package jid provides JaWS element identifiers and helpers for writing them into HTML.
|
Package jid provides JaWS element identifiers and helpers for writing them into HTML. |
|
named
Package named provides named boolean values and collections used by select, option and radio widgets.
|
Package named provides named boolean values and collections used by select, option and radio widgets. |
|
tag
Package tag expands JaWS tag values into the comparable keys used to find elements during dirtying, broadcasts and event routing.
|
Package tag expands JaWS tag values into the comparable keys used to find elements during dirtying, broadcasts and event routing. |
|
templatereloader
Package templatereloader provides a jaws.TemplateLookuper that reparses templates from disk while running in debug or race builds.
|
Package templatereloader provides a jaws.TemplateLookuper that reparses templates from disk while running in debug or race builds. |
|
ui
Package ui contains the standard JaWS widget implementations.
|
Package ui contains the standard JaWS widget implementations. |
|
what
Package what defines commands used by the JaWS wire protocol.
|
Package what defines commands used by the JaWS wire protocol. |
|
wire
Package wire formats and parses the line-based JaWS WebSocket protocol.
|
Package wire formats and parses the line-based JaWS WebSocket protocol. |