Skip to content

Commit 233dad0

Browse files
committed
Reduce code duplications in _parseRequest
This function had two loops for parsing headers, one used for GET like requests and one for POST like request. we can combine them in one, which reduces PROGMEM footprint by over 250 bytes.
1 parent 709ba79 commit 233dad0

File tree

1 file changed

+149
-182
lines changed

1 file changed

+149
-182
lines changed

libraries/ESP8266WebServer/src/Parsing-impl.h

Lines changed: 149 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
#endif
3838

3939
static const char Content_Type[] PROGMEM = "Content-Type";
40-
static const char filename[] PROGMEM = "filename";
4140

4241
template <typename ServerType>
4342
static bool readBytesWithTimeout(typename ServerType::ClientType& client, size_t maxLength, String& data, int timeout_ms)
@@ -62,216 +61,183 @@ static bool readBytesWithTimeout(typename ServerType::ClientType& client, size_t
6261

6362
template <typename ServerType>
6463
bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
65-
// Read the first line of HTTP request
66-
String req = client.readStringUntil('\r');
64+
// Read the first line of HTTP request
65+
String req = client.readStringUntil('\r');
6766
#ifdef DEBUG_ESP_HTTP_SERVER
6867
DEBUG_OUTPUT.print("request: ");
6968
DEBUG_OUTPUT.println(req);
7069
#endif
71-
client.readStringUntil('\n');
72-
//reset header value
73-
for (int i = 0; i < _headerKeysCount; ++i) {
74-
_currentHeaders[i].value =String();
75-
}
76-
77-
// First line of HTTP request looks like "GET /path HTTP/1.1"
78-
// Retrieve the "/path" part by finding the spaces
79-
int addr_start = req.indexOf(' ');
80-
int addr_end = req.indexOf(' ', addr_start + 1);
81-
if (addr_start == -1 || addr_end == -1) {
70+
client.readStringUntil('\n');
71+
//reset header value
72+
for (size_t i = 0; i < _headerKeysCount; ++i) {
73+
_currentHeaders[i].value.clear();
74+
}
75+
76+
// First line of HTTP request looks like "GET /path HTTP/1.1"
77+
// Retrieve the "/path" part by finding the spaces
78+
int addr_start = req.indexOf(' ');
79+
int addr_end = req.indexOf(' ', addr_start + 1);
80+
if (addr_start == -1 || addr_end == -1) {
8281
#ifdef DEBUG_ESP_HTTP_SERVER
83-
DEBUG_OUTPUT.println("Invalid request");
82+
DEBUG_OUTPUT.println("Invalid request");
8483
#endif
85-
return false;
86-
}
84+
return false;
85+
}
8786

88-
String methodStr = req.substring(0, addr_start);
89-
String url = req.substring(addr_start + 1, addr_end);
90-
String versionEnd = req.substring(addr_end + 8);
91-
_currentVersion = atoi(versionEnd.c_str());
92-
String searchStr;
93-
int hasSearch = url.indexOf('?');
94-
if (hasSearch != -1){
95-
searchStr = url.substring(hasSearch + 1);
96-
url = url.substring(0, hasSearch);
97-
}
98-
_currentUri = url;
99-
_chunked = false;
100-
101-
HTTPMethod method = HTTP_GET;
102-
if (methodStr == F("HEAD")) {
103-
method = HTTP_HEAD;
104-
} else if (methodStr == F("POST")) {
105-
method = HTTP_POST;
106-
} else if (methodStr == F("DELETE")) {
107-
method = HTTP_DELETE;
108-
} else if (methodStr == F("OPTIONS")) {
109-
method = HTTP_OPTIONS;
110-
} else if (methodStr == F("PUT")) {
111-
method = HTTP_PUT;
112-
} else if (methodStr == F("PATCH")) {
113-
method = HTTP_PATCH;
114-
}
115-
_currentMethod = method;
87+
String methodStr = req.substring(0, addr_start);
88+
String url = req.substring(addr_start + 1, addr_end);
89+
_currentVersion = req.substring(addr_end + 8).toInt();
90+
String searchStr;
91+
int hasSearch = url.indexOf('?');
92+
if (hasSearch != -1) {
93+
searchStr = url.substring(hasSearch + 1);
94+
url = url.substring(0, hasSearch);
95+
}
96+
_currentUri = url;
97+
_chunked = false;
98+
99+
HTTPMethod method = HTTP_GET;
100+
if (methodStr == F("HEAD")) {
101+
method = HTTP_HEAD;
102+
} else if (methodStr == F("POST")) {
103+
method = HTTP_POST;
104+
} else if (methodStr == F("DELETE")) {
105+
method = HTTP_DELETE;
106+
} else if (methodStr == F("OPTIONS")) {
107+
method = HTTP_OPTIONS;
108+
} else if (methodStr == F("PUT")) {
109+
method = HTTP_PUT;
110+
} else if (methodStr == F("PATCH")) {
111+
method = HTTP_PATCH;
112+
}
113+
_currentMethod = method;
116114

117115
#ifdef DEBUG_ESP_HTTP_SERVER
118-
DEBUG_OUTPUT.print("method: ");
119-
DEBUG_OUTPUT.print(methodStr);
120-
DEBUG_OUTPUT.print(" url: ");
121-
DEBUG_OUTPUT.print(url);
122-
DEBUG_OUTPUT.print(" search: ");
123-
DEBUG_OUTPUT.println(searchStr);
116+
DEBUG_OUTPUT.print("method: ");
117+
DEBUG_OUTPUT.print(methodStr);
118+
DEBUG_OUTPUT.print(" url: ");
119+
DEBUG_OUTPUT.print(url);
120+
DEBUG_OUTPUT.print(" search: ");
121+
DEBUG_OUTPUT.println(searchStr);
124122
#endif
125123

126-
//attach handler
127-
RequestHandlerType* handler;
128-
for (handler = _firstHandler; handler; handler = handler->next()) {
129-
if (handler->canHandle(_currentMethod, _currentUri))
130-
break;
131-
}
132-
_currentHandler = handler;
124+
//attach handler
125+
RequestHandlerType* handler;
126+
for (handler = _firstHandler; handler; handler = handler->next()) {
127+
if (handler->canHandle(_currentMethod, _currentUri))
128+
break;
129+
}
130+
_currentHandler = handler;
133131

134-
String formData;
135-
// below is needed only when POST type request
136-
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
137-
String boundaryStr;
138132
String headerName;
139133
String headerValue;
134+
String boundaryStr;
140135
bool isForm = false;
141136
bool isEncoded = false;
142137
uint32_t contentLength = 0;
143-
//parse headers
144-
while(1){
145-
req = client.readStringUntil('\r');
146-
client.readStringUntil('\n');
147-
if (req.isEmpty()) break;//no moar headers
148-
int headerDiv = req.indexOf(':');
149-
if (headerDiv == -1){
150-
break;
151-
}
152-
headerName = req.substring(0, headerDiv);
153-
headerValue = req.substring(headerDiv + 1);
154-
headerValue.trim();
155-
_collectHeader(headerName.c_str(),headerValue.c_str());
156-
157-
#ifdef DEBUG_ESP_HTTP_SERVER
158-
DEBUG_OUTPUT.print("headerName: ");
159-
DEBUG_OUTPUT.println(headerName);
160-
DEBUG_OUTPUT.print("headerValue: ");
161-
DEBUG_OUTPUT.println(headerValue);
162-
#endif
163-
164-
if (headerName.equalsIgnoreCase(FPSTR(Content_Type))){
165-
using namespace mime;
166-
if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))){
167-
isForm = false;
168-
} else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){
169-
isForm = false;
170-
isEncoded = true;
171-
} else if (headerValue.startsWith(F("multipart/"))){
172-
boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1);
173-
boundaryStr.replace("\"","");
174-
isForm = true;
175-
}
176-
} else if (headerName.equalsIgnoreCase(F("Content-Length"))){
177-
contentLength = headerValue.toInt();
178-
} else if (headerName.equalsIgnoreCase(F("Host"))){
179-
_hostHeader = headerValue;
180-
}
181-
}
138+
// parse headers
139+
while (true) {
140+
req = client.readStringUntil('\r');
141+
client.readStringUntil('\n');
182142

183-
String plainBuf;
184-
if ( !isForm
185-
&& // read content into plainBuf
186-
( !readBytesWithTimeout<ServerType>(client, contentLength, plainBuf, HTTP_MAX_POST_WAIT)
187-
|| (plainBuf.length() < contentLength)
188-
)
189-
)
190-
{
191-
return false;
192-
}
143+
int headerDiv = req.indexOf(':');
144+
if (headerDiv < 0) {
145+
break;
146+
}
147+
headerName = req.substring(0, headerDiv);
148+
headerValue = req.substring(headerDiv + 1);
149+
headerValue.trim();
150+
_collectHeader(headerName, headerValue);
193151

194-
if (isEncoded) {
195-
// isEncoded => !isForm => plainBuf is not empty
196-
// add plainBuf in search str
197-
if (searchStr.length())
198-
searchStr += '&';
199-
searchStr += plainBuf;
152+
#ifdef DEBUG_ESP_HTTP_SERVER
153+
DEBUG_OUTPUT.print(F("headerName: "));
154+
DEBUG_OUTPUT.println(headerName);
155+
DEBUG_OUTPUT.print(F("headerValue: "));
156+
DEBUG_OUTPUT.println(headerValue);
157+
#endif
158+
if (headerName.equalsIgnoreCase(FPSTR(Content_Type))) {
159+
if (headerValue.startsWith(FPSTR(::mime::mimeTable[::mime::txt].mimeType))) {
160+
isForm = false;
161+
} else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))) {
162+
isForm = false;
163+
isEncoded = true;
164+
} else if (headerValue.startsWith(F("multipart/"))) {
165+
boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1);
166+
boundaryStr.replace("\"", "");
167+
isForm = true;
168+
}
169+
} else if (headerName.equalsIgnoreCase(F("Content-Length"))) {
170+
contentLength = headerValue.toInt();
171+
} else if (headerName.equalsIgnoreCase(F("Host"))) {
172+
_hostHeader = headerValue;
173+
}
200174
}
201-
202-
// parse searchStr for key/value pairs
203175
_parseArguments(searchStr);
204176

