io_cgi.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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_cgi(struct stream *stream, const void *buf, size_t len)
  13. {
  14. assert(stream->chan.sock != -1);
  15. assert(stream->flags & FLAG_W);
  16. return (send(stream->chan.sock, buf, len, 0));
  17. }
  18. static int
  19. read_cgi(struct stream *stream, void *buf, size_t len)
  20. {
  21. struct headers parsed;
  22. char status[4];
  23. int n;
  24. assert(stream->chan.sock != -1);
  25. assert(stream->flags & FLAG_R);
  26. stream->flags &= ~FLAG_DONT_CLOSE;
  27. n = recv(stream->chan.sock, buf, len, 0);
  28. if (stream->flags & FLAG_HEADERS_PARSED)
  29. return (n);
  30. if (n <= 0 && ERRNO != EWOULDBLOCK) {
  31. _shttpd_send_server_error(stream->conn, 500,
  32. "Error running CGI");
  33. return (n);
  34. }
  35. /*
  36. * CGI script may output Status: and Location: headers, which
  37. * may alter the status code. Buffer in headers, parse
  38. * them, send correct status code and then forward all data
  39. * from CGI script back to the remote end.
  40. * Reply line was alredy appended to the IO buffer in
  41. * decide_what_to_do(), with blank status code.
  42. */
  43. stream->flags |= FLAG_DONT_CLOSE;
  44. io_inc_head(&stream->io, n);
  45. stream->headers_len = _shttpd_get_headers_len(stream->io.buf,
  46. stream->io.head);
  47. if (stream->headers_len < 0) {
  48. stream->flags &= ~FLAG_DONT_CLOSE;
  49. _shttpd_send_server_error(stream->conn, 500,
  50. "Bad headers sent");
  51. _shttpd_elog(E_LOG, stream->conn,
  52. "CGI script sent invalid headers: "
  53. "[%.*s]", stream->io.head - CGI_REPLY_LEN,
  54. stream->io.buf + CGI_REPLY_LEN);
  55. return (0);
  56. }
  57. /*
  58. * If we did not received full headers yet, we must not send any
  59. * data read from the CGI back to the client. Suspend sending by
  60. * setting tail = head, which tells that there is no data in IO buffer
  61. */
  62. if (stream->headers_len == 0) {
  63. stream->io.tail = stream->io.head;
  64. return (0);
  65. }
  66. /* Received all headers. Set status code for the connection. */
  67. (void) memset(&parsed, 0, sizeof(parsed));
  68. _shttpd_parse_headers(stream->io.buf, stream->headers_len, &parsed);
  69. stream->content_len = parsed.cl.v_big_int;
  70. stream->conn->status = (int) parsed.status.v_big_int;
  71. /* If script outputs 'Location:' header, set status code to 302 */
  72. if (parsed.location.v_vec.len > 0)
  73. stream->conn->status = 302;
  74. /*
  75. * If script did not output neither 'Location:' nor 'Status' headers,
  76. * set the default status code 200, which means 'success'.
  77. */
  78. if (stream->conn->status == 0)
  79. stream->conn->status = 200;
  80. /* Append the status line to the beginning of the output */
  81. (void) _shttpd_snprintf(status,
  82. sizeof(status), "%3d", stream->conn->status);
  83. (void) memcpy(stream->io.buf + 9, status, 3);
  84. DBG(("read_cgi: content len %lu status %s",
  85. stream->content_len, status));
  86. /* Next time, pass output directly back to the client */
  87. assert((big_int_t) stream->headers_len <= stream->io.total);
  88. stream->io.total -= stream->headers_len;
  89. stream->io.tail = 0;
  90. stream->flags |= FLAG_HEADERS_PARSED;
  91. /* Return 0 because we've already shifted the head */
  92. return (0);
  93. }
  94. static void
  95. close_cgi(struct stream *stream)
  96. {
  97. assert(stream->chan.sock != -1);
  98. (void) closesocket(stream->chan.sock);
  99. }
  100. const struct io_class _shttpd_io_cgi = {
  101. "cgi",
  102. read_cgi,
  103. write_cgi,
  104. close_cgi
  105. };