/* Parse the CGI output headers. Sample CGI program output: Content-type: text/html <html..... */ static bool parseCgiHeaders(Cgi *cgi, HttpPacket *packet) { HttpConn *conn; MprBuf *buf; char *endHeaders, *headers, *key, *value; ssize blen; int len; conn = cgi->conn; value = 0; buf = packet->content; headers = mprGetBufStart(buf); blen = mprGetBufLength(buf); /* Split the headers from the body. Add null to ensure we can search for line terminators. */ len = 0; if ((endHeaders = sncontains(headers, "\r\n\r\n", blen)) == NULL) { if ((endHeaders = sncontains(headers, "\n\n", blen)) == NULL) { if (mprGetCmdFd(cgi->cmd, MPR_CMD_STDOUT) >= 0 && strlen(headers) < ME_MAX_HEADERS) { /* Not EOF and less than max headers and have not yet seen an end of headers delimiter */ return 0; } } len = 2; } else { len = 4; } if (endHeaders > buf->end) { assert(endHeaders <= buf->end); return 0; } if (endHeaders) { endHeaders[len - 1] = '\0'; endHeaders += len; } /* Want to be tolerant of CGI programs that omit the status line. */ if (strncmp((char*) buf->start, "HTTP/1.", 7) == 0) { if (!parseFirstCgiResponse(cgi, packet)) { /* httpError already called */ return 0; } } if (endHeaders && strchr(mprGetBufStart(buf), ':')) { while (mprGetBufLength(buf) > 0 && buf->start[0] && (buf->start[0] != '\r' && buf->start[0] != '\n')) { if ((key = getCgiToken(buf, ":")) == 0) { key = "Bad Header"; } value = getCgiToken(buf, "\n"); while (isspace((uchar) *value)) { value++; } len = (int) strlen(value); while (len > 0 && (value[len - 1] == '\r' || value[len - 1] == '\n')) { value[len - 1] = '\0'; len--; } key = slower(key); if (strcmp(key, "location") == 0) { cgi->location = value; } else if (strcmp(key, "status") == 0) { httpSetStatus(conn, atoi(value)); } else if (strcmp(key, "content-type") == 0) { httpSetHeaderString(conn, "Content-Type", value); } else if (strcmp(key, "content-length") == 0) { httpSetContentLength(conn, (MprOff) stoi(value)); httpSetChunkSize(conn, 0); } else { /* Now pass all other headers back to the client */ key = ssplit(key, ":\r\n\t ", NULL); httpSetHeaderString(conn, key, value); } } buf->start = endHeaders; } return 1; }
/* Parse the CGI output headers. Sample CGI program: Content-type: text/html <html..... */ static bool parseHeader(MaConn *conn, MprCmd *cmd) { MaResponse *resp; MaQueue *q; MprBuf *buf; char *endHeaders, *headers, *key, *value, *location; int fd, len; resp = conn->response; location = 0; value = 0; buf = mprGetCmdBuf(cmd, MPR_CMD_STDOUT); mprAddNullToBuf(buf); headers = mprGetBufStart(buf); /* Split the headers from the body. */ len = 0; fd = mprGetCmdFd(cmd, MPR_CMD_STDOUT); if ((endHeaders = strstr(headers, "\r\n\r\n")) == NULL) { if ((endHeaders = strstr(headers, "\n\n")) == NULL) { if (fd >= 0 && strlen(headers) < MA_MAX_HEADERS) { /* Not EOF and less than max headers and have not yet seen an end of headers delimiter */ return 0; } } else len = 2; } else { len = 4; } if (endHeaders) { endHeaders[len - 1] = '\0'; endHeaders += len; } /* Want to be tolerant of CGI programs that omit the status line. */ if (strncmp((char*) buf->start, "HTTP/1.", 7) == 0) { if (!parseFirstCgiResponse(conn, cmd)) { /* maFailConnection already called */ return 0; } } if (endHeaders && strchr(mprGetBufStart(buf), ':')) { mprLog(conn, 4, "CGI: parseHeader: header\n%s", headers); while (mprGetBufLength(buf) > 0 && buf->start[0] && (buf->start[0] != '\r' && buf->start[0] != '\n')) { if ((key = getCgiToken(buf, ":")) == 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad header format"); return 0; } value = getCgiToken(buf, "\n"); while (isspace((int) *value)) { value++; } len = (int) strlen(value); while (len > 0 && (value[len - 1] == '\r' || value[len - 1] == '\n')) { value[len - 1] = '\0'; len--; } mprStrLower(key); if (strcmp(key, "location") == 0) { location = value; } else if (strcmp(key, "status") == 0) { maSetResponseCode(conn, atoi(value)); } else if (strcmp(key, "content-type") == 0) { maSetResponseMimeType(conn, value); } else if (strcmp(key, "content-length") == 0) { maSetEntityLength(conn, (MprOff) mprAtoi(value, 10)); resp->chunkSize = 0; } else { /* Now pass all other headers back to the client */ maSetHeader(conn, 0, key, "%s", value); } } buf->start = endHeaders; } if (location) { maRedirect(conn, resp->code, location); q = resp->queue[MA_QUEUE_SEND].nextQ; maPutForService(q, maCreateEndPacket(q), 1); } cmd->userFlags |= MA_CGI_SEEN_HEADER; return 1; }