3 #include "AudioTools/CoreAudio/AudioBasic/Collections.h"
4 #include "AudioTools/CoreAudio/AudioBasic/Str.h"
5 #include "AudioConfig.h"
6 #include "HttpLineReader.h"
10 #if defined(ARDUINO) || defined(IS_DESKTOP)
18 static const char* CONTENT_TYPE =
"Content-Type";
19 static const char* CONTENT_LENGTH =
"Content-Length";
20 static const char* CONNECTION =
"Connection";
21 static const char* CON_CLOSE =
"close";
22 static const char* CON_KEEP_ALIVE =
"keep-alive";
23 static const char* TRANSFER_ENCODING =
"Transfer-Encoding";
24 static const char* CHUNKED =
"chunked";
25 static const char* ACCEPT =
"Accept";
26 static const char* ACCEPT_ALL =
"*/*";
27 static const char* SUCCESS =
"Success";
28 static const char* USER_AGENT =
"User-Agent";
29 static const char* DEFAULT_AGENT =
30 "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
31 static const char* HOST_C =
"Host";
32 static const char* ACCEPT_ENCODING =
"Accept-Encoding";
33 static const char* IDENTITY =
"identity";
34 static const char* LOCATION =
"Location";
37 static const char* methods[] = {
"?",
"GET",
"HEAD",
"POST",
38 "PUT",
"DELETE",
"TRACE",
"OPTIONS",
39 "CONNECT",
"PATCH",
nullptr};
72 protocol_str =
"HTTP/1.1";
99 for (
auto& ptr : lines){
106 HttpHeader& put(
const char* key,
const char* value) {
107 if (value !=
nullptr && strlen(value) > 0) {
108 LOGD(
"HttpHeader::put %s %s", key, value);
111 if (create_new_lines)
112 LOGE(
"HttpHeader::put - did not add HttpHeaderLine for %s", key);
117 LOGD(
"HttpHeader::put -> '%s' : '%s'", key, value);
122 if (StrView(key) == TRANSFER_ENCODING && StrView(value) == CHUNKED) {
123 LOGD(
"HttpHeader::put -> is_chunked!!!");
124 this->is_chunked =
true;
127 LOGD(
"HttpHeader::put - value ignored because it is null for %s", key);
134 LOGD(
"HttpHeader::put %s %d", key, value);
138 LOGW(
"value is > 1000");
144 LOGI(
"%s %s", key, hl->value.
c_str());
150 LOGD(
"HttpHeader::put -> %s", (
const char*)line);
153 char* key = (
char*)line;
157 const char* value = line + pos + 1;
158 if (value[0] ==
' ') {
159 value = line + pos + 2;
161 return put((
const char*)key, value);
165 const char* get(
const char* key) {
166 for (
auto& line_ptr : lines) {
169 line_ptr->key = trimmed.c_str();
171 if (StrView(line_ptr->key.c_str()).equalsIgnoreCase(key)) {
172 const char* result = line_ptr->value.c_str();
173 return line_ptr->active ? result :
nullptr;
180 int readLine(Client& in,
char* str,
int len) {
181 int result = reader.readlnInternal(in, (uint8_t*)str, len,
false);
182 LOGD(
"HttpHeader::readLine -> %s", str);
187 void writeHeaderLine(Client& out, HttpHeaderLine& header) {
188 LOGD(
"HttpHeader::writeHeaderLine: %s", header.key.c_str());
189 if (!header.active) {
190 LOGD(
"HttpHeader::writeHeaderLine - not active");
193 if (header.value.c_str() ==
nullptr) {
194 LOGD(
"HttpHeader::writeHeaderLine - ignored because value is null");
198 char* msg = tempBuffer();
199 StrView msg_str(msg, HTTP_MAX_LEN);
200 msg_str = header.key.c_str();
202 msg_str += header.value.c_str();
204 out.print(msg_str.c_str());
208 int len = strlen(msg);
210 LOGI(
" -> %s ", msg);
216 const char* urlPath() {
return url_path.
c_str(); }
218 MethodID method() {
return method_id; }
220 int statusCode() {
return status_code; }
222 const char* statusMessage() {
return status_msg.
c_str(); }
231 LOGD(
"HttpHeader::read");
235 char* line = tempBuffer();
236 if (in.connected()) {
237 if (in.available() == 0) {
239 uint32_t timeout =
millis() + timeout_ms;
240 while (in.available() == 0) {
244 LOGI(
"Waiting for data...");
248 LOGE(
"Request timed out after %d ms", (
int)timeout_ms);
253 LOGI(
"Data available: %d", in.available());
256 readLine(in, line, HTTP_MAX_LEN);
259 int len = readLine(in, line, HTTP_MAX_LEN);
260 if (len == 0 && in.available() == 0)
break;
276 LOGI(
"HttpHeader::write");
278 for (
auto& line_ptr : lines) {
279 writeHeaderLine(out, *line_ptr);
287 void setProcessed() {
288 for (
auto& line :lines) {
289 line->active =
false;
295 create_new_lines = is_auto_line;
314 void setProtocol(
const char* protocal) { protocol_str = protocal; }
317 int status_code = UNDEFINED;
318 bool is_written =
false;
319 bool is_chunked =
false;
320 bool create_new_lines =
true;
325 Str protocol_str{10};
328 List<HttpHeaderLine*> lines;
329 HttpLineReader reader;
330 const char* CRLF =
"\r\n";
331 int timeout_ms = URL_CLIENT_TIMEOUT;
340 void crlf(Client& out) {
342 LOGI(
" -> %s ",
"<CR LF>");
346 HttpHeaderLine* headerLine(
const char* key) {
347 if (key !=
nullptr) {
348 for (
auto& line_ptr : lines) {
349 if (line_ptr->key.c_str() !=
nullptr) {
350 if (line_ptr->key.equalsIgnoreCase(key)) {
351 line_ptr->active =
true;
356 if (create_new_lines || StrView(key).equalsIgnoreCase(CONTENT_LENGTH) ||
357 StrView(key).equalsIgnoreCase(CONTENT_TYPE)) {
358 HttpHeaderLine *new_line =
new HttpHeaderLine(key);
359 lines.push_back(new_line);
363 LOGI(
"HttpHeader::headerLine %s",
"The key must not be null");
368 MethodID getMethod(
const char* line) {
370 for (
int j = 0; methods[j] !=
nullptr; j++) {
371 const char* action = methods[j];
372 int len = strlen(action);
373 if (strncmp(action, line, len) == 0) {
380 virtual void write1stLine(Client& out) = 0;
381 virtual void parse1stLine(
const char* line) = 0;
392 HttpHeader& setValues(MethodID
id,
const char* urlPath,
394 this->method_id = id;
395 this->url_path = urlPath;
397 LOGD(
"HttpRequestHeader::setValues - path: %s", this->url_path.
c_str());
405 void write1stLine(
Client& out) {
406 LOGD(
"HttpRequestHeader::write1stLine");
407 char* msg = tempBuffer();
408 StrView msg_str(msg, HTTP_MAX_LEN);
410 const char* method_str = methods[this->method_id];
411 msg_str = method_str;
413 msg_str += this->url_path.
c_str();
415 msg_str += this->protocol_str.
c_str();
419 int len = strlen(msg);
426 void parse1stLine(
const char* line) {
427 LOGD(
"HttpRequestHeader::parse1stLine %s", line);
429 int space1 = line_str.
indexOf(
" ");
430 int space2 = line_str.
indexOf(
" ", space1 + 1);
432 this->method_id = getMethod(line);
434 this->url_path.
substring(line_str, space1 + 1, space2);
435 this->url_path.
trim();
437 LOGD(
"->method %s", methods[this->method_id]);
438 LOGD(
"->protocol %s", protocol_str.
c_str());
439 LOGD(
"->url_path %s", url_path.
c_str());
451 void setValues(
int statusCode,
const char* msg =
"",
453 LOGI(
"HttpReplyHeader::setValues");
455 status_code = statusCode;
462 void readExt(
Client& in) {
463 LOGI(
"HttpReplyHeader::readExt");
464 char* line = tempBuffer();
465 readLine(in, line, HTTP_MAX_LEN);
466 while (strlen(line) != 0) {
468 readLine(in, line, HTTP_MAX_LEN);
473 void write1stLine(
Client& out) {
474 LOGI(
"HttpReplyHeader::write1stLine");
475 char* msg = tempBuffer();
476 StrView msg_str(msg, HTTP_MAX_LEN);
477 msg_str = this->protocol_str.
c_str();
479 msg_str += this->status_code;
481 msg_str += this->status_msg.
c_str();
490 void parse1stLine(
const char* line) {
491 LOGD(
"HttpReplyHeader::parse1stLine: %s", line);
493 int space1 = line_str.
indexOf(
' ', 0);
494 int space2 = line_str.
indexOf(
' ', space1 + 1);
497 protocol_str.
substring(line_str, 0, space1);
502 status.
substring(line_str, space1 + 1, space2);
503 status_code = atoi(status_c);