cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
html_response.h File Reference

HTML page and fragment responses for HTML-over-the-wire handlers. More...

#include <cwist/net/http/http.h>
#include <cwist/core/html/builder.h>
#include <cwist/core/html/component.h>
#include <stdbool.h>
Include dependency graph for html_response.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define CWIST_HTML_FRAGMENT_VARY   "HX-Request, HX-History-Restore-Request, HX-Target, Turbo-Frame"
 Request headers that select between a page and a fragment.
 

Functions

bool cwist_http_request_wants_fragment (const cwist_http_request *req)
 Whether the client asked for a fragment instead of a full page.
 
const char * cwist_http_request_fragment_target (const cwist_http_request *req)
 The element id the client will swap the fragment into, if it said.
 
int cwist_http_response_set_html (cwist_http_response *res, cwist_html_element_t *root, bool document)
 Render an element tree into the response body as HTML.
 
int cwist_http_response_set_view (const cwist_http_request *req, cwist_http_response *res, cwist_html_element_t *content, cwist_html_component_t *layout, const void *layout_props)
 Answer with a fragment or a full page, depending on the request.
 
int cwist_http_response_add_oob (const cwist_http_request *req, cwist_http_response *res, cwist_html_element_t *el)
 Append an out-of-band element to a fragment response.
 
int cwist_http_response_html_redirect (const cwist_http_request *req, cwist_http_response *res, const char *location)
 Redirect in a way both plain navigation and fragment clients follow.
 

Detailed Description

HTML page and fragment responses for HTML-over-the-wire handlers.

A handler builds the content it owns (usually with cwist_html_component_t) and lets these helpers decide how to answer: a full document on a normal navigation, only the fragment when the client asked for one. Out-of-band elements and redirects follow the same request-driven switch, so the same route works with and without client-side script (progressive enhancement).

Fragment requests are recognised by the request headers that fragment- swapping clients already send: HX-Request: true (htmx) and Turbo-Frame (Hotwire Turbo frames). Nothing here requires either library.

Definition in file html_response.h.

Macro Definition Documentation

◆ CWIST_HTML_FRAGMENT_VARY

#define CWIST_HTML_FRAGMENT_VARY   "HX-Request, HX-History-Restore-Request, HX-Target, Turbo-Frame"

Request headers that select between a page and a fragment.

cwist_http_response_set_view() sends this as Vary so shared caches keep the two representations of one URL apart.

Definition at line 34 of file html_response.h.

Function Documentation

◆ cwist_http_request_fragment_target()

const char * cwist_http_request_fragment_target ( const cwist_http_request *  req)

The element id the client will swap the fragment into, if it said.

Turbo-Frame first, then HX-Target. Only meaningful when cwist_http_request_wants_fragment() is true.

Returns
Request-owned string, or NULL when not a fragment request or no target was sent.

The element id the client will swap the fragment into, if it said.

Parameters
reqRequest to inspect; may be NULL.
Returns
The Turbo-Frame id when present, otherwise the HX-Target header when present, or NULL when the client did not request a fragment or no target was given. The pointer is owned by the request.

Definition at line 57 of file html_response.c.

References cap, and cwist_http_request_wants_fragment().

◆ cwist_http_request_wants_fragment()

bool cwist_http_request_wants_fragment ( const cwist_http_request *  req)

Whether the client asked for a fragment instead of a full page.

True for HX-Request: true (unless HX-History-Restore-Request: true, which asks for the whole page again) and for any non-empty Turbo-Frame header.

Parameters
reqParsed request. NULL gives false.

Whether the client asked for a fragment instead of a full page.

A fragment is requested when the htmx HX-Request header is "true" (and it is not a history restore) or when a non-empty Turbo-Frame header is set.

Parameters
reqRequest to inspect; may be NULL.
Returns
true if only a fragment of the page should be returned.

Definition at line 43 of file html_response.c.

References cap.

Referenced by cwist_http_request_fragment_target(), cwist_http_response_add_oob(), and cwist_http_response_set_view().

◆ cwist_http_response_add_oob()

int cwist_http_response_add_oob ( const cwist_http_request *  req,
cwist_http_response *  res,
cwist_html_element_t *  el 
)

Append an out-of-band element to a fragment response.

For a fragment request the element is rendered after the current body with hx-swap-oob="true" (unless the caller already set hx-swap-oob), so the client replaces the element with the same id elsewhere on the page. For a full-page request it is dropped: the page being sent already contains that region. Clients that do not implement out-of-band swaps ignore the extra element.

Call it after the main content has been set.

Parameters
reqRequest being answered.
resResponse whose body receives the element.
elElement to append; must carry a non-empty id attribute. Always consumed, including on failure.
Returns
0 when appended or deliberately dropped, -1 on invalid arguments (a missing id included) or allocation failure.

Append an out-of-band element to a fragment response.

The element must have an "id" attribute. When the client requested a full page the element is dropped (a full page already contains the region) and success is reported. Otherwise an hx-swap-oob="true" attribute is added if missing and the element is rendered and appended to the body. Takes ownership of el in all cases.

