Repository files navigation

Link

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Link helps you communicate with APIs and backend services in Arduino ESP32 projects. It is designed for production firmware that needs thread-safe request submission, predictable request/response limits, and clear result-based errors.

CIReleaseLicense: MIT

Why use Link?

  • Fetch-style requests - submit get, post, getJson, postJson, or getStream work from normal FreeRTOS tasks.
  • Concurrent workers - run more than one HTTP request at the same time with a bounded worker pool.
  • ESP32-friendly memory - accepted request bodies, response bodies, URLs, headers, serialized JSON, callbacks, and stream buffers have explicit limits.
  • Clear API - operations return LinkResult; HTTP status codes stay separate from transport failures.
  • Production-minded - no exceptions, serialized lifecycle transitions, atomic queue publication, bindable callbacks, and PSRAM-preferred payload buffers.

Install

PlatformIO

Link is built for Arduino ESP32 and depends on ArduinoJson v7.

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/link.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Link is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Link

Quick start

#include<Arduino.h>
#include<Link.h>
Link client;
voidonResponse(const LinkResponse &response) {
if (!response) {
Serial.println(response.error.message);
return;
}
Serial.println(response.httpStatus);
Serial.println(response.body.c_str());
}
voidsetup() {
Serial.begin(115200);
LinkConfig config;
config.maxConcurrentRequests = 2;
config.maxResponseBodySize = 8192;
LinkResult initResult = client.init(config);
if (!initResult) {
Serial.println(initResult.message);
return;
}
client.get("https://example.com", onResponse);
}
voidloop() {
delay(1000);
}

Important notes

Important

Link callbacks run inside Link worker tasks. If maxConcurrentRequests > 1, multiple callbacks may run at the same time.

  • Protect shared application state touched from callbacks.
  • Requests are started in queue order, but may complete out of order when more than one worker is enabled.
  • Submission preparation, queue publication, and worker signaling form one runtime critical section. A successful submission always owns a slot and has a corresponding worker permit.
  • User callbacks are not called while Link's runtime mutex is held. A callback may submit another request; if shutdown has started, the submission returns Stopping.
  • Every accepted request receives exactly one terminal callback before a successful deinit() returns.
  • LinkJsonResponse::json is valid only during the callback unless the user copies the needed data.
  • Allocation-backed response storage is move-only. Use explicit result-returning copyFrom() operations when duplication is required.
  • HTTPS uses the ESP-IDF certificate bundle when available. If the project/core does not provide usable certificate bundle support, verified HTTPS fails with TlsFailed.
  • deinit() lets worker tasks cancel queued requests and waits for active workers to exit. If the public wait times out, Link stays in Stopping and keeps worker-owned storage alive so a later deinit() can finish cleanup.
  • The destructor performs blocking shutdown. It assumes active HTTP operations eventually return through their configured nonzero request timeout.
  • Do not call deinit() or destroy a Link instance from one of its callbacks; shutdown waits for that callback's worker task to exit.
  • Redirect following is limited to GET requests with absolute http:// or https://Location headers. Same-origin redirects are allowed by default; cross-origin and HTTPS-to-HTTP redirects require explicit opt-in. Caller-supplied headers are stripped after an origin change. Intermediate redirect bodies are discarded.

Examples

The repository includes topic-focused Arduino sketches in the examples/ folder.

ExampleDescription
basic-getInitialize Link and run one buffered GET request.
post-jsonSend a JSON request body and parse a JSON response.
custom-headersAdd custom request headers and inspect response headers.
stream-downloadDownload a large response in bounded chunks.
class-callbackBind a private class method as a response callback.

Start with:

examples/basic-get

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/api.mdPublic classes, result types, ownership, and callback aliases.
docs/callbacks.mdCallback storage, binding, and execution context.
docs/concurrency.mdQueue publication, worker pool, lifecycle, and completion guarantees.
docs/errors.mdError codes and HTTP status behavior.
docs/json.mdArduinoJson helpers and JSON lifetime rules.
docs/streaming.mdStreaming downloads and cancellation.
docs/memory.mdBounded memory, ESP-IDF ranges, and explicit copy behavior.
docs/persistent-http.mdOptional per-worker persistent HTTP clients.
docs/release-validation.mdAutomated gates and physical v0.1.1 qualification.

API overview

Link client;
LinkResult init(const LinkConfig &config);
LinkResult deinit();
LinkResult fetch(const LinkRequest &request);
LinkDiagnostics diagnostics() const;
client.get(url, callback);
client.post(url, body, callback);
client.getJson(url, callback);
client.postJson(url, json, callback);
client.getStream(url, onStart, onChunk, onEnd);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
NetworkingESP-IDF esp_http_client
HTTPSESP-IDF certificate bundle when available
PSRAMPayload buffers prefer PSRAM; worker stacks can optionally use PSRAM
Dependenciesbblanchon/ArduinoJson >= 7.0.0
ExceptionsNot used
Status0.1.1

Configuration

LinkConfig config;
config.queueSize = 10;
config.maxConcurrentRequests = 3;
config.defaultTimeoutMs = 15000;
config.connectionMode = LinkConnectionMode::PerRequest;
config.persistentIdleTimeoutMs = 5U * 60U * 1000U;
config.persistentMaxRequestsPerHandle = 0;
config.maxUrlSize = 512;
config.maxRequestBodySize = 8192;
config.maxResponseBodySize = 8192;
config.maxSerializedJsonSize = 8192;
config.maxTotalHeaderSize = 4096;
config.streamChunkSize = 1024;
config.allowCrossOriginRedirects = false;
config.allowHttpsToHttpRedirects = false;

queueSize is the maximum number of accepted in-flight requests, including queued and actively running requests. It must be at least maxConcurrentRequests.

Request body factories return non-owning LinkBodyView values. Link validates a view against the active configuration and copies it into owned queue storage before submission returns. The source text, bytes, or JsonDocument therefore only needs to remain valid until fetch(), post(), or postJson() returns.

Timeouts, request body limits, and stream buffer sizes are validated before being narrowed to signed ESP-IDF parameters. An oversized explicit request timeout returns InvalidTimeout before queue publication.

maxSerializedJsonSize limits serialized JSON request and response bytes. ArduinoJson's parsed document uses additional heap memory based on the JSON structure.

For all options, see docs/memory.md.

Error handling

LinkResult result = client.get(url, callback);
if (!result) {
Serial.println(result.message);
}

HTTP status codes are not Link transport failures. A valid server response with 404 still produces a successful LinkResponse with response.httpStatus == 404.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

ZekStack libraries are designed to provide small, reusable building blocks for ESP32 applications.

