AlteriomPainlessMesh v2.1.1
Easy ESP32/ESP8266 mesh networking with Alteriom IoT extensions
Loading...
Searching...
No Matches
Classes | Typedefs | Enumerations | Functions
painlessmesh::gateway Namespace Reference

Classes

class  ChunkedBodyDecoder
 Removes HTTP/1.1 chunked transfer framing from a body read a byte at a time, and passes the content on to a ResponseExcerpt. More...
 
class  GatewayAckPackage
 Gateway Acknowledgment Package for delivery confirmations. More...
 
class  GatewayDataPackage
 Gateway Data Package for routing Internet requests through mesh. More...
 
struct  HttpRequestOutcome
 The outcome of a gateway HTTP request, classified for the ack. More...
 
class  InternetHealthChecker
 Internet Health Checker class for periodic connectivity monitoring. More...
 
struct  InternetStatus
 Structure to hold Internet connectivity check results. More...
 
struct  MeshChannelCandidate
 One occurrence of the mesh SSID seen by an all-channel scan. More...
 
class  NegativeDnsCache
 A few hosts that recently failed to resolve, and when. More...
 
class  ResponseExcerpt
 The start and the end of a response body, read a byte at a time. More...
 
struct  SharedGatewayConfig
 Configuration structure for Shared Gateway Mode. More...
 
struct  ValidationResult
 Validation result structure for SharedGatewayConfig. More...
 

Typedefs

typedef std::function< void(bool available)> InternetChangedCallback_t
 Callback type for Internet connectivity change events.
 

Enumerations

enum class  GatewayPriority : uint8_t { PRIORITY_CRITICAL = 0 , PRIORITY_HIGH = 1 , PRIORITY_NORMAL = 2 , PRIORITY_LOW = 3 }
 Priority levels for GatewayDataPackage messages. More...
 
enum  HttpTransportError : int {
  HTTP_TRANSPORT_CONNECTION_REFUSED = -1 , HTTP_TRANSPORT_SEND_HEADER_FAILED = -2 , HTTP_TRANSPORT_SEND_PAYLOAD_FAILED = -3 , HTTP_TRANSPORT_NOT_CONNECTED = -4 ,
  HTTP_TRANSPORT_CONNECTION_LOST = -5 , HTTP_TRANSPORT_NO_STREAM = -6 , HTTP_TRANSPORT_NO_HTTP_SERVER = -7 , HTTP_TRANSPORT_TOO_LESS_RAM = -8 ,
  HTTP_TRANSPORT_ENCODING = -9 , HTTP_TRANSPORT_STREAM_WRITE = -10 , HTTP_TRANSPORT_READ_TIMEOUT = -11
}
 HTTPClient transport errors, by what they say about the request. More...
 

Functions

constexpr bool isValidMeshChannel (uint8_t channel)
 
constexpr bool shouldFollowBridgeChannel (uint32_t localNodeId, uint32_t electedBridgeId, uint8_t currentChannel, uint8_t announcedChannel)
 
uint8_t pickMeshChannel (const std::vector< MeshChannelCandidate > &candidates, uint8_t avoidChannel, uint8_t routerChannel=0)
 Where a node that has lost the mesh should go.
 
bool transportErrorMayHaveReachedServer (int rawCode)
 Can a request that failed with this transport error have reached the server?
 
bool responseContains (const TSTRING &haystack, const char *needle)
 
TSTRING summarizeResponseBody (const TSTRING &body, size_t maxLen=GATEWAY_RESPONSE_REASON_MAX)
 Reduce a response body to one line fit for a log or an error string.
 
bool transferEncodingIsChunked (const TSTRING &value)
 
uint32_t parseRetryAfterMs (const TSTRING &value)
 The delay a Retry-After header asks for, in milliseconds.
 
TSTRING requestIdFor (uint32_t originNode, uint32_t messageId, uint32_t requestNonce=0)
 The identifier the gateway sends with a request, as both X-Request-Id and Idempotency-Key.
 
uint32_t newRequestNonce ()
 
HttpRequestOutcome classifyHttpResult (int rawCode, const TSTRING &body=TSTRING())
 Classify an HTTPClient result for the gateway acknowledgment.
 
TSTRING hostFromUrl (const TSTRING &url)
 The host of an http(s) URL, without scheme, port, path or query.
 
