2 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
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.
14 write_cgi(struct stream *stream, const void *buf, size_t len)
16 assert(stream->chan.sock != -1);
17 assert(stream->flags & FLAG_W);
19 return (send(stream->chan.sock, buf, len, 0));
23 read_cgi(struct stream *stream, void *buf, size_t len)
25 struct headers parsed;
29 assert(stream->chan.sock != -1);
30 assert(stream->flags & FLAG_R);
32 stream->flags &= ~FLAG_DONT_CLOSE;
34 n = recv(stream->chan.sock, buf, len, 0);
36 if (stream->flags & FLAG_HEADERS_PARSED)
39 if (n <= 0 && ERRNO != EWOULDBLOCK) {
40 _shttpd_send_server_error(stream->conn, 500,
46 * CGI script may output Status: and Location: headers, which
47 * may alter the status code. Buffer in headers, parse
48 * them, send correct status code and then forward all data
49 * from CGI script back to the remote end.
50 * Reply line was alredy appended to the IO buffer in
51 * decide_what_to_do(), with blank status code.
54 stream->flags |= FLAG_DONT_CLOSE;
55 io_inc_head(&stream->io, n);
57 stream->headers_len = _shttpd_get_headers_len(stream->io.buf,
59 if (stream->headers_len < 0) {
60 stream->flags &= ~FLAG_DONT_CLOSE;
61 _shttpd_send_server_error(stream->conn, 500,
63 _shttpd_elog(E_LOG, stream->conn,
64 "CGI script sent invalid headers: "
65 "[%.*s]", stream->io.head - CGI_REPLY_LEN,
66 stream->io.buf + CGI_REPLY_LEN);
71 * If we did not received full headers yet, we must not send any
72 * data read from the CGI back to the client. Suspend sending by
73 * setting tail = head, which tells that there is no data in IO buffer
76 if (stream->headers_len == 0) {
77 stream->io.tail = stream->io.head;
81 /* Received all headers. Set status code for the connection. */
82 (void) memset(&parsed, 0, sizeof(parsed));
83 _shttpd_parse_headers(stream->io.buf, stream->headers_len, &parsed);
84 stream->content_len = parsed.cl.v_big_int;
85 stream->conn->status = (int) parsed.status.v_big_int;
87 /* If script outputs 'Location:' header, set status code to 302 */
88 if (parsed.location.v_vec.len > 0)
89 stream->conn->status = 302;
92 * If script did not output neither 'Location:' nor 'Status' headers,
93 * set the default status code 200, which means 'success'.
95 if (stream->conn->status == 0)
96 stream->conn->status = 200;
98 /* Append the status line to the beginning of the output */
99 (void) _shttpd_snprintf(status,
100 sizeof(status), "%3d", stream->conn->status);
101 (void) memcpy(stream->io.buf + 9, status, 3);
102 DBG(("read_cgi: content len %lu status %s",
103 stream->content_len, status));
105 /* Next time, pass output directly back to the client */
106 assert((big_int_t) stream->headers_len <= stream->io.total);
107 stream->io.total -= stream->headers_len;
109 stream->flags |= FLAG_HEADERS_PARSED;
111 /* Return 0 because we've already shifted the head */
116 close_cgi(struct stream *stream)
118 assert(stream->chan.sock != -1);
119 (void) closesocket(stream->chan.sock);
122 const struct io_class _shttpd_io_cgi = {