About

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Link

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Link helps you communicate with APIs and backend services in Arduino ESP32 projects. It is designed for production firmware that needs thread-safe request submission, predictable request/response limits, and clear result-based errors.

CIReleaseLicense: MIT

Why use Link?

  • Fetch-style requests - submit get, post, getJson, postJson, or getStream work from normal FreeRTOS tasks.
  • Concurrent workers - run more than one HTTP request at the same time with a bounded worker pool.
  • ESP32-friendly memory - accepted request bodies, response bodies, URLs, headers, serialized JSON, callbacks, and stream buffers have explicit limits.
  • Clear API - operations return LinkResult; HTTP status codes stay separate from transport failures.
  • Production-minded - no exceptions, serialized lifecycle transitions, atomic queue publication, bindable callbacks, and PSRAM-preferred payload buffers.

Install

PlatformIO

Link is built for Arduino ESP32 and depends on ArduinoJson v7.

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/link.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Link is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Link

Quick start

#include<Arduino.h>
#include<Link.h>
Link client;
voidonResponse(const LinkResponse &response) {
if (!response) {
Serial.println(response.error.message);
return;
}
Serial.println(response.httpStatus);
Serial.println(response.body.c_str());
}
voidsetup() {
Serial.begin(115200);
LinkConfig config;
config.maxConcurrentRequests = 2;
config.maxResponseBodySize = 8192;
LinkResult initResult = client.init(config);
if (!initResult) {
Serial.println(initResult.message);
return;
}
client.get("https://example.com", onResponse);
}
voidloop() {
delay(1000);
}

Important notes

Important

Link callbacks run inside Link worker tasks. If maxConcurrentRequests > 1, multiple callbacks may run at the same time.

  • Protect shared application state touched from callbacks.
  • Requests are started in queue order, but may complete out of order when more than one worker is enabled.
  • Submission preparation, queue publication, and worker signaling form one runtime critical section. A successful submission always owns a slot and has a corresponding worker permit.
  • User callbacks are not called while Link's runtime mutex is held. A callback may submit another request; if shutdown has started, the submission returns Stopping.
  • Every accepted request receives exactly one terminal callback before a successful deinit() returns.
  • LinkJsonResponse::json is valid only during the callback unless the user copies the needed data.
  • Allocation-backed response storage is move-only. Use explicit result-returning copyFrom() operations when duplication is required.
  • HTTPS uses the ESP-IDF certificate bundle when available. If the project/core does not provide usable certificate bundle support, verified HTTPS fails with TlsFailed.
  • deinit() lets worker tasks cancel queued requests and waits for active workers to exit. If the public wait times out, Link stays in Stopping and keeps worker-owned storage alive so a later deinit() can finish cleanup.
  • The destructor performs blocking shutdown. It assumes active HTTP operations eventually return through their configured nonzero request timeout.
  • Do not call deinit() or destroy a Link instance from one of its callbacks; shutdown waits for that callback's worker task to exit.
  • Redirect following is limited to GET requests with absolute http:// or https://Location headers. Same-origin redirects are allowed by default; cross-origin and HTTPS-to-HTTP redirects require explicit opt-in. Caller-supplied headers are stripped after an origin change. Intermediate redirect bodies are discarded.

Examples

The repository includes topic-focused Arduino sketches in the examples/ folder.

ExampleDescription
basic-getInitialize Link and run one buffered GET request.
post-jsonSend a JSON request body and parse a JSON response.
custom-headersAdd custom request headers and inspect response headers.
stream-downloadDownload a large response in bounded chunks.
class-callbackBind a private class method as a response callback.

Start with:

examples/basic-get

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/api.mdPublic classes, result types, ownership, and callback aliases.
docs/callbacks.mdCallback storage, binding, and execution context.
docs/concurrency.mdQueue publication, worker pool, lifecycle, and completion guarantees.
docs/errors.mdError codes and HTTP status behavior.
docs/json.mdArduinoJson helpers and JSON lifetime rules.
docs/streaming.mdStreaming downloads and cancellation.
docs/memory.mdBounded memory, ESP-IDF ranges, and explicit copy behavior.
docs/persistent-http.mdOptional per-worker persistent HTTP clients.
docs/release-validation.mdAutomated gates and physical v0.1.1 qualification.

API overview

Link client;
LinkResult init(const LinkConfig &config);
LinkResult deinit();
LinkResult fetch(const LinkRequest &request);
LinkDiagnostics diagnostics() const;
client.get(url, callback);
client.post(url, body, callback);
client.getJson(url, callback);
client.postJson(url, json, callback);
client.getStream(url, onStart, onChunk, onEnd);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
NetworkingESP-IDF esp_http_client
HTTPSESP-IDF certificate bundle when available
PSRAMPayload buffers prefer PSRAM; worker stacks can optionally use PSRAM
Dependenciesbblanchon/ArduinoJson >= 7.0.0
ExceptionsNot used
Status0.1.1

Configuration

LinkConfig config;
config.queueSize = 10;
config.maxConcurrentRequests = 3;
config.defaultTimeoutMs = 15000;
config.connectionMode = LinkConnectionMode::PerRequest;
config.persistentIdleTimeoutMs = 5U * 60U * 1000U;
config.persistentMaxRequestsPerHandle = 0;
config.maxUrlSize = 512;
config.maxRequestBodySize = 8192;
config.maxResponseBodySize = 8192;
config.maxSerializedJsonSize = 8192;
config.maxTotalHeaderSize = 4096;
config.streamChunkSize = 1024;
config.allowCrossOriginRedirects = false;
config.allowHttpsToHttpRedirects = false;

queueSize is the maximum number of accepted in-flight requests, including queued and actively running requests. It must be at least maxConcurrentRequests.

Request body factories return non-owning LinkBodyView values. Link validates a view against the active configuration and copies it into owned queue storage before submission returns. The source text, bytes, or JsonDocument therefore only needs to remain valid until fetch(), post(), or postJson() returns.

Timeouts, request body limits, and stream buffer sizes are validated before being narrowed to signed ESP-IDF parameters. An oversized explicit request timeout returns InvalidTimeout before queue publication.

maxSerializedJsonSize limits serialized JSON request and response bytes. ArduinoJson's parsed document uses additional heap memory based on the JSON structure.

For all options, see docs/memory.md.

Error handling

LinkResult result = client.get(url, callback);
if (!result) {
Serial.println(result.message);
}

HTTP status codes are not Link transport failures. A valid server response with 404 still produces a successful LinkResponse with response.httpStatus == 404.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

ZekStack libraries are designed to provide small, reusable building blocks for ESP32 applications.

About

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Link

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Link helps you communicate with APIs and backend services in Arduino ESP32 projects. It is designed for production firmware that needs thread-safe request submission, predictable request/response limits, and clear result-based errors.

