-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWebServer.cpp
196 lines (162 loc) · 5.21 KB
/
WebServer.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
#include "WebServer.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __AVR__
#include <avr/pgmspace.h>
#else
#define strncasecmp_P strncmp
#define strcpy_P strcpy
#define strlen_P strlen
#define sprintf_P sprintf
#endif
#define min(a,b) a >= b ? b : a
//request types
static const char GET_REQUEST[] PROGMEM = {"GET"};
static const PROGMEM char HTTP_TRAILER[] = "\r\n";
//HTTP response codes
static const PROGMEM char ERR_200[] = "HTTP/1.1 200 OK";
static const PROGMEM char ERR_404[] = "HTTP/1.1 404 File Not Found";
static const PROGMEM char ERR_500[] = "HTTP/1.1 500 Internal Server Error";
//HTTP response header fields
static const char CONTENT_TYPE[] PROGMEM = {"Content-Type text/html; charset=utf-8"};
static const char TRANSFER_ENCODING[] PROGMEM = {"Transfer-Encoding: chunked"};
static const char FINAL_CHUNK[] PROGMEM = {"0\r\n\r\n"};
static const char CONTENT_LENGTH_FMT[] PROGMEM = {"Content-Length: %lu"};
WebServer::WebServer(FileSystem &fileSystem, Network &network)
: m_fileSystem(fileSystem),
m_network(network)
{}
/** @brief process and HTTP request and populate a response buffer
Algorithm:
1. Tokenize request.
a. First token assumed to be request type.
b. Second token assumed to be file path ('/' resolves to index.html)
2. If type is anything other than GET, return failure (not yet implemented).
3. Call f_stat on the requested file path.
a. If f_stat fails, return 404 response.
b. Else, continue
5. Fill in 200 OK error code
6. Fill in Content Length based on f_stat results
7. Fill in Content Type
8. Fill in Transfer-Encoding
9. Fill in HTTP_TRAILER to begin data section
10. Max bytes of the file to read:
*resSize - (bytesWrittenSoFar - 4)
or
file length, whichever is shorter.
Subtract 2 bytes for chunk size and 2 bytes for \r\n.
11. Read bytes from file into buffer.
12. Fill in chunk header based on bytes read
13. Fill in chunk data
*/
int WebServer::processRequest(
const char *request,
size_t reqSize) {
//just define variables up front to support possible C99 transition in the future
char *tmpRequest = 0;
char *tmpRequestCopy = 0;
char *p = 0;
int res = 0;
FILINFO info;
FIL file;
char *path = 0;
FRESULT fresult;
int written;
if (0 == request || 0 == reqSize) {
return 2;
}
//temporary pointer used for tokenizing request
tmpRequest = (char*)malloc(strlen(request) + 1);
tmpRequestCopy = tmpRequest;
strcpy(tmpRequest, request);
//get request type
p = strtok(tmpRequestCopy, " ");
//for some reason strncasecmp_P was giving me problems, so GET_REQUEST is not a prog_char
res = strncasecmp_P(p, GET_REQUEST, min(strlen(p), strlen_P(GET_REQUEST)));
if (0 != res) {
free(tmpRequest);
return 2;
}
//get file path
p = strtok(NULL, " ");
if (0 == strcmp(p, "/")) {
path = "index.html";
}
else {
path = p;
}
fresult = m_fileSystem.myf_stat(path, &info);
//if stat failed, assume the file does not exist
if (FR_OK != fresult) {
netWrite_P(ERR_404, strlen_P(ERR_404));
netWrite_P(HTTP_TRAILER, strlen(HTTP_TRAILER));
free(tmpRequest);
return 0;
}
fresult = m_fileSystem.myf_open(&file, path, FA_READ);
//don't need this anymore after this
free(tmpRequest);
path = 0;
if (FR_OK != fresult) {
netWrite_P(ERR_500, strlen_P(ERR_500));
netWrite_P(HTTP_TRAILER, strlen(HTTP_TRAILER));
return 0;
}
//found file, response 200
netWrite_P(ERR_200, strlen_P(ERR_200));
netWrite_P(HTTP_TRAILER, strlen(HTTP_TRAILER));
//fill in Content-Length
char tmpBuf[30];
written = sprintf_P(tmpBuf, CONTENT_LENGTH_FMT, info.fsize);
netWrite(tmpBuf, written);
netWrite_P(HTTP_TRAILER, strlen(HTTP_TRAILER));
//fill in Content-Type
netWrite_P(CONTENT_TYPE, strlen(CONTENT_TYPE));
netWrite_P(HTTP_TRAILER, strlen(HTTP_TRAILER));
//fill in Transfer-Encoding
netWrite_P(TRANSFER_ENCODING, strlen(TRANSFER_ENCODING));
netWrite_P(HTTP_TRAILER, strlen(HTTP_TRAILER));
//finished with header, start data
netWrite_P(HTTP_TRAILER, strlen(HTTP_TRAILER));
chunkedFileRead(&file);
return 0;
}
void WebServer::chunkedFileRead(FIL *file)
{
size_t bytesRead = 0;
char tmpBuf[10];
int written;
if (0 == file) {
return;
}
do
{
m_fileSystem.myf_read(file, m_tmpFileBuf, TMP_FILE_BUF_SIZE, &bytesRead);
//write chunk header
written = sprintf(tmpBuf, "%02X", bytesRead);
netWrite(tmpBuf, written);
netWrite_P(HTTP_TRAILER, strlen_P(HTTP_TRAILER));
//write chunk data
netWrite(m_tmpFileBuf, bytesRead);
netWrite_P(HTTP_TRAILER, strlen_P(HTTP_TRAILER));
} while (TMP_FILE_BUF_SIZE == bytesRead);
m_fileSystem.myf_close(file);
//write final chunk
netWrite_P(FINAL_CHUNK, strlen_P(FINAL_CHUNK));
}
void WebServer::netWrite_P(const char *toWrite, size_t size) {
#ifdef _WIN32
netWrite(toWrite, size);
#else
while (pgm_read_byte(toWrite) != 0x00) {
m_network.writeByte(pgm_read_byte(toWrite++));
}
#endif
}
void WebServer::netWrite(const char *toWrite, size_t size) {
size_t i;
for (i = 0; i < size; i++) {
m_network.writeByte(toWrite[i]);
}
}