bool looksLikeIpLiteral (const TSTRING &host)
 
constexpr unsigned long gatewayBlockingBudgetMs ()
 Wall-clock ceiling of the gateway's blocking calls on the GATEWAY_DATA path, in milliseconds (issue #416).
 
template<typename T >
bool reserveGatewayBlockingBudget (T &connection)
 
template<typename T >
size_t refreshPeerWatchdogs (T &mesh, unsigned long stalledMs)
 Give every peer's running watchdog back the time a blocking call hid.
 

Typedef Documentation

◆ InternetChangedCallback_t

typedef std::function<void(bool available)> painlessmesh::gateway::InternetChangedCallback_t

Callback type for Internet connectivity change events.

Enumeration Type Documentation

◆ GatewayPriority

enum class painlessmesh::gateway::GatewayPriority : uint8_t
strong

Priority levels for GatewayDataPackage messages.

Defines the priority levels for message routing through the gateway. Lower values indicate higher priority.

Note
Uses PRIORITY_ prefix to avoid conflicts with Arduino macros (HIGH and LOW are defined in esp32-hal-gpio.h)
Enumerator
PRIORITY_CRITICAL 

Critical messages - immediate processing.

PRIORITY_HIGH 

High priority - processed before normal.

PRIORITY_NORMAL 

Normal priority - standard processing.

PRIORITY_LOW 

Low priority - processed when idle.

◆ HttpTransportError

HTTPClient transport errors, by what they say about the request.

HTTPClient::GET()/POST() return a negative code when the request failed below HTTP. The ESP32 and ESP8266 cores number them the same way. What matters to a retry is whether the request can have reached the server: resending one that did delivers it twice, which for a request with an effect – a message, a payment, a counter – is a second effect (the rig showed a timed-out send issued four times).

Where each is raised, in both cores' HTTPClient::sendRequest() for the buffer GET()/POST() the gateway uses: -1, -2 and -3 before the request is complete; everything else from handleHeaderResponse(), which runs after the whole request was written – so -4 is a connection that closed before the reply began and -7 a reply that was not HTTP, both with the request already at the server. -6 and -8 come from stream sends and from reading a body.

Enumerator
HTTP_TRANSPORT_CONNECTION_REFUSED 

no connection was made

HTTP_TRANSPORT_SEND_HEADER_FAILED 

the request line never completed

HTTP_TRANSPORT_SEND_PAYLOAD_FAILED 

the body never completed

HTTP_TRANSPORT_NOT_CONNECTED 

closed before the reply began; sent

HTTP_TRANSPORT_CONNECTION_LOST 

dropped while reading the reply; sent

HTTP_TRANSPORT_NO_STREAM 

no stream to send or read with

HTTP_TRANSPORT_NO_HTTP_SERVER 

the reply was not HTTP; sent

HTTP_TRANSPORT_TOO_LESS_RAM 

out of memory sending or reading

HTTP_TRANSPORT_ENCODING 

the reply arrived malformed; sent

HTTP_TRANSPORT_STREAM_WRITE 

the reply could not be stored; sent

HTTP_TRANSPORT_READ_TIMEOUT 

sent, and no reply in time

Function Documentation

◆ classifyHttpResult()

HttpRequestOutcome painlessmesh::gateway::classifyHttpResult ( int  rawCode,
const TSTRING &  body = TSTRING() 
)
inline

Classify an HTTPClient result for the gateway acknowledgment.

HTTP semantics only. 200, 201, 202 and 204 are a success. Any other 2xx is an unverified failure the server answered; 1xx, 3xx, 4xx and 5xx are failures. A retry is allowed only when it cannot produce a second copy of the request: a transport error that failed before the request was complete, or 429/503, where the server says it did not take the request. The reason is a summary of whatever body was read, so the application learns why.

Parameters
rawCodeThe int returned by HTTPClient::GET() or ::POST()
bodyThe start of the response body, empty if none was read
Returns
Classified outcome, safe to place in a GatewayAckPackage

◆ gatewayBlockingBudgetMs()

constexpr unsigned long painlessmesh::gateway::gatewayBlockingBudgetMs ( )
constexpr

Wall-clock ceiling of the gateway's blocking calls on the GATEWAY_DATA path, in milliseconds (issue #416).