CIReleaseLicense: MIT

Why use Link?

  • Fetch-style requests - submit get, post, getJson, postJson, or getStream work from normal FreeRTOS tasks.
  • Concurrent workers - run more than one HTTP request at the same time with a bounded worker pool.
  • ESP32-friendly memory - accepted request bodies, response bodies, URLs, headers, serialized JSON, callbacks, and stream buffers have explicit limits.
  • Clear API - operations return LinkResult; HTTP status codes stay separate from transport failures.
  • Production-minded - no exceptions, serialized lifecycle transitions, atomic queue publication, bindable callbacks, and PSRAM-preferred payload buffers.

Install

PlatformIO

Link is built for Arduino ESP32 and depends on ArduinoJson v7.

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/link.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Link is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Link

Quick start

#include<Arduino.h>
#include<Link.h>
Link client;
voidonResponse(const LinkResponse &response) {
if (!response) {
Serial.println(response.error.message);
return;
}
Serial.println(response.httpStatus);
Serial.println(response.body.c_str());
}
voidsetup() {
Serial.begin(115200);
LinkConfig config;
config.maxConcurrentRequests = 2;
config.maxResponseBodySize = 8192;
LinkResult initResult = client.init(config);
if (!initResult) {
Serial.println(initResult.message);
return;
}
client.get("https://example.com", onResponse);
}
voidloop() {
delay(1000);
}

Important notes

Important

Link callbacks run inside Link worker tasks. If maxConcurrentRequests > 1, multiple callbacks may run at the same time.

  • Protect shared application state touched from callbacks.
  • Requests are started in queue order, but may complete out of order when more than one worker is enabled.
  • Submission preparation, queue publication, and worker signaling form one runtime critical section. A successful submission always owns a slot and has a corresponding worker permit.
  • User callbacks are not called while Link's runtime mutex is held. A callback may submit another request; if shutdown has started, the submission returns Stopping.
  • Every accepted request receives exactly one terminal callback before a successful deinit() returns.
  • LinkJsonResponse::json is valid only during the callback unless the user copies the needed data.
  • Allocation-backed response storage is move-only. Use explicit result-returning copyFrom() operations when duplication is required.
  • HTTPS uses the ESP-IDF certificate bundle when available. If the project/core does not provide usable certificate bundle support, verified HTTPS fails with TlsFailed.
  • deinit() lets worker tasks cancel queued requests and waits for active workers to exit. If the public wait times out, Link stays in Stopping and keeps worker-owned storage alive so a later deinit() can finish cleanup.
  • The destructor performs blocking shutdown. It assumes active HTTP operations eventually return through their configured nonzero request timeout.
  • Do not call deinit() or destroy a Link instance from one of its callbacks; shutdown waits for that callback's worker task to exit.
  • Redirect following is limited to GET requests with absolute http:// or https://Location headers. Same-origin redirects are allowed by default; cross-origin and HTTPS-to-HTTP redirects require explicit opt-in. Caller-supplied headers are stripped after an origin change. Intermediate redirect bodies are discarded.

Examples

The repository includes topic-focused Arduino sketches in the examples/ folder.

ExampleDescription
basic-getInitialize Link and run one buffered GET request.
post-jsonSend a JSON request body and parse a JSON response.
custom-headersAdd custom request headers and inspect response headers.
stream-downloadDownload a large response in bounded chunks.
class-callbackBind a private class method as a response callback.

Start with:

examples/basic-get

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/api.mdPublic classes, result types, ownership, and callback aliases.
docs/callbacks.mdCallback storage, binding, and execution context.
docs/concurrency.mdQueue publication, worker pool, lifecycle, and completion guarantees.
docs/errors.mdError codes and HTTP status behavior.
docs/json.mdArduinoJson helpers and JSON lifetime rules.
docs/streaming.mdStreaming downloads and cancellation.
docs/memory.mdBounded memory, ESP-IDF ranges, and explicit copy behavior.
docs/persistent-http.mdOptional per-worker persistent HTTP clients.
docs/release-validation.mdAutomated gates and physical v0.1.1 qualification.

API overview

Link client;
LinkResult init(const LinkConfig &config);
LinkResult deinit();
LinkResult fetch(const LinkRequest &request);
LinkDiagnostics diagnostics() const;
client.get(url, callback);
client.post(url, body, callback);
client.getJson(url, callback);
client.postJson(url, json, callback);
client.getStream(url, onStart, onChunk, onEnd);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
NetworkingESP-IDF esp_http_client
HTTPSESP-IDF certificate bundle when available
PSRAMPayload buffers prefer PSRAM; worker stacks can optionally use PSRAM
Dependenciesbblanchon/ArduinoJson >= 7.0.0
ExceptionsNot used
Status0.1.1

Configuration

LinkConfig config;
config.queueSize = 10;
config.maxConcurrentRequests = 3;
config.defaultTimeoutMs = 15000;
config.connectionMode = LinkConnectionMode::PerRequest;
config.persistentIdleTimeoutMs = 5U * 60U * 1000U;
config.persistentMaxRequestsPerHandle = 0;
config.maxUrlSize = 512;
config.maxRequestBodySize = 8192;
config.maxResponseBodySize = 8192;
config.maxSerializedJsonSize = 8192;
config.maxTotalHeaderSize = 4096;
config.streamChunkSize = 1024;
config.allowCrossOriginRedirects = false;
config.allowHttpsToHttpRedirects = false;

queueSize is the maximum number of accepted in-flight requests, including queued and actively running requests. It must be at least maxConcurrentRequests.

Request body factories return non-owning LinkBodyView values. Link validates a view against the active configuration and copies it into owned queue storage before submission returns. The source text, bytes, or JsonDocument therefore only needs to remain valid until fetch(), post(), or postJson() returns.

Timeouts, request body limits, and stream buffer sizes are validated before being narrowed to signed ESP-IDF parameters. An oversized explicit request timeout returns InvalidTimeout before queue publication.

maxSerializedJsonSize limits serialized JSON request and response bytes. ArduinoJson's parsed document uses additional heap memory based on the JSON structure.

For all options, see docs/memory.md.

Error handling

LinkResult result = client.get(url, callback);
if (!result) {
Serial.println(result.message);
}

HTTP status codes are not Link transport failures. A valid server response with 404 still produces a successful LinkResponse with response.httpStatus == 404.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

ZekStack libraries are designed to provide small, reusable building blocks for ESP32 applications.

About

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Link

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Link helps you communicate with APIs and backend services in Arduino ESP32 projects. It is designed for production firmware that needs thread-safe request submission, predictable request/response limits, and clear result-based errors.

