Skip to content

Commit 64dd492

Browse files
d-a-vdevyte
authored andcommitted
fix parseArgument esp8266#2 (esp8266#5252)
* fix parseArgument * remove recursion * keep variable naming style consistent * inviting Alexa * fix alexa fix (well, trying...) * fix pos substrings key_end_pos--; dont count down here, it will cut of every key by -1 ("save" will be "sav") (substring (end = up to, but not including, so no need to -1) Parsing cpp L329 arg.value = urlDecode(data.substring(equal_index + 1, next_index - 1)); => -1 is too less for substring (substring (end = up to, but not including, so no need to -1) * alexa invite: add workaround for malformed x-www-form-urlencoded * when !form, alway add content in key "plain" * fix memleak * parse arguments: use functors * cleaning
1 parent d742df8 commit 64dd492

File tree

2 files changed

+133
-129
lines changed

2 files changed

+133
-129
lines changed

libraries/ESP8266WebServer/src/ESP8266WebServer.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ class ESP8266WebServer
142142
void _handleRequest();
143143
void _finalizeResponse();
144144
bool _parseRequest(WiFiClient& client);
145-
void _parseArguments(String data);
145+
void _parseArguments(const String& data);
146+
int _parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler);
146147
static String _responseCodeToString(int code);
147148
bool _parseForm(WiFiClient& client, String boundary, uint32_t len);
148149
bool _parseFormUploadAborted();

libraries/ESP8266WebServer/src/Parsing.cpp

+131-128
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
139139
String headerName;
140140
String headerValue;
141141
bool isForm = false;
142-
bool isEncoded = false;
142+
//bool isEncoded = false;
143143
uint32_t contentLength = 0;
144144
//parse headers
145145
while(1){
@@ -168,7 +168,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
168168
isForm = false;
169169
} else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){
170170
isForm = false;
171-
isEncoded = true;
171+
//isEncoded = true;
172172
} else if (headerValue.startsWith(F("multipart/"))){
173173
boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1);
174174
boundaryStr.replace("\"","");
@@ -181,43 +181,34 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
181181
}
182182
}
183183

