io_file.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
  3. * All rights reserved
  4. *
  5. * "THE BEER-WARE LICENSE" (Revision 42):
  6. * Sergey Lyubka wrote this file. As long as you retain this notice you
  7. * can do whatever you want with this stuff. If we meet some day, and you think
  8. * this stuff is worth it, you can buy me a beer in return.
  9. */
  10. #include "defs.h"
  11. static int
  12. write_file(struct stream *stream, const void *buf, size_t len)
  13. {
  14. struct stat st;
  15. struct stream *rem = &stream->conn->rem;
  16. int n, fd = stream->chan.fd;
  17. assert(fd != -1);
  18. n = write(fd, buf, len);
  19. DBG(("put_file(%p, %d): %d bytes", (void *) stream, (int) len, n));
  20. if (n <= 0 || (rem->io.total >= (big_int_t) rem->content_len)) {
  21. (void) fstat(fd, &st);
  22. stream->io.head = stream->headers_len =
  23. _shttpd_snprintf(stream->io.buf,
  24. stream->io.size, "HTTP/1.1 %d OK\r\n"
  25. "Content-Length: %lu\r\nConnection: close\r\n\r\n",
  26. stream->conn->status, st.st_size);
  27. _shttpd_stop_stream(stream);
  28. }
  29. return (n);
  30. }
  31. static int
  32. read_file(struct stream *stream, void *buf, size_t len)
  33. {
  34. #ifdef USE_SENDFILE
  35. struct iovec vec;
  36. struct sf_hdtr hd = {&vec, 1, NULL, 0}, *hdp = &hd;
  37. int sock, fd, n;
  38. size_t nbytes;
  39. off_t sent;
  40. sock = stream->conn->rem.chan.sock;
  41. fd = stream->chan.fd;
  42. /* If this is the first call for this file, send the headers */
  43. vec.iov_base = stream->io.buf;
  44. vec.iov_len = stream->headers_len;
  45. if (stream->io.total > 0)
  46. hdp = NULL;
  47. nbytes = stream->content_len - stream->io.total;
  48. n = sendfile(fd, sock, lseek(fd, 0, SEEK_CUR), nbytes, hdp, &sent, 0);
  49. if (n == -1 && ERRNO != EAGAIN) {
  50. stream->flags &= ~FLAG_DONT_CLOSE;
  51. return (n);
  52. }
  53. stream->conn->ctx->out += sent;
  54. /* If we have sent the HTTP headers in this turn, clear them off */
  55. if (stream->io.total == 0) {
  56. assert(sent >= stream->headers_len);
  57. sent -= stream->headers_len;
  58. io_clear(&stream->io);
  59. }
  60. (void) lseek(fd, sent, SEEK_CUR);
  61. stream->io.total += sent;
  62. stream->flags |= FLAG_DONT_CLOSE;
  63. return (0);
  64. #endif /* USE_SENDFILE */
  65. assert(stream->chan.fd != -1);
  66. return (read(stream->chan.fd, buf, len));
  67. }
  68. static void
  69. close_file(struct stream *stream)
  70. {
  71. assert(stream->chan.fd != -1);
  72. (void) close(stream->chan.fd);
  73. }
  74. void
  75. _shttpd_get_file(struct conn *c, struct stat *stp)
  76. {
  77. char date[64], lm[64], etag[64], range[64] = "";
  78. size_t n, status = 200;
  79. unsigned long r1, r2;
  80. const char *fmt = "%a, %d %b %Y %H:%M:%S GMT", *msg = "OK";
  81. big_int_t cl; /* Content-Length */
  82. if (c->mime_type.len == 0)
  83. _shttpd_get_mime_type(c->ctx, c->uri,
  84. strlen(c->uri), &c->mime_type);
  85. cl = (big_int_t) stp->st_size;
  86. /* If Range: header specified, act accordingly */
  87. if (c->ch.range.v_vec.len > 0 &&
  88. (n = sscanf(c->ch.range.v_vec.ptr,"bytes=%lu-%lu",&r1, &r2)) > 0) {
  89. status = 206;
  90. (void) lseek(c->loc.chan.fd, r1, SEEK_SET);
  91. cl = n == 2 ? r2 - r1 + 1: cl - r1;
  92. (void) _shttpd_snprintf(range, sizeof(range),
  93. "Content-Range: bytes %lu-%lu/%lu\r\n",
  94. r1, r1 + cl - 1, (unsigned long) stp->st_size);
  95. msg = "Partial Content";
  96. }
  97. /* Prepare Etag, Date, Last-Modified headers */
  98. (void) strftime(date, sizeof(date),
  99. fmt, localtime(&_shttpd_current_time));
  100. (void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
  101. (void) _shttpd_snprintf(etag, sizeof(etag), "%lx.%lx",
  102. (unsigned long) stp->st_mtime, (unsigned long) stp->st_size);
  103. /*
  104. * We do not do io_inc_head here, because it will increase 'total'
  105. * member in io. We want 'total' to be equal to the content size,
  106. * and exclude the headers length from it.
  107. */
  108. c->loc.io.head = c->loc.headers_len = _shttpd_snprintf(c->loc.io.buf,
  109. c->loc.io.size,
  110. "HTTP/1.1 %d %s\r\n"
  111. "Date: %s\r\n"
  112. "Last-Modified: %s\r\n"
  113. "Etag: \"%s\"\r\n"
  114. "Content-Type: %.*s\r\n"
  115. "Content-Length: %lu\r\n"
  116. "Accept-Ranges: bytes\r\n"
  117. "%s\r\n",
  118. status, msg, date, lm, etag,
  119. c->mime_type.len, c->mime_type.ptr, cl, range);
  120. c->status = status;
  121. c->loc.content_len = cl;
  122. c->loc.io_class = &_shttpd_io_file;
  123. c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
  124. if (c->method == METHOD_HEAD)
  125. _shttpd_stop_stream(&c->loc);
  126. }
  127. const struct io_class _shttpd_io_file = {
  128. "file",
  129. read_file,
  130. write_file,
  131. close_file
  132. };