1010#include " TankController.h"
1111
1212// class variables
13- EthernetServer_TC* EthernetServer_TC::_instance = nullptr ;
13+ EthernetServer_TC * EthernetServer_TC::_instance = nullptr ;
1414
1515// class methods
1616/* *
1717 * accessor for singleton
1818 */
19- EthernetServer_TC* EthernetServer_TC::instance () {
19+ EthernetServer_TC * EthernetServer_TC::instance () {
2020 if (!_instance) {
2121 _instance = new EthernetServer_TC (80 );
2222 }
@@ -69,17 +69,25 @@ void EthernetServer_TC::get() {
6969 fileSetup ();
7070 } else {
7171 serial (F (" get \" %s\" not recognized!" ), buffer + 4 );
72- sendBadRequestHeaders ( );
72+ sendResponse (HTTP_NOT_FOUND );
7373 state = FINISHED;
7474 }
7575}
7676
77+ // Handles an HTTP OPTIONS request
78+ void EthernetServer_TC::options () {
79+ // Method not allowed
80+ sendResponse (HTTP_NOT_PERMITTED);
81+ state = FINISHED;
82+ }
83+
7784// Handles an HTTP POST request
7885void EthernetServer_TC::post () {
7986 if (memcmp_P (buffer + 6 , F (" api" ), 3 ) == 0 ) {
8087 keypress ();
8188 } else {
8289 serial (F (" post \" %s\" not recognized!" ), buffer + 6 );
90+ sendResponse (HTTP_BAD_REQUEST);
8391 state = FINISHED;
8492 }
8593}
@@ -99,21 +107,22 @@ void EthernetServer_TC::apiHandler() {
99107 } else if (memcmp_P (buffer + 11 , F (" display" ), 7 ) == 0 ) {
100108 display ();
101109 } else if (memcmp_P (buffer + 11 , F (" rootdir" ), 7 ) == 0 ) {
102- rootdir ();
110+ rootdirSetup ();
103111 } else if (memcmp_P (buffer + 11 , F (" testRead" ), 8 ) == 0 ) {
104112 testReadSpeed ();
105113 } else if (memcmp_P (buffer + 11 , F (" testWrite" ), 9 ) == 0 ) {
106114 testWriteSpeed ();
107115 } else {
108116 // Unimplemented in API 1
109117 serial (F (" Request unimplemented in API 1" ));
110- sendBadRequestHeaders ( );
118+ sendResponse (HTTP_BAD_REQUEST );
111119 state = FINISHED;
112120 }
113121 } else {
114122 // Later API versions may be implemented here
115123 serial (F (" unhandled API version" ));
116- sendBadRequestHeaders ();
124+ sendResponse (HTTP_BAD_REQUEST);
125+ ;
117126 state = FINISHED;
118127 }
119128}
@@ -122,7 +131,7 @@ void EthernetServer_TC::apiHandler() {
122131void EthernetServer_TC::current () {
123132 JSONBuilder builder;
124133 int size = builder.buildCurrentValues ();
125- char * text = builder.bufferPtr ();
134+ char * text = builder.bufferPtr ();
126135 // First send headers
127136 sendHeadersWithSize (size);
128137 // Write JSON file to client (will be null-terminated)
@@ -150,24 +159,24 @@ void EthernetServer_TC::display() {
150159void EthernetServer_TC::keypress () {
151160 if (buffer[23 ] != ' ' ) {
152161 serial (F (" value too long" ));
153- sendBadRequestHeaders ( );
162+ sendResponse (HTTP_BAD_REQUEST );
154163 } else {
155164 // We have a one character keypress, check to see if valid character
156165 char key = buffer[22 ];
157166 if (key == ' #' || key == ' *' || (key >= ' 0' && key <= ' 9' ) || (key >= ' A' && key <= ' D' )) {
158167 // States will handle keypresses appropriately
159168 TankController::instance ()->setNextKey (key);
160- sendRedirectHeaders ( );
169+ sendResponse (HTTP_REDIRECT );
161170 } else {
162171 serial (F (" bad character: %c" ), key);
163- sendBadRequestHeaders ( );
172+ sendResponse (HTTP_BAD_REQUEST );
164173 }
165174 }
166175 state = FINISHED;
167176}
168177
169178// Non-member callback wrapper for singleton
170- void writeToClientBufferCallback (char * buffer, bool isFinished) {
179+ void writeToClientBufferCallback (char * buffer, bool isFinished) {
171180 // The boolean value in the callback is true when the process is complete
172181 EthernetServer_TC::instance ()->writeToClientBuffer (buffer, isFinished);
173182}
@@ -178,30 +187,37 @@ void countFilesCallback(int fileCount) {
178187 EthernetServer_TC::instance ()->sendHeadersForRootdir (fileCount);
179188}
180189
181- // List the root directory to the client
182- void EthernetServer_TC::rootdir () {
183- // Call function on SD Card
184- // Provide callback to call when writing to the client buffer
185- if (state != LISTING_FILES) {
186- state = LISTING_FILES;
187- isDoneCountingFiles = false ;
188- startTime = millis ();
189- serial (F (" Preparing list of files in root directory..." ));
190- } else {
191- if (isDoneCountingFiles) {
192- SD_TC::instance ()->listRootToBuffer (writeToClientBufferCallback);
193- } else {
190+ // Count files in root directory so that the Content-Length
191+ // for the header may be calculated
192+ void EthernetServer_TC::rootdirSetup () {
193+ if (state == COUNTING_FILES) {
194194#ifndef MOCK_PINS_COUNT
195- SD_TC::instance ()->countFiles (countFilesCallback);
195+ if (!SD_TC::instance ()->countFiles (countFilesCallback)) {
196+ sendResponse (HTTP_ERROR);
197+ state = FINISHED;
198+ };
196199#else
197- countFilesCallback (0 );
200+ countFilesCallback (0 );
198201#endif
199- }
202+ } else {
203+ state = COUNTING_FILES;
204+ startTime = millis ();
205+ serial (F (" Preparing list of files in root directory..." ));
200206 }
201207}
202208
209+ // List the root directory to the client
210+ void EthernetServer_TC::rootdir () {
211+ // Call function on SD Card
212+ // Provide callback to call when writing to the client buffer
213+ if (!SD_TC::instance ()->listRootToBuffer (writeToClientBufferCallback)) {
214+ sendResponse (HTTP_ERROR);
215+ state = FINISHED;
216+ };
217+ }
218+
203219// Write to the client buffer
204- void EthernetServer_TC::writeToClientBuffer (char * buffer, bool isFinished) {
220+ void EthernetServer_TC::writeToClientBuffer (char * buffer, bool isFinished) {
205221 // Write to client and return (ASSUME NULL-TERMINATED)
206222 client.write (buffer);
207223 if (isFinished) {
@@ -215,12 +231,10 @@ void EthernetServer_TC::writeToClientBuffer(char* buffer, bool isFinished) {
215231
216232void EthernetServer_TC::sendHeadersForRootdir (int fileCount) {
217233#ifndef MOCK_PINS_COUNT
218- isDoneCountingFiles = true ;
219234 serial (F (" ...%i files..." ), fileCount);
220235 sendHeadersWithSize ((uint32_t )fileCount * 24 ); // 24 characters per line
221- state = LISTING_FILES; // TODO: This is here only because sendHeadersWithSize() changes the state prematurely.
236+ state = LISTING_FILES;
222237#else
223- isDoneCountingFiles = true ;
224238 sendHeadersWithSize ((uint32_t )49 );
225239 state = LISTING_FILES;
226240#endif
@@ -331,8 +345,10 @@ void EthernetServer_TC::loop() {
331345 state = FINISHED;
332346 }
333347 break ;
348+ case COUNTING_FILES:
349+ rootdirSetup ();
350+ break ;
334351 case LISTING_FILES:
335- // Listing files from SD Card
336352 rootdir ();
337353 break ;
338354 case NOT_CONNECTED:
@@ -348,24 +364,33 @@ void EthernetServer_TC::loop() {
348364 break ;
349365 }
350366 }
351- if (bufferContentsSize > 0 ) {
367+ if (bufferContentsSize == 0 ) {
368+ if (millis () - connectedAt > TIMEOUT) {
369+ sendResponse (HTTP_TIMEOUT);
370+ state = FINISHED;
371+ }
372+ } else {
352373 if (memcmp_P (buffer, F (" GET " ), 4 ) == 0 ) {
353374 state = GET_REQUEST;
354375 get ();
355- break ;
356376 } else if (memcmp_P (buffer, F (" POST " ), 5 ) == 0 ) {
357377 state = POST_REQUEST;
358378 post ();
359- break ;
379+ } else if (memcmp_P (buffer, F (" OPTIONS " ), 8 ) == 0 ) {
380+ state = OPTIONS_REQUEST;
381+ options ();
360382 } else {
361383 serial (buffer);
362- serial (F (" Bad or unsupported request" ));
363- sendBadRequestHeaders ( );
384+ serial (F (" Unsupported request" ));
385+ sendResponse (HTTP_NOT_IMPLEMENTED );
364386 state = FINISHED;
365- break ;
366387 }
367388 }
389+ break ;
368390 default :
391+ serial (F (" loop() - unknown state: %i\n Reseting state" ), (int )state);
392+ sendResponse (HTTP_ERROR);
393+ state = FINISHED;
369394 break ;
370395 }
371396 } else if (state != NOT_CONNECTED) { // In case client disconnects early
@@ -404,29 +429,58 @@ void EthernetServer_TC::sendHeadersWithSize(uint32_t size) {
404429 // blank line indicates end of headers
405430 client.write (' \r ' );
406431 client.write (' \n ' );
407- state = FINISHED; // TODO: Why?! This is awkward when we want to send a body next.
408432}
409433
410- // 303 response
411- void EthernetServer_TC::sendRedirectHeaders () {
412- static const char response[] PROGMEM =
434+ void EthernetServer_TC::sendResponse (int code) {
435+ static const char response_303[] PROGMEM =
413436 " HTTP/1.1 303 See Other\r\n "
414437 " Location: /api/1/display\r\n "
415438 " Access-Control-Allow-Origin: *\r\n "
416439 " \r\n " ;
417- char buffer[sizeof (response)];
418- strncpy_P (buffer, (PGM_P)response, sizeof (buffer));
419- client.write (buffer);
420- state = FINISHED;
421- }
422-
423- // 400 response
424- void EthernetServer_TC::sendBadRequestHeaders () {
425- char buffer[30 ];
426- static const char response[] PROGMEM = " HTTP/1.1 400 Bad Request\r\n\r\n " ;
427- strncpy_P (buffer, (PGM_P)response, sizeof (buffer));
440+ static const char response_400[] PROGMEM =
441+ " HTTP/1.1 400 Bad Request\r\n "
442+ " \r\n " ;
443+ static const char response_404[] PROGMEM =
444+ " HTTP/1.1 404 Not Found\r\n "
445+ " \r\n " ;
446+ static const char response_405[] PROGMEM =
447+ " HTTP/1.1 405 Method Not Allowed\r\n "
448+ " Allow: GET, POST\r\n "
449+ " \r\n " ;
450+ static const char response_408[] PROGMEM =
451+ " HTTP/1.1 408 Request Timeout\r\n "
452+ " Connection: close\r\n "
453+ " \r\n " ;
454+ static const char response_500[] PROGMEM =
455+ " HTTP/1.1 500 Internal Server Error\r\n "
456+ " \r\n " ;
457+ static const char response_501[] PROGMEM =
458+ " HTTP/1.1 501 Not Implemented\r\n "
459+ " \r\n " ;
460+ char buffer[sizeof (response_303)];
461+ switch (code) {
462+ case HTTP_REDIRECT:
463+ strncpy_P (buffer, (PGM_P)response_303, sizeof (buffer));
464+ break ;
465+ case HTTP_BAD_REQUEST:
466+ strncpy_P (buffer, (PGM_P)response_400, sizeof (buffer));
467+ break ;
468+ case HTTP_NOT_FOUND:
469+ strncpy_P (buffer, (PGM_P)response_404, sizeof (buffer));
470+ break ;
471+ case HTTP_NOT_PERMITTED:
472+ strncpy_P (buffer, (PGM_P)response_405, sizeof (buffer));
473+ break ;
474+ case HTTP_TIMEOUT:
475+ strncpy_P (buffer, (PGM_P)response_408, sizeof (buffer));
476+ break ;
477+ case HTTP_NOT_IMPLEMENTED:
478+ strncpy_P (buffer, (PGM_P)response_501, sizeof (buffer));
479+ break ;
480+ default :
481+ strncpy_P (buffer, (PGM_P)response_500, sizeof (buffer));
482+ };
428483 client.write (buffer);
429- state = FINISHED;
430484}
431485
432486// Calculate day of week in proleptic Gregorian calendar. Sunday == 0.
0 commit comments