Arduino DLNA Server
Loading...
Searching...
No Matches
HttpRequest.h
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4
5#include "HttpChunkReader.h"
6#include "HttpChunkWriter.h"
7#include "HttpHeader.h"
8#include "IHttpRequest.h"
9#include "basic/StrPrint.h"
10#ifdef ESP32
11#include <WiFi.h>
12#include <lwip/sockets.h>
13#endif
14
15namespace tiny_dlna {
16
30template <typename ClientType>
31class HttpRequest : public IHttpRequest {
32 public:
35 DlnaLogger.log(DlnaLogLevel::Debug, "HttpRequest (default client)");
37 }
38
40 explicit HttpRequest(ClientType& client) {
41 DlnaLogger.log(DlnaLogLevel::Debug, "HttpRequest");
43 }
44
46 ~HttpRequest() override { stop(); }
47
49 void setClient(Client& client) override { this->client_ptr = &client; }
50
52 void setHost(const char* host) override {
53 DlnaLogger.log(DlnaLogLevel::Info, "HttpRequest::setHost: ", host);
54 this->host_name = host;
55 }
56
58 operator bool() override {
59 return client_ptr != nullptr && static_cast<bool>(*client_ptr);
60 }
61
63 bool connected() override { return client_ptr->connected(); }
64
66 int available() override {
67 if (reply_header.isChunked()) {
68 return chunk_reader.available();
69 }
70 return client_ptr->available();
71 }
72
74 void stop() override {
75 DlnaLogger.log(DlnaLogLevel::Info, "HttpRequest::stop");
76 if (client_ptr != nullptr) {
77 client_ptr->stop();
78 // delay(300);
79 }
80 }
81
83 int post(Url& url, const char* mime, const char* data,
84 int len = -1) override {
85 DlnaLogger.log(DlnaLogLevel::Info, "post %s", url.url());
86 return process(T_POST, url, mime, data, len);
87 }
88
90 int post(Url& url, size_t len, std::function<size_t(Print&, void*)> writer,
91 const char* mime = nullptr, void* ref = nullptr) override {
92 return process(T_POST, url, len, writer, mime, ref);
93 }
94
96 int notify(Url& url, std::function<size_t(Print&, void*)> writer,
97 const char* mime = nullptr, void* ref = nullptr) override {
98 NullPrint nop;
99 int len = writer(nop, ref);
100 return process(T_NOTIFY, url, len, writer, mime, ref);
101 }
102
104 int put(Url& url, const char* mime, const char* data, int len = -1) override {
105 DlnaLogger.log(DlnaLogLevel::Info, "put %s", url.url());
106 return process(T_PUT, url, mime, data, len);
107 }
108
110 int del(Url& url, const char* mime = nullptr, const char* data = nullptr,
111 int len = -1) override {
112 DlnaLogger.log(DlnaLogLevel::Info, "del %s", url.url());
113 return process(T_DELETE, url, mime, data, len);
114 }
115
117 int get(Url& url, const char* acceptMime = nullptr,
118 const char* data = nullptr, int len = -1) override {
119 DlnaLogger.log(DlnaLogLevel::Info, "get %s", str(url.url()));
120 this->accept = acceptMime;
121 return process(T_GET, url, nullptr, data, len);
122 }
123
125 int head(Url& url, const char* acceptMime = nullptr,
126 const char* data = nullptr, int len = -1) override {
127 DlnaLogger.log(DlnaLogLevel::Info, "head %s", url.url());
128 this->accept = acceptMime;
129 return process(T_HEAD, url, nullptr, data, len);
130 }
131
133 int subscribe(Url& url) override {
134 DlnaLogger.log(DlnaLogLevel::Info, "%s %s", methods[T_SUBSCRIBE], url.path());
135 return process(T_SUBSCRIBE, url, nullptr, nullptr, 0);
136 }
137
139 int unsubscribe(Url& url, const char* sid) override {
140 DlnaLogger.log(DlnaLogLevel::Info, "%s %s (SID=%s)", methods[T_UNSUBSCRIBE],
141 url.path(), sid);
142 if (sid != nullptr) {
143 request_header.put("SID", sid);
144 }
145 return process(T_UNSUBSCRIBE, url, nullptr, nullptr, 0);
146 }
147
149 int read(uint8_t* str, int len) override {
150 if (reply_header.isChunked()) {
151 return chunk_reader.read(*client_ptr, str, len);
152 } else {
153 return client_ptr->read(str, len);
154 }
155 }
156
158 int readln(uint8_t* str, int len, bool incl_nl = true) override {
159 if (reply_header.isChunked()) {
160 return chunk_reader.readln(*client_ptr, str, len);
161 } else {
162 return chunk_reader.readlnInternal(*client_ptr, str, len, incl_nl);
163 }
164 }
165
167 HttpReplyHeader& reply() override { return reply_header; }
168
171
173 void setAgent(const char* agent) override { this->agent = agent; }
174
176 void setConnection(const char* connection) override {
177 this->connection = connection;
178 }
179
181 void setAcceptsEncoding(const char* enc) override {
182 this->accept_encoding = enc;
183 }
184
186 Client* client() override { return client_ptr; }
187
189 void setTimeout(int ms) override { client_ptr->setTimeout(ms); }
190
192
193 protected:
194 template <typename T>
195 static auto clearIfSupported(T* client, int) -> decltype(client->clear(), void()) {
196 client->clear();
197 }
198
199 template <typename T>
200 static void clearIfSupported(T*, long) {}
201
202 template <typename T>
203 static auto setNoDelayIfSupported(T* client, bool value, int)
204 -> decltype(client->setNoDelay(value), void()) {
205 client->setNoDelay(value);
206 }
207
208 template <typename T>
209 static void setNoDelayIfSupported(T*, bool, long) {}
210
211#ifdef ESP32
212 template <typename T>
213 static auto setSocketReuseAddrIfSupported(T* client, int)
214 -> decltype(client->setSocketOption(SOL_SOCKET, SO_REUSEADDR,
215 (const void*)nullptr,
216 (socklen_t)0),
217 void()) {
218 int enable = 1;
219 client->setSocketOption(SOL_SOCKET, SO_REUSEADDR, &enable,
220 sizeof(enable));
221 }
222
223 template <typename T>
224 static void setSocketReuseAddrIfSupported(T*, long) {}
225#endif
226
227 ClientType default_client;
228 Client* client_ptr = nullptr;
234 const char* agent = nullptr;
235 const char* connection = CON_CLOSE;
236 const char* accept = ACCEPT_ALL;
237 const char* accept_encoding = nullptr;
238
240 const char* str(const char* in) { return in == nullptr ? "" : in; }
241
243 virtual int connect(const char* ip, uint16_t port) {
244 this->client_ptr->setTimeout(DLNA_HTTP_REQUEST_TIMEOUT_MS);
245 if (!isKeepAlive() && this->client_ptr->connected()) {
246 stop();
247 }
248#ifdef ESP32
249 // clear input buffer
250 clearIfSupported(static_cast<ClientType*>(client_ptr), 0);
251 // static_cast<ClientType*>(client_ptr)
252 // ->setConnectionTimeout(DLNA_HTTP_REQUEST_TIMEOUT_MS);
253 setNoDelayIfSupported(static_cast<ClientType*>(client_ptr), true, 0);
254 setSocketReuseAddrIfSupported(static_cast<ClientType*>(client_ptr), 0);
255#endif
256 DlnaLogger.log(DlnaLogLevel::Info, "HttpRequest::connect %s:%d", ip, port);
257 uint64_t end = millis() + client_ptr->getTimeout();
258 bool rc = false;
259 for (int j = 0; j < 3; j++) {
260 rc = this->client_ptr->connect(ip, port);
261 if (rc) break;
262 delay(200);
263 }
264 if (!connected()) {
265 DlnaLogger.log(
266 DlnaLogLevel::Error, "Connected: %s (rc=%d) with timeout %ld",
267 connected() ? "true" : "false", rc, client_ptr->getTimeout());
268 } else {
269 DlnaLogger.log(
270 DlnaLogLevel::Debug, "Connected: %s (rc=%d) with timeout %ld",
271 connected() ? "true" : "false", rc, client_ptr->getTimeout());
272 }
273 return rc;
274 }
275
277 virtual int process(TinyMethodID action, Url& url, const char* mime,
278 const char* data, int len = -1) {
279 if (!connected()) {
280 DlnaLogger.log(DlnaLogLevel::Info, "Connecting to host %s port %d",
281 url.host(), url.port());
282
283 connect(url.host(), url.port());
284 }
285
286 if (!connected()) {
287 DlnaLogger.log(DlnaLogLevel::Info, "Connected: %s",
288 connected() ? "true" : "false");
289 return -1;
290 }
291
292 if (host_name.isEmpty()) {
293 host_name = url.host();
294 host_name.add(":");
296 }
297
298 request_header.setValues(action, url.path());
299 if (len == -1 && data != nullptr) {
300 len = strlen(data);
301 }
302 if (len > 0) {
304 }
305 if (!host_name.isEmpty()) {
307 }
308 if (agent != nullptr) {
310 }
311 if (accept_encoding != nullptr) {
313 }
314 if (mime != nullptr) {
316 }
317
320
322
323 if (len > 0) {
324 DlnaLogger.log(DlnaLogLevel::Info, "process - writing data: %d bytes",
325 len);
326 client_ptr->write((const uint8_t*)data, len);
327 }
328
330
331 // if we use chunked tranfer we need to read the first chunked length
332 if (reply_header.isChunked()) {
334 };
335
336 return reply_header.statusCode();
337 }
338
341 virtual int process(TinyMethodID method, Url& url, size_t len,
342 std::function<size_t(Print&, void*)> writer,
343 const char* mime = nullptr, void* ref = nullptr) {
344 DlnaLogger.log(DlnaLogLevel::Info, "%s %s", methods[method], url.url());
345
346 if (!connected()) {
347 connect(url.host(), url.port());
348 }
349
350 if (!connected()) {
351 DlnaLogger.log(DlnaLogLevel::Info, "Connected: %s",
352 connected() ? "true" : "false");
353 return -1;
354 }
355
356 if (host_name.isEmpty()) {
357 host_name = url.host();
358 host_name.add(":");
360 }
361
362 // prepare request header
363 request_header.setValues(method, url.path());
364 if (!host_name.isEmpty()) {
366 }
367 if (agent != nullptr) request_header.put(USER_AGENT, agent);
368 if (accept_encoding != nullptr)
370 if (mime != nullptr) request_header.put(CONTENT_TYPE, mime);
371 if (len > 0) request_header.put(CONTENT_LENGTH, len);
372
375
376 // write request header to client
378
379 // write callback (writer returns number of bytes written)
380 if (writer) {
381 size_t written = writer(*client_ptr, ref);
382#if DLNA_LOG_XML
383 writer(Serial, ref);
384#endif
385 if (written != len) {
386 DlnaLogger.log(DlnaLogLevel::Error,
387 "HttpRequest wrote %d bytes: expected %d", written, len);
388 }
389#if DLNA_CHECK_XML_LENGTH
390 StrPrint test;
391 size_t test_len = writer(test, ref);
392 if (strlen(test.c_str()) != len) {
393 DlnaLogger.log(
395 "HttpRequest test wrote %d bytes: expected %d / strlen: %d",
396 test_len, len, strlen(test.c_str()));
397 }
398#endif
399 }
400
401 // read reply header and prepare chunk reader if needed
403 if (reply_header.isChunked()) {
405 }
406
407 return reply_header.statusCode();
408 }
409};
410
411} // namespace tiny_dlna
412
413// using ma = tiny_dlna::HttpRequest;
Http might reply with chunks. So we need to dechunk the data. see https://en.wikipedia....
Definition: HttpChunkReader.h:13
void open(Client &client)
Definition: HttpChunkReader.h:28
virtual int read(Client &client, uint8_t *str, int len)
Definition: HttpChunkReader.h:35
virtual int readln(Client &client, uint8_t *str, int len, bool incl_nl=true)
Definition: HttpChunkReader.h:55
int available()
Definition: HttpChunkReader.h:73
void read(Client &in)
Definition: HttpHeader.h:233
int statusCode()
Definition: HttpHeader.h:223
bool isChunked()
Definition: HttpHeader.h:227
HttpHeader & put(const char *key, const char *value)
Definition: HttpHeader.h:105
void write(Client &out)
Definition: HttpHeader.h:267
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:409
Reading and writing of Http Requests.
Definition: HttpHeader.h:355
HttpHeader & setValues(TinyMethodID id, const char *urlPath, const char *protocol=nullptr)
Definition: HttpHeader.h:358
Simple API to process get, put, post, del http requests I tried to use Arduino HttpClient,...
Definition: HttpRequest.h:31
Client * client_ptr
Definition: HttpRequest.h:228
void setConnection(const char *connection) override
Sets the connection type.
Definition: HttpRequest.h:176
static auto setNoDelayIfSupported(T *client, bool value, int) -> decltype(client->setNoDelay(value), void())
Definition: HttpRequest.h:203
HttpChunkReader chunk_reader
Definition: HttpRequest.h:232
HttpRequestHeader & request() override
Returns the request header.
Definition: HttpRequest.h:170
void stop() override
Stops the connection.
Definition: HttpRequest.h:74
HttpRequest()
Default constructor.
Definition: HttpRequest.h:34
const char * accept
Definition: HttpRequest.h:236
const char * str(const char *in)
Returns an empty string if input is null, otherwise returns the input.
Definition: HttpRequest.h:240
~HttpRequest() override
Destructor.
Definition: HttpRequest.h:46
HttpRequestHeader request_header
Definition: HttpRequest.h:230
void setHost(const char *host) override
Sets the host name for the requests.
Definition: HttpRequest.h:52
HttpReplyHeader & reply() override
Returns the reply header.
Definition: HttpRequest.h:167
HttpReplyHeader reply_header
Definition: HttpRequest.h:231
const char * connection
Definition: HttpRequest.h:235
int read(uint8_t *str, int len) override
Reads reply data.
Definition: HttpRequest.h:149
static void clearIfSupported(T *, long)
Definition: HttpRequest.h:200
int unsubscribe(Url &url, const char *sid) override
Sends an UNSUBSCRIBE request.
Definition: HttpRequest.h:139
Url url
Definition: HttpRequest.h:229
int put(Url &url, const char *mime, const char *data, int len=-1) override
Sends a PUT request.
Definition: HttpRequest.h:104
void setAcceptsEncoding(const char *enc) override
Sets the accepted encodings.
Definition: HttpRequest.h:181
void setClient(Client &client) override
Sets the client to be used for the requests.
Definition: HttpRequest.h:49
int available() override
Returns the number of available bytes to read.
Definition: HttpRequest.h:66
ClientType default_client
Definition: HttpRequest.h:227
virtual int process(TinyMethodID method, Url &url, size_t len, std::function< size_t(Print &, void *)> writer, const char *mime=nullptr, void *ref=nullptr)
Definition: HttpRequest.h:341
bool connected() override
Checks if connected to the server.
Definition: HttpRequest.h:63
virtual int connect(const char *ip, uint16_t port)
opens a connection to the indicated host
Definition: HttpRequest.h:243
static void setNoDelayIfSupported(T *, bool, long)
Definition: HttpRequest.h:209
int post(Url &url, size_t len, std::function< size_t(Print &, void *)> writer, const char *mime=nullptr, void *ref=nullptr) override
Sends a POST request with streaming body.
Definition: HttpRequest.h:90
const char * accept_encoding
Definition: HttpRequest.h:237
int notify(Url &url, std::function< size_t(Print &, void *)> writer, const char *mime=nullptr, void *ref=nullptr) override
Sends a NOTIFY request with streaming body.
Definition: HttpRequest.h:96
int post(Url &url, const char *mime, const char *data, int len=-1) override
Sends a POST request with data.
Definition: HttpRequest.h:83
bool isKeepAlive()
Do not close the connection after request.
Definition: HttpRequest.h:191
int get(Url &url, const char *acceptMime=nullptr, const char *data=nullptr, int len=-1) override
Sends a GET request.
Definition: HttpRequest.h:117
HttpRequest(ClientType &client)
Constructor with specified client.
Definition: HttpRequest.h:40
Client * client() override
Returns the client pointer.
Definition: HttpRequest.h:186
const char * agent
Definition: HttpRequest.h:234
int del(Url &url, const char *mime=nullptr, const char *data=nullptr, int len=-1) override
Sends a DELETE request.
Definition: HttpRequest.h:110
int head(Url &url, const char *acceptMime=nullptr, const char *data=nullptr, int len=-1) override
Sends a HEAD request.
Definition: HttpRequest.h:125
int readln(uint8_t *str, int len, bool incl_nl=true) override
Reads reply data up to the next new line.
Definition: HttpRequest.h:158
Str host_name
Definition: HttpRequest.h:233
virtual int process(TinyMethodID action, Url &url, const char *mime, const char *data, int len=-1)
sends request and reads the reply_header from the server
Definition: HttpRequest.h:277
void setTimeout(int ms) override
Sets the timeout.
Definition: HttpRequest.h:189
int subscribe(Url &url) override
Sends a SUBSCRIBE request.
Definition: HttpRequest.h:133
static auto clearIfSupported(T *client, int) -> decltype(client->clear(), void())
Definition: HttpRequest.h:195
void setAgent(const char *agent) override
Sets the user agent.
Definition: HttpRequest.h:173
Abstract interface for HTTP client request functionality.
Definition: IHttpRequest.h:21
Class with does not do any output: it can be used to determine the length of the output.
Definition: NullPrint.h:12
Print to a dynamic string.
Definition: StrPrint.h:13
const char * c_str()
Definition: StrPrint.h:39
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 bool equals(const char *str)
checks if the string equals indicated parameter string
Definition: StrView.h:177
Heap-backed string utility used throughout tiny_dlna.
Definition: Str.h:27
bool isEmpty() const
True if empty.
Definition: Str.h:54
void add(const char *append)
Append C-string (ignored if nullptr)
Definition: Str.h:96
const char * c_str() const
C-string pointer to internal buffer.
Definition: Str.h:88
URL parser which breaks a full url string up into its individual parts.
Definition: Url.h:18
int port()
Definition: Url.h:46
const char * host()
Definition: Url.h:41
const char * url()
Definition: Url.h:39
const char * path()
Definition: Url.h:40
#define DLNA_HTTP_REQUEST_TIMEOUT_MS
Define the default http request timeout.
Definition: dlna_config.h:25
Definition: Allocator.h:13
const char * CONTENT_TYPE
Definition: HttpHeader.h:16
const char * CON_KEEP_ALIVE
Definition: HttpHeader.h:20
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_DELETE
Definition: HttpHeader.h:41
@ T_UNSUBSCRIBE
Definition: HttpHeader.h:46
@ 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_NOTIFY
Definition: HttpHeader.h:48
const char * ACCEPT
Definition: HttpHeader.h:23
const char * ACCEPT_ENCODING
Definition: HttpHeader.h:30
const char * ACCEPT_ALL
Definition: HttpHeader.h:24
const char * USER_AGENT
Definition: HttpHeader.h:26
const char * CONNECTION
Definition: HttpHeader.h:18