205-
if (!isForm) {
206-
if (contentLength) {
207-
// add key=value: plain={body} (post json or other data)
208-
RequestArgument& arg = _currentArgs[_currentArgCount++];
209-
arg.key = F("plain");
210-
arg.value = plainBuf;
211-
_currentArgsHavePlain = 1;
212-
}
213-
} else { // isForm is true
214-
// here: content is not yet read (plainBuf is still empty)
215-
if (!_parseForm(client, boundaryStr, contentLength)) {
216-
return false;
217-
}
218-
}
219-
} else {
220-
String headerName;
221-
String headerValue;
222-
//parse headers
223-
while(1){
224-
req = client.readStringUntil('\r');
225-
client.readStringUntil('\n');
226-
if (req.isEmpty()) break;//no moar headers
227-
int headerDiv = req.indexOf(':');
228-
if (headerDiv == -1){
229-
break;
230-
}
231-
headerName = req.substring(0, headerDiv);
232-
headerValue = req.substring(headerDiv + 2);
233-
_collectHeader(headerName.c_str(),headerValue.c_str());
177+
// below is needed only when POST type request
178+
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE) {
179+
String plainBuf;
180+
if (!isForm &&
181+
// read content into plainBuf
182+
(!readBytesWithTimeout<ServerType>(client, contentLength, plainBuf, HTTP_MAX_POST_WAIT)
183+
|| (plainBuf.length() < contentLength)))
184+
{
185+
return false;
186+
}
234187

235-
#ifdef DEBUG_ESP_HTTP_SERVER
236-
DEBUG_OUTPUT.print(F("headerName: "));
237-
DEBUG_OUTPUT.println(headerName);
238-
DEBUG_OUTPUT.print(F("headerValue: "));
239-
DEBUG_OUTPUT.println(headerValue);
240-
#endif
188+
if (isEncoded) {
189+
// isEncoded => !isForm => plainBuf is not empty
190+
// add plainBuf in search str
191+
if (searchStr.length())
192+
searchStr += '&';
193+
searchStr += plainBuf;
194+
}
241195

242-
if (headerName.equalsIgnoreCase(F("Host"))){
243-
_hostHeader = headerValue;
244-
}
196+
// parse searchStr for key/value pairs
197+
_parseArguments(searchStr);
198+
199+
if (!isForm) {
200+
if (contentLength) {
201+
// add key=value: plain={body} (post json or other data)
202+
RequestArgument& arg = _currentArgs[_currentArgCount++];
203+
arg.key = F("plain");
204+
arg.value = plainBuf;
205+
_currentArgsHavePlain = 1;
206+
}
207+
} else { // isForm is true
208+
// here: content is not yet read (plainBuf is still empty)
209+
if (!_parseForm(client, boundaryStr, contentLength)) {
210+
return false;
211+
}
212+
}
245213
}
246-
_parseArguments(searchStr);
247-
}
248-
client.flush();
214+
client.flush();
249215