CIReleaseLicense: MIT

Why use Link?

  • Fetch-style requests - submit get, post, getJson, postJson, or getStream work from normal FreeRTOS tasks.
  • Concurrent workers - run more than one HTTP request at the same time with a bounded worker pool.
  • ESP32-friendly memory - accepted request bodies, response bodies, URLs, headers, serialized JSON, callbacks, and stream buffers have explicit limits.
  • Clear API - operations return LinkResult; HTTP status codes stay separate from transport failures.
  • Production-minded - no exceptions, serialized lifecycle transitions, atomic queue publication, bindable callbacks, and PSRAM-preferred payload buffers.

Install

PlatformIO

Link is built for Arduino ESP32 and depends on ArduinoJson v7.

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/link.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Link is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Link

Quick start

#include<Arduino.h>
#include<Link.h>
Link client;
voidonResponse(const LinkResponse &response) {
if (!response) {
Serial.println(response.error.message);
return;
}
Serial.println(response.httpStatus);
Serial.println(response.body.c_str());
}
voidsetup() {
Serial.begin(115200);
LinkConfig config;
config.maxConcurrentRequests = 2;
config.maxResponseBodySize = 8192;
LinkResult initResult = client.init(config);
if (!initResult) {
Serial.println(initResult.message);
return;
}
client.get("https://example.com", onResponse);
}
voidloop() {
delay(1000);
}

Important notes

Important

Link callbacks run inside Link worker tasks. If maxConcurrentRequests > 1, multiple callbacks may run at the same time.

  • Protect shared application state touched from callbacks.
  • Requests are started in queue order, but may complete out of order when more than one worker is enabled.
  • Submission preparation, queue publication, and worker signaling form one runtime critical section. A successful submission always owns a slot and has a corresponding worker permit.
  • User callbacks are not called while Link's runtime mutex is held. A callback may submit another request; if shutdown has started, the submission returns Stopping.
  • Every accepted request receives exactly one terminal callback before a successful deinit() returns.
  • LinkJsonResponse::json is valid only during the callback unless the user copies the needed data.
  • Allocation-backed response storage is move-only. Use explicit result-returning copyFrom() operations when duplication is required.
  • HTTPS uses the ESP-IDF certificate bundle when available. If the project/core does not provide usable certificate bundle support, verified HTTPS fails with TlsFailed.
  • deinit() lets worker tasks cancel queued requests and waits for active workers to exit. If the public wait times out, Link stays in Stopping and keeps worker-owned storage alive so a later deinit() can finish cleanup.
  • The destructor performs blocking shutdown. It assumes active HTTP operations eventually return through their configured nonzero request timeout.
  • Do not call deinit() or destroy a Link instance from one of its callbacks; shutdown waits for that callback's worker task to exit.
  • Redirect following is limited to GET requests with absolute http:// or https://Location headers. Same-origin redirects are allowed by default; cross-origin and HTTPS-to-HTTP redirects require explicit opt-in. Caller-supplied headers are stripped after an origin change. Intermediate redirect bodies are discarded.

Examples

The repository includes topic-focused Arduino sketches in the examples/ folder.

ExampleDescription
basic-getInitialize Link and run one buffered GET request.
post-jsonSend a JSON request body and parse a JSON response.
custom-headersAdd custom request headers and inspect response headers.
stream-downloadDownload a large response in bounded chunks.
class-callbackBind a private class method as a response callback.

Start with:

examples/basic-get

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/api.mdPublic classes, result types, ownership, and callback aliases.
docs/callbacks.mdCallback storage, binding, and execution context.
docs/concurrency.mdQueue publication, worker pool, lifecycle, and completion guarantees.
docs/errors.mdError codes and HTTP status behavior.
docs/json.mdArduinoJson helpers and JSON lifetime rules.
docs/streaming.mdStreaming downloads and cancellation.
docs/memory.mdBounded memory, ESP-IDF ranges, and explicit copy behavior.
docs/persistent-http.mdOptional per-worker persistent HTTP clients.
docs/release-validation.mdAutomated gates and physical v0.1.1 qualification.

API overview

Link client;
LinkResult init(const LinkConfig &config);
LinkResult deinit();
LinkResult fetch(const LinkRequest &request);
LinkDiagnostics diagnostics() const;
client.get(url, callback);
client.post(url, body, callback);
client.getJson(url, callback);
client.postJson(url, json, callback);
client.getStream(url, onStart, onChunk, onEnd);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
NetworkingESP-IDF esp_http_client
HTTPSESP-IDF certificate bundle when available
PSRAMPayload buffers prefer PSRAM; worker stacks can optionally use PSRAM
Dependenciesbblanchon/ArduinoJson >= 7.0.0
ExceptionsNot used
Status0.1.1

Configuration

LinkConfig config;
config.queueSize = 10;
config.maxConcurrentRequests = 3;
config.defaultTimeoutMs = 15000;
config.connectionMode = LinkConnectionMode::PerRequest;
config.persistentIdleTimeoutMs = 5U * 60U * 1000U;
config.persistentMaxRequestsPerHandle = 0;
config.maxUrlSize = 512;
config.maxRequestBodySize = 8192;
config.maxResponseBodySize = 8192;
config.maxSerializedJsonSize = 8192;
config.maxTotalHeaderSize = 4096;
config.streamChunkSize = 1024;
config.allowCrossOriginRedirects = false;
config.allowHttpsToHttpRedirects = false;

queueSize is the maximum number of accepted in-flight requests, including queued and actively running requests. It must be at least maxConcurrentRequests.

Request body factories return non-owning LinkBodyView values. Link validates a view against the active configuration and copies it into owned queue storage before submission returns. The source text, bytes, or JsonDocument therefore only needs to remain valid until fetch(), post(), or postJson() returns.

Timeouts, request body limits, and stream buffer sizes are validated before being narrowed to signed ESP-IDF parameters. An oversized explicit request timeout returns InvalidTimeout before queue publication.

maxSerializedJsonSize limits serialized JSON request and response bytes. ArduinoJson's parsed document uses additional heap memory based on the JSON structure.

For all options, see docs/memory.md.

Error handling

LinkResult result = client.get(url, callback);
if (!result) {
Serial.println(result.message);
}

HTTP status codes are not Link transport failures. A valid server response with 404 still produces a successful LinkResponse with response.httpStatus == 404.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

ZekStack libraries are designed to provide small, reusable building blocks for ESP32 applications.

About

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Link

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Link helps you communicate with APIs and backend services in Arduino ESP32 projects. It is designed for production firmware that needs thread-safe request submission, predictable request/response limits, and clear result-based errors.

CIReleaseLicense: MIT