Parameters
reqRequest used to decide whether an OOB update applies.
resResponse whose body the rendered element is appended to.
elElement to append; consumed regardless of the result.
Returns
0 on success (including the dropped full-page case), -1 if the response cannot hold HTML, the element has no id, or rendering or appending fails.

Definition at line 226 of file html_response.c.

References cwist_http_response::body, cap, cwist_error_dispose(), cwist_html_element_add_attr(), cwist_html_element_destroy(), cwist_http_request_wants_fragment(), cwist_sstring_append(), cwist_sstring_destroy(), and cwist_sstring::data.

◆ cwist_http_response_html_redirect()

int cwist_http_response_html_redirect ( const cwist_http_request *  req,
cwist_http_response *  res,
const char *  location 
)

Redirect in a way both plain navigation and fragment clients follow.

An htmx request (HX-Request: true) gets 200 with HX-Redirect: <location>, because a script-driven request follows a 3xx itself and would swap the target page into the fragment slot. Every other request, Turbo frames included, gets 303 See Other with Location, which also turns a form POST into a GET. The body is emptied in both cases.

Parameters
reqRequest being answered.
resResponse to fill.
locationTarget URL; must be non-empty and free of control characters (CR/LF would split the response).
Returns
0 on success, -1 on invalid arguments or allocation failure.

Redirect in a way both plain navigation and fragment clients follow.

For htmx requests (HX-Request: true) the status stays 200 and the target is sent via the HX-Redirect header; otherwise a 303 See Other response with a Location header is produced. Any previous Location and HX-Redirect headers are removed and the body is cleared.

Parameters
reqRequest used to decide between htmx and regular redirects.
resResponse to modify.
locationRedirect target; must be non-empty and free of control bytes.
Returns
0 on success, -1 if the response cannot hold HTML, the location is invalid, or header/body updates fail.

Definition at line 282 of file html_response.c.

References cwist_http_response::body, cap, cwist_error_dispose(), cwist_http_header_remove(), CWIST_HTTP_OK, CWIST_HTTP_SEE_OTHER, cwist_sstring_assign(), cwist_http_response::headers, and cwist_http_response::status_code.

◆ cwist_http_response_set_html()

int cwist_http_response_set_html ( cwist_http_response *  res,
cwist_html_element_t *  root,
bool  document 
)

Render an element tree into the response body as HTML.

Replaces the body, sets Content-Type: text/html; charset=utf-8 (replacing any earlier Content-Type) and leaves the status code alone. With document set, the body starts with <!DOCTYPE html>.

Parameters
resResponse to fill.
rootElement tree to render; always consumed (destroyed), including on failure.
documentTrue for a full document, false for a fragment.
Returns
0 on success, -1 on invalid arguments or allocation failure (the body is then left unchanged).

Render an element tree into the response body as HTML.

Always takes ownership of root and destroys it. On success the Content-Type header is set to text/html; charset=utf-8.

Parameters
resResponse whose body is replaced.
rootElement tree to render; consumed regardless of the result.
documentWhen true, render as a full document with a doctype.
Returns
0 on success, -1 if the response cannot hold an HTML body, the element is NULL, or rendering/assignment fails.

Definition at line 141 of file html_response.c.

References cwist_http_response::body, cap, cwist_error_dispose(), cwist_html_element_destroy(), cwist_sstring_assign_len(), and cwist_sstring_destroy().

Referenced by cwist_http_response_set_view().

◆ cwist_http_response_set_view()

int cwist_http_response_set_view ( const cwist_http_request *  req,
cwist_http_response *  res,
cwist_html_element_t *  content,
cwist_html_component_t *  layout,
const void *  layout_props 
)

Answer with a fragment or a full page, depending on the request.

A fragment request gets content alone. Any other request gets a document: content wrapped by layout when one is given (the layout component receives content as its single child, see cwist_html_component_instantiate()), or content itself as the document root otherwise. Both answers carry Vary: CWIST_HTML_FRAGMENT_VARY.

Parameters
reqRequest being answered.
resResponse to fill.
contentPage content; always consumed, including on failure.
layoutOptional layout component for full-page answers (not consumed).
layout_propsProps passed to layout (may be NULL).
Returns
0 on success, -1 on invalid arguments, a failed layout render, or allocation failure.

Answer with a fragment or a full page, depending on the request.

When the client requested a fragment, content is rendered alone; otherwise it is embedded into the layout component (or rendered as a full document when no layout is given). Takes ownership of content in all cases. On success a Vary header for fragment-negotiation is appended.

Parameters
reqRequest used to decide between fragment and full-page rendering.
resResponse whose body is replaced.
contentElement tree for the page content; consumed regardless of the result.
layoutOptional layout component that wraps content; may be NULL.
layout_propsProps passed to the layout component.
Returns
0 on success, -1 if the response cannot hold HTML, content is NULL, or rendering fails.

Definition at line 178 of file html_response.c.

References cap, cwist_html_component_instantiate(), cwist_html_element_destroy(), CWIST_HTML_FRAGMENT_VARY, cwist_http_request_wants_fragment(), and cwist_http_response_set_html().