250216
#ifdef DEBUG_ESP_HTTP_SERVER
251-
DEBUG_OUTPUT.print(F("Request: "));
252-
DEBUG_OUTPUT.println(url);
253-
DEBUG_OUTPUT.print(F("Arguments: "));
254-
DEBUG_OUTPUT.println(searchStr);
255-
256-
DEBUG_OUTPUT.println(F("final list of key/value pairs:"));
257-
for (int i = 0; i < _currentArgCount; i++)
258-
DEBUG_OUTPUT.printf(" key:'%s' value:'%s'\r\n",
259-
_currentArgs[i].key.c_str(),
260-
_currentArgs[i].value.c_str());
217+
DEBUG_OUTPUT.print(F("Request: "));
218+
DEBUG_OUTPUT.println(url);
219+
DEBUG_OUTPUT.print(F("Arguments: "));
220+
DEBUG_OUTPUT.println(searchStr);
221+
222+
DEBUG_OUTPUT.println(F("final list of key/value pairs:"));
223+
for (int i = 0; i < _currentArgCount; i++)
224+
DEBUG_OUTPUT.printf(" key:'%s' value:'%s'\r\n",
225+
_currentArgs[i].key.c_str(),
226+
_currentArgs[i].value.c_str());
261227
#endif
262228