Why use Link?

  • Fetch-style requests - submit get, post, getJson, postJson, or getStream work from normal FreeRTOS tasks.
  • Concurrent workers - run more than one HTTP request at the same time with a bounded worker pool.
  • ESP32-friendly memory - accepted request bodies, response bodies, URLs, headers, serialized JSON, callbacks, and stream buffers have explicit limits.
  • Clear API - operations return LinkResult; HTTP status codes stay separate from transport failures.
  • Production-minded - no exceptions, serialized lifecycle transitions, atomic queue publication, bindable callbacks, and PSRAM-preferred payload buffers.

Install

PlatformIO

Link is built for Arduino ESP32 and depends on ArduinoJson v7.

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/link.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Link is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Link

Quick start

#include<Arduino.h>
#include<Link.h>
Link client;
voidonResponse(const LinkResponse &response) {
if (!response) {
Serial.println(response.error.message);
return;
}
Serial.println(response.httpStatus);
Serial.println(response.body.c_str());
}
voidsetup() {
Serial.begin(115200);
LinkConfig config;
config.maxConcurrentRequests = 2;
config.maxResponseBodySize = 8192;
LinkResult initResult = client.init(config);
if (!initResult) {
Serial.println(initResult.message);
return;
}
client.get("https://example.com", onResponse);
}
voidloop() {
delay(1000);
}

Important notes

Important

Link callbacks run inside Link worker tasks. If maxConcurrentRequests > 1, multiple callbacks may run at the same time.

  • Protect shared application state touched from callbacks.
  • Requests are started in queue order, but may complete out of order when more than one worker is enabled.
  • Submission preparation, queue publication, and worker signaling form one runtime critical section. A successful submission always owns a slot and has a corresponding worker permit.
  • User callbacks are not called while Link's runtime mutex is held. A callback may submit another request; if shutdown has started, the submission returns Stopping.
  • Every accepted request receives exactly one terminal callback before a successful deinit() returns.
  • LinkJsonResponse::json is valid only during the callback unless the user copies the needed data.
  • Allocation-backed response storage is move-only. Use explicit result-returning copyFrom() operations when duplication is required.
  • HTTPS uses the ESP-IDF certificate bundle when available. If the project/core does not provide usable certificate bundle support, verified HTTPS fails with TlsFailed.
  • deinit() lets worker tasks cancel queued requests and waits for active workers to exit. If the public wait times out, Link stays in Stopping and keeps worker-owned storage alive so a later deinit() can finish cleanup.
  • The destructor performs blocking shutdown. It assumes active HTTP operations eventually return through their configured nonzero request timeout.
  • Do not call deinit() or destroy a Link instance from one of its callbacks; shutdown waits for that callback's worker task to exit.
  • Redirect following is limited to GET requests with absolute http:// or https://Location headers. Same-origin redirects are allowed by default; cross-origin and HTTPS-to-HTTP redirects require explicit opt-in. Caller-supplied headers are stripped after an origin change. Intermediate redirect bodies are discarded.

Examples

The repository includes topic-focused Arduino sketches in the examples/ folder.

ExampleDescription
basic-getInitialize Link and run one buffered GET request.
post-jsonSend a JSON request body and parse a JSON response.
custom-headersAdd custom request headers and inspect response headers.
stream-downloadDownload a large response in bounded chunks.
class-callbackBind a private class method as a response callback.

Start with:

examples/basic-get

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/api.mdPublic classes, result types, ownership, and callback aliases.
docs/callbacks.mdCallback storage, binding, and execution context.
docs/concurrency.mdQueue publication, worker pool, lifecycle, and completion guarantees.
docs/errors.mdError codes and HTTP status behavior.
docs/json.mdArduinoJson helpers and JSON lifetime rules.
docs/streaming.mdStreaming downloads and cancellation.
docs/memory.mdBounded memory, ESP-IDF ranges, and explicit copy behavior.
docs/persistent-http.mdOptional per-worker persistent HTTP clients.
docs/release-validation.mdAutomated gates and physical v0.1.1 qualification.

API overview

Link client;
LinkResult init(const LinkConfig &config);
LinkResult deinit();
LinkResult fetch(const LinkRequest &request);
LinkDiagnostics diagnostics() const;
client.get(url, callback);
client.post(url, body, callback);
client.getJson(url, callback);
client.postJson(url, json, callback);
client.getStream(url, onStart, onChunk, onEnd);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
NetworkingESP-IDF esp_http_client
HTTPSESP-IDF certificate bundle when available
PSRAMPayload buffers prefer PSRAM; worker stacks can optionally use PSRAM
Dependenciesbblanchon/ArduinoJson >= 7.0.0
ExceptionsNot used
Status0.1.1

Configuration

LinkConfig config;
config.queueSize = 10;
config.maxConcurrentRequests = 3;
config.defaultTimeoutMs = 15000;
config.connectionMode = LinkConnectionMode::PerRequest;
config.persistentIdleTimeoutMs = 5U * 60U * 1000U;
config.persistentMaxRequestsPerHandle = 0;
config.maxUrlSize = 512;
config.maxRequestBodySize = 8192;
config.maxResponseBodySize = 8192;
config.maxSerializedJsonSize = 8192;
config.maxTotalHeaderSize = 4096;
config.streamChunkSize = 1024;
config.allowCrossOriginRedirects = false;
config.allowHttpsToHttpRedirects = false;

queueSize is the maximum number of accepted in-flight requests, including queued and actively running requests. It must be at least maxConcurrentRequests.

Request body factories return non-owning LinkBodyView values. Link validates a view against the active configuration and copies it into owned queue storage before submission returns. The source text, bytes, or JsonDocument therefore only needs to remain valid until fetch(), post(), or postJson() returns.

Timeouts, request body limits, and stream buffer sizes are validated before being narrowed to signed ESP-IDF parameters. An oversized explicit request timeout returns InvalidTimeout before queue publication.

maxSerializedJsonSize limits serialized JSON request and response bytes. ArduinoJson's parsed document uses additional heap memory based on the JSON structure.

For all options, see docs/memory.md.

Error handling

LinkResult result = client.get(url, callback);
if (!result) {
Serial.println(result.message);
}

HTTP status codes are not Link transport failures. A valid server response with 404 still produces a successful LinkResponse with response.httpStatus == 404.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

ZekStack libraries are designed to provide small, reusable building blocks for ESP32 applications.

About

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Link

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Link helps you communicate with APIs and backend services in Arduino ESP32 projects. It is designed for production firmware that needs thread-safe request submission, predictable request/response limits, and clear result-based errors.

CIReleaseLicense: MIT

