add embeded http library so we can test remote scenarios
[platform/upstream/libzypp.git] / vendor / shttpd / io_cgi.c
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
11 #include "defs.h"
12
13 static int
14 write_cgi(struct stream *stream, const void *buf, size_t len)
15 {
16         assert(stream->chan.sock != -1);
17         assert(stream->flags & FLAG_W);
18
19         return (send(stream->chan.sock, buf, len, 0));
20 }
21
22 static int
23 read_cgi(struct stream *stream, void *buf, size_t len)
24 {
25         struct headers  parsed;
26         char            status[4];
27         int             n;
28
29         assert(stream->chan.sock != -1);
30         assert(stream->flags & FLAG_R);
31
32         stream->flags &= ~FLAG_DONT_CLOSE;
33
34         n = recv(stream->chan.sock, buf, len, 0);
35
36         if (stream->flags & FLAG_HEADERS_PARSED)
37                 return (n);
38
39         if (n <= 0 && ERRNO != EWOULDBLOCK) {
40                 _shttpd_send_server_error(stream->conn, 500,
41                     "Error running CGI");
42                 return (n);
43         }
44
45         /*
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.
52          */
53
54         stream->flags |= FLAG_DONT_CLOSE;
55         io_inc_head(&stream->io, n);
56
57         stream->headers_len = _shttpd_get_headers_len(stream->io.buf,
58             stream->io.head);
59         if (stream->headers_len < 0) {
60                 stream->flags &= ~FLAG_DONT_CLOSE;
61                 _shttpd_send_server_error(stream->conn, 500,
62                     "Bad headers sent");
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);
67                 return (0);
68         }
69
70         /*
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
74          */
75
76         if (stream->headers_len == 0) {
77                 stream->io.tail = stream->io.head;
78                 return (0);
79         }
80
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;
86
87         /* If script outputs 'Location:' header, set status code to 302 */
88         if (parsed.location.v_vec.len > 0)
89                 stream->conn->status = 302;
90
91         /*
92          * If script did not output neither 'Location:' nor 'Status' headers,
93          * set the default status code 200, which means 'success'.
94          */
95         if (stream->conn->status == 0)
96                 stream->conn->status = 200;
97
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));
104
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;
108         stream->io.tail = 0;
109         stream->flags |= FLAG_HEADERS_PARSED;
110
111         /* Return 0 because we've already shifted the head */
112         return (0);
113 }
114
115 static void
116 close_cgi(struct stream *stream)
117 {
118         assert(stream->chan.sock != -1);
119         (void) closesocket(stream->chan.sock);
120 }
121
122 const struct io_class   _shttpd_io_cgi =  {
123         "cgi",
124         read_cgi,
125         write_cgi,
126         close_cgi
127 };