263-
return true;
229+
return true;
264230
}
265231

266232
template <typename ServerType>
267-
bool ESP8266WebServerTemplate<ServerType>::_collectHeader(const char* headerName, const char* headerValue) {
268-
for (int i = 0; i < _headerKeysCount; i++) {
269-
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
270-
_currentHeaders[i].value=headerValue;
233+
bool ESP8266WebServerTemplate<ServerType>::_collectHeader(const String& headerName, const String& headerValue) {
234+
for (int i = 0; i < _headerKeysCount; i++) {
235+
if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
236+
_currentHeaders[i].value = headerValue;
271237
return true;
272238
}
273-
}
274-
return false;
239+
}
240+
return false;
275241
}
276242

277243
template <typename ServerType>
@@ -359,9 +325,9 @@ int ESP8266WebServerTemplate<ServerType>::_parseArgumentsPrivate(const String& d
359325
}
360326

361327
template <typename ServerType>
362-
void ESP8266WebServerTemplate<ServerType>::_uploadWriteByte(uint8_t b){
363-
if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN){
364-
if(_currentHandler && _currentHandler->canUpload(_currentUri))
328+
void ESP8266WebServerTemplate<ServerType>::_uploadWriteByte(uint8_t b) {
329+
if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN) {
330+
if (_currentHandler && _currentHandler->canUpload(_currentUri))
365331
_currentHandler->upload(*this, _currentUri, *_currentUpload);
366332
_currentUpload->totalSize += _currentUpload->currentSize;
367333
_currentUpload->currentSize = 0;
@@ -427,8 +393,9 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
427393
DEBUG_OUTPUT.println(argFilename);
428394
#endif
429395
//use GET to set the filename if uploading using blob
430-
if (argFilename == F("blob") && hasArg(FPSTR(filename)))
431-
argFilename = arg(FPSTR(filename));
396+
String filenameArg = arg(F("filename"));
397+
if (argFilename == F("blob") && !filenameArg.isEmpty())
398+
argFilename = filenameArg;
432399
}
433400
#ifdef DEBUG_ESP_HTTP_SERVER
434401
DEBUG_OUTPUT.print("PostArg Name: ");

0 commit comments

Comments
 (0)