Each HTTPClient call chain is counted at two socket waits — GET()/POST() (send + header read) and getString() (body read) — because HTTPClient::setTimeout() bounds an individual socket wait, not the whole call. The captive-portal probe and the destination request both have that shape and run back to back, and the DNS reachability probe (bounded by GATEWAY_DNS_TIMEOUT_MS on ESP8266, skipped on ESP32) runs before them whenever the GATEWAY_CONNECTIVITY_CACHE_MS window has expired.

Warning
One residual is not in this budget: hostname resolution on ESP32 is not separately boundable in the cores this library targets. Since issue #453 the handler performs that lookup itself on ESP32 and remembers a failure for GATEWAY_DNS_NEGATIVE_TTL_MS, so on a network with blackholed DNS the request path can still exceed this ceiling on ESP32, but at most once per TTL per destination host rather than once per attempt. On ESP8266 the core bounds HTTPClient's own lookup by the HTTP timeout. See SECURITY.md "Gateway blocking: the mesh partition risk".

◆ hostFromUrl()

TSTRING painlessmesh::gateway::hostFromUrl ( const TSTRING &  url)
inline

The host of an http(s) URL, without scheme, port, path or query.

"https://api.example.com:8443/x?y" -> "api.example.com". Empty when the URL has no host.

◆ isValidMeshChannel()

constexpr bool painlessmesh::gateway::isValidMeshChannel ( uint8_t  channel)
constexpr

Return whether a channel can be announced for a 2.4 GHz mesh takeover.

◆ looksLikeIpLiteral()

bool painlessmesh::gateway::looksLikeIpLiteral ( const TSTRING &  host)
inline

True for a dotted-decimal IPv4 literal, which needs no DNS.

◆ newRequestNonce()

uint32_t painlessmesh::gateway::newRequestNonce ( )
inline

A request nonce: random, and never the 0 that means "none".

◆ parseRetryAfterMs()

uint32_t painlessmesh::gateway::parseRetryAfterMs ( const TSTRING &  value)
inline

The delay a Retry-After header asks for, in milliseconds.

Only the delay-seconds form is read; an HTTP-date, or anything else, is 0 – no instruction – rather than a guess. Values past GATEWAY_RETRY_AFTER_MAX_MS are returned as they are, so the caller can see the server asked for longer than it will wait.

◆ pickMeshChannel()

uint8_t painlessmesh::gateway::pickMeshChannel ( const std::vector< MeshChannelCandidate > &  candidates,
uint8_t  avoidChannel,
uint8_t  routerChannel = 0 
)
inline

Where a node that has lost the mesh should go.

Channel re-detection runs only after a node's own partition has produced nothing new for a while, so the mesh it can still see on its current channel is the partition it is stranded in. When the SSID is also visible on another channel, that is the rest of the network — a bridge that moved to its router's channel, most often — and the node should go there.

Returning the first match, as this used to, made the outcome depend on scan order: a stranded node that happened to see its own partition first concluded nothing had changed and stayed stranded.

A node that knows the router has a better signal than strength: a bridge's AP is on its router's channel, so a mesh with a bridge lives there. A failover backup that booted while the mesh was split across two channels saw one AP on each, two dB apart, and joined the one without the bridge; it had a partition of two to itself for the hundred seconds it took the re-detection rules to move it, and the election ran out of time.

Parameters
candidatesEvery channel the mesh SSID was seen on, with RSSI.
avoidChannelThe node's current mesh channel; 0 = no preference.
routerChannelThe channel the router was seen on; 0 = unknown.
Returns
The candidate on routerChannel if there is one; else the strongest on a channel other than avoidChannel; failing that the strongest on avoidChannel; 0 if there are none.

◆ refreshPeerWatchdogs()

template<typename T >
size_t painlessmesh::gateway::refreshPeerWatchdogs ( T &  mesh,
unsigned long  stalledMs 
)

Give every peer's running watchdog back the time a blocking call hid.

Call this immediately after returning from a blocking Internet call, before the scheduler next runs, passing the measured wall-clock duration of the stall.