Why use Link?

  • Fetch-style requests - submit get, post, getJson, postJson, or getStream work from normal FreeRTOS tasks.
  • Concurrent workers - run more than one HTTP request at the same time with a bounded worker pool.
  • ESP32-friendly memory - accepted request bodies, response bodies, URLs, headers, serialized JSON, callbacks, and stream buffers have explicit limits.
  • Clear API - operations return LinkResult; HTTP status codes stay separate from transport failures.
  • Production-minded - no exceptions, serialized lifecycle transitions, atomic queue publication, bindable callbacks, and PSRAM-preferred payload buffers.

Install

PlatformIO

Link is built for Arduino ESP32 and depends on ArduinoJson v7.

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/link.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Link is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Link

Quick start

#include<Arduino.h>
#include<Link.h>
Link client;
voidonResponse(const LinkResponse &response) {
if (!response) {
Serial.println(response.error.message);
return;
}
Serial.println(response.httpStatus);
Serial.println(response.body.c_str());
}
voidsetup() {
Serial.begin(115200);
LinkConfig config;
config.maxConcurrentRequests = 2;
config.maxResponseBodySize = 8192;
LinkResult initResult = client.init(config);
if (!initResult) {
Serial.println(initResult.message);
return;
}
client.get("https://example.com", onResponse);
}
voidloop() {
delay(1000);
}

Important notes

Important

Link callbacks run inside Link worker tasks. If maxConcurrentRequests > 1, multiple callbacks may run at the same time.

  • Protect shared application state touched from callbacks.
  • Requests are started in queue order, but may complete out of order when more than one worker is enabled.
  • Submission preparation, queue publication, and worker signaling form one runtime critical section. A successful submission always owns a slot and has a corresponding worker permit.
  • User callbacks are not called while Link's runtime mutex is held. A callback may submit another request; if shutdown has started, the submission returns Stopping.
  • Every accepted request receives exactly one terminal callback before a successful deinit() returns.
  • LinkJsonResponse::json is valid only during the callback unless the user copies the needed data.
  • Allocation-backed response storage is move-only. Use explicit result-returning copyFrom() operations when duplication is required.
  • HTTPS uses the ESP-IDF certificate bundle when available. If the project/core does not provide usable certificate bundle support, verified HTTPS fails with TlsFailed.
  • deinit() lets worker tasks cancel queued requests and waits for active workers to exit. If the public wait times out, Link stays in Stopping and keeps worker-owned storage alive so a later deinit() can finish cleanup.
  • The destructor performs blocking shutdown. It assumes active HTTP operations eventually return through their configured nonzero request timeout.
  • Do not call deinit() or destroy a Link instance from one of its callbacks; shutdown waits for that callback's worker task to exit.
  • Redirect following is limited to GET requests with absolute http:// or https://Location headers. Same-origin redirects are allowed by default; cross-origin and HTTPS-to-HTTP redirects require explicit opt-in. Caller-supplied headers are stripped after an origin change. Intermediate redirect bodies are discarded.

Examples

The repository includes topic-focused Arduino sketches in the examples/ folder.

ExampleDescription
basic-getInitialize Link and run one buffered GET request.
post-jsonSend a JSON request body and parse a JSON response.
custom-headersAdd custom request headers and inspect response headers.
stream-downloadDownload a large response in bounded chunks.
class-callbackBind a private class method as a response callback.

Start with:

examples/basic-get

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/api.mdPublic classes, result types, ownership, and callback aliases.
docs/callbacks.mdCallback storage, binding, and execution context.
docs/concurrency.mdQueue publication, worker pool, lifecycle, and completion guarantees.
docs/errors.mdError codes and HTTP status behavior.
docs/json.mdArduinoJson helpers and JSON lifetime rules.
docs/streaming.mdStreaming downloads and cancellation.
docs/memory.mdBounded memory, ESP-IDF ranges, and explicit copy behavior.
docs/persistent-http.mdOptional per-worker persistent HTTP clients.
docs/release-validation.mdAutomated gates and physical v0.1.1 qualification.

API overview

Link client;
LinkResult init(const LinkConfig &config);
LinkResult deinit();
LinkResult fetch(const LinkRequest &request);
LinkDiagnostics diagnostics() const;
client.get(url, callback);
client.post(url, body, callback);
client.getJson(url, callback);
client.postJson(url, json, callback);
client.getStream(url, onStart, onChunk, onEnd);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
NetworkingESP-IDF esp_http_client
HTTPSESP-IDF certificate bundle when available
PSRAMPayload buffers prefer PSRAM; worker stacks can optionally use PSRAM
Dependenciesbblanchon/ArduinoJson >= 7.0.0
ExceptionsNot used
Status0.1.1

Configuration

LinkConfig config;
config.queueSize = 10;
config.maxConcurrentRequests = 3;
config.defaultTimeoutMs = 15000;
config.connectionMode = LinkConnectionMode::PerRequest;
config.persistentIdleTimeoutMs = 5U * 60U * 1000U;
config.persistentMaxRequestsPerHandle = 0;
config.maxUrlSize = 512;
config.maxRequestBodySize = 8192;
config.maxResponseBodySize = 8192;
config.maxSerializedJsonSize = 8192;
config.maxTotalHeaderSize = 4096;
config.streamChunkSize = 1024;
config.allowCrossOriginRedirects = false;
config.allowHttpsToHttpRedirects = false;

queueSize is the maximum number of accepted in-flight requests, including queued and actively running requests. It must be at least maxConcurrentRequests.

Request body factories return non-owning LinkBodyView values. Link validates a view against the active configuration and copies it into owned queue storage before submission returns. The source text, bytes, or JsonDocument therefore only needs to remain valid until fetch(), post(), or postJson() returns.

Timeouts, request body limits, and stream buffer sizes are validated before being narrowed to signed ESP-IDF parameters. An oversized explicit request timeout returns InvalidTimeout before queue publication.

maxSerializedJsonSize limits serialized JSON request and response bytes. ArduinoJson's parsed document uses additional heap memory based on the JSON structure.

For all options, see docs/memory.md.

Error handling

LinkResult result = client.get(url, callback);
if (!result) {
Serial.println(result.message);
}

HTTP status codes are not Link transport failures. A valid server response with 404 still produces a successful LinkResponse with response.httpStatus == 404.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

ZekStack libraries are designed to provide small, reusable building blocks for ESP32 applications.

About

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Link

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Link helps you communicate with APIs and backend services in Arduino ESP32 projects. It is designed for production firmware that needs thread-safe request submission, predictable request/response limits, and clear result-based errors.

CIReleaseLicense: MIT

