[autobuild] allow sendfile() in cross-compile (fixes #2836)
[lighttpd.git] / tests / scgi-responder.c
blob0c4244b7792e9dfd61b1817771130eb0697aa00e
1 /*
2 * simple and trivial SCGI server with hard-coded results for use in unit tests
3 * - listens on STDIN_FILENO (socket on STDIN_FILENO must be set up by caller)
4 * - processes a single SCGI request at a time
5 * - arbitrary limitation: reads request headers netstring up to 64k in size
6 * - expect recv data for request headers netstring every 10ms or less
7 * - no read timeouts for request body; might block reading request body
8 * - no write timeouts; might block writing response
9 */
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <assert.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <limits.h>
17 #include <poll.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <unistd.h>
23 #ifndef MSG_DONTWAIT
24 #define MSG_DONTWAIT 0
25 #endif
27 static int finished;
28 static char buf[65536];
31 static char *
32 scgi_getenv(char *r, const unsigned long rlen, const char * const name)
34 /* simple search;
35 * if many lookups are done, then should use more efficient data structure*/
36 char * const end = r+rlen;
37 char *z;
38 const size_t len = strlen(name);
39 do {
40 if (0 == strcmp(r, name)) return r+len+1;
42 z = memchr(r, '\0', (size_t)(end-r));
43 if (NULL == z) return NULL;
44 z = memchr(z+1, '\0', (size_t)(end-r));
45 if (NULL == z) return NULL;
46 r = z+1;
47 } while (r < end);
48 return NULL;
52 static void
53 scgi_process (const int fd)
55 ssize_t rd = 0, offset = 0;
56 int num_requests = 1;
57 char *p = NULL, *r;
58 unsigned long rlen;
59 long long cl;
61 assert(fd == STDOUT_FILENO); /*(required for response sent with printf())*/
62 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
64 do {
65 struct pollfd pfd = { fd, POLLIN, 0 };
66 switch (poll(&pfd, 1, 10)) { /* 10ms timeout */
67 default: /* 1; the only pfd has revents */
68 break;
69 case -1: /* error */
70 case 0: /* timeout */
71 pfd.revents |= POLLERR;
72 break;
74 if (!(pfd.revents & POLLIN))
75 break;
76 do {
77 rd = recv(fd, buf+offset, sizeof(buf)-offset, MSG_DONTWAIT);
78 } while (rd < 0 && errno == EINTR);
79 if (rd > 0)
80 offset += rd;
81 else if (0 == rd) {
82 p = memchr(buf, ':', offset);
83 break;
85 else if (errno == EAGAIN || errno == EWOULDBLOCK)
86 continue;
87 else
88 break;
89 } while (NULL == (p = memchr(buf,':',offset)) && offset < 21);
90 if (NULL == p)
91 return; /* timeout or error receiving start of netstring */
92 rlen = strtoul(buf, &p, 10);
93 if (*buf == '-' || *p != ':' || p == buf || rlen == ULONG_MAX)
94 return; /* invalid netstring (and rlen == ULONG_MAX is too long)*/
95 if (rlen > sizeof(buf) - (p - buf) - 2)
96 return; /* netstring longer than arbitrary limit we accept here */
97 rlen += (p - buf) + 2;
99 while ((ssize_t)rlen < offset) {
100 struct pollfd pfd = { fd, POLLIN, 0 };
101 switch (poll(&pfd, 1, 10)) { /* 10ms timeout */
102 default: /* 1; the only pfd has revents */
103 break;
104 case -1: /* error */
105 case 0: /* timeout */
106 pfd.revents |= POLLERR;
107 break;
109 if (!(pfd.revents & POLLIN))
110 break;
111 do {
112 rd = recv(fd, buf+offset, sizeof(buf)-offset, MSG_DONTWAIT);
113 } while (rd < 0 && errno == EINTR);
114 if (rd > 0)
115 offset += rd;
116 else if (0 == rd)
117 break;
118 else if (errno == EAGAIN || errno == EWOULDBLOCK)
119 continue;
120 else
121 break;
123 if (offset < (ssize_t)rlen)
124 return; /* timeout or error receiving netstring */
125 if (buf[rlen-1] != ',')
126 return; /* invalid netstring */
127 rlen -= (p - buf) + 2;
128 r = p+1;
130 /* not checking for empty headers in SCGI request (empty values allowed) */
132 /* SCGI request must contain "SCGI" header with value "1" */
133 p = scgi_getenv(r, rlen, "SCGI");
134 if (NULL == p || p[0] != '1' || p[1] != '\0')
135 return; /* missing or invalid SCGI header */
137 /* CONTENT_LENGTH must be first header in SCGI request; always required */
138 if (0 != strcmp(r, "CONTENT_LENGTH"))
139 return; /* missing CONTENT_LENGTH */
141 errno = 0;
142 cl = strtoll(r+sizeof("CONTENT_LENGTH"), &p, 10);
143 if (*p != '\0' || p == r+sizeof("CONTENT_LENGTH") || cl < 0 || 0 != errno)
144 return; /* invalid CONTENT_LENGTH */
146 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
148 /* read,discard request body (currently ignored in these SCGI unit tests)
149 * (make basic effort to read body; ignore any timeouts or errors here) */
150 cl -= (offset - (r+rlen+1 - buf));
151 while (cl > 0) {
152 char x[8192];
153 do {
154 rd = recv(fd, x, (cl>(long long)sizeof(x)?sizeof(x):(size_t)cl), 0);
155 } while (rd < 0 && errno == EINTR);
156 if (rd <= 0)
157 break;
158 cl -= rd;
161 /*(from fcgi-responder.c, substituting scgi_getenv() for getenv())*/
163 if (NULL != (p = scgi_getenv(r, rlen, "QUERY_STRING"))) {
164 if (0 == strcmp(p, "lf")) {
165 printf("Status: 200 OK\n\n");
166 } else if (0 == strcmp(p, "crlf")) {
167 printf("Status: 200 OK\r\n\r\n");
168 } else if (0 == strcmp(p, "slow-lf")) {
169 printf("Status: 200 OK\n");
170 fflush(stdout);
171 printf("\n");
172 } else if (0 == strcmp(p,"slow-crlf")) {
173 printf("Status: 200 OK\r\n");
174 fflush(stdout);
175 printf("\r\n");
176 } else if (0 == strcmp(p, "die-at-end")) {
177 printf("Status: 200 OK\r\n\r\n");
178 num_requests--;
179 } else {
180 printf("Status: 200 OK\r\n\r\n");
182 } else {
183 printf("Status: 500 Internal Foo\r\n\r\n");
186 if (0 == strcmp(p, "path_info")) {
187 printf("%s", scgi_getenv(r, rlen, "PATH_INFO"));
188 } else if (0 == strcmp(p, "script_name")) {
189 printf("%s", scgi_getenv(r, rlen, "SCRIPT_NAME"));
190 } else if (0 == strcmp(p, "var")) {
191 p = scgi_getenv(r, rlen, "X_LIGHTTPD_FCGI_AUTH");
192 printf("%s", p ? p : "(no value)");
193 } else {
194 printf("test123");
198 fflush(stdout);
199 if (0 == num_requests) finished = 1;
204 main (void)
206 int fd;
207 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) & ~O_NONBLOCK);
208 close(STDOUT_FILENO); /*(so that accept() returns fd to STDOUT_FILENO)*/
210 do {
211 fd = accept(STDIN_FILENO, NULL, NULL);
212 if (fd < 0)
213 continue;
214 assert(fd == STDOUT_FILENO);
215 scgi_process(fd);
216 } while (fd > 0 ? 0 == close(fd) && !finished : errno == EINTR);
218 return 0;