//#define ENABLE_CORS_SUPER_FUN_TIME #include "http-server.h" #include "../server.h" //FIM_BUFFER_LEN uint16_t intLen(uint16_t i) { if(i >= 10000) return 5; if(i >= 1000) return 4; if(i >= 100) return 3; if(i >= 10) return 2; /*(i >= 1) */ return 1; } char * responseHeader(int docLen, int *outLen) { static char buf[2048]; char dateBuf[2048], saved[2][2048], *fmt = TYPICAL_HEADER; time_t n; struct tm *t; // save the previous locale and timezone strcpy(saved[0], setlocale(LC_ALL, NULL)); strcpy(saved[1], tzname[0]); // update locale to "C" (English day/month names are required by HTTP) setlocale(LC_ALL, "C"); // set the timezone to Greenwich Mean Time as per RFC7231 7.1.1.2 setenv("TZ", "GMT", 1); tzset(); // get the current time n = time(NULL); // split the time into parts (seconds, minutes, ..., day-in-the-year) t = gmtime(&n); /* copy the time into our buffer, using this format: * %a: abbreviated name of day * | ,: a comma * | | %d: day of month as decimal 01-31 * | | | %b: abbreviated month name * | | | | %Y: Year as decimal includes century * | | | | | %H: 24-hour clock decimal 00-23 * | | | | | | * Date: Tue, 11 Aug 2020 15:29:59 GMT * | | | * minute decimal 00-59: %M | | * second decimal 00-59: %S | * timezone: %Z * * [!] all four white spaces are required, the total lenght should always * be exactly 29 characters long */ strftime(dateBuf, sizeof(dateBuf), "%a, %d %b %Y %H:%M:%S %Z", t); // generate a warning message when we goof up the Date header if(RFC7231_DATE_LENGTH != strlen(dateBuf)) fprintf(stderr, "[!] bug - a date that does not match http/1.1" "rules was generated\ninvalid date: [%s] should be %zu characters" "long but is not\n", dateBuf, strlen(dateBuf)); // restore the locale and timezone setlocale(LC_ALL, saved[0]); strcpy(tzname[0], saved[1]); setenv("TZ", tzname[0], 1); tzset(); // store the header in a contiguous buffer snprintf(buf, 2048, fmt, dateBuf, docLen); #ifdef PRINT_HTTP_HEADER // send http response to stderr fprintf(stderr, buf); #endif // set output length *outLen = strlen(buf); return buf; } char *httpEcho(char *s, int len, int *limit) { int headerLen, prediction; char *p, *buf, *header, *out = "Hola! You sent: "; /* for http responses to fit inside of a fixed length buffer we may need * to limit "echo" responses because of overhead from the http header * contents and our own canned text, unfortunately we cannot easily and * precisely say what that limit is due to the http header having * a variable lenght header, (Content-Length) * Most HTTP servers limit this header to 8K, meaning that 1-4 decimal * digits can be used to represent lengths between 0 and 8000. * * predict the header length, then reduce len if needed to fit into * server.h FIM_BUFFER_LEN by multiplying each occurence of a printf format * substitution symbol by two, and subtracting the result from the length * of http-server.c response header format template length (TYPICAL_HEADER) * then add in the length of the date header and the number of digits * needed to represent the length of the message body subtract this total * from FIM_BUFFER_LEN -- this is just a prediction, after truncating length, * the number of digits needed to represent the new length could be fewer */ prediction = (sizeof(TYPICAL_HEADER) - 1) - (2 * TYPICAL_HEADER_FORMAT_SUBSTITUTIONS); prediction += (RFC7231_DATE_LENGTH + intLen(len + strlen(out))); prediction = (FIM_BUFFER_LEN - prediction); // also remove the length required for canned text prediction -= strlen(out); // shorten the echoed part of the response when buffer limit would be reached if(prediction >= len); else { fprintf(stderr, "buffer can only fit %d of %d characters for the " "response to client\n", prediction, len); len = 0 < prediction ?prediction :0; } // get the response headers header = responseHeader(strlen(out) + len, &headerLen); // do not send too much data to the client if((0 < headerLen) && (300 > headerLen)); else { fprintf(stderr, "Why the weird amount of data?" "headerLen [%d]\n" "client data length [%d]\n" "my hola length [%zu]\n", headerLen, len, strlen(out)); return out; } if(0) fprintf(stderr, "webpage length: head %d + server %zu + body %d = %zu\n", headerLen, strlen(out), len, strlen(out) + len + headerLen); // report the length of buffer for caller *limit = strlen(out) + len + headerLen; // generate a larger buffer buf = (char *) malloc(strlen(out) + len + headerLen); // make a copy of the pointer so we can copy data p = buf; // copy the header for(uint32_t i = headerLen; i--;) *p++ = *header++; // copy the canned out message for(uint32_t i = strlen(out); i--;) *p++ = *out++; // copy the client message for(uint32_t i = len; i--;) *p++ = *s++; return buf; }