-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathWebUtil.cpp
214 lines (200 loc) · 7.36 KB
/
WebUtil.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include "WebUtil.h"
const char *CRLF = "\r\n";
const char *SP = " ";
const String no_err = String("");
// '/' was excluded from the reserved symbols to be able to check the path as a whole.
const char reserved[9] = {';', '?', ':', '@', '&', '=', '+', '$', ','};
const String methods[8] = {String("GET"), String("HEAD"),
String("POST"), String("PUT"), String("DELETE"), String("CONNECT"),
String("OPTIONS"), String("TRACE")};
const String supportedMethods[1] = {String("GET")};
const String supportedVersions[3] = {String("HTTP/1.0"), String("HTTP/1.1"), String("HTTP/2.0")};
String parseRequestField(String line, Request &request) {
// A request field consists of a key and a value, which are
// separated by a colon
if (line.indexOf(":") == -1) {
return String("Invalid format for request field.");
}
// Get the key and the value.
// It is expected that the key contains no colon.
String key = line.substring(0, line.indexOf(":"));
key.trim();
String value = line.substring(line.indexOf(":") + 1);
value.trim();
// We currently ignore all fields but the user agent
// If you want to save other fields at them here in the same manner.
if (key == String("User-Agent")) {
request.user_agent = value;
}
return no_err;
}
String parseMethod(const String methodString, String &target) {
bool found = false;
for (int i = 0; i < 8; i++) {
if (methods[i] == methodString) {
found = true;
break;
}
}
if (!found) {
return String("Unknown request method.");
}
found = false;
for (int i = 0; i < 8; i++) {
if (methods[i] == methodString) {
target = methodString;
return no_err;
}
}
if (!found) {
return String("Unsupported request method.");
}
}
String parseUri(const String uri, String &target) {
if (uri.length() == 0) {
target = String("/");
} else {
target = uri;
}
if (target[0] != '/') {
return String("Only supporting absolute paths as URIs. (") + target + ")";
}
for (int i = 0; i < 9; i++) {
if (target.indexOf(reserved[i]) != -1) {
return String("URI contains reserved symbol. (") + reserved[i] + ")";
}
}
target = uri;
return no_err;
}
String parseProtocolVersion(const String str, String &version) {
for (int i = 0; i < 3; i++) {
if (str == supportedVersions[i]) {
version = str;
return no_err;
}
}
return String("Unknown HTTP Version detected. (") + str + ")";
}
String parseRequestLine(String requestLine, Request &request) {
int idx = requestLine.indexOf(SP);
if (idx == -1) {
return String("Request line is malformed. (no spaces)");
}
String methodString = requestLine.substring(0, idx);
String error = parseMethod(methodString, request.method);
if (error.length() > 0) { return error; }
int scd_idx = requestLine.indexOf(SP, idx+1);
if (scd_idx == -1) {
return String("Request line is malformed. (missing space)");
}
String uri = requestLine.substring(idx+1, scd_idx);
error = parseUri(uri, request.uri);
if (error.length() > 0) { return error; }
if (scd_idx+1 >= requestLine.length()) {
return String("Missing version string.");
}
String version = requestLine.substring(scd_idx+1);
error = parseProtocolVersion(version, request.protocol_version);
if (error.length() > 0) { return error; }
return no_err;
}
String parseRequest(String requestString, Request &request) {
// Search for first line break and try to parse line as request line.
int line_idx = 0;
int pointer = requestString.indexOf(CRLF);
if (pointer == -1) {
return String("Request doesn't contain a single line ending.");
}
String requestLine = requestString.substring(0, pointer);
// If there was an error when parsing the request line return that error.
String error = parseRequestLine(requestLine, request);
if (error.length() > 0) { return error; }
// Otherwise parse all preceding lines until one line is empty.
// A line is empty if the position in memory of two succeeding linebreaks is 2.
int nextPointer = requestString.indexOf(CRLF, pointer + 2);
while (pointer != nextPointer - 2) {
// If no line break was found, the request did not end with a empty line, as it should.
if (nextPointer == -1) {
return String("Did not receive blank line at end of request.");
}
// Parse all lines as request fields.
String line = requestString.substring(pointer, nextPointer);
error = parseRequestField(line, request);
if (error.length() > 0) { return error; }
// search for next line break
pointer = nextPointer;
nextPointer = requestString.indexOf(CRLF, pointer + 2);
}
return no_err;
}
int parseRequestSafe(String requestString, Request &request) {
// Try to parse request.
String error = parseRequest(requestString, request);
// If it fails print the error and return false as error code
if (error.length() > 0) {
Serial.println(String("An exception occurred when parsing the request: ") + error);
return 0;
}
// Otherwise return 1
return 1;
}
String buildSuccessfulResponse(const Request &request, const String &html) {
String responseHeader = String("HTTP/1.1 200 OK") + CRLF;
responseHeader = responseHeader + "Content-Type: text/html" + CRLF;
responseHeader = responseHeader + "Connection: close" + CRLF + CRLF;
return responseHeader + html;
}
String buildNotFoundResponse(const Request &request) {
String responseHeader = String("HTTP/1.1 404 Not Found") + CRLF;
responseHeader = responseHeader + "Content-Type: text/html" + CRLF;
responseHeader = responseHeader + "Connection: close" + CRLF + CRLF;
return responseHeader + String("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
"<html>"
"<head>"
" <title>404 Not Found</title>"
"</head>"
"<body>"
" <h1>Not Found</h1>"
" <p>The requested URL was not found on this server.</p>"
"</body>"
"</html>");
}
String buildHTML(const String &header, const String &body) {
return String("<!DOCTYPE HTML>") + CRLF
+ "<html>" + CRLF
+ "<head>" + CRLF
+ header + CRLF
+ "</head>" + CRLF
+ "<body>" + CRLF
+ body + CRLF
+ "</body>" + CRLF
+ "</html>";
}
String buildTag(const String &tag, const String &content) {
return String("<") + tag + ">" + content + "</" + tag + ">";
}
String listenClient(WiFiClient &client) {
boolean currentLineIsBlank = true;
String request = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
request += c;
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
return request;
}