Skip to content

Commit 549776b

Browse files
committed
Tunnel
1 parent da6345e commit 549776b

2 files changed

Lines changed: 123 additions & 1 deletion

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
3+
4+
//
5+
// Shows how to trigger an async client request from a browser request and send the client response back to the browser through websocket
6+
//
7+
8+
#include <Arduino.h>
9+
#ifdef ESP32
10+
#include <AsyncTCP.h>
11+
#include <WiFi.h>
12+
#elif defined(ESP8266)
13+
#include <ESP8266WiFi.h>
14+
#include <ESPAsyncTCP.h>
15+
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
16+
#include <RPAsyncTCP.h>
17+
#include <WiFi.h>
18+
#endif
19+
20+
#include <ESPAsyncWebServer.h>
21+
22+
#define WIFI_SSID "IoT"
23+
#define WIFI_PASSWORD ""
24+
25+
static AsyncWebServer server(80);
26+
static AsyncWebSocketMessageHandler wsHandler;
27+
static AsyncWebSocket ws("/ws", wsHandler.eventHandler());
28+
29+
void setup() {
30+
Serial.begin(115200);
31+
32+
#ifndef CONFIG_IDF_TARGET_ESP32H2
33+
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
34+
while (WiFi.status() != WL_CONNECTED) {
35+
delay(500);
36+
}
37+
Serial.println("Connected to WiFi!");
38+
Serial.println(WiFi.localIP());
39+
#endif
40+
41+
//
42+
// HOW TO RUN THIS EXAMPLE:
43+
// 1. websocat --binary ws://192.168.125.127/ws
44+
// 2. curl -v "http://192.168.125.127?host=www.google.com&port=80"
45+
//
46+
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
47+
if (!request->getParam("host") || !request->getParam("host")->value().length()) {
48+
request->send(400, "text/plain", "Missing host parameter");
49+
return;
50+
}
51+
52+
if (!request->getParam("port") || !request->getParam("port")->value().length()) {
53+
request->send(400, "text/plain", "Missing port parameter");
54+
return;
55+
}
56+
57+
const String host = request->getParam("host")->value();
58+
const String port = request->getParam("port")->value();
59+
AsyncClient *client = new AsyncClient();
60+
61+
// when client disconnects, we clear the buffer which signals the end of the chunk request
62+
// note: onDisconnect also called on error
63+
client->onDisconnect([](void *arg, AsyncClient *client) {
64+
Serial.printf("Tunnel disconnected!\n");
65+
delete client;
66+
});
67+
68+
// register a callback when an error occurs
69+
client->onError([](void *arg, AsyncClient *client, int8_t error) {
70+
Serial.printf("Tunnel error: %s\n", client->errorToString(error));
71+
});
72+
73+
// register a callback when data arrives, to accumulate it
74+
client->onData([](void *arg, AsyncClient *client, void *data, size_t len) {
75+
Serial.printf("Tunnel received %u bytes...\n", len);
76+
ws.binaryAll(static_cast<const uint8_t *>(data), len);
77+
});
78+
79+
client->onConnect([host, port](void *arg, AsyncClient *client) {
80+
Serial.printf("Tunnel connected!\n");
81+
82+
client->write("GET / HTTP/1.1\r\n");
83+
client->write("Host: ");
84+
client->write(host.c_str());
85+
client->write(":");
86+
client->write(port.c_str());
87+
client->write("\r\n");
88+
client->write("User-Agent: ESP32\r\n");
89+
client->write("Accept: */*\r\n");
90+
client->write("Connection: close\r\n");
91+
client->write("\r\n");
92+
});
93+
94+
Serial.printf("Fetching: http://%s:%s...\n", host.c_str(), port.c_str());
95+
96+
if (!client->connect(host.c_str(), port.toInt())) {
97+
Serial.printf("Failed to open tunnel!\n");
98+
delete client;
99+
request->send(500, "text/plain", "Failed to open tunnel");
100+
} else {
101+
request->send(200, "text/plain", "Tunnel opened, data will be sent through websocket\n");
102+
}
103+
});
104+
105+
wsHandler.onConnect([](AsyncWebSocket *server, AsyncWebSocketClient *client) {
106+
Serial.printf("Client %" PRIu32 " connected\n", client->id());
107+
server->textAll("New client: " + String(client->id()));
108+
});
109+
110+
wsHandler.onDisconnect([](AsyncWebSocket *server, uint32_t clientId) {
111+
Serial.printf("Client %" PRIu32 " disconnected\n", clientId);
112+
server->textAll("Client " + String(clientId) + " disconnected");
113+
});
114+
115+
server.addHandler(&ws);
116+
server.begin();
117+
}
118+
119+
void loop() {
120+
delay(100);
121+
}

platformio.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
default_envs = arduino-2, arduino-3, esp8266, raspberrypi
33
lib_dir = .
44
; src_dir = examples/AsyncResponseStream
5+
; src_dir = examples/AsyncTunnel
56
; src_dir = examples/Auth
67
; src_dir = examples/CaptivePortal
78
; src_dir = examples/CatchAllHandler
@@ -18,7 +19,7 @@ lib_dir = .
1819
; src_dir = examples/Middleware
1920
; src_dir = examples/Params
2021
; src_dir = examples/PartitionDownloader
21-
src_dir = examples/PerfTests
22+
; src_dir = examples/PerfTests
2223
; src_dir = examples/RateLimit
2324
; src_dir = examples/Redirect
2425
; src_dir = examples/RequestContinuation

0 commit comments

Comments
 (0)