Why use Link?

  • Fetch-style requests - submit get, post, getJson, postJson, or getStream work from normal FreeRTOS tasks.
  • Concurrent workers - run more than one HTTP request at the same time with a bounded worker pool.
  • ESP32-friendly memory - accepted request bodies, response bodies, URLs, headers, serialized JSON, callbacks, and stream buffers have explicit limits.
  • Clear API - operations return LinkResult; HTTP status codes stay separate from transport failures.
  • Production-minded - no exceptions, serialized lifecycle transitions, atomic queue publication, bindable callbacks, and PSRAM-preferred payload buffers.

Install

PlatformIO

Link is built for Arduino ESP32 and depends on ArduinoJson v7.

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/link.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Link is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Link

Quick start

#include<Arduino.h>
#include<Link.h>
Link client;
voidonResponse(const LinkResponse &response) {
if (!response) {
Serial.println(response.error.message);
return;
}
Serial.println(response.httpStatus);
Serial.println(response.body.c_str());
}
voidsetup() {
Serial.begin(115200);
LinkConfig config;
config.maxConcurrentRequests = 2;
config.maxResponseBodySize = 8192;
LinkResult initResult = client.init(config);
if (!initResult) {
Serial.println(initResult.message);
return;
}
client.get("https://example.com", onResponse);
}
voidloop() {
delay(1000);
}

Important notes

Important

Link callbacks run inside Link worker tasks. If maxConcurrentRequests > 1, multiple callbacks may run at the same time.

  • Protect shared application state touched from callbacks.
  • Requests are started in queue order, but may complete out of order when more than one worker is enabled.
  • Submission preparation, queue publication, and worker signaling form one runtime critical section. A successful submission always owns a slot and has a corresponding worker permit.
  • User callbacks are not called while Link's runtime mutex is held. A callback may submit another request; if shutdown has started, the submission returns Stopping.
  • Every accepted request receives exactly one terminal callback before a successful deinit() returns.
  • LinkJsonResponse::json is valid only during the callback unless the user copies the needed data.
  • Allocation-backed response storage is move-only. Use explicit result-returning copyFrom() operations when duplication is required.
  • HTTPS uses the ESP-IDF certificate bundle when available. If the project/core does not provide usable certificate bundle support, verified HTTPS fails with TlsFailed.
  • deinit() lets worker tasks cancel queued requests and waits for active workers to exit. If the public wait times out, Link stays in Stopping and keeps worker-owned storage alive so a later deinit() can finish cleanup.
  • The destructor performs blocking shutdown. It assumes active HTTP operations eventually return through their configured nonzero request timeout.
  • Do not call deinit() or destroy a Link instance from one of its callbacks; shutdown waits for that callback's worker task to exit.
  • Redirect following is limited to GET requests with absolute http:// or https://Location headers. Same-origin redirects are allowed by default; cross-origin and HTTPS-to-HTTP redirects require explicit opt-in. Caller-supplied headers are stripped after an origin change. Intermediate redirect bodies are discarded.

Examples

The repository includes topic-focused Arduino sketches in the examples/ folder.

ExampleDescription
basic-getInitialize Link and run one buffered GET request.
post-jsonSend a JSON request body and parse a JSON response.
custom-headersAdd custom request headers and inspect response headers.
stream-downloadDownload a large response in bounded chunks.
class-callbackBind a private class method as a response callback.

Start with:

examples/basic-get

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/api.mdPublic classes, result types, ownership, and callback aliases.
docs/callbacks.mdCallback storage, binding, and execution context.
docs/concurrency.mdQueue publication, worker pool, lifecycle, and completion guarantees.
docs/errors.mdError codes and HTTP status behavior.
docs/json.mdArduinoJson helpers and JSON lifetime rules.
docs/streaming.mdStreaming downloads and cancellation.
docs/memory.mdBounded memory, ESP-IDF ranges, and explicit copy behavior.
docs/persistent-http.mdOptional per-worker persistent HTTP clients.
docs/release-validation.mdAutomated gates and physical v0.1.1 qualification.

API overview

Link client;
LinkResult init(const LinkConfig &config);
LinkResult deinit();
LinkResult fetch(const LinkRequest &request);
LinkDiagnostics diagnostics() const;
client.get(url, callback);
client.post(url, body, callback);
client.getJson(url, callback);
client.postJson(url, json, callback);
client.getStream(url, onStart, onChunk, onEnd);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
NetworkingESP-IDF esp_http_client
HTTPSESP-IDF certificate bundle when available
PSRAMPayload buffers prefer PSRAM; worker stacks can optionally use PSRAM
Dependenciesbblanchon/ArduinoJson >= 7.0.0
ExceptionsNot used
Status0.1.1

Configuration

LinkConfig config;
config.queueSize = 10;
config.maxConcurrentRequests = 3;
config.defaultTimeoutMs = 15000;
config.connectionMode = LinkConnectionMode::PerRequest;
config.persistentIdleTimeoutMs = 5U * 60U * 1000U;
config.persistentMaxRequestsPerHandle = 0;
config.maxUrlSize = 512;
config.maxRequestBodySize = 8192;
config.maxResponseBodySize = 8192;
config.maxSerializedJsonSize = 8192;
config.maxTotalHeaderSize = 4096;
config.streamChunkSize = 1024;
config.allowCrossOriginRedirects = false;
config.allowHttpsToHttpRedirects = false;

queueSize is the maximum number of accepted in-flight requests, including queued and actively running requests. It must be at least maxConcurrentRequests.

Request body factories return non-owning LinkBodyView values. Link validates a view against the active configuration and copies it into owned queue storage before submission returns. The source text, bytes, or JsonDocument therefore only needs to remain valid until fetch(), post(), or postJson() returns.

Timeouts, request body limits, and stream buffer sizes are validated before being narrowed to signed ESP-IDF parameters. An oversized explicit request timeout returns InvalidTimeout before queue publication.

maxSerializedJsonSize limits serialized JSON request and response bytes. ArduinoJson's parsed document uses additional heap memory based on the JSON structure.

For all options, see docs/memory.md.

Error handling

LinkResult result = client.get(url, callback);
if (!result) {
Serial.println(result.message);
}

HTTP status codes are not Link transport failures. A valid server response with 404 still produces a successful LinkResponse with response.httpStatus == 404.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

ZekStack libraries are designed to provide small, reusable building blocks for ESP32 applications.

About

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Link

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Link helps you communicate with APIs and backend services in Arduino ESP32 projects. It is designed for production firmware that needs thread-safe request submission, predictable request/response limits, and clear result-based errors.

CIReleaseLicense: MIT

