Skip to content

Commit 7d911b9

Browse files
authored
Handle large octet-stream (#9430)
* Handle large octet-stream * Add exemple Upload Huge File * Remove unuse function printDirectory * Fix upload path * Simplify and generalize the body parsing.
1 parent c2a8e25 commit 7d911b9

File tree

7 files changed

+157
-1
lines changed

7 files changed

+157
-1
lines changed
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Upload Huge File To SD Over Http
2+
3+
This project is an example of an HTTP server designed to facilitate the transfer of large files using the PUT method, in accordance with RFC specifications.
4+
5+
### Example cURL Command
6+
7+
```bash
8+
curl -X PUT -T ./my-file.mp3 http://esp-ip/upload/my-file.mp3
9+
```
10+
11+
## Resources
12+
13+
- RFC HTTP/1.0 - Additional Request Methods - PUT : [Link](https://datatracker.ietf.org/doc/html/rfc1945#appendix-D.1.1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#include <WiFi.h>
2+
#include <WiFiClient.h>
3+
#include <WebServer.h>
4+
#include <uri/UriRegex.h>
5+
#include <SD.h>
6+
7+
const char* ssid = "**********";
8+
const char* password = "**********";
9+
10+
WebServer server(80);
11+
12+
File rawFile;
13+
void handleCreate() {
14+
server.send(200, "text/plain", "");
15+
}
16+
void handleCreateProcess() {
17+
String path = "/" + server.pathArg(0);
18+
HTTPRaw& raw = server.raw();
19+
if (raw.status == RAW_START) {
20+
if (SD.exists((char *)path.c_str())) {
21+
SD.remove((char *)path.c_str());
22+
}
23+
rawFile = SD.open(path.c_str(), FILE_WRITE);
24+
Serial.print("Upload: START, filename: ");
25+
Serial.println(path);
26+
} else if (raw.status == RAW_WRITE) {
27+
if (rawFile) {
28+
rawFile.write(raw.buf, raw.currentSize);
29+
}
30+
Serial.print("Upload: WRITE, Bytes: ");
31+
Serial.println(raw.currentSize);
32+
} else if (raw.status == RAW_END) {
33+
if (rawFile) {
34+
rawFile.close();
35+
}
36+
Serial.print("Upload: END, Size: ");
37+
Serial.println(raw.totalSize);
38+
}
39+
}
40+
41+
void returnFail(String msg) {
42+
server.send(500, "text/plain", msg + "\r\n");
43+
}
44+
45+
void handleNotFound() {
46+
String message = "File Not Found\n\n";
47+
message += "URI: ";
48+
message += server.uri();
49+
message += "\nMethod: ";
50+
message += (server.method() == HTTP_GET) ? "GET" : "POST";
51+
message += "\nArguments: ";
52+
message += server.args();
53+
message += "\n";
54+
for (uint8_t i = 0; i < server.args(); i++) {
55+
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
56+
}
57+
server.send(404, "text/plain", message);
58+
}
59+
60+
void setup(void) {
61+
Serial.begin(115200);
62+
63+
while (!SD.begin()) delay(1);
64+
Serial.println("SD Card initialized.");
65+
66+
WiFi.mode(WIFI_STA);
67+
WiFi.begin(ssid, password);
68+
69+
while (WiFi.status() != WL_CONNECTED) {
70+
delay(500);
71+
Serial.print(".");
72+
}
73+
Serial.print("Connected to ");
74+
Serial.println(ssid);
75+
Serial.print("IP address: ");
76+
Serial.println(WiFi.localIP());
77+
78+
server.on(UriRegex("/upload/(.*)"), HTTP_PUT, handleCreate, handleCreateProcess);
79+
server.onNotFound(handleNotFound);
80+
server.begin();
81+
Serial.println("HTTP server started");
82+
83+
}
84+
85+
void loop(void) {
86+
server.handleClient();
87+
delay(2);//allow the cpu to switch to other tasks
88+
}

Diff for: libraries/WebServer/src/Parsing.cpp

+24-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,30 @@ bool WebServer::_parseRequest(WiFiClient& client) {
173173
}
174174
}
175175

176-
if (!isForm){
176+
if (!isForm && _currentHandler && _currentHandler->canRaw(_currentUri)){
177+
log_v("Parse raw");
178+
_currentRaw.reset(new HTTPRaw());
179+
_currentRaw->status = RAW_START;
180+
_currentRaw->totalSize = 0;
181+
_currentRaw->currentSize = 0;
182+
log_v("Start Raw");
183+
_currentHandler->raw(*this, _currentUri, *_currentRaw);
184+
_currentRaw->status = RAW_WRITE;
185+
186+
while (_currentRaw->totalSize < _clientContentLength) {
187+
_currentRaw->currentSize = client.readBytes(_currentRaw->buf, HTTP_RAW_BUFLEN);
188+
_currentRaw->totalSize += _currentRaw->currentSize;
189+
if (_currentRaw->currentSize == 0) {
190+
_currentRaw->status = RAW_ABORTED;
191+
_currentHandler->raw(*this, _currentUri, *_currentRaw);
192+
return false;
193+
}
194+
_currentHandler->raw(*this, _currentUri, *_currentRaw);
195+
}
196+
_currentRaw->status = RAW_END;
197+
_currentHandler->raw(*this, _currentUri, *_currentRaw);
198+
log_v("Finish Raw");
199+
} else if (!isForm) {
177200
size_t plainLength;
178201
char* plainBuf = readBytesWithTimeout(client, _clientContentLength, plainLength, HTTP_MAX_POST_WAIT);
179202
if (plainLength < _clientContentLength) {

Diff for: libraries/WebServer/src/WebServer.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ void WebServer::handleClient() {
333333
_currentClient = WiFiClient();
334334
_currentStatus = HC_NONE;
335335
_currentUpload.reset();
336+
_currentRaw.reset();
336337
}
337338

338339
if (callYield) {

Diff for: libraries/WebServer/src/WebServer.h

+16
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
3434
UPLOAD_FILE_ABORTED };
35+
enum HTTPRawStatus { RAW_START, RAW_WRITE, RAW_END, RAW_ABORTED };
3536
enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
3637
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
3738

@@ -41,6 +42,10 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
4142
#define HTTP_UPLOAD_BUFLEN 1436
4243
#endif
4344

45+
#ifndef HTTP_RAW_BUFLEN
46+
#define HTTP_RAW_BUFLEN 1436
47+
#endif
48+
4449
#define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request
4550
#define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive
4651
#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
@@ -61,6 +66,15 @@ typedef struct {
6166
uint8_t buf[HTTP_UPLOAD_BUFLEN];
6267
} HTTPUpload;
6368

69+
typedef struct
70+
{
71+
HTTPRawStatus status;
72+
size_t totalSize; // content size
73+
size_t currentSize; // size of data currently in buf
74+
uint8_t buf[HTTP_UPLOAD_BUFLEN];
75+
void *data; // additional data
76+
} HTTPRaw;
77+
6478
#include "detail/RequestHandler.h"
6579

6680
namespace fs {
@@ -97,6 +111,7 @@ class WebServer
97111
HTTPMethod method() { return _currentMethod; }
98112
virtual WiFiClient client() { return _currentClient; }
99113
HTTPUpload& upload() { return *_currentUpload; }
114+
HTTPRaw& raw() { return *_currentRaw; }
100115

101116
String pathArg(unsigned int i); // get request path argument by number
102117
String arg(String name); // get request argument value by name
@@ -196,6 +211,7 @@ class WebServer
196211
RequestArgument* _postArgs;
197212

198213
std::unique_ptr<HTTPUpload> _currentUpload;
214+
std::unique_ptr<HTTPRaw> _currentRaw;
199215

200216
int _headerKeysCount;
201217
RequestArgument* _currentHeaders;

Diff for: libraries/WebServer/src/detail/RequestHandler.h

+2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ class RequestHandler {
99
virtual ~RequestHandler() { }
1010
virtual bool canHandle(HTTPMethod method, String uri) { (void) method; (void) uri; return false; }
1111
virtual bool canUpload(String uri) { (void) uri; return false; }
12+
virtual bool canRaw(String uri) { (void) uri; return false; }
1213
virtual bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; }
1314
virtual void upload(WebServer& server, String requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; }
15+
virtual void raw(WebServer& server, String requestUri, HTTPRaw& raw) { (void) server; (void) requestUri; (void) raw; }
1416

1517
RequestHandler* next() { return _next; }
1618
void next(RequestHandler* r) { _next = r; }

Diff for: libraries/WebServer/src/detail/RequestHandlersImpl.h

+13
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ class FunctionRequestHandler : public RequestHandler {
3636

3737
return true;
3838
}
39+
bool canRaw(String requestUri) override {
40+
if (!_ufn || _method == HTTP_GET)
41+
return false;
42+
43+
return true;
44+
}
3945

4046
bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
4147
(void) server;
@@ -53,6 +59,13 @@ class FunctionRequestHandler : public RequestHandler {
5359
_ufn();
5460
}
5561

62+
void raw(WebServer& server, String requestUri, HTTPRaw& raw) override {
63+
(void)server;
64+
(void)raw;
65+
if (canRaw(requestUri))
66+
_ufn();
67+
}
68+
5669
protected:
5770
WebServer::THandlerFunction _fn;
5871
WebServer::THandlerFunction _ufn;

0 commit comments

Comments
 (0)