in netutils/thttpd/thttpd_cgi.c [443:711]
static inline int cgi_interpose_output(struct cgi_conn_s *cc)
{
ssize_t nbytes_read;
char *br = NULL;
int status;
const char *title;
char *cp;
/* Make sure the connection is in blocking mode. It should already be
* blocking, but we might as well be sure.
*/
httpd_clear_ndelay(cc->connfd);
/* Loop while there are things we can do without waiting for more input */
ninfo("state: %d\n", cc->outbuf.state);
switch (cc->outbuf.state)
{
case CGI_OUTBUFFER_READHEADER:
{
/* Slurp in all headers as they become available from the client. */
do
{
/* Read until we successfully read data or until an error
* occurs. EAGAIN is not an error, but it is still cause to
* return.
*/
nbytes_read = read(cc->rdfd, cc->inbuf.buffer,
CONFIG_THTTPD_CGIINBUFFERSIZE);
ninfo("Read %d bytes from fd %d\n", nbytes_read, cc->rdfd);
if (nbytes_read < 0)
{
if (errno != EINTR)
{
if (errno != EAGAIN)
{
nerr("ERROR: read: %d\n", errno);
}
return 1;
}
}
else
{
cgi_dumpbuffer("Received from CGI:", cc->inbuf.buffer,
nbytes_read);
}
}
while (nbytes_read < 0);
/* Check for end-of-file */
if (nbytes_read <= 0)
{
ninfo("End-of-file\n");
br = &(cc->outbuf.buffer[cc->outbuf.len]);
cc->outbuf.state = CGI_OUTBUFFER_HEADERREAD;
}
else
{
/* Accumulate more header data */
httpd_realloc_str(&cc->outbuf.buffer, &cc->outbuf.size,
cc->outbuf.len + nbytes_read);
memcpy(&(cc->outbuf.buffer[cc->outbuf.len]), cc->inbuf.buffer,
nbytes_read);
cc->outbuf.len += nbytes_read;
cc->outbuf.buffer[cc->outbuf.len] = '\0';
ninfo("Header bytes accumulated: %d\n", cc->outbuf.len);
/* Check for end of header */
if ((br = strstr(cc->outbuf.buffer, "\r\n\r\n")) != NULL ||
(br = strstr(cc->outbuf.buffer, "\012\012")) != NULL)
{
ninfo("End-of-header\n");
cc->outbuf.state = CGI_OUTBUFFER_HEADERREAD;
}
else
{
/* All of the headers have not yet been read ... Return.
* We will be called again when more data is available
* in the pipe connected to the CGI task.
*/
return 0;
}
}
}
/* Otherwise, fall through and parse status in the HTTP headers */
case CGI_OUTBUFFER_HEADERREAD:
{
/* If there were no headers, bail. */
if (cc->outbuf.buffer[0] == '\0')
{
cc->outbuf.state = CGI_OUTBUFFER_DONE;
return 1;
}
/* Figure out the status. Look for a Status: or Location: header;
* else if there's an HTTP header line, get it from there; else
* default to 200.
*/
status = 200;
if (strncmp(cc->outbuf.buffer, "HTTP/", 5) == 0)
{
cp = cc->outbuf.buffer;
cp += strcspn(cp, " \t");
status = atoi(cp);
}
if ((cp = strstr(cc->outbuf.buffer, "Status:")) != NULL &&
cp < br && (cp == cc->outbuf.buffer || *(cp - 1) == '\012'))
{
cp += 7;
cp += strspn(cp, " \t");
status = atoi(cp);
}
if ((cp = strstr(cc->outbuf.buffer, "Location:")) != NULL &&
cp < br && (cp == cc->outbuf.buffer || *(cp - 1) == '\012'))
{
status = 302;
}
/* Write the status line. */
ninfo("Status: %d\n", status);
switch (status)
{
case 200:
title = ok200title;
break;
case 302:
title = err302title;
break;
case 304:
title = err304title;
break;
case 400:
BADREQUEST("status");
title = httpd_err400title;
break;
#ifdef CONFIG_THTTPD_AUTH_FILE
case 401:
title = err401title;
break;
#endif
case 403:
title = err403title;
break;
case 404:
title = err404title;
break;
case 408:
title = httpd_err408title;
break;
case 500:
INTERNALERROR("status");
title = err500title;
break;
case 501:
NOTIMPLEMENTED("status");
title = err501title;
break;
case 503:
title = httpd_err503title;
break;
default:
title = "Something";
break;
}
snprintf(cc->inbuf.buffer, CONFIG_THTTPD_CGIINBUFFERSIZE,
"HTTP/1.0 %d %s\r\n", status, title);
httpd_write(cc->connfd, cc->inbuf.buffer,
strlen(cc->inbuf.buffer));
/* Write the saved cc->outbuf.buffer to the client. */
httpd_write(cc->connfd, cc->outbuf.buffer, cc->outbuf.len);
}
/* Then set up to read the data following the header from the CGI
* program and pass it back to the client. We return now; we will
* be called again when data is available on the pipe.
*/
cc->outbuf.state = CGI_OUTBUFFER_READDATA;
return 0;
case CGI_OUTBUFFER_READDATA:
{
/* Read data from the pipe. */
do
{
/* Read until we successfully read data or until an error
* occurs. EAGAIN is not an error, but it is still cause
* to return.
*/
nbytes_read = read(cc->rdfd, cc->inbuf.buffer,
CONFIG_THTTPD_CGIINBUFFERSIZE);
ninfo("Read %d bytes from fd %d\n", nbytes_read, cc->rdfd);
if (nbytes_read < 0)
{
if (errno != EINTR)
{
if (errno != EAGAIN)
{
nerr("ERROR: read: %d\n", errno);
}
return 1;
}
}
else
{
cgi_dumpbuffer("Received from CGI:", cc->inbuf.buffer,
nbytes_read);
}
}
while (nbytes_read < 0);
/* Check for end of file */
if (nbytes_read == 0)
{
ninfo("End-of-file\n");
cc->outbuf.state = CGI_OUTBUFFER_DONE;
return 1;
}
else
{
/* Forward the data from the CGI program to the client */
httpd_write(cc->connfd, cc->inbuf.buffer, nbytes_read);
}
}
break;
case CGI_OUTBUFFER_DONE:
default:
return 1;
}
return 0;
}