Why use Link?

  • Fetch-style requests - submit get, post, getJson, postJson, or getStream work from normal FreeRTOS tasks.
  • Concurrent workers - run more than one HTTP request at the same time with a bounded worker pool.
  • ESP32-friendly memory - accepted request bodies, response bodies, URLs, headers, serialized JSON, callbacks, and stream buffers have explicit limits.
  • Clear API - operations return LinkResult; HTTP status codes stay separate from transport failures.
  • Production-minded - no exceptions, serialized lifecycle transitions, atomic queue publication, bindable callbacks, and PSRAM-preferred payload buffers.

Install

PlatformIO

Link is built for Arduino ESP32 and depends on ArduinoJson v7.

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/link.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Link is not published to Arduino Library Manager yet.

Install it by downloading the repository ZIP or cloning it into your Arduino libraries folder.

Arduino/libraries/Link

Quick start

#include<Arduino.h>
#include<Link.h>
Link client;
voidonResponse(const LinkResponse &response) {
if (!response) {
Serial.println(response.error.message);
return;
}
Serial.println(response.httpStatus);
Serial.println(response.body.c_str());
}
voidsetup() {
Serial.begin(115200);
LinkConfig config;
config.maxConcurrentRequests = 2;
config.maxResponseBodySize = 8192;
LinkResult initResult = client.init(config);
if (!initResult) {
Serial.println(initResult.message);
return;
}
client.get("https://example.com", onResponse);
}
voidloop() {
delay(1000);
}

Important notes

Important

Link callbacks run inside Link worker tasks. If maxConcurrentRequests > 1, multiple callbacks may run at the same time.

  • Protect shared application state touched from callbacks.
  • Requests are started in queue order, but may complete out of order when more than one worker is enabled.
  • Submission preparation, queue publication, and worker signaling form one runtime critical section. A successful submission always owns a slot and has a corresponding worker permit.
  • User callbacks are not called while Link's runtime mutex is held. A callback may submit another request; if shutdown has started, the submission returns Stopping.
  • Every accepted request receives exactly one terminal callback before a successful deinit() returns.
  • LinkJsonResponse::json is valid only during the callback unless the user copies the needed data.
  • Allocation-backed response storage is move-only. Use explicit result-returning copyFrom() operations when duplication is required.
  • HTTPS uses the ESP-IDF certificate bundle when available. If the project/core does not provide usable certificate bundle support, verified HTTPS fails with TlsFailed.
  • deinit() lets worker tasks cancel queued requests and waits for active workers to exit. If the public wait times out, Link stays in Stopping and keeps worker-owned storage alive so a later deinit() can finish cleanup.
  • The destructor performs blocking shutdown. It assumes active HTTP operations eventually return through their configured nonzero request timeout.
  • Do not call deinit() or destroy a Link instance from one of its callbacks; shutdown waits for that callback's worker task to exit.
  • Redirect following is limited to GET requests with absolute http:// or https://Location headers. Same-origin redirects are allowed by default; cross-origin and HTTPS-to-HTTP redirects require explicit opt-in. Caller-supplied headers are stripped after an origin change. Intermediate redirect bodies are discarded.

Examples

The repository includes topic-focused Arduino sketches in the examples/ folder.

ExampleDescription
basic-getInitialize Link and run one buffered GET request.
post-jsonSend a JSON request body and parse a JSON response.
custom-headersAdd custom request headers and inspect response headers.
stream-downloadDownload a large response in bounded chunks.
class-callbackBind a private class method as a response callback.

Start with:

examples/basic-get

Documentation

Detailed documentation is available in the docs/ folder.

DocumentDescription
docs/api.mdPublic classes, result types, ownership, and callback aliases.
docs/callbacks.mdCallback storage, binding, and execution context.
docs/concurrency.mdQueue publication, worker pool, lifecycle, and completion guarantees.
docs/errors.mdError codes and HTTP status behavior.
docs/json.mdArduinoJson helpers and JSON lifetime rules.
docs/streaming.mdStreaming downloads and cancellation.
docs/memory.mdBounded memory, ESP-IDF ranges, and explicit copy behavior.
docs/persistent-http.mdOptional per-worker persistent HTTP clients.
docs/release-validation.mdAutomated gates and physical v0.1.1 qualification.

API overview

Link client;
LinkResult init(const LinkConfig &config);
LinkResult deinit();
LinkResult fetch(const LinkRequest &request);
LinkDiagnostics diagnostics() const;
client.get(url, callback);
client.post(url, body, callback);
client.getJson(url, callback);
client.postJson(url, json, callback);
client.getStream(url, onStart, onChunk, onEnd);

For the full API, see docs/api.md.

Compatibility

ItemSupport
FrameworkArduino ESP32
Platformespressif32
LanguageC++20
NetworkingESP-IDF esp_http_client
HTTPSESP-IDF certificate bundle when available
PSRAMPayload buffers prefer PSRAM; worker stacks can optionally use PSRAM
Dependenciesbblanchon/ArduinoJson >= 7.0.0
ExceptionsNot used
Status0.1.1

Configuration

LinkConfig config;
config.queueSize = 10;
config.maxConcurrentRequests = 3;
config.defaultTimeoutMs = 15000;
config.connectionMode = LinkConnectionMode::PerRequest;
config.persistentIdleTimeoutMs = 5U * 60U * 1000U;
config.persistentMaxRequestsPerHandle = 0;
config.maxUrlSize = 512;
config.maxRequestBodySize = 8192;
config.maxResponseBodySize = 8192;
config.maxSerializedJsonSize = 8192;
config.maxTotalHeaderSize = 4096;
config.streamChunkSize = 1024;
config.allowCrossOriginRedirects = false;
config.allowHttpsToHttpRedirects = false;

queueSize is the maximum number of accepted in-flight requests, including queued and actively running requests. It must be at least maxConcurrentRequests.

Request body factories return non-owning LinkBodyView values. Link validates a view against the active configuration and copies it into owned queue storage before submission returns. The source text, bytes, or JsonDocument therefore only needs to remain valid until fetch(), post(), or postJson() returns.

Timeouts, request body limits, and stream buffer sizes are validated before being narrowed to signed ESP-IDF parameters. An oversized explicit request timeout returns InvalidTimeout before queue publication.

maxSerializedJsonSize limits serialized JSON request and response bytes. ArduinoJson's parsed document uses additional heap memory based on the JSON structure.

For all options, see docs/memory.md.

Error handling

LinkResult result = client.get(url, callback);
if (!result) {
Serial.println(result.message);
}

HTTP status codes are not Link transport failures. A valid server response with 404 still produces a successful LinkResponse with response.httpStatus == 404.

License

MIT - see LICENSE.md.

ZekStack

Part of the ZekStack ESP32 library stack.

ZekStack libraries are designed to provide small, reusable building blocks for ESP32 applications.

About

Link is an async HTTP client library for ESP32 with fetch-style requests and bounded memory.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages