arduino-audio-tools
Loading...
Searching...
No Matches
HttpHeader.h
Go to the documentation of this file.
1#pragma once
2
5#include "AudioToolsConfig.h"
6#include "AudioClient.h" // for Client
7#include "HttpLineReader.h"
8#include "HttpTypes.h"
9#include "Url.h"
10
11namespace audio_tools {
12
13// Class Configuration
14
15// Define relevant header content
16static const char* CONTENT_TYPE = "Content-Type";
17static const char* CONTENT_LENGTH = "Content-Length";
18static const char* CONNECTION = "Connection";
19static const char* CON_CLOSE = "close";
20static const char* CON_KEEP_ALIVE = "keep-alive";
21static const char* TRANSFER_ENCODING = "Transfer-Encoding";
22static const char* CHUNKED = "chunked";
23static const char* ACCEPT = "Accept";
24static const char* ACCEPT_ALL = "*/*";
25static const char* SUCCESS = "Success";
26static const char* USER_AGENT = "User-Agent";
27static const char* DEFAULT_AGENT =
28 "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
29static const char* HOST_C = "Host";
30static const char* ACCEPT_ENCODING = "Accept-Encoding";
31static const char* IDENTITY = "identity";
32static const char* LOCATION = "Location";
33
34// Http methods
35static const char* methods[] = {"?", "GET", "HEAD", "POST",
36 "PUT", "DELETE", "TRACE", "OPTIONS",
37 "CONNECT", "PATCH", nullptr};
38constexpr int HTTP_STATUS_UNDEFINED = -1;
46 bool active = true;
47 HttpHeaderLine(const char* k) { key = k; }
48};
49
60 public:
62 LOGD("HttpHeader");
63 // set default values
64 protocol_str = "HTTP/1.1";
65 url_path = "/";
66 status_msg = "";
67 }
69 LOGD("~HttpHeader");
70 clear();
71 }
72
75 is_written = false;
76 is_chunked = false;
77 url_path = "/";
78 // delete HttpHeaderLine objects
79 for (auto& ptr : lines){
80 delete ptr;
81 }
82 lines.clear();
83 return *this;
84 }
85
86 HttpHeader& put(const char* key, const char* value) {
87 if (value != nullptr && strlen(value) > 0) {
88 LOGD("HttpHeader::put %s %s", key, value);
89 HttpHeaderLine* hl = headerLine(key);
90 if (hl == nullptr) {
92 LOGE("HttpHeader::put - did not add HttpHeaderLine for %s", key);
93 }
94 return *this;
95 }
96
97 // log entry
98 LOGD("HttpHeader::put -> '%s' : '%s'", key, value);
99
100 hl->value = value;
101 hl->active = true;
102
103 // determine if chunked
104 if (StrView(key).equalsIgnoreCase(TRANSFER_ENCODING) && StrView(value) == CHUNKED) {
105 LOGI("- is_chunked!!!");
106 this->is_chunked = true;
107 }
108 // log content type
109 if (StrView(key).equalsIgnoreCase(CONTENT_TYPE)) {
110 LOGI("%s: %s", CONTENT_TYPE, value);
111 }
112 } else {
113 LOGD("HttpHeader::put - value ignored because it is null for %s", key);
114 }
115 return *this;
116 }
117
119 HttpHeader& put(const char* key, int value) {
120 LOGD("HttpHeader::put %s %d", key, value);
121 HttpHeaderLine* hl = headerLine(key);
122
123 if (value > 1000) {
124 LOGW("value is > 1000");
125 }
126
127 // add value
128 hl->value = value;
129 hl->active = true;
130 LOGI("%s %s", key, hl->value.c_str());
131 return *this;
132 }
133
135 HttpHeader& put(const char* line) {
136 LOGD("HttpHeader::put -> %s", (const char*)line);
137 StrView keyStr(line);
138 int pos = keyStr.indexOf(":");
139 if (pos <= 0) {
140 LOGD("HttpHeader::put - no key found in %s", line);
141 return *this;
142 }
143 char* key = (char*)line;
144 key[pos] = 0;
145
146 // usually there is a leading space - but unfurtunately not always
147 const char* value = line + pos + 1;
148 if (value[0] == ' ') {
149 value = line + pos + 2;
150 }
151 return put((const char*)key, value);
152 }
153
154 // determines a header value with the key
155 const char* get(const char* key) {
156 for (auto& line_ptr : lines) {
157 StrView trimmed{line_ptr->key.c_str()};
158 trimmed.trim();
159 line_ptr->key = trimmed.c_str();
160 //line->key.rtrim();
161 if (StrView(line_ptr->key.c_str()).equalsIgnoreCase(key)) {
162 const char* result = line_ptr->value.c_str();
163 return line_ptr->active ? result : nullptr;
164 }
165 }
166 return nullptr;
167 }
168
169 // reads a single header line
170 int readLine(Client& in, char* str, int len) {
171 int result = reader.readlnInternal(in, (uint8_t*)str, len, false);
172 LOGD("HttpHeader::readLine -> %s", str);
173 return result;
174 }
175
176 // writes a lingle header line
178 LOGD("HttpHeader::writeHeaderLine: %s", header.key.c_str());
179 if (!header.active) {
180 LOGD("HttpHeader::writeHeaderLine - not active");
181 return;
182 }
183 if (header.value.c_str() == nullptr) {
184 LOGD("HttpHeader::writeHeaderLine - ignored because value is null");
185 return;
186 }
187
188 char* msg = tempBuffer();
189 StrView msg_str(msg, HTTP_MAX_LEN);
190 msg_str = header.key.c_str();
191 msg_str += ": ";
192 msg_str += header.value.c_str();
193 msg_str += CRLF;
194 out.print(msg_str.c_str());
195
196 // remove crlf from log
197
198 int len = strlen(msg);
199 msg[len - 2] = 0;
200 LOGI(" -> %s ", msg);
201
202 // mark as processed
203 // header->active = false;
204 }
205
206 const char* urlPath() { return url_path.c_str(); }
207
209
210 int statusCode() { return status_code; }
211
212 const char* statusMessage() { return status_msg.c_str(); }
213
214 bool isChunked() {
215 // the value is automatically set from the reply
216 return is_chunked;
217 }
218
220 bool read(Client& in) {
221 LOGD("HttpHeader::read");
222 // remove all existing value
223 clear();
224
225 char* line = tempBuffer();
226 if (in.connected()) {
227 if (in.available() == 0) {
228 int count = 0;
229 uint32_t timeout = millis() + timeout_ms;
230 while (in.available() == 0) {
231 delay(50);
232 count++;
233 if (count == 2) {
234 LOGI("Waiting for data...");
235 }
236 // If we dont get an answer, we abort
237 if (millis() > timeout) {
238 LOGE("Request timed out after %d ms", (int)timeout_ms);
239 status_code = 401;
240 return false;
241 }
242 }
243 LOGI("Data available: %d", in.available());
244 }
245
246 if (readLine(in, line, HTTP_MAX_LEN) <= 0) {
247 LOGE("Failed to read the first line of the header");
249 return false;
250 }
251 parse1stLine(line);
252 while (true) {
253 int len = readLine(in, line, HTTP_MAX_LEN);
254 if (len == 0 && in.available() == 0) break;
255 if (len < 0) {
256 if (isValidStatus()) LOGE("Failed to read header line");
257 return false;
258 }
259 if (isValidStatus() || isRedirectStatus()) {
260 StrView lineStr(line);
261 lineStr.ltrim();
262 if (lineStr.isEmpty()) {
263 break;
264 }
265 put(line);
266 }
267 }
268 }
269 return true;
270 }
271
273 bool write(Client& out) {
274 LOGI("HttpHeader::write");
275 if (!write1stLine(out)) {
276 return false;
277 }
278 for (auto& line_ptr : lines) {
279 writeHeaderLine(out, *line_ptr);
280 }
281 // print empty line
282 crlf(out);
283 out.flush();
284 is_written = true;
285 return true;
286 }
287
289 for (auto& line :lines) {
290 line->active = false;
291 }
292 }
293
295 void setAutoCreateLines(bool is_auto_line) {
296 create_new_lines = is_auto_line;
297 }
298
300 bool isValidStatus() { return status_code >= 200 && status_code < 300; }
301
303 bool isRedirectStatus() { return status_code >= 300 && status_code < 400; }
304
306 void end() { temp_buffer.resize(0); }
307
309 void setTimeout(int timeoutMs) { timeout_ms = timeoutMs; }
310
312 const char* protocol() { return protocol_str.c_str(); }
313
315 void setProtocol(const char* protocal) { protocol_str = protocal; }
316
318 bool resize(size_t bufferSize){
319 return temp_buffer.resize(bufferSize);
320 }
321
326
327 protected:
329 bool is_written = false;
330 bool is_chunked = false;
331 bool create_new_lines = true;
333 // we store the values on the heap. this is acceptable because we just have
334 // one instance for the requests and one for the replys: which needs about
335 // 2*100 bytes
341 const char* CRLF = "\r\n";
344
345 char* tempBuffer() {
347 return temp_buffer.data();
348 }
349
350 // the headers need to delimited with CR LF
351 void crlf(Client& out) {
352 out.print(CRLF);
353 LOGI(" -> %s ", "<CR LF>");
354 }
355
356 // gets or creates a header line by key
357 HttpHeaderLine* headerLine(const char* key) {
358 if (key != nullptr) {
359 for (auto& line_ptr : lines) {
360 if (line_ptr->key.c_str() != nullptr) {
361 if (line_ptr->key.equalsIgnoreCase(key)) {
362 line_ptr->active = true;
363 return line_ptr;
364 }
365 }
366 }
369 HttpHeaderLine *new_line = new HttpHeaderLine(key);
370 lines.push_back(new_line);
371 return new_line;
372 }
373 } else {
374 LOGI("HttpHeader::headerLine %s", "The key must not be null");
375 }
376 return nullptr;
377 }
378
379 MethodID getMethod(const char* line) {
380 // set action
381 for (int j = 0; methods[j] != nullptr; j++) {
382 const char* action = methods[j];
383 int len = strlen(action);
384 if (strncmp(action, line, len) == 0) {
385 return (MethodID)j;
386 }
387 }
388 return (MethodID)0;
389 }
390
391 virtual bool write1stLine(Client& out) = 0;
392 virtual void parse1stLine(const char* line) = 0;
393};
394
401 public:
402 // Defines the action id, url path and http version for an request
404 const char* protocol = nullptr) {
405 this->method_id = id;
406 this->url_path = urlPath;
407
408 LOGD("HttpRequestHeader::setValues - path: %s", this->url_path.c_str());
409 if (protocol != nullptr) {
410 this->protocol_str = protocol;
411 }
412 return *this;
413 }
414
415 // action path protocol
416 bool write1stLine(Client& out) override {
417 LOGD("HttpRequestHeader::write1stLine");
418 char* msg = tempBuffer();
419 StrView msg_str(msg, HTTP_MAX_LEN);
420
421 const char* method_str = methods[this->method_id];
422 msg_str = method_str;
423 msg_str += " ";
424 msg_str += this->url_path.c_str();
425 msg_str += " ";
426 msg_str += this->protocol_str.c_str();
427 msg_str += CRLF;
428 size_t written = out.print(msg);
429
430 int len = strlen(msg);
431 msg[len - 2] = 0;
432 LOGI("-> %s", msg);
433 if (written != len) {
434 LOGE("Failed to write the first line of the header: %d of %d", (int)written, len);
435 return false;
436 }
437 return true;
438 }
439
440 // parses the requestline
441 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
442 void parse1stLine(const char* line) override {
443 LOGI("HttpRequestHeader::parse1stLine %s", line);
444 StrView line_str(line);
445 int space1 = line_str.indexOf(" ");
446 int space2 = line_str.indexOf(" ", space1 + 1);
447
448 this->method_id = getMethod(line);
449 this->protocol_str.substring(line_str, space2 + 1, line_str.length());
450 this->url_path.substring(line_str, space1 + 1, space2);
451 this->url_path.trim();
452
453 LOGD("->method %s", methods[this->method_id]);
454 LOGD("->protocol %s", protocol_str.c_str());
455 LOGD("->url_path %s", url_path.c_str());
456 }
457};
458
465 public:
466 // defines the values for the rely
467 void setValues(int statusCode, const char* msg = "",
468 const char* protocol = nullptr) {
469 LOGI("HttpReplyHeader::setValues");
470 status_msg = msg;
472 if (protocol != nullptr) {
473 this->protocol_str = protocol;
474 }
475 }
476
477 // reads the final chunked reply headers
478 void readExt(Client& in) {
479 LOGI("HttpReplyHeader::readExt");
480 char* line = tempBuffer();
481 readLine(in, line, HTTP_MAX_LEN);
482 while (strlen(line) != 0) {
483 put(line);
484 readLine(in, line, HTTP_MAX_LEN);
485 }
486 }
487
488 // HTTP-Version SP Status-Code SP Reason-Phrase CRLF
489 bool write1stLine(Client& out) override {
490 LOGI("HttpReplyHeader::write1stLine");
491 char* msg = tempBuffer();
492 StrView msg_str(msg, HTTP_MAX_LEN);
493 msg_str = this->protocol_str.c_str();
494 msg_str += " ";
495 msg_str += this->status_code;
496 msg_str += " ";
497 msg_str += this->status_msg.c_str();
498 LOGI("-> %s", msg);
499 int written = out.print(msg);
500 int len = strlen(msg);
501 crlf(out);
502 if (written != len) {
503 LOGE("Failed to write the first line of the header: %d of %d", written, len);
504 return false;
505 }
506 return true;
507 }
508
509 // HTTP-Version SP Status-Code SP Reason-Phrase CRLF
510 // we just update the pointers to point to the correct position in the
511 // http_status_line
512 void parse1stLine(const char* line) override {
513 LOGD("HttpReplyHeader::parse1stLine: %s", line);
514 StrView line_str(line);
515 int space1 = line_str.indexOf(' ', 0);
516 int space2 = line_str.indexOf(' ', space1 + 1);
517
518 // save http version
519 protocol_str.substring(line_str, 0, space1);
520
521 // find response status code after the first space
522 char status_c[6];
523 StrView status(status_c, 6);
524 status.substring(line_str, space1 + 1, space2);
525 status_code = atoi(status_c);
526
527 // get reason-phrase after last SP
528 status_msg.substring(line_str, space2 + 1, line_str.length());
529 LOGI("status code: %d %s", status_code, status_msg.c_str());
530 }
531};
532
533} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define HTTP_MAX_LEN
Definition AudioToolsConfig.h:153
MethodID
supported http methods
Definition HttpTypes.h:3
Definition Arduino.h:162
bool connected()
Definition Arduino.h:167
virtual void flush()
Definition Arduino.h:130
virtual int available()
Definition Arduino.h:139
In a http request and reply we need to process header information. With this API we can define and qu...
Definition HttpHeader.h:59
List< HttpHeaderLine * > & getHeaderLines()
Provides the http parameter lines.
Definition HttpHeader.h:323
void setTimeout(int timeoutMs)
Set the timout.
Definition HttpHeader.h:309
void crlf(Client &out)
Definition HttpHeader.h:351
Str status_msg
Definition HttpHeader.h:338
virtual void parse1stLine(const char *line)=0
bool isChunked()
Definition HttpHeader.h:214
const char * statusMessage()
Definition HttpHeader.h:212
HttpLineReader reader
Definition HttpHeader.h:340
HttpHeader & clear()
clears the data
Definition HttpHeader.h:74
void setProcessed()
Definition HttpHeader.h:288
int statusCode()
Definition HttpHeader.h:210
HttpHeaderLine * headerLine(const char *key)
Definition HttpHeader.h:357
void setProtocol(const char *protocal)
Defines the protocol: e.g. HTTP/1.1.
Definition HttpHeader.h:315
bool write(Client &out)
writes the full header to the indicated HttpStreamedMultiOutput stream
Definition HttpHeader.h:273
MethodID method_id
Definition HttpHeader.h:332
Vector< char > temp_buffer
Definition HttpHeader.h:343
void writeHeaderLine(Client &out, HttpHeaderLine &header)
Definition HttpHeader.h:177
HttpHeader & put(const char *key, const char *value)
Definition HttpHeader.h:86
HttpHeader()
Definition HttpHeader.h:61
~HttpHeader()
Definition HttpHeader.h:68
Str url_path
Definition HttpHeader.h:337
bool isRedirectStatus()
returns true if the status code is >=300 and < 400
Definition HttpHeader.h:303
int readLine(Client &in, char *str, int len)
Definition HttpHeader.h:170
void end()
release static temp buffer
Definition HttpHeader.h:306
void setAutoCreateLines(bool is_auto_line)
automatically create new lines
Definition HttpHeader.h:295
HttpHeader & put(const char *key, int value)
adds a new line to the header - e.g. for content size
Definition HttpHeader.h:119
bool read(Client &in)
reads the full header from the request (stream)
Definition HttpHeader.h:220
bool isValidStatus()
returns true if status code >=200 and < 300
Definition HttpHeader.h:300
List< HttpHeaderLine * > lines
Definition HttpHeader.h:339
Str protocol_str
Definition HttpHeader.h:336
virtual bool write1stLine(Client &out)=0
int status_code
Definition HttpHeader.h:328
bool create_new_lines
Definition HttpHeader.h:331
int timeout_ms
Definition HttpHeader.h:342
MethodID getMethod(const char *line)
Definition HttpHeader.h:379
const char * protocol()
Provide the protocol.
Definition HttpHeader.h:312
bool is_chunked
Definition HttpHeader.h:330
const char * get(const char *key)
Definition HttpHeader.h:155
HttpHeader & put(const char *line)
adds a received new line to the header
Definition HttpHeader.h:135
const char * urlPath()
Definition HttpHeader.h:206
bool resize(size_t bufferSize)
Resizes the internal read buffer.
Definition HttpHeader.h:318
bool is_written
Definition HttpHeader.h:329
MethodID method()
Definition HttpHeader.h:208
char * tempBuffer()
Definition HttpHeader.h:345
const char * CRLF
Definition HttpHeader.h:341
We read a single line. A terminating 0 is added to the string to make it compliant for c string funct...
Definition HttpLineReader.h:14
virtual int readlnInternal(Stream &client, uint8_t *str, int len, bool incl_nl=true)
reads up the the next CR LF - but never more then the indicated len.
Definition HttpLineReader.h:20
Reading and Writing of Http Replys.
Definition HttpHeader.h:464
bool write1stLine(Client &out) override
Definition HttpHeader.h:489
void readExt(Client &in)
Definition HttpHeader.h:478
void parse1stLine(const char *line) override
Definition HttpHeader.h:512
void setValues(int statusCode, const char *msg="", const char *protocol=nullptr)
Definition HttpHeader.h:467
Reading and writing of Http Requests.
Definition HttpHeader.h:400
HttpHeader & setValues(MethodID id, const char *urlPath, const char *protocol=nullptr)
Definition HttpHeader.h:403
bool write1stLine(Client &out) override
Definition HttpHeader.h:416
void parse1stLine(const char *line) override
Definition HttpHeader.h:442
Double linked list.
Definition List.h:18
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
void clear() override
clears and resizes to 0
Definition Str.h:167
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
virtual int length()
Definition StrView.h:384
virtual bool equalsIgnoreCase(const char *alt)
Compares the string ignoring the case.
Definition StrView.h:565
virtual bool isEmpty()
checks if the string is empty
Definition StrView.h:387
virtual void trim()
remove leading and traling spaces
Definition StrView.h:505
virtual void ltrim()
remove leading spaces
Definition StrView.h:522
virtual void substring(StrView &from, int start, int end)
copies a substring into the current string
Definition StrView.h:478
virtual const char * c_str()
provides the string value as const char*
Definition StrView.h:380
virtual int indexOf(const char c, int start=0)
Definition StrView.h:260
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
void clear()
Definition Vector.h:176
bool resize(size_t newSize, T value)
Definition Vector.h:266
T * data()
Definition Vector.h:316
#define URL_CLIENT_TIMEOUT
Definition esp8266.h:23
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
static const char * CHUNKED
Definition HttpHeader.h:22
static const char * DEFAULT_AGENT
Definition HttpHeader.h:27
static const char * IDENTITY
Definition HttpHeader.h:31
static const char * TRANSFER_ENCODING
Definition HttpHeader.h:21
static const char * methods[]
Definition HttpHeader.h:35
static const char * USER_AGENT
Definition HttpHeader.h:26
static const char * CONTENT_LENGTH
Definition HttpHeader.h:17
void delay(uint32_t ms)
Definition Arduino.h:255
static const char * ACCEPT_ENCODING
Definition HttpHeader.h:30
constexpr int HTTP_STATUS_UNDEFINED
Definition HttpHeader.h:38
static const char * LOCATION
Definition HttpHeader.h:32
static const char * CON_KEEP_ALIVE
Definition HttpHeader.h:20
static const char * SUCCESS
Definition HttpHeader.h:25
static const char * ACCEPT_ALL
Definition HttpHeader.h:24
static const char * CON_CLOSE
Definition HttpHeader.h:19
uint32_t millis()
Returns the milliseconds since the start.
Definition Arduino.h:256
static const char * CONTENT_TYPE
Definition HttpHeader.h:16
static const char * CONNECTION
Definition HttpHeader.h:18
static const char * HOST_C
Definition HttpHeader.h:29
static const char * ACCEPT
Definition HttpHeader.h:23
A individual key - value header line.
Definition HttpHeader.h:43
bool active
Definition HttpHeader.h:46
Str key
Definition HttpHeader.h:44
Str value
Definition HttpHeader.h:45
HttpHeaderLine(const char *k)
Definition HttpHeader.h:47