http-server.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. //#define ENABLE_CORS_SUPER_FUN_TIME
  2. #include "http-server.h"
  3. #include "../server.h" //FIM_BUFFER_LEN
  4. uint16_t intLen(uint16_t i) {
  5. if(i >= 10000) return 5;
  6. if(i >= 1000) return 4;
  7. if(i >= 100) return 3;
  8. if(i >= 10) return 2;
  9. /*(i >= 1) */ return 1;
  10. }
  11. char * responseHeader(int docLen, int *outLen) {
  12. static char buf[2048];
  13. char dateBuf[2048], saved[2][2048], *fmt = TYPICAL_HEADER;
  14. time_t n;
  15. struct tm *t;
  16. // save the previous locale and timezone
  17. strcpy(saved[0], setlocale(LC_ALL, NULL)); strcpy(saved[1], tzname[0]);
  18. // update locale to "C" (English day/month names are required by HTTP)
  19. setlocale(LC_ALL, "C");
  20. // set the timezone to Greenwich Mean Time as per RFC7231 7.1.1.2
  21. setenv("TZ", "GMT", 1); tzset();
  22. // get the current time
  23. n = time(NULL);
  24. // split the time into parts (seconds, minutes, ..., day-in-the-year)
  25. t = gmtime(&n);
  26. /* copy the time into our buffer, using this format:
  27. * %a: abbreviated name of day
  28. * | ,: a comma
  29. * | | %d: day of month as decimal 01-31
  30. * | | | %b: abbreviated month name
  31. * | | | | %Y: Year as decimal includes century
  32. * | | | | | %H: 24-hour clock decimal 00-23
  33. * | | | | | |
  34. * Date: Tue, 11 Aug 2020 15:29:59 GMT
  35. * | | |
  36. * minute decimal 00-59: %M | |
  37. * second decimal 00-59: %S |
  38. * timezone: %Z
  39. *
  40. * [!] all four white spaces are required, the total lenght should always
  41. * be exactly 29 characters long
  42. */
  43. strftime(dateBuf, sizeof(dateBuf), "%a, %d %b %Y %H:%M:%S %Z", t);
  44. // generate a warning message when we goof up the Date header
  45. if(RFC7231_DATE_LENGTH != strlen(dateBuf))
  46. fprintf(stderr, "[!] bug - a date that does not match http/1.1"
  47. "rules was generated\ninvalid date: [%s] should be %zu characters"
  48. "long but is not\n", dateBuf, strlen(dateBuf));
  49. // restore the locale and timezone
  50. setlocale(LC_ALL, saved[0]);
  51. strcpy(tzname[0], saved[1]); setenv("TZ", tzname[0], 1); tzset();
  52. // store the header in a contiguous buffer
  53. snprintf(buf, 2048, fmt, dateBuf, docLen);
  54. #ifdef PRINT_HTTP_HEADER
  55. // send http response to stderr
  56. fprintf(stderr, buf);
  57. #endif
  58. // set output length
  59. *outLen = strlen(buf);
  60. return buf;
  61. }
  62. char *httpEcho(char *s, int len, int *limit) {
  63. int headerLen, prediction;
  64. char *p, *buf, *header, *out = "Hola! You sent: ";
  65. /* for http responses to fit inside of a fixed length buffer we may need
  66. * to limit "echo" responses because of overhead from the http header
  67. * contents and our own canned text, unfortunately we cannot easily and
  68. * precisely say what that limit is due to the http header having
  69. * a variable lenght header, (Content-Length)
  70. * Most HTTP servers limit this header to 8K, meaning that 1-4 decimal
  71. * digits can be used to represent lengths between 0 and 8000.
  72. *
  73. * predict the header length, then reduce len if needed to fit into
  74. * server.h FIM_BUFFER_LEN by multiplying each occurence of a printf format
  75. * substitution symbol by two, and subtracting the result from the length
  76. * of http-server.c response header format template length (TYPICAL_HEADER)
  77. * then add in the length of the date header and the number of digits
  78. * needed to represent the length of the message body subtract this total
  79. * from FIM_BUFFER_LEN -- this is just a prediction, after truncating length,
  80. * the number of digits needed to represent the new length could be fewer
  81. */
  82. prediction = (sizeof(TYPICAL_HEADER) - 1) - (2 * TYPICAL_HEADER_FORMAT_SUBSTITUTIONS);
  83. prediction += (RFC7231_DATE_LENGTH + intLen(len + strlen(out)));
  84. prediction = (FIM_BUFFER_LEN - prediction);
  85. // also remove the length required for canned text
  86. prediction -= strlen(out);
  87. // shorten the echoed part of the response when buffer limit would be reached
  88. if(prediction >= len); else {
  89. fprintf(stderr, "buffer can only fit %d of %d characters for the "
  90. "response to client\n", prediction, len);
  91. len = 0 < prediction ?prediction :0;
  92. }
  93. // get the response headers
  94. header = responseHeader(strlen(out) + len, &headerLen);
  95. // do not send too much data to the client
  96. if((0 < headerLen) && (300 > headerLen)); else {
  97. fprintf(stderr, "Why the weird amount of data?"
  98. "headerLen [%d]\n"
  99. "client data length [%d]\n"
  100. "my hola length [%zu]\n", headerLen, len, strlen(out));
  101. return out;
  102. }
  103. if(0) fprintf(stderr, "webpage length: head %d + server %zu + body %d = %zu\n",
  104. headerLen, strlen(out), len,
  105. strlen(out) + len + headerLen);
  106. // report the length of buffer for caller
  107. *limit = strlen(out) + len + headerLen;
  108. // generate a larger buffer
  109. buf = (char *) malloc(strlen(out) + len + headerLen);
  110. // make a copy of the pointer so we can copy data
  111. p = buf;
  112. // copy the header
  113. for(uint32_t i = headerLen; i--;) *p++ = *header++;
  114. // copy the canned out message
  115. for(uint32_t i = strlen(out); i--;) *p++ = *out++;
  116. // copy the client message
  117. for(uint32_t i = len; i--;) *p++ = *s++;
  118. return buf;
  119. }