Add pragma to disable some level 4 compile warnings under Win32.
[mod_fastcgi.git] / fcgi_protocol.c
blob4e67d66b7a218572378be23e36a31f17af27ffab
1 /*
2 * $Id: fcgi_protocol.c,v 1.19 2001/03/26 15:23:24 robs Exp $
3 */
6 #include "fcgi.h"
7 #include "fcgi_protocol.h"
9 #ifdef WIN32
10 #pragma warning( disable : 4706)
11 #endif
13 /*******************************************************************************
14 * Build and queue a FastCGI message header. It is the caller's
15 * responsibility to make sure that there's enough space in the buffer, and
16 * that the data bytes (specified by 'len') are queued immediately following
17 * this header.
19 static void queue_header(fcgi_request *fr, unsigned char type, unsigned int len)
21 FCGI_Header header;
23 ap_assert(type > 0 && type <= FCGI_MAXTYPE);
24 ap_assert(len >= 0 && len <= 0xffff);
25 ap_assert(BufferFree(fr->serverOutputBuffer) >= sizeof(FCGI_Header));
27 /* Assemble and queue the packet header. */
28 header.version = FCGI_VERSION;
29 header.type = type;
30 header.requestIdB1 = (unsigned char) (fr->requestId >> 8);
31 header.requestIdB0 = (unsigned char) fr->requestId;
32 header.contentLengthB1 = (unsigned char) (len / 256); /* MSB */
33 header.contentLengthB0 = (unsigned char) (len % 256); /* LSB */
34 header.paddingLength = 0;
35 header.reserved = 0;
36 fcgi_buf_add_block(fr->serverOutputBuffer, (char *) &header, sizeof(FCGI_Header));
39 /*******************************************************************************
40 * Build a FCGI_BeginRequest message body.
42 static void build_begin_request(unsigned int role, unsigned char keepConnection,
43 FCGI_BeginRequestBody *body)
45 ap_assert((role >> 16) == 0);
46 body->roleB1 = (unsigned char) (role >> 8);
47 body->roleB0 = (unsigned char) role;
48 body->flags = (unsigned char) ((keepConnection) ? FCGI_KEEP_CONN : 0);
49 memset(body->reserved, 0, sizeof(body->reserved));
52 /*******************************************************************************
53 * Build and queue a FastCGI "Begin Request" message.
55 void fcgi_protocol_queue_begin_request(fcgi_request *fr)
57 FCGI_BeginRequestBody body;
58 int bodySize = sizeof(FCGI_BeginRequestBody);
60 /* We should be the first ones to use this buffer */
61 ap_assert(BufferLength(fr->serverOutputBuffer) == 0);
63 build_begin_request(fr->role, FALSE, &body);
64 queue_header(fr, FCGI_BEGIN_REQUEST, bodySize);
65 fcgi_buf_add_block(fr->serverOutputBuffer, (char *) &body, bodySize);
68 /*******************************************************************************
69 * Build a FastCGI name-value pair (env) header.
71 static void build_env_header(int nameLen, int valueLen,
72 unsigned char *headerBuffPtr, int *headerLenPtr)
74 unsigned char *startHeaderBuffPtr = headerBuffPtr;
76 ap_assert(nameLen >= 0);
78 if (nameLen < 0x80) {
79 *headerBuffPtr++ = (unsigned char) nameLen;
80 } else {
81 *headerBuffPtr++ = (unsigned char) ((nameLen >> 24) | 0x80);
82 *headerBuffPtr++ = (unsigned char) (nameLen >> 16);
83 *headerBuffPtr++ = (unsigned char) (nameLen >> 8);
84 *headerBuffPtr++ = (unsigned char) nameLen;
87 ap_assert(valueLen >= 0);
89 if (valueLen < 0x80) {
90 *headerBuffPtr++ = (unsigned char) valueLen;
91 } else {
92 *headerBuffPtr++ = (unsigned char) ((valueLen >> 24) | 0x80);
93 *headerBuffPtr++ = (unsigned char) (valueLen >> 16);
94 *headerBuffPtr++ = (unsigned char) (valueLen >> 8);
95 *headerBuffPtr++ = (unsigned char) valueLen;
97 *headerLenPtr = headerBuffPtr - startHeaderBuffPtr;
100 /* A static fn stolen from Apache's util_script.c...
101 * Obtain the Request-URI from the original request-line, returning
102 * a new string from the request pool containing the URI or "".
104 static char *apache_original_uri(request_rec *r)
106 char *first, *last;
108 if (r->the_request == NULL)
109 return (char *) ap_pcalloc(r->pool, 1);
111 first = r->the_request; /* use the request-line */
113 while (*first && !ap_isspace(*first))
114 ++first; /* skip over the method */
116 while (ap_isspace(*first))
117 ++first; /* and the space(s) */
119 last = first;
120 while (*last && !ap_isspace(*last))
121 ++last; /* end at next whitespace */
123 return ap_pstrndup(r->pool, first, last - first);
126 /* Based on Apache's ap_add_cgi_vars() in util_script.c.
127 * Apache's spins in sub_req_lookup_uri() trying to setup PATH_TRANSLATED,
128 * so we just don't do that part.
130 static void add_auth_cgi_vars(request_rec *r, const int compat)
132 table *e = r->subprocess_env;
134 ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1");
135 ap_table_setn(e, "SERVER_PROTOCOL", r->protocol);
136 ap_table_setn(e, "REQUEST_METHOD", r->method);
137 ap_table_setn(e, "QUERY_STRING", r->args ? r->args : "");
138 ap_table_setn(e, "REQUEST_URI", apache_original_uri(r));
140 /* The FastCGI spec precludes sending of CONTENT_LENGTH, PATH_INFO,
141 * PATH_TRANSLATED, and SCRIPT_NAME (for some reason?). PATH_TRANSLATED we
142 * don't have, its the variable that causes Apache to break trying to set
143 * up (and thus the reason this fn exists vs. using ap_add_cgi_vars()). */
144 if (compat) {
145 ap_table_unset(e, "CONTENT_LENGTH");
146 return;
149 /* Note that the code below special-cases scripts run from includes,
150 * because it "knows" that the sub_request has been hacked to have the
151 * args and path_info of the original request, and not any that may have
152 * come with the script URI in the include command. Ugh. */
153 if (!strcmp(r->protocol, "INCLUDED")) {
154 ap_table_setn(e, "SCRIPT_NAME", r->uri);
155 if (r->path_info && *r->path_info)
156 ap_table_setn(e, "PATH_INFO", r->path_info);
158 else if (!r->path_info || !*r->path_info)
159 ap_table_setn(e, "SCRIPT_NAME", r->uri);
160 else {
161 int path_info_start = ap_find_path_info(r->uri, r->path_info);
163 ap_table_setn(e, "SCRIPT_NAME", ap_pstrndup(r->pool, r->uri, path_info_start));
164 ap_table_setn(e, "PATH_INFO", r->path_info);
168 static void add_pass_header_vars(fcgi_request *fr)
170 const array_header *ph = fr->dynamic ? dynamic_pass_headers : fr->fs->pass_headers;
172 if (ph) {
173 const char **elt = (const char **)ph->elts;
174 int i = ph->nelts;
176 for ( ; i; --i, ++elt) {
177 const char *val = ap_table_get(fr->r->headers_in, *elt);
178 if (val) {
179 ap_table_setn(fr->r->subprocess_env, *elt, val);
185 /*******************************************************************************
186 * Build and queue the environment name-value pairs. Returns TRUE if the
187 * complete ENV was buffered, FALSE otherwise. Note: envp is updated to
188 * reflect the current position in the ENV.
190 int fcgi_protocol_queue_env(request_rec *r, fcgi_request *fr, env_status *env)
192 int charCount;
194 if (env->envp == NULL) {
195 ap_add_common_vars(r);
196 add_pass_header_vars(fr);
198 if (fr->role == FCGI_RESPONDER)
199 ap_add_cgi_vars(r);
200 else
201 add_auth_cgi_vars(r, fr->auth_compat);
203 env->envp = ap_create_environment(r->pool, r->subprocess_env);
204 env->pass = PREP;
207 while (*env->envp) {
208 switch (env->pass)
210 case PREP:
211 env->equalPtr = strchr(*env->envp, '=');
212 ap_assert(env->equalPtr != NULL);
213 env->nameLen = env->equalPtr - *env->envp;
214 env->valueLen = strlen(++env->equalPtr);
215 build_env_header(env->nameLen, env->valueLen, env->headerBuff, &env->headerLen);
216 env->totalLen = env->headerLen + env->nameLen + env->valueLen;
217 env->pass = HEADER;
218 /* drop through */
220 case HEADER:
221 if (BufferFree(fr->serverOutputBuffer) < (int)(sizeof(FCGI_Header) + env->headerLen)) {
222 return (FALSE);
224 queue_header(fr, FCGI_PARAMS, env->totalLen);
225 fcgi_buf_add_block(fr->serverOutputBuffer, (char *)env->headerBuff, env->headerLen);
226 env->pass = NAME;
227 /* drop through */
229 case NAME:
230 charCount = fcgi_buf_add_block(fr->serverOutputBuffer, *env->envp, env->nameLen);
231 if (charCount != env->nameLen) {
232 *env->envp += charCount;
233 env->nameLen -= charCount;
234 return (FALSE);
236 env->pass = VALUE;
237 /* drop through */
239 case VALUE:
240 charCount = fcgi_buf_add_block(fr->serverOutputBuffer, env->equalPtr, env->valueLen);
241 if (charCount != env->valueLen) {
242 env->equalPtr += charCount;
243 env->valueLen -= charCount;
244 return (FALSE);
246 env->pass = PREP;
248 ++env->envp;
251 if (BufferFree(fr->serverOutputBuffer) < sizeof(FCGI_Header)) {
252 return(FALSE);
254 queue_header(fr, FCGI_PARAMS, 0);
255 return(TRUE);
258 /*******************************************************************************
259 * Queue data from the client input buffer to the FastCGI server output
260 * buffer (encapsulating the data in FastCGI protocol messages).
262 void fcgi_protocol_queue_client_buffer(fcgi_request *fr)
264 int movelen;
265 int in_len, out_free;
267 if (fr->eofSent)
268 return;
271 * If there's some client data and room for at least one byte
272 * of data in the output buffer (after protocol overhead), then
273 * move some data to the output buffer.
275 in_len = BufferLength(fr->clientInputBuffer);
276 out_free = max(0, BufferFree(fr->serverOutputBuffer) - sizeof(FCGI_Header));
277 movelen = min(in_len, out_free);
278 if (movelen > 0) {
279 queue_header(fr, FCGI_STDIN, movelen);
280 fcgi_buf_get_to_buf(fr->serverOutputBuffer, fr->clientInputBuffer, movelen);
284 * If all the client data has been sent, and there's room
285 * in the output buffer, indicate EOF.
287 if (movelen == in_len && fr->expectingClientContent <= 0
288 && BufferFree(fr->serverOutputBuffer) >= sizeof(FCGI_Header))
290 queue_header(fr, FCGI_STDIN, 0);
291 fr->eofSent = TRUE;
295 /*******************************************************************************
296 * Read FastCGI protocol messages from the FastCGI server input buffer into
297 * fr->header when parsing headers, to fr->fs_stderr when reading stderr data,
298 * or to the client output buffer otherwises.
300 int fcgi_protocol_dequeue(pool *p, fcgi_request *fr)
302 FCGI_Header header;
303 int len;
305 while (BufferLength(fr->serverInputBuffer) > 0) {
307 * State #1: looking for the next complete packet header.
309 if (fr->gotHeader == FALSE) {
310 if (BufferLength(fr->serverInputBuffer) < sizeof(FCGI_Header)) {
311 return OK;
313 fcgi_buf_get_to_block(fr->serverInputBuffer, (char *) &header,
314 sizeof(FCGI_Header));
316 * XXX: Better handling of packets with other version numbers
317 * and other packet problems.
319 if (header.version != FCGI_VERSION) {
320 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
321 "FastCGI: comm with server \"%s\" aborted: protocol error: invalid version: %d != FCGI_VERSION(%d)",
322 fr->fs_path, header.version, FCGI_VERSION);
323 return SERVER_ERROR;
325 if (header.type > FCGI_MAXTYPE) {
326 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
327 "FastCGI: comm with server \"%s\" aborted: protocol error: invalid type: %d > FCGI_MAXTYPE(%d)",
328 fr->fs_path, header.type, FCGI_MAXTYPE);
329 return SERVER_ERROR;
332 fr->packetType = header.type;
333 fr->dataLen = (header.contentLengthB1 << 8)
334 + header.contentLengthB0;
335 fr->gotHeader = TRUE;
336 fr->paddingLen = header.paddingLength;
340 * State #2: got a header, and processing packet bytes.
342 len = min(fr->dataLen, BufferLength(fr->serverInputBuffer));
343 ap_assert(len >= 0);
344 switch (fr->packetType) {
345 case FCGI_STDOUT:
346 if (len > 0) {
347 switch(fr->parseHeader) {
348 case SCAN_CGI_READING_HEADERS:
349 fcgi_buf_get_to_array(fr->serverInputBuffer, fr->header, len);
350 break;
351 case SCAN_CGI_FINISHED:
352 len = min(BufferFree(fr->clientOutputBuffer), len);
353 if (len > 0) {
354 fcgi_buf_get_to_buf(fr->clientOutputBuffer, fr->serverInputBuffer, len);
355 } else {
356 return OK;
358 break;
359 default:
360 /* Toss data on the floor */
361 break;
363 fr->dataLen -= len;
365 break;
367 case FCGI_STDERR:
369 if (fr->fs_stderr == NULL)
371 fr->fs_stderr = ap_palloc(p, FCGI_SERVER_MAX_STDERR_LINE_LEN + 1);
374 /* We're gonna consume all thats here */
375 fr->dataLen -= len;
377 while (len > 0)
379 char *null, *end, *start = fr->fs_stderr;
381 /* Get as much as will fit in the buffer */
382 int get_len = min(len, FCGI_SERVER_MAX_STDERR_LINE_LEN - fr->fs_stderr_len);
383 fcgi_buf_get_to_block(fr->serverInputBuffer, start + fr->fs_stderr_len, get_len);
384 len -= get_len;
385 fr->fs_stderr_len += get_len;
386 *(start + fr->fs_stderr_len) = '\0';
388 /* Disallow nulls, we could be nicer but this is the motivator */
389 while ((null = memchr(start, '\0', fr->fs_stderr_len)))
391 int discard = ++null - start;
392 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
393 "FastCGI: server \"%s\" sent a null character in the stderr stream!?, "
394 "discarding %d characters of stderr", fr->fs_path, discard);
395 start = null;
396 fr->fs_stderr_len -= discard;
399 /* Print as much as possible */
400 while ((end = strpbrk(start, "\r\n")))
402 if (start != end)
404 *end = '\0';
405 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: server \"%s\" stderr: %s", fr->fs_path, start);
407 end += strspn(++end, "\r\n");
408 fr->fs_stderr_len -= (end - start);
409 start = end;
412 if (fr->fs_stderr_len)
414 if (start != fr->fs_stderr)
416 /* Move leftovers down */
417 memmove(fr->fs_stderr, start, fr->fs_stderr_len);
419 else if (fr->fs_stderr_len == FCGI_SERVER_MAX_STDERR_LINE_LEN)
421 /* Full buffer, dump it and complain */
422 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: server \"%s\" stderr: %s", fr->fs_path, fr->fs_stderr);
423 ap_log_rerror(FCGI_LOG_WARN_NOERRNO, fr->r,
424 "FastCGI: too much stderr received from server \"%s\", "
425 "increase FCGI_SERVER_MAX_STDERR_LINE_LEN (%d) and rebuild "
426 "or use \"\\n\" to terminate lines",
427 fr->fs_path, FCGI_SERVER_MAX_STDERR_LINE_LEN);
428 fr->fs_stderr_len = 0;
432 break;
434 case FCGI_END_REQUEST:
435 if (!fr->readingEndRequestBody) {
436 if (fr->dataLen != sizeof(FCGI_EndRequestBody)) {
437 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
438 "FastCGI: comm with server \"%s\" aborted: protocol error: invalid FCGI_END_REQUEST size: "
439 "%d != sizeof(FCGI_EndRequestBody)(%d)",
440 fr->fs_path, fr->dataLen, sizeof(FCGI_EndRequestBody));
441 return SERVER_ERROR;
443 fr->readingEndRequestBody = TRUE;
445 if (len>0) {
446 fcgi_buf_get_to_buf(fr->erBufPtr, fr->serverInputBuffer, len);
447 fr->dataLen -= len;
449 if (fr->dataLen == 0) {
450 FCGI_EndRequestBody *erBody = &fr->endRequestBody;
451 fcgi_buf_get_to_block(
452 fr->erBufPtr, (char *) &fr->endRequestBody,
453 sizeof(FCGI_EndRequestBody));
454 if (erBody->protocolStatus != FCGI_REQUEST_COMPLETE) {
456 * XXX: What to do with FCGI_OVERLOADED?
458 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
459 "FastCGI: comm with server \"%s\" aborted: protocol error: invalid FCGI_END_REQUEST status: "
460 "%d != FCGI_REQUEST_COMPLETE(%d)", fr->fs_path,
461 erBody->protocolStatus, FCGI_REQUEST_COMPLETE);
462 return SERVER_ERROR;
464 fr->exitStatus = (erBody->appStatusB3 << 24)
465 + (erBody->appStatusB2 << 16)
466 + (erBody->appStatusB1 << 8)
467 + (erBody->appStatusB0 );
468 fr->exitStatusSet = TRUE;
469 fr->readingEndRequestBody = FALSE;
471 break;
472 case FCGI_GET_VALUES_RESULT:
473 /* XXX coming soon */
474 case FCGI_UNKNOWN_TYPE:
475 /* XXX coming soon */
478 * XXX Ignore unknown packet types from the FastCGI server.
480 default:
481 fcgi_buf_toss(fr->serverInputBuffer, len);
482 fr->dataLen -= len;
483 break;
484 } /* switch */
487 * Discard padding, then start looking for
488 * the next header.
490 if (fr->dataLen == 0) {
491 if (fr->paddingLen > 0) {
492 len = min(fr->paddingLen,
493 BufferLength(fr->serverInputBuffer));
494 fcgi_buf_toss(fr->serverInputBuffer, len);
495 fr->paddingLen -= len;
497 if (fr->paddingLen == 0) {
498 fr->gotHeader = FALSE;
501 } /* while */
502 return OK;