184-
if (!isForm){
185-
size_t plainLength;
186-
char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
187-
if (plainLength < contentLength) {
188-
free(plainBuf);
189-
return false;
190-
}
191-
if (contentLength > 0) {
192-
if(isEncoded){
193-
//url encoded form
194-
if (searchStr != "") searchStr += '&';
195-
searchStr += plainBuf;
196-
}
197-
_parseArguments(searchStr);
198-
if(!isEncoded){
199-
//plain post json or other data
200-
RequestArgument& arg = _currentArgs[_currentArgCount++];
201-
arg.key = F("plain");
202-
arg.value = String(plainBuf);
184+
// always parse url for key/value pairs
185+
_parseArguments(searchStr);
186+
187+
if (!isForm) {
188+
if (contentLength) {
189+
190+
// add key=value: plain={body} (post json or other data)
191+
192+
size_t plainLength;
193+
char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
194+
if (plainLength < contentLength) {
195+
free(plainBuf);
196+
return false;
203197
}
204198

205-
#ifdef DEBUG_ESP_HTTP_SERVER
206-
DEBUG_OUTPUT.print("Plain: ");
207-
DEBUG_OUTPUT.println(plainBuf);
208-
#endif
199+
RequestArgument& arg = _currentArgs[_currentArgCount++];
200+
arg.key = F("plain");
201+
arg.value = String(plainBuf);
202+
209203
free(plainBuf);
210-
} else {
211-
// No content - but we can still have arguments in the URL.
212-
_parseArguments(searchStr);
204+
213205
}
214-
}
206+
} else { // isForm is true
215207

216-
if (isForm){
217-
_parseArguments(searchStr);
218208
if (!_parseForm(client, boundaryStr, contentLength)) {
219209
return false;
220210
}
211+
221212
}
222213
} else {
223214
String headerName;
@@ -235,14 +226,14 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
235226
headerValue = req.substring(headerDiv + 2);
236227
_collectHeader(headerName.c_str(),headerValue.c_str());
237228

238-
#ifdef DEBUG_ESP_HTTP_SERVER
239-
DEBUG_OUTPUT.print("headerName: ");
240-
DEBUG_OUTPUT.println(headerName);
241-
DEBUG_OUTPUT.print("headerValue: ");
242-
DEBUG_OUTPUT.println(headerValue);
243-
#endif
229+
#ifdef DEBUG_ESP_HTTP_SERVER
230+
DEBUG_OUTPUT.print(F("headerName: "));
231+
DEBUG_OUTPUT.println(headerName);
232+
DEBUG_OUTPUT.print(F("headerValue: "));
233+
DEBUG_OUTPUT.println(headerValue);
234+
#endif
244235

245-
if (headerName.equalsIgnoreCase("Host")){
236+
if (headerName.equalsIgnoreCase("Host")){
246237
_hostHeader = headerValue;
247238
}
248239
}
@@ -251,10 +242,16 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
251242
client.flush();
252243

253244
#ifdef DEBUG_ESP_HTTP_SERVER
254-
DEBUG_OUTPUT.print("Request: ");
245+
DEBUG_OUTPUT.print(F("Request: "));
255246
DEBUG_OUTPUT.println(url);
256-
DEBUG_OUTPUT.print(" Arguments: ");
247+
DEBUG_OUTPUT.print(F("Arguments: "));
257248
DEBUG_OUTPUT.println(searchStr);
249+
250+
DEBUG_OUTPUT.println(F("final list of key/value pairs:"));
251+
for (int i = 0; i < _currentArgCount; i++)
252+
DEBUG_OUTPUT.printf(" key:'%s' value:'%s'\r\n",
253+
_currentArgs[i].key.c_str(),
254+
_currentArgs[i].value.c_str());
258255
#endif
259256

260257
return true;
@@ -270,79 +267,85 @@ bool ESP8266WebServer::_collectHeader(const char* headerName, const char* header
270267
return false;
271268
}
272269

273-
void ESP8266WebServer::_parseArguments(String data) {
274-
#ifdef DEBUG_ESP_HTTP_SERVER
275-
DEBUG_OUTPUT.print("args: ");
276-
DEBUG_OUTPUT.println(data);
277-
#endif
278-
if (_currentArgs)
279-
delete[] _currentArgs;
280-
_currentArgs = 0;
281-
if (data.length() == 0) {
282-
_currentArgCount = 0;
283-
_currentArgs = new RequestArgument[1];
284-
return;
270+
struct storeArgHandler
271+
{
272+
void operator() (String& key, String& value, const String& data, int equal_index, int pos, int key_end_pos, int next_index)
273+
{
274+
key = ESP8266WebServer::urlDecode(data.substring(pos, key_end_pos));
275+
if ((equal_index != -1) && ((equal_index < next_index - 1) || (next_index == -1)))
276+
value = ESP8266WebServer::urlDecode(data.substring(equal_index + 1, next_index));
285277
}
286-
_currentArgCount = 1;
278+
};
287279

288-
for (int i = 0; i < (int)data.length(); ) {
289-
i = data.indexOf('&', i);
290-
if (i == -1)
291-
break;
292-
++i;
293-
++_currentArgCount;
280+
struct nullArgHandler
281+
{
282+
void operator() (String& key, String& value, const String& data, int equal_index, int pos, int key_end_pos, int next_index) {
283+
(void)key; (void)value; (void)data; (void)equal_index; (void)pos; (void)key_end_pos; (void)next_index;
284+
// do nothing
294285
}
295-
#ifdef DEBUG_ESP_HTTP_SERVER
296-
DEBUG_OUTPUT.print("args count: ");
297-
DEBUG_OUTPUT.println(_currentArgCount);
298-
#endif
286+
};
287+
288+
void ESP8266WebServer::_parseArguments(const String& data) {
289+
if (_currentArgs)
290+
delete[] _currentArgs;
291+
292+
_currentArgCount = _parseArgumentsPrivate(data, nullArgHandler());
293+
294+
// allocate one more, this is needed because {"plain": plainBuf} is always added
295+
_currentArgs = new RequestArgument[_currentArgCount + 1];
296+
297+
(void)_parseArgumentsPrivate(data, storeArgHandler());
298+
}
299+
300+
int ESP8266WebServer::_parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler) {
299301

300-
_currentArgs = new RequestArgument[_currentArgCount+1];
301-
int pos = 0;
302-
int iarg;
303-
for (iarg = 0; iarg < _currentArgCount;) {
304-
int equal_sign_index = data.indexOf('=', pos);
305-
int next_arg_index = data.indexOf('&', pos);
306-
#ifdef DEBUG_ESP_HTTP_SERVER
307-
DEBUG_OUTPUT.print("pos ");
308-
DEBUG_OUTPUT.print(pos);
309-
DEBUG_OUTPUT.print("=@ ");
310-
DEBUG_OUTPUT.print(equal_sign_index);
311-
DEBUG_OUTPUT.print(" &@ ");
312-
DEBUG_OUTPUT.println(next_arg_index);
313-
#endif
314-
if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
315302
#ifdef DEBUG_ESP_HTTP_SERVER
316-
DEBUG_OUTPUT.print("arg missing value: ");
317-
DEBUG_OUTPUT.println(iarg);
303+
DEBUG_OUTPUT.print("args: ");
304+
DEBUG_OUTPUT.println(data);
318305
#endif
319-
if (next_arg_index == -1)
306+
307+
size_t pos = 0;
308+
int arg_total = 0;
309+
310+
while (true) {
311+
312+
// skip empty expression
313+
while (data[pos] == '&' || data[pos] == ';')
314+
if (++pos >= data.length())
320315
break;
321-
pos = next_arg_index + 1;
322-
continue;
316+
317+
// locate separators
318+
int equal_index = data.indexOf('=', pos);
319+
int key_end_pos = equal_index;
320+
int next_index = data.indexOf('&', pos);
321+
int next_index2 = data.indexOf(';', pos);
322+
if ((next_index == -1) || (next_index2 != -1 && next_index2 < next_index))
323+
next_index = next_index2;
324+
if ((key_end_pos == -1) || ((key_end_pos > next_index) && (next_index != -1)))
325+
key_end_pos = next_index;
326+
if (key_end_pos == -1)
327+
key_end_pos = data.length();
328+
329+
// handle key/value
330+
if ((int)pos < key_end_pos) {
331+
332+
RequestArgument& arg = _currentArgs[arg_total];
333+
handler(arg.key, arg.value, data, equal_index, pos, key_end_pos, next_index);
334+
335+
++arg_total;
336+
pos = next_index + 1;
323337
}
324-
RequestArgument& arg = _currentArgs[iarg];
325-
arg.key = urlDecode(data.substring(pos, equal_sign_index));
326-
arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index));
327-
#ifdef DEBUG_ESP_HTTP_SERVER
328-
DEBUG_OUTPUT.print("arg ");
329-
DEBUG_OUTPUT.print(iarg);
330-
DEBUG_OUTPUT.print(" key: ");
331-
DEBUG_OUTPUT.print(arg.key);
332-
DEBUG_OUTPUT.print(" value: ");
333-
DEBUG_OUTPUT.println(arg.value);
334-
#endif
335-
++iarg;
336-
if (next_arg_index == -1)
338+
339+
if (next_index == -1)
337340
break;
338-
pos = next_arg_index + 1;
339341
}
340-
_currentArgCount = iarg;
342+
341343
#ifdef DEBUG_ESP_HTTP_SERVER
342344
DEBUG_OUTPUT.print("args count: ");
343-
DEBUG_OUTPUT.println(_currentArgCount);
345+
DEBUG_OUTPUT.println(arg_total);
344346
#endif
345347

348+
return arg_total;
346349
}
347350

348351
void ESP8266WebServer::_uploadWriteByte(uint8_t b){
@@ -410,7 +413,7 @@ bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t
410413
DEBUG_OUTPUT.println(argFilename);
411414
#endif
412415
//use GET to set the filename if uploading using blob
413-
if (argFilename == F("blob") && hasArg(FPSTR(filename)))
416+
if (argFilename == F("blob") && hasArg(FPSTR(filename)))
414417
argFilename = arg(FPSTR(filename));
415418
}
416419
#ifdef DEBUG_ESP_HTTP_SERVER
@@ -566,7 +569,7 @@ bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t
566569
arg.value = postArgs[iarg].value;
567570
}
568571
_currentArgCount = iarg;
569-
if (postArgs)
572+
if (postArgs)
570573
delete[] postArgs;
571574
return true;
572575
}
@@ -579,33 +582,33 @@ bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t
579582

580583
String ESP8266WebServer::urlDecode(const String& text)
581584
{
582-
String decoded = "";
583-
char temp[] = "0x00";
584-
unsigned int len = text.length();
585-
unsigned int i = 0;
586-
while (i < len)
587-
{
588-
char decodedChar;
589-
char encodedChar = text.charAt(i++);
590-
if ((encodedChar == '%') && (i + 1 < len))
591-
{
592-
temp[2] = text.charAt(i++);
593-
temp[3] = text.charAt(i++);
594-
595-
decodedChar = strtol(temp, NULL, 16);
596-
}
597-
else {
598-
if (encodedChar == '+')
599-
{
600-
decodedChar = ' ';
601-
}
602-
else {
603-
decodedChar = encodedChar; // normal ascii char
604-
}
605-
}
606-
decoded += decodedChar;
607-
}
608-
return decoded;
585+
String decoded = "";
586+
char temp[] = "0x00";
587+
unsigned int len = text.length();
588+
unsigned int i = 0;
589+
while (i < len)
590+
{
591+
char decodedChar;
592+
char encodedChar = text.charAt(i++);
593+
if ((encodedChar == '%') && (i + 1 < len))
594+
{
595+
temp[2] = text.charAt(i++);
596+
temp[3] = text.charAt(i++);
597+
598+
decodedChar = strtol(temp, NULL, 16);
599+
}
600+
else {
601+
if (encodedChar == '+')
602+
{
603+
decodedChar = ' ';
604+
}
605+
else {
606+
decodedChar = encodedChar; // normal ascii char
607+
}
608+
}
609+
decoded += decodedChar;
610+
}
611+
return decoded;
609612
}
610613

611614
bool ESP8266WebServer::_parseFormUploadAborted(){

0 commit comments

Comments
 (0)