Arduino DLNA Server
Loading...
Searching...
No Matches
HttpHeader.h
Go to the documentation of this file.
1#pragma once
2
3// #include "Platform/AltClient.h"
4#include "HttpLineReader.h"
5#include "basic/Url.h"
6#include "basic/List.h"
7#include "basic/Logger.h"
8#include "basic/Str.h"
9
10namespace tiny_dlna {
11
12// Class Configuration
13const int MaxHeaderLineLength = 200;
14
15// Define relevant header content
16const char* CONTENT_TYPE = "Content-Type";
17const char* CONTENT_LENGTH = "Content-Length";
18const char* CONNECTION = "Connection";
19const char* CON_CLOSE = "close";
20const char* CON_KEEP_ALIVE = "keep-alive";
21const char* TRANSFER_ENCODING = "Transfer-Encoding";
22const char* CHUNKED = "chunked";
23const char* ACCEPT = "Accept";
24const char* ACCEPT_ALL = "*/*";
25const char* SUCCESS = "Success";
26const char* USER_AGENT = "User-Agent";
27const char* DEFAULT_AGENT =
28 "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
29const char* HOST_C = "Host";
30const char* ACCEPT_ENCODING = "Accept-Encoding";
31const char* IDENTITY = "identity";
32const char* LOCATION = "Location";
33
34// Http methods
49};
50const char* methods[] = {"?", "GET", "HEAD", "POST",
51 "PUT", "DELETE", "TRACE", "OPTIONS",
52 "CONNECT", "PATCH", "UNSUBSCRIBE", "SUBSCRIBE", "NOTIFY", nullptr};
53
61 bool active;
62};
63
72 public:
74 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader");
75 // set default values
76 protocol_str = "HTTP/1.1";
77 url_path = "/";
78 status_msg = "";
79 }
81 DlnaLogger.log(DlnaLogLevel::Debug, "~HttpHeader");
82 clear(false);
83 }
84
86 HttpHeader& clear(bool activeFlag = true) {
87 is_written = false;
88 is_chunked = false;
89 url_path = "/";
90 for (auto it = lines.begin(); it != lines.end(); ++it) {
91 if (it != nullptr) {
92 if (activeFlag) {
93 (*it)->active = false;
94 } else {
95 delete *it;
96 }
97 }
98 }
99 if (!activeFlag) {
100 lines.clear();
101 }
102 return *this;
103 }
104
105 HttpHeader& put(const char* key, const char* value) {
106 if (value != nullptr) {
107 HttpHeaderLine* hl = headerLine(key);
108 if (hl == nullptr) {
109 DlnaLogger.log(DlnaLogLevel::Error,
110 "HttpHeader::put - did not add HttpHeaderLine for %s",
111 key);
112 return *this;
113 }
114
115 // log entry
116 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader::put '%s' : %s", key, value);
117 hl->value = value;
118 hl->active = true;
119
120 if (StrView(key) == TRANSFER_ENCODING && StrView(value) == CHUNKED) {
121 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader::put -> is_chunked!!!");
122 this->is_chunked = true;
123 }
124 } else {
125 DlnaLogger.log(
126 DlnaLogLevel::Debug, "HttpHeader::put - value ignored because it is null for %s",
127 key);
128 }
129 return *this;
130 }
131
133 HttpHeader& put(const char* key, int value) {
134 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader::put %s %d", key, value);
135 HttpHeaderLine* hl = headerLine(key);
136
137 // add value
138 hl->value = value;
139 hl->active = true;
140 DlnaLogger.log(DlnaLogLevel::Debug, "%s: %s", key, hl->value.c_str());
141 return *this;
142 }
143
145 HttpHeader& put(const char* line) {
146 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader::put -> %s", (const char*)line);
147 StrView keyStr(line);
148 int pos = keyStr.indexOf(":");
149 char* key = (char*)line;
150 key[pos] = 0;
151
152 // usually there is a leading space - but unfurtunately not always
153 const char* value = line + pos + 1;
154 if (value[0] == ' ') {
155 value = line + pos + 2;
156 }
157 return put((const char*)key, value);
158 }
159
160 // determines a header value with the key
161 const char* get(const char* key) {
162 for (auto it = lines.begin(); it != lines.end(); ++it) {
163 HttpHeaderLine* line = *it;
164 line->key.trim();
165 if (line->key.equalsIgnoreCase(key)) {
166 const char* result = line->value.c_str();
167 return line->active ? result : nullptr;
168 }
169 }
170 return nullptr;
171 }
172
173 // reads a single header line
174 void readLine(Client& in, char* str, int len) {
175 reader.readlnInternal(in, (uint8_t*)str, len, false);
176 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader::readLine -> %s", str);
177 }
178
179 // writes a lingle header line
180 void writeHeaderLine(Client& out, HttpHeaderLine* header) {
181 if (header == nullptr) {
182 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader::writeHeaderLine",
183 "the value must not be null");
184 return;
185 }
186 if (!header->active) {
187 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader::writeHeaderLine %s - not active",
188 header->key.c_str());
189 return;
190 }
191 if (header->value.c_str() == nullptr) {
192 DlnaLogger.log(
194 "HttpHeader::writeHeaderLine - ignored because value is null");
195 return;
196 }
197
198 char msg[400];
199 StrView msg_str(msg, 400);
200 msg_str = header->key.c_str();
201 msg_str += ": ";
202 msg_str += header->value.c_str();
203 msg_str += CRLF;
204 out.print(msg);
205
206 // remove crlf from log
207 int len = strnlen(msg, 200);
208 msg[len - 2] = 0;
209 DlnaLogger.log(DlnaLogLevel::Debug, "writeHeaderLine -> %s", msg);
210
211 // marke as processed
212 header->active = false;
213 }
214
215 const char* urlPath() { return url_path.c_str(); }
216
217 const char* protocol() { return protocol_str.c_str(); }
218
220
221 const char* accept() { return get(ACCEPT); }
222
223 int statusCode() { return status_code; }
224
225 const char* statusMessage() { return status_msg.c_str(); }
226
227 bool isChunked() {
228 // the value is automatically set from the reply
229 return is_chunked;
230 }
231
232 // reads the full header from the request (stream)
233 void read(Client& in) {
234 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader::read");
235 // remove all existing value
236 clear();
237
238 char line[MaxHeaderLineLength];
239 if (in.connected()) {
240 delay(200);
241 if (in.available() == 0) {
242 DlnaLogger.log(DlnaLogLevel::Warning, "Waiting for data...");
243 uint64_t end = millis() + in.getTimeout();
244 while (in.available() == 0) {
245 delay(100);
246 if (millis()>end) break;
247 }
248 }
249 readLine(in, line, MaxHeaderLineLength);
250 parse1stLine(line);
251 while (in.available()) {
252 readLine(in, line, MaxHeaderLineLength);
253 StrView lineStr(line);
254 if (lineStr.isEmpty() || lineStr.isNewLine()) {
255 break;
256 }
257 // Always add header lines for requests and replies. Previously this
258 // only added lines for valid/redirect reply statuses which caused
259 // request headers (e.g. CALLBACK for SUBSCRIBE) to be ignored.
260 lineStr.ltrim();
261 put(line);
262 }
263 }
264 }
265
266 // writes the full header to the indicated stream
267 void write(Client& out) {
268 DlnaLogger.log(DlnaLogLevel::Debug, "HttpHeader::write");
269 write1stLine(out);
270 for (auto it = lines.begin(); it != lines.end(); ++it) {
271 writeHeaderLine(out, *it);
272 }
273 // print empty line
274 crlf(out);
275 out.flush();
276 is_written = true;
277 }
278
279 // automatically create new lines
280 void setAutoCreateLines(bool is_auto_line) {
281 create_new_lines = is_auto_line;
282 }
284 bool isValidStatus() { return status_code >= 200 && status_code < 300; }
285
286 bool isRedirectStatus() { return status_code >= 300 && status_code < 400; }
287
288 protected:
290 bool is_written = false;
291 bool is_chunked = false;
292 bool create_new_lines = true;
294 // we store the values on the heap. this is acceptable because we just have
295 // one instance for the requests and one for the replys: which needs about
296 // 2*100 bytes
302 const char* CRLF = "\r\n";
303
304 // the headers need to delimited with CR LF
305 void crlf(Client& out) {
306 out.print(CRLF);
307 DlnaLogger.log(DlnaLogLevel::Debug, " -> %s", "<CR LF>");
308 }
309
310 // gets or creates a header line by key
311 HttpHeaderLine* headerLine(const char* key) {
312 if (key != nullptr) {
313 for (auto it = lines.begin(); it != lines.end(); ++it) {
314 HttpHeaderLine* pt = (*it);
315 if (pt != nullptr && pt->key.c_str() != nullptr) {
316 if (pt->key.equalsIgnoreCase(key)) {
317 pt->active = true;
318 return pt;
319 }
320 }
321 }
322 if (create_new_lines) {
323 HttpHeaderLine* newLine = new HttpHeaderLine();
324 DlnaLogger.log(DlnaLogLevel::Debug,
325 "HttpHeader::headerLine - new line created for %s", key);
326 newLine->active = true;
327 newLine->key = key;
328 lines.push_back(newLine);
329 return newLine;
330 }
331 } else {
332 DlnaLogger.log(DlnaLogLevel::Error, "HttpHeader::headerLine",
333 "The key must not be null");
334 }
335 return nullptr;
336 }
337
338 TinyMethodID getMethod(const char* line) {
339 // set action
340 for (int j = 0; methods[j] != nullptr; j++) {
341 const char* action = methods[j];
342 int len = strlen(action);
343 if (strncmp(action, line, len) == 0) {
344 return (TinyMethodID)j;
345 }
346 }
347 return (TinyMethodID)0;
348 }
349
350 virtual void write1stLine(Client& out) = 0;
351 virtual void parse1stLine(const char* line) = 0;
352};
353
359 public:
360 // Defines the action id, url path and http version for an request
362 const char* protocol = nullptr) {
363 this->method_id = id;
364 this->url_path = urlPath;
365
366 DlnaLogger.log(DlnaLogLevel::Debug, "HttpRequestHeader::setValues - path: %s",
367 this->url_path.c_str());
368 if (protocol != nullptr) {
369 this->protocol_str = protocol;
370 }
371 return *this;
372 }
373
374 // action path protocol
375 void write1stLine(Client& out) {
376 char msg[201] = {0};
377
378 const char* method_str = methods[this->method_id];
379 strncat(msg, method_str, 200);
380 strncat(msg, " ", 200);
381 strncat(msg, this->url_path.c_str(), 200);
382 strncat(msg, " ", 200);
383 strncat(msg, this->protocol_str.c_str(), 200);
384 strncat(msg, CRLF, 200);
385 out.print(msg);
386 DlnaLogger.log(DlnaLogLevel::Debug, "HttpRequestHeader::write1stLine: %s", msg);
387 }
388
389 // parses the requestline
390 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
391 void parse1stLine(const char* line) {
392 DlnaLogger.log(DlnaLogLevel::Debug, "HttpRequestHeader::parse1stLine %s", line);
393 StrView line_str(line);
394 int space1 = line_str.indexOf(" ");
395 int space2 = line_str.indexOf(" ", space1 + 1);
396
397 this->method_id = getMethod(line);
398 this->protocol_str.substrView(line_str, space2 + 1, line_str.length());
399 this->url_path.substrView(line_str, space1 + 1, space2);
400 this->url_path.trim();
401
402 DlnaLogger.log(DlnaLogLevel::Debug, "->method: %s", methods[this->method_id]);
403 DlnaLogger.log(DlnaLogLevel::Debug, "->protocol: %s", protocol_str.c_str());
404 DlnaLogger.log(DlnaLogLevel::Debug, "->url_path: %s", url_path.c_str());
405 }
406};
407
413 public:
414 // defines the values for the rely
415 void setValues(int statusCode, const char* msg = "",
416 const char* protocol = nullptr) {
417 DlnaLogger.log(DlnaLogLevel::Debug, "HttpReplyHeader::setValues %d", statusCode);
418 status_msg = msg;
420 if (protocol != nullptr) {
421 this->protocol_str = protocol;
422 }
423 }
424
425 // reads the final chunked reply headers
426 void readExt(Client& in) {
427 DlnaLogger.log(DlnaLogLevel::Debug, "HttpReplyHeader::readExt");
428 char line[MaxHeaderLineLength];
429 readLine(in, line, MaxHeaderLineLength);
430 while (strlen(line) != 0) {
431 put(line);
432 readLine(in, line, MaxHeaderLineLength);
433 }
434 }
435
436 // HTTP-Version SP Status-Code SP Reason-Phrase CRLF
437 void write1stLine(Client& out) {
438 char msg[200];
439 StrView msg_str(msg, 200);
440 msg_str = this->protocol_str.c_str();
441 msg_str += " ";
442 msg_str += this->status_code;
443 msg_str += " ";
444 msg_str += this->status_msg.c_str();
445 DlnaLogger.log(DlnaLogLevel::Debug, "HttpReplyHeader::write1stLine: %s", msg);
446 out.print(msg);
447 crlf(out);
448 }
449
450 // HTTP-Version SP Status-Code SP Reason-Phrase CRLF
451 // we just update the pointers to point to the correct position in the
452 // http_status_line
453 void parse1stLine(const char* line) {
454 DlnaLogger.log(DlnaLogLevel::Debug, "HttpReplyHeader::parse1stLine %s", line);
455 StrView line_str(line);
456 int space1 = line_str.indexOf(' ', 0);
457 int space2 = line_str.indexOf(' ', space1 + 1);
458
459 // save http version
460 protocol_str.substrView(line_str, 0, space1);
461
462 // find response status code after the first space
463 char status_c[6];
464 StrView status(status_c, 6);
465 status.substrView(line_str, space1 + 1, space2);
466 status_code = atoi(status_c);
467
468 // get reason-phrase after last SP
469 status_msg.substrView(line_str, space2 + 1, line_str.length());
470 }
471};
472
473} // namespace tiny_dlna
In a http request and reply we need to process header Debugrmation. With this API we can define and q...
Definition: HttpHeader.h:71
List< HttpHeaderLine * > lines
Definition: HttpHeader.h:300
virtual void parse1stLine(const char *line)=0
const char * statusMessage()
Definition: HttpHeader.h:225
void writeHeaderLine(Client &out, HttpHeaderLine *header)
Definition: HttpHeader.h:180
const char * accept()
Definition: HttpHeader.h:221
bool isRedirectStatus()
Definition: HttpHeader.h:286
void read(Client &in)
Definition: HttpHeader.h:233
HttpHeader & clear(bool activeFlag=true)
clears the data - usually we do not delete but we just set the active flag
Definition: HttpHeader.h:86
HttpHeaderLine * headerLine(const char *key)
Definition: HttpHeader.h:311
int statusCode()
Definition: HttpHeader.h:223
HttpHeader & put(const char *key, int value)
adds a new line to the header - e.g. for content size
Definition: HttpHeader.h:133
const char * CRLF
Definition: HttpHeader.h:302
TinyMethodID method()
Definition: HttpHeader.h:219
TinyMethodID method_id
Definition: HttpHeader.h:293
HttpHeader & put(const char *line)
adds a received new line to the header
Definition: HttpHeader.h:145
TinyMethodID getMethod(const char *line)
Definition: HttpHeader.h:338
const char * urlPath()
Definition: HttpHeader.h:215
void crlf(Client &out)
Definition: HttpHeader.h:305
HttpHeader()
Definition: HttpHeader.h:73
virtual void write1stLine(Client &out)=0
Str protocol_str
Definition: HttpHeader.h:297
~HttpHeader()
Definition: HttpHeader.h:80
const char * protocol()
Definition: HttpHeader.h:217
Str url_path
Definition: HttpHeader.h:298
bool is_written
Definition: HttpHeader.h:290
bool isChunked()
Definition: HttpHeader.h:227
void readLine(Client &in, char *str, int len)
Definition: HttpHeader.h:174
HttpLineReader reader
Definition: HttpHeader.h:301
int status_code
Definition: HttpHeader.h:289
bool isValidStatus()
returns true if status code >=200 and < 300
Definition: HttpHeader.h:284
HttpHeader & put(const char *key, const char *value)
Definition: HttpHeader.h:105
Str status_msg
Definition: HttpHeader.h:299
void setAutoCreateLines(bool is_auto_line)
Definition: HttpHeader.h:280
bool create_new_lines
Definition: HttpHeader.h:292
bool is_chunked
Definition: HttpHeader.h:291
void write(Client &out)
Definition: HttpHeader.h:267
const char * get(const char *key)
Definition: HttpHeader.h:161
We read a single line. A terminating 0 is added to the string to make it compliant for c string funct...
Definition: HttpLineReader.h:13
virtual int readlnInternal(Stream &client, uint8_t *str, int len, bool incl_nl=true)
Definition: HttpLineReader.h:18
Reading and Writing of Http Replys.
Definition: HttpHeader.h:412
void parse1stLine(const char *line)
Definition: HttpHeader.h:453
void write1stLine(Client &out)
Definition: HttpHeader.h:437
void setValues(int statusCode, const char *msg="", const char *protocol=nullptr)
Definition: HttpHeader.h:415
void readExt(Client &in)
Definition: HttpHeader.h:426
Reading and writing of Http Requests.
Definition: HttpHeader.h:358
void write1stLine(Client &out)
Definition: HttpHeader.h:375
void parse1stLine(const char *line)
Definition: HttpHeader.h:391
HttpHeader & setValues(TinyMethodID id, const char *urlPath, const char *protocol=nullptr)
Definition: HttpHeader.h:361
Double linked list.
Definition: List.h:19
A simple wrapper to provide string functions on char*. If the underlying char* is a const we do not a...
Definition: StrView.h:18
virtual void substrView(StrView &from, int start, int end)
copies a substring into the current string
Definition: StrView.h:505
virtual bool isEmpty()
checks if the string is empty
Definition: StrView.h:383
virtual int indexOf(const char c, int start=0)
Definition: StrView.h:274
virtual int length()
Definition: StrView.h:380
virtual void ltrim()
remove leading spaces
Definition: StrView.h:548
virtual bool isNewLine()
Definition: StrView.h:385
Heap-backed string utility used throughout tiny_dlna.
Definition: Str.h:27
bool equalsIgnoreCase(const char *other) const
Case-insensitive equality with C-string.
Definition: Str.h:183
void trim()
Trim spaces on both ends.
Definition: Str.h:235
void substrView(StrView &from, int start, int end)
Assign substring view from StrView [start,end)
Definition: Str.h:69
const char * c_str() const
C-string pointer to internal buffer.
Definition: Str.h:88
Definition: Allocator.h:13
const char * CONTENT_TYPE
Definition: HttpHeader.h:16
const char * IDENTITY
Definition: HttpHeader.h:31
const char * DEFAULT_AGENT
Definition: HttpHeader.h:27
const char * CON_KEEP_ALIVE
Definition: HttpHeader.h:20
const int MaxHeaderLineLength
Definition: HttpHeader.h:13
const char * methods[]
Definition: HttpHeader.h:50
const char * CONTENT_LENGTH
Definition: HttpHeader.h:17
const char * CON_CLOSE
Definition: HttpHeader.h:19
const char * HOST_C
Definition: HttpHeader.h:29
TinyMethodID
Definition: HttpHeader.h:35
@ T_TRACE
Definition: HttpHeader.h:42
@ T_DELETE
Definition: HttpHeader.h:41
@ T_CONNECT
Definition: HttpHeader.h:44
@ T_UNSUBSCRIBE
Definition: HttpHeader.h:46
@ T_UNDEFINED
Definition: HttpHeader.h:36
@ T_OPTIONS
Definition: HttpHeader.h:43
@ T_SUBSCRIBE
Definition: HttpHeader.h:47
@ T_HEAD
Definition: HttpHeader.h:38
@ T_GET
Definition: HttpHeader.h:37
@ T_PUT
Definition: HttpHeader.h:40
@ T_POST
Definition: HttpHeader.h:39
@ T_PATCH
Definition: HttpHeader.h:45
@ T_NOTIFY
Definition: HttpHeader.h:48
const char * TRANSFER_ENCODING
Definition: HttpHeader.h:21
const char * SUCCESS
Definition: HttpHeader.h:25
const char * ACCEPT
Definition: HttpHeader.h:23
const char * ACCEPT_ENCODING
Definition: HttpHeader.h:30
const char * CHUNKED
Definition: HttpHeader.h:22
const char * ACCEPT_ALL
Definition: HttpHeader.h:24
const char * USER_AGENT
Definition: HttpHeader.h:26
const char * CONNECTION
Definition: HttpHeader.h:18
const char * LOCATION
Definition: HttpHeader.h:32
A individual key - value header line.
Definition: HttpHeader.h:58
bool active
Definition: HttpHeader.h:61
Str key
Definition: HttpHeader.h:59
Str value
Definition: HttpHeader.h:60