file.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. file.c -- File handler
  3. This module serves static file documents
  4. */
  5. /********************************* Includes ***********************************/
  6. #include "goahead.h"
  7. /*********************************** Locals ***********************************/
  8. static char *websIndex; /* Default page name */
  9. static char *websDocuments; /* Default Web page directory */
  10. /**************************** Forward Declarations ****************************/
  11. static void fileWriteEvent(Webs *wp);
  12. /*********************************** Code *************************************/
  13. /*
  14. Serve static files
  15. Return true to indicate the request was handled, even for errors.
  16. */
  17. static bool fileHandler(Webs *wp)
  18. {
  19. WebsFileInfo info;
  20. char *tmp, *date;
  21. ssize nchars;
  22. int code;
  23. assert(websValid(wp));
  24. assert(wp->method);
  25. assert(wp->filename && wp->filename[0]);
  26. #if !ME_ROM
  27. if (smatch(wp->method, "DELETE")) {
  28. if (unlink(wp->filename) < 0) {
  29. websError(wp, HTTP_CODE_NOT_FOUND, "Cannot delete the URI");
  30. } else {
  31. /* No content */
  32. websResponse(wp, 204, 0);
  33. }
  34. } else if (smatch(wp->method, "PUT")) {
  35. /* Code is already set for us by processContent() */
  36. websResponse(wp, wp->code, 0);
  37. } else
  38. #endif /* !ME_ROM */
  39. {
  40. /*
  41. If the file is a directory, redirect using the nominated default page
  42. */
  43. if (websPageIsDirectory(wp)) {
  44. nchars = strlen(wp->path);
  45. if (wp->path[nchars - 1] == '/' || wp->path[nchars - 1] == '\\') {
  46. wp->path[--nchars] = '\0';
  47. }
  48. tmp = sfmt("%s/%s", wp->path, websIndex);
  49. websRedirect(wp, tmp);
  50. wfree(tmp);
  51. return 1;
  52. }
  53. if (websPageOpen(wp, O_RDONLY | O_BINARY, 0666) < 0) {
  54. #if ME_DEBUG
  55. if (wp->referrer) {
  56. trace(1, "From %s", wp->referrer);
  57. }
  58. #endif
  59. websError(wp, HTTP_CODE_NOT_FOUND, "Cannot open document for: %s", wp->path);
  60. return 1;
  61. }
  62. if (websPageStat(wp, &info) < 0) {
  63. websError(wp, HTTP_CODE_NOT_FOUND, "Cannot stat page for URL");
  64. return 1;
  65. }
  66. code = 200;
  67. if (wp->since && info.mtime <= wp->since) {
  68. code = 304;
  69. info.size = 0;
  70. }
  71. websSetStatus(wp, code);
  72. websWriteHeaders(wp, info.size, 0);
  73. if ((date = websGetDateString(&info)) != NULL) {
  74. websWriteHeader(wp, "Last-Modified", "%s", date);
  75. wfree(date);
  76. }
  77. websWriteEndHeaders(wp);
  78. /*
  79. All done if the browser did a HEAD request
  80. */
  81. if (smatch(wp->method, "HEAD")) {
  82. websDone(wp);
  83. return 1;
  84. }
  85. if (info.size > 0) {
  86. websSetBackgroundWriter(wp, fileWriteEvent);
  87. } else {
  88. websDone(wp);
  89. }
  90. }
  91. return 1;
  92. }
  93. /*
  94. Do output back to the browser in the background. This is a socket write handler.
  95. This bypasses the output buffer and writes directly to the socket.
  96. */
  97. static void fileWriteEvent(Webs *wp)
  98. {
  99. char *buf;
  100. ssize len, wrote;
  101. int err;
  102. assert(wp);
  103. assert(websValid(wp));
  104. if ((buf = walloc(ME_GOAHEAD_LIMIT_BUFFER)) == NULL) {
  105. websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot get memory");
  106. return;
  107. }
  108. while ((len = websPageReadData(wp, buf, ME_GOAHEAD_LIMIT_BUFFER)) > 0) {
  109. if ((wrote = websWriteSocket(wp, buf, len)) < 0) {
  110. err = socketGetError(wp->sid);
  111. if (err == EWOULDBLOCK || err == EAGAIN) {
  112. websPageSeek(wp, -len, SEEK_CUR);
  113. } else {
  114. /* Will call websDone below */
  115. wp->state = WEBS_COMPLETE;
  116. }
  117. break;
  118. }
  119. if (wrote != len) {
  120. websPageSeek(wp, - (len - wrote), SEEK_CUR);
  121. break;
  122. }
  123. }
  124. wfree(buf);
  125. if (len <= 0) {
  126. websDone(wp);
  127. }
  128. }
  129. #if !ME_ROM
  130. PUBLIC bool websProcessPutData(Webs *wp)
  131. {
  132. ssize nbytes;
  133. assert(wp);
  134. assert(wp->putfd >= 0);
  135. assert(wp->input.buf);
  136. nbytes = bufLen(&wp->input);
  137. wp->putLen += nbytes;
  138. if (wp->putLen > ME_GOAHEAD_LIMIT_PUT) {
  139. websError(wp, HTTP_CODE_REQUEST_TOO_LARGE | WEBS_CLOSE, "Put file too large");
  140. } else if (write(wp->putfd, wp->input.servp, (int) nbytes) != nbytes) {
  141. websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR | WEBS_CLOSE, "Cannot write to file");
  142. }
  143. websConsumeInput(wp, nbytes);
  144. return 1;
  145. }
  146. #endif
  147. static void fileClose()
  148. {
  149. wfree(websIndex);
  150. websIndex = NULL;
  151. wfree(websDocuments);
  152. websDocuments = NULL;
  153. }
  154. PUBLIC void websFileOpen(void)
  155. {
  156. websIndex = sclone("index.html");
  157. websDefineHandler("file", 0, fileHandler, fileClose, 0);
  158. }
  159. /*
  160. Get the default page for URL requests ending in "/"
  161. */
  162. PUBLIC cchar *websGetIndex(void)
  163. {
  164. return websIndex;
  165. }
  166. PUBLIC char *websGetDocuments(void)
  167. {
  168. return websDocuments;
  169. }
  170. /*
  171. Set the default page for URL requests ending in "/"
  172. */
  173. PUBLIC void websSetIndex(cchar *page)
  174. {
  175. assert(page && *page);
  176. if (websIndex) {
  177. wfree(websIndex);
  178. }
  179. websIndex = sclone(page);
  180. }
  181. /*
  182. Set the default web directory
  183. */
  184. PUBLIC void websSetDocuments(cchar *dir)
  185. {
  186. assert(dir && *dir);
  187. if (websDocuments) {
  188. wfree(websDocuments);
  189. }
  190. websDocuments = sclone(dir);
  191. }
  192. /*
  193. Copyright (c) Embedthis Software. All Rights Reserved.
  194. This software is distributed under commercial and open source licenses.
  195. You may use the Embedthis GoAhead open source license or you may acquire
  196. a commercial license from Embedthis Software. You agree to be fully bound
  197. by the terms of either license. Consult the LICENSE.md distributed with
  198. this software for full details and other copyrights.
  199. */