Nothing executes while the gateway is inside HTTPClient::GET()/POST(), but wall-clock time keeps passing. So a timeOutTask whose NODE_TIMEOUT deadline fell during the stall is already overdue when the scheduler resumes, and fires on the very next execute() – closing a peer that never actually went missing (issues #318, #332).

The compensation equals the measured stall, no more (issue #417): each enabled watchdog's existing deadline is postponed by stalledMs via Task::adjust(), which extends iDelay without touching the task's baseline. A peer's watchdog therefore measures only time the mesh was actually able to observe the peer. The earlier implementation restarted every watchdog from zero (restartDelayed()) on every exit path – which meant any live peer's gateway traffic granted a genuinely dead peer a fresh full NODE_TIMEOUT, so the dead peer was never reaped. Task::delay() would be wrong in the other direction: it resets the baseline to now, shortening the deadline of a peer that still had more than stalledMs remaining and disconnecting healthy peers early.

Paths that did not block must pass 0 and compensate nothing.

Only enabled watchdogs are touched, and this matters: timeOutTask is armed by nodeSyncTask when a sync request goes out and disabled again when the reply lands, so a disabled watchdog means the peer has nothing outstanding. Enabling it here would invent a NODE_TIMEOUT deadline for an idle-but-healthy link whose callback is Connection::close() – turning a reliability fix into a disconnect bug.

This protects the gateway's own view of its peers. The peers' view of the gateway depends on keeping the stall shorter than their watchdog, which is what gatewayBlockingBudgetMs() is meant to bound – though see the

Warning
there for why that bound is not airtight (#416). Request-side reservation plus gateway-side compensation are both needed.
Template Parameters
TMesh type exposing subs (see painlessmesh::layout::Layout).
Parameters
meshThe mesh whose direct peer connections should be refreshed.
stalledMsMeasured wall-clock duration of the blocking section, in milliseconds. 0 means nothing blocked and nothing is compensated.
Returns
Number of peers whose watchdog deadline was postponed.

◆ requestIdFor()

TSTRING painlessmesh::gateway::requestIdFor ( uint32_t  originNode,
uint32_t  messageId,
uint32_t  requestNonce = 0 
)
inline

The identifier the gateway sends with a request, as both X-Request-Id and Idempotency-Key.

The same for every attempt at one sendToInternet() call, so a service that honours Idempotency-Key treats a retry as the request it already has, and anything recording requests can count the copies one call produced.

The request's nonce keeps it unique beyond its messageId, whose counter wraps after 65,535 requests in a boot. A package from a node that predates the nonce (0) keeps the two-part form it always had.

◆ reserveGatewayBlockingBudget()

template<typename T >
bool painlessmesh::gateway::reserveGatewayBlockingBudget ( T &  connection)

Reserve the blocking HTTP budget on the requester's route watchdog.

A gateway compensates its own peer watchdogs after a blocking request, but it cannot modify the requester's timer. That timer may already be partly spent, so extend its existing deadline by the bounded gateway budget before sending. Disabled watchdogs remain disabled.

◆ responseContains()

bool painlessmesh::gateway::responseContains ( const TSTRING &  haystack,
const char *  needle 
)
inline

◆ shouldFollowBridgeChannel()

constexpr bool painlessmesh::gateway::shouldFollowBridgeChannel ( uint32_t  localNodeId,
uint32_t  electedBridgeId,
uint8_t  currentChannel,
uint8_t  announcedChannel 
)
constexpr

Decide whether a peer must follow an elected bridge to another channel.

◆ summarizeResponseBody()

TSTRING painlessmesh::gateway::summarizeResponseBody ( const TSTRING &  body,
size_t  maxLen = GATEWAY_RESPONSE_REASON_MAX 
)
inline

Reduce a response body to one line fit for a log or an error string.

Tags are dropped and whitespace collapsed, so an HTML page reads as text in a serial log. A body longer than maxLen keeps its beginning and its end, joined by " ... ": services often lead with an echo of the request and finish with the verdict, and a summary that keeps only the beginning keeps the echo and loses the verdict (issue #463).

◆ transferEncodingIsChunked()

bool painlessmesh::gateway::transferEncodingIsChunked ( const TSTRING &  value)
inline

Whether a Transfer-Encoding header value names chunked encoding.

◆ transportErrorMayHaveReachedServer()

bool painlessmesh::gateway::transportErrorMayHaveReachedServer ( int  rawCode)
inline

Can a request that failed with this transport error have reached the server?

True – the safe answer – for every code that is not known to fail before the request was complete, including codes this library does not know.