arduino-audio-tools
Loading...
Searching...
No Matches
URLStream.h
1#pragma once
2
3#include "AudioToolsConfig.h"
4
5#if defined(USE_WIFI)
6# include "WiFiInclude.h"
7#endif
8
9#include "AudioTools/CoreAudio/AudioBasic/Str.h"
10#include "AudioTools/Communication/HTTP/AbstractURLStream.h"
11#include "AudioTools/Communication/HTTP/HttpRequest.h"
12#include "AudioTools/Communication/HTTP/URLStreamBufferedT.h"
13
14namespace audio_tools {
15
27 public:
28 URLStream(int readBufferSize = DEFAULT_BUFFER_SIZE) {
29 TRACED();
30 setReadBufferSize(readBufferSize);
31 }
32
33 URLStream(Client& clientPar, int readBufferSize = DEFAULT_BUFFER_SIZE) {
34 TRACED();
35 setReadBufferSize(readBufferSize);
36 setClient(clientPar);
37 }
38
39 URLStream(const char* network, const char* password,
40 int readBufferSize = DEFAULT_BUFFER_SIZE) {
41 TRACED();
42 setReadBufferSize(readBufferSize);
43 setSSID(network);
44 setPassword(password);
45 }
46
47 URLStream(const URLStream&) = delete;
48
49 ~URLStream() {
50 TRACED();
51 end();
52 if (clientSecure != nullptr) {
53 delete clientSecure;
54 clientSecure = nullptr;
55 }
56 if (clientInsecure != nullptr) {
57 delete clientInsecure;
58 clientInsecure = nullptr;
59 }
60 }
61
63 void setClient(Client& clientPar) override { client = &clientPar; }
64
66 void setSSID(const char* ssid) override { this->network = ssid; }
67
69 void setPassword(const char* password) override { this->password = password; }
70
72 void setReadBufferSize(int readBufferSize) {
73 read_buffer_size = readBufferSize;
74 }
75
77 virtual bool begin(const char* urlStr, const char* acceptMime = nullptr,
78 MethodID action = GET, const char* reqMime = "",
79 const char* reqData = "") override {
80 LOGI("%s: %s", LOG_METHOD, urlStr);
81 if (!preProcess(urlStr, acceptMime)) {
82 LOGE("preProcess failed");
83 return false;
84 }
85 int result = process<const char*>(action, url, reqMime, reqData);
86 if (result > 0) {
87 size = request.contentLength();
88 LOGI("contentLength: %d", (int)size);
89 if (size >= 0 && wait_for_data) {
90 waitForData(clientTimeout);
91 }
92 }
93 total_read = 0;
94 active = result == 200;
95 LOGI("==> http status: %d", result);
96 return active;
97 }
98
100 virtual bool begin(const char* urlStr, const char* acceptMime,
101 MethodID action, const char* reqMime, Stream& reqData,
102 int len = -1) {
103 LOGI("%s: %s", LOG_METHOD, urlStr);
104 if (!preProcess(urlStr, acceptMime)) {
105 LOGE("preProcess failed");
106 return false;
107 }
108 int result = process<Stream&>(action, url, reqMime, reqData, len);
109 if (result > 0) {
110 size = request.contentLength();
111 LOGI("size: %d", (int)size);
112 if (size >= 0 && wait_for_data) {
113 waitForData(clientTimeout);
114 }
115 }
116 total_read = 0;
117 active = result == 200;
118 LOGI("==> http status: %d", result);
119 return active;
120 }
121
122 virtual void end() override {
123 if (active) request.stop();
124 active = false;
125 clear();
126 }
127
128 virtual int available() override {
129 if (!active) return 0;
130
131 int result = request.available();
132 LOGD("available: %d", result);
133 return result;
134 }
135
136 virtual size_t readBytes(uint8_t* data, size_t len) override {
137 if (!active) return 0;
138
139 int read = request.read((uint8_t*)&data[0], len);
140 if (read < 0) {
141 read = 0;
142 }
143 total_read += read;
144 LOGD("readBytes %d -> %d", (int)len, read);
145 return read;
146 }
147
148 virtual int read() override {
149 if (!active) return -1;
150 // lazy allocation since this is rarely used
151 read_buffer.resize(read_buffer_size);
152
153 fillBuffer();
154 total_read++;
155 return isEOS() ? -1 : read_buffer[read_pos++];
156 }
157
158 virtual int peek() override {
159 if (!active) return -1;
160 // lazy allocation since this is rarely used
161 read_buffer.resize(read_buffer_size);
162
163 fillBuffer();
164 return isEOS() ? -1 : read_buffer[read_pos];
165 }
166
167 virtual void flush() override {}
168
169 virtual size_t write(uint8_t) override { return not_supported(0); }
170
171 virtual size_t write(const uint8_t*, size_t len) override {
172 return not_supported(0);
173 }
174
176 virtual HttpRequest& httpRequest() override { return request; }
177
178 operator bool() override { return active && request.isReady(); }
179
181 virtual void setTimeout(int ms) { clientTimeout = ms; }
182
185 void setPowerSave(bool ps) override { is_power_save = ps; }
186
188 void setAutoCreateLines(bool flag) {
189 httpRequest().reply().setAutoCreateLines(flag);
190 }
191
193 void setConnectionClose(bool close) override {
194 httpRequest().setConnection(close ? CON_CLOSE : CON_KEEP_ALIVE);
195 }
196
198 void clear() {
199 httpRequest().reply().clear();
201 read_buffer.resize(0);
202 read_pos = 0;
203 read_size = 0;
204 }
205
207 void addRequestHeader(const char* key, const char* value) override {
208 request.header().put(key, value);
209 }
210
211 const char* getReplyHeader(const char* key) override {
212 return request.reply().get(key);
213 }
214
216 void setOnConnectCallback(void (*callback)(
217 HttpRequest& request, Url& url, HttpRequestHeader& request_header)) {
218 request.setOnConnectCallback(callback);
219 }
220
221 void setWaitForData(bool flag) { wait_for_data = flag; }
222
223 int contentLength() override { return size; }
224
225 size_t totalRead() override { return total_read; }
227 bool waitForData (int timeout) override{
228 TRACED();
229 uint32_t end = millis() + timeout;
230 if (request.available() == 0) {
231 LOGI("Request written ... waiting for reply");
232 while (request.available() == 0) {
233 if (millis() > end) break;
234 // stop waiting if we got an error
235 if (request.reply().statusCode() >= 300) {
236 LOGE("Error code recieved ... stop waiting for reply");
237 break;
238 }
239 delay(500);
240 }
241 }
242 int avail = request.available();
243 LOGD("available: %d", avail);
244 return avail > 0;
245 }
246
247
248 const char* urlStr() override { return url_str.c_str(); }
249
251 void setCACert(const char* cert) override{
252 if (clientSecure!=nullptr) clientSecure->setCACert(cert);
253 }
254
255 protected:
256 HttpRequest request;
257 Str url_str;
258 Url url;
259 long size;
260 long total_read;
261 // buffered single byte read
262 Vector<uint8_t> read_buffer{0};
263 uint16_t read_buffer_size = DEFAULT_BUFFER_SIZE;
264 uint16_t read_pos;
265 uint16_t read_size;
266 bool active = false;
267 bool wait_for_data = true;
268 // optional
269 const char* network = nullptr;
270 const char* password = nullptr;
271 Client* client = nullptr; // client defined via setClient
272 WiFiClient* clientInsecure = nullptr; // wifi client for http
273 WiFiClientSecure* clientSecure = nullptr; // wifi client for https
274 int clientTimeout = URL_CLIENT_TIMEOUT; // 60000;
275 unsigned long handshakeTimeout = URL_HANDSHAKE_TIMEOUT; // 120000
276 bool is_power_save = false;
277
278 bool preProcess(const char* urlStr, const char* acceptMime) {
279 TRACED();
280 url_str = urlStr;
281 url.setUrl(url_str.c_str());
282 int result = -1;
283
284 // close it - if we have an active connection
285 if (active) end();
286
287 // optional: login if necessary if no external client is defined
288 if (client == nullptr){
289 if (!login()){
290 LOGE("Not connected");
291 return false;
292 }
293 }
294
295 // request.reply().setAutoCreateLines(false);
296 if (acceptMime != nullptr) {
297 request.setAcceptMime(acceptMime);
298 }
299
300 // setup client
301 Client& client = getClient(url.isSecure());
302 request.setClient(client);
303
304 // set timeout
305 client.setTimeout(clientTimeout / 1000);
306 request.setTimeout(clientTimeout);
307
308#if defined(ESP32)
309 // There is a bug in IDF 4!
310 if (clientSecure != nullptr) {
311 clientSecure->setHandshakeTimeout(handshakeTimeout);
312 }
313
314 // Performance optimization for ESP32
315 if (!is_power_save) {
316 esp_wifi_set_ps(WIFI_PS_NONE);
317 }
318#endif
319
320 return true;
321 }
322
324 template <typename T>
325 int process(MethodID action, Url& url, const char* reqMime, T reqData,
326 int len = -1) {
327 TRACED();
328 // keep icy across redirect requests ?
329 const char* icy = request.header().get("Icy-MetaData");
330
331 int status_code = request.process(action, url, reqMime, reqData, len);
332 // redirect
333 while (request.reply().isRedirectStatus()) {
334 const char* redirect_url = request.reply().get(LOCATION);
335 if (redirect_url != nullptr) {
336 LOGW("Redirected to: %s", redirect_url);
337 url.setUrl(redirect_url);
338 Client* p_client = &getClient(url.isSecure());
339 p_client->stop();
340 request.setClient(*p_client);
341 if (icy) {
342 request.header().put("Icy-MetaData", icy);
343 }
344 status_code = request.process(action, url, reqMime, reqData, len);
345 } else {
346 LOGE("Location is null");
347 break;
348 }
349 }
350 return status_code;
351 }
352
354 Client& getClient(bool isSecure) {
355 if (isSecure) {
356 if (clientSecure == nullptr) {
357 clientSecure = new WiFiClientSecure();
358 clientSecure->setInsecure();
359 }
360 LOGI("WiFiClientSecure");
361 return *clientSecure;
362 }
363 if (clientInsecure == nullptr) {
364 clientInsecure = new WiFiClient();
365 LOGI("WiFiClient");
366 }
367 return *clientInsecure;
368 }
369
370 inline void fillBuffer() {
371 if (isEOS()) {
372 // if we consumed all bytes we refill the buffer
373 read_size = readBytes(&read_buffer[0], read_buffer_size);
374 read_pos = 0;
375 }
376 }
377
378 inline bool isEOS() { return read_pos >= read_size; }
379
380 bool login() {
381 if (network != nullptr && password != nullptr &&
382 WiFi.status() != WL_CONNECTED) {
383 TRACEI();
384 WiFi.begin(network, password);
385 while (WiFi.status() != WL_CONNECTED) {
386 Serial.print(".");
387 delay(500);
388 }
389 Serial.println();
390 delay(10);
391 return WiFi.status() == WL_CONNECTED;
392 }
393 return WiFi.status() == WL_CONNECTED;
394 }
395};
396
397
398#if defined(USE_CONCURRENCY)
401
402#endif
403
404} // namespace audio_tools
Abstract Base class for all URLStream implementations.
Definition AbstractURLStream.h:17
Definition NoArduino.h:169
HttpHeader & clear()
clears the data
Definition HttpHeader.h:74
bool isRedirectStatus()
returns true if the status code is >=300 and < 400
Definition HttpHeader.h:288
void setAutoCreateLines(bool is_auto_line)
automatically create new lines
Definition HttpHeader.h:280
Reading and writing of Http Requests.
Definition HttpHeader.h:385
Simple API to process get, put, post, del http requests I tried to use Arduino HttpClient,...
Definition HttpRequest.h:25
void setTimeout(size_t timeoutMs)
Defines the client timeout in ms.
Definition HttpRequest.h:338
void setOnConnectCallback(void(*callback)(HttpRequest &request, Url &url, HttpRequestHeader &request_header))
Callback which allows you to add additional paramters dynamically.
Definition HttpRequest.h:332
bool isReady()
Definition HttpRequest.h:188
virtual HttpRequestHeader & header()
provides access to the request header
Definition HttpRequest.h:160
Str which keeps the data on the heap. We grow the allocated memory only if the copy source is not fit...
Definition Str.h:24
virtual const char * c_str()
provides the string value as const char*
Definition StrView.h:379
Definition NoArduino.h:142
URLStream implementation for the ESP32 based on a separate FreeRTOS task: the.
Definition URLStreamBufferedT.h:154
Represents the content of a URL as Stream. We use the WiFi.h API. If you run into performance issues,...
Definition URLStream.h:26
size_t totalRead() override
Total amout of data that was consumed so far.
Definition URLStream.h:225
virtual bool begin(const char *urlStr, const char *acceptMime=nullptr, MethodID action=GET, const char *reqMime="", const char *reqData="") override
Execute http request: by default we use a GET request.
Definition URLStream.h:77
virtual void setTimeout(int ms)
Defines the client timeout.
Definition URLStream.h:181
bool waitForData(int timeout) override
waits for some data - returns false if the request has failed
Definition URLStream.h:227
void setConnectionClose(bool close) override
Sets if the connection should be close automatically.
Definition URLStream.h:193
void setReadBufferSize(int readBufferSize)
Defines the buffer that is used by individual read() or peek() calls.
Definition URLStream.h:72
void setPowerSave(bool ps) override
Definition URLStream.h:185
Client & getClient(bool isSecure)
Determines the client.
Definition URLStream.h:354
int process(MethodID action, Url &url, const char *reqMime, T reqData, int len=-1)
Process the Http request and handle redirects.
Definition URLStream.h:325
void addRequestHeader(const char *key, const char *value) override
Adds/Updates a request header.
Definition URLStream.h:207
void setSSID(const char *ssid) override
Sets the ssid that will be used for logging in (when calling begin)
Definition URLStream.h:66
virtual bool begin(const char *urlStr, const char *acceptMime, MethodID action, const char *reqMime, Stream &reqData, int len=-1)
Execute e.g. http POST request which submits the content as a stream.
Definition URLStream.h:100
const char * urlStr() override
Provides the url as string.
Definition URLStream.h:248
void setClient(Client &clientPar) override
(Re-)defines the client
Definition URLStream.h:63
void setCACert(const char *cert) override
Define the Root PEM Certificate for SSL.
Definition URLStream.h:251
void setOnConnectCallback(void(*callback)(HttpRequest &request, Url &url, HttpRequestHeader &request_header))
Callback which allows you to add additional paramters dynamically.
Definition URLStream.h:216
void clear()
Releases the memory from the request and reply.
Definition URLStream.h:198
int contentLength() override
Provides the reported data size from the http reply.
Definition URLStream.h:223
void setAutoCreateLines(bool flag)
If set to true, new undefined reply parameters will be stored.
Definition URLStream.h:188
void setPassword(const char *password) override
Sets the password that will be used for logging in (when calling begin)
Definition URLStream.h:69
virtual HttpRequest & httpRequest() override
provides access to the HttpRequest
Definition URLStream.h:176
const char * getReplyHeader(const char *key) override
Provides reply header information.
Definition URLStream.h:211
URL parser which breaks a full url string up into its individual parts.
Definition Url.h:22
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
uint32_t millis()
Returns the milliseconds since the start.
Definition Time.h:12