/* * Split a packet at a given offset and return a new packet containing the data after the offset. * The suffix data migrates to the new packet. */ MaPacket *maSplitPacket(MprCtx ctx, MaPacket *orig, int offset) { MaPacket *packet; int count, size; if (orig->esize) { if ((packet = maCreateEntityPacket(ctx, orig->epos + offset, orig->esize - offset, orig->fill)) == 0) { return 0; } orig->esize = offset; } else { if (offset >= maGetPacketLength(orig)) { mprAssert(offset < maGetPacketLength(orig)); return 0; } count = maGetPacketLength(orig) - offset; size = max(count, MA_BUFSIZE); size = MA_PACKET_ALIGN(size); if ((packet = maCreateDataPacket(ctx, size)) == 0) { return 0; } mprAdjustBufEnd(orig->content, -count); if (mprPutBlockToBuf(packet->content, mprGetBufEnd(orig->content), count) != count) { return 0; } #if BLD_DEBUG mprAddNullToBuf(orig->content); #endif } packet->flags = orig->flags; return packet; }
/* Read the required number of bytes into the response content buffer. Count < 0 means transfer the entire content. Returns the number of bytes read. Returns null on EOF. */ static ssize readHttpData(Ejs *ejs, EjsHttp *hp, ssize count) { MprBuf *buf; HttpConn *conn; ssize len, space, nbytes; conn = hp->conn; buf = hp->responseContent; mprResetBufIfEmpty(buf); while (count < 0 || mprGetBufLength(buf) < count) { len = (count < 0) ? BIT_MAX_BUFFER : (count - mprGetBufLength(buf)); space = mprGetBufSpace(buf); if (space < len) { mprGrowBuf(buf, len - space); } if ((nbytes = httpRead(conn, mprGetBufEnd(buf), len)) < 0) { ejsThrowIOError(ejs, "Cannot read required data"); return MPR_ERR_CANT_READ; } mprAdjustBufEnd(buf, nbytes); if (hp->conn->async || (nbytes == 0 && conn->state > HTTP_STATE_CONTENT)) { break; } } if (count < 0) { return mprGetBufLength(buf); } return min(count, mprGetBufLength(buf)); }
/* Populate a packet with file data. Return the number of bytes read or a negative error code. Will not return with a short read. */ static ssize readFileData(HttpQueue *q, HttpPacket *packet, MprOff pos, ssize size) { HttpConn *conn; HttpTx *tx; ssize nbytes; // int btint = 0; conn = q->conn; tx = conn->tx; if (packet->content == 0 && (packet->content = mprCreateBuf(size, -1)) == 0) { return MPR_ERR_MEMORY; } assure(size <= mprGetBufSpace(packet->content)); mprLog(7, "readFileData size %d, pos %Ld", size, pos); if (pos >= 0) { mprSeekFile(tx->file, SEEK_SET, pos); } if ((nbytes = mprReadFile(tx->file, mprGetBufStart(packet->content), size)) != size) { /* As we may have sent some data already to the client, the only thing we can do is abort and hope the client notices the short data. */ httpError(conn, HTTP_CODE_SERVICE_UNAVAILABLE, "Can't read file %s", tx->filename); return MPR_ERR_CANT_READ; } mprAdjustBufEnd(packet->content, nbytes); packet->esize -= nbytes; assure(packet->esize == 0); return nbytes; }
/* Remove trailing whitespace in a token and ensure it is terminated with a NULL for easy parsing */ static void trimToken(MprXml *xp) { while (isspace(mprLookAtLastCharInBuf(xp->tokBuf))) { mprAdjustBufEnd(xp->tokBuf, -1); } mprAddNullToBuf(xp->tokBuf); }
/* * Process a socket readable event */ static void readEvent(MaConn *conn) { MaPacket *packet; MprBuf *content; int nbytes, len; do { if ((packet = getPacket(conn, &len)) == 0) { break; } mprAssert(len > 0); content = packet->content; nbytes = mprReadSocket(conn->sock, mprGetBufEnd(content), len); showRequest(content, nbytes, len); if (nbytes > 0) { mprAdjustBufEnd(content, nbytes); maProcessReadEvent(conn, packet); } else { if (mprIsSocketEof(conn->sock)) { conn->dedicated = 0; if (conn->request) { maProcessReadEvent(conn, packet); } } else if (nbytes < 0) { maFailConnection(conn, MPR_HTTP_CODE_COMMS_ERROR, "Communications read error"); } } } while (!conn->disconnected && conn->dedicated); }
/* Expand a template with {word} tokens from the given options objects function template(pattern: String, ...options): Uri */ static EjsUri *uri_template(Ejs *ejs, EjsUri *up, int argc, EjsObj **argv) { EjsArray *options; EjsObj *obj, *value; MprBuf *buf; cchar *pattern, *cp, *ep, *str; char *token; int i, len; pattern = ejsToMulti(ejs, argv[0]); options = (EjsArray*) argv[1]; buf = mprCreateBuf(-1, -1); for (cp = pattern; *cp; cp++) { if (*cp == '~' && (cp == pattern || cp[-1] != '\\')) { for (i = 0; i < options->length; i++) { obj = options->data[i]; if ((value = ejsGetPropertyByName(ejs, obj, N(NULL, "scriptName"))) != 0 && ejsIsDefined(ejs, value)) { str = ejsToMulti(ejs, value); if (str && *str) { mprPutStringToBuf(buf, str); break; } else { value = 0; } } } } else if (*cp == '{' && (cp == pattern || cp[-1] != '\\')) { if ((ep = strchr(++cp, '}')) != 0) { len = (int) (ep - cp); token = mprMemdup(cp, len + 1); token[len] = '\0'; value = 0; for (i = 0; i < options->length; i++) { obj = options->data[i]; if ((value = ejsGetPropertyByName(ejs, obj, N(NULL, token))) != 0 && ejsIsDefined(ejs, value)) { str = ejsToMulti(ejs, value); if (str && *str) { mprPutStringToBuf(buf, str); break; } else { value = 0; } } } if (!ejsIsDefined(ejs, value)) { // MOB - remove this. Should not be erasing the prior "/" if (cp >= &pattern[2] && cp[-2] == '/') { mprAdjustBufEnd(buf, -1); } } cp = ep; } } else { mprPutCharToBuf(buf, *cp); } } mprAddNullToBuf(buf); return ejsCreateUriFromAsc(ejs, mprGetBufStart(buf)); }
/* Parse the cert info and write properties to the buffer. Modifies the info argument. */ static void parseCertFields(MprBuf *buf, char *info) { char c, *cp, *term, *key, *value; if (info) { key = 0; term = cp = info; do { c = *cp; if (c == '/' || c == '\0') { *cp = '\0'; key = ssplit(term, "=", &value); if (smatch(key, "emailAddress")) { key = "email"; } mprPutToBuf(buf, "\"%s\":\"%s\",", key, value); term = &cp[1]; *cp = c; } } while (*cp++ != '\0'); if (key) { mprAdjustBufEnd(buf, -1); } } }
void maAdjustPacketEnd(MaPacket *packet, MprOff size) { if (packet->esize) { packet->esize += size; } else if (packet->content) { mprAdjustBufEnd(packet->content, (int) size); } }
/* * Default callback routine for the mprRunCmd routines. Uses may supply their own callback instead of this routine. * The callback is run whenever there is I/O to read/write to the CGI gateway. */ static int cmdCallback(MprCmd *cmd, int channel, void *data) { MprBuf *buf; int len, space; /* * Note: stdin, stdout and stderr are named from the client's perspective */ buf = 0; switch (channel) { case MPR_CMD_STDIN: return 0; case MPR_CMD_STDOUT: buf = cmd->stdoutBuf; break; case MPR_CMD_STDERR: buf = cmd->stderrBuf; break; } /* * Read and aggregate the result into a single string */ space = mprGetBufSpace(buf); if (space < (MPR_BUFSIZE / 4)) { if (mprGrowBuf(buf, MPR_BUFSIZE) < 0) { mprCloseCmdFd(cmd, channel); return 0; } space = mprGetBufSpace(buf); } len = mprReadCmdPipe(cmd, channel, mprGetBufEnd(buf), space); if (len <= 0) { if (len == 0 || (len < 0 && !(errno == EAGAIN || errno == EWOULDBLOCK))) { if (channel == MPR_CMD_STDOUT && cmd->flags & MPR_CMD_ERR) { /* * Now that stdout is complete, enable stderr to receive an EOF or any error output. * This is serialized to eliminate both stdin and stdout events on different threads at the same time. * Do before closing as the stderr event may come on another thread and we want to ensure avoid locking. */ mprCloseCmdFd(cmd, channel); } else { mprCloseCmdFd(cmd, channel); } return 0; } } else { mprAdjustBufEnd(buf, len); } return 0; }
/* Default callback routine for the mprRunCmd routines. Uses may supply their own callback instead of this routine. The callback is run whenever there is I/O to read/write to the CGI gateway. */ static void defaultCmdCallback(MprCmd *cmd, int channel, void *data) { MprBuf *buf; ssize len, space; int errCode; /* Note: stdin, stdout and stderr are named from the client's perspective */ buf = 0; switch (channel) { case MPR_CMD_STDIN: return; case MPR_CMD_STDOUT: buf = cmd->stdoutBuf; break; case MPR_CMD_STDERR: buf = cmd->stderrBuf; break; default: /* Child death notification */ return; } /* Read and aggregate the result into a single string */ space = mprGetBufSpace(buf); if (space < (ME_BUFSIZE / 4)) { if (mprGrowBuf(buf, ME_BUFSIZE) < 0) { mprCloseCmdFd(cmd, channel); return; } space = mprGetBufSpace(buf); } len = mprReadCmd(cmd, channel, mprGetBufEnd(buf), space); errCode = mprGetError(); if (len <= 0) { if (len == 0 || (len < 0 && !(errCode == EAGAIN || errCode == EWOULDBLOCK))) { mprCloseCmdFd(cmd, channel); return; } } else { mprAdjustBufEnd(buf, len); } mprAddNullToBuf(buf); mprEnableCmdEvents(cmd, channel); }
PUBLIC cchar *sjoinArgs(int argc, cchar **argv, cchar *sep) { MprBuf *buf; int i; if (sep == 0) { sep = ""; } buf = mprCreateBuf(0, 0); for (i = 0; i < argc; i++) { mprPutToBuf(buf, "%s%s", argv[i], sep); } if (argc > 0) { mprAdjustBufEnd(buf, -1); } return mprBufToString(buf); }
/* Fill the read buffer. Return the new buffer length. Only called when the buffer is empty. */ static ssize fillBuf(MprFile *file) { MprFileSystem *fs; MprBuf *bp; ssize len; bp = file->buf; fs = file->fileSystem; assert(mprGetBufLength(bp) == 0); mprFlushBuf(bp); len = fs->readFile(file, mprGetBufStart(bp), mprGetBufSpace(bp)); if (len <= 0) { return len; } mprAdjustBufEnd(bp, len); return len; }
/* Get the SSL state of the socket in a buffer */ static char *getOssState(MprSocket *sp) { OpenSocket *osp; MprBuf *buf; X509 *cert; char subject[512], issuer[512]; osp = sp->sslSocket; buf = mprCreateBuf(0, 0); mprPutToBuf(buf, "{\"provider\":\"openssl\",\"cipher\":\"%s\",\"session\":\"%s\",", SSL_get_cipher(osp->handle), sp->session); mprPutToBuf(buf, "\"peer\":\"%s\",", sp->peerName); mprPutToBuf(buf, "\"%s\":{", sp->acceptIp ? "client" : "server"); if ((cert = SSL_get_peer_certificate(osp->handle)) != 0) { X509_NAME_oneline(X509_get_issuer_name(cert), issuer, sizeof(issuer) -1); mprPutToBuf(buf, "\"issuer\": {"); parseCertFields(buf, &issuer[1]); mprPutToBuf(buf, "},"); X509_NAME_oneline(X509_get_subject_name(cert), subject, sizeof(subject) -1); mprPutToBuf(buf, "\"subject\": {"); parseCertFields(buf, &subject[1]); mprPutToBuf(buf, "},"); X509_free(cert); } if ((cert = SSL_get_certificate(osp->handle)) != 0) { mprPutToBuf(buf, "\"issuer\": {"); X509_NAME_oneline(X509_get_issuer_name(cert), issuer, sizeof(issuer) -1); parseCertFields(buf, &issuer[1]); mprPutToBuf(buf, "},"); mprPutToBuf(buf, "\"subject\": {"); X509_NAME_oneline(X509_get_subject_name(cert), subject, sizeof(subject) -1); parseCertFields(buf, &subject[1]); mprPutToBuf(buf, "},"); /* Don't call X509_free on own cert */ } mprAdjustBufEnd(buf, -1); mprPutToBuf(buf, "}}"); return mprBufToString(buf); }
/* Read data from the peer. This will use the existing conn->input packet or allocate a new packet if required to hold the data. The number of bytes read is stored in conn->lastRead. SSL connections are traced. Socket error messages are stored in conn->errorMsg. */ static void readPeerData(HttpConn *conn) { HttpPacket *packet; ssize size; if ((packet = getPacket(conn, &size)) != 0) { conn->lastRead = mprReadSocket(conn->sock, mprGetBufEnd(packet->content), size); if (conn->lastRead > 0) { mprAdjustBufEnd(packet->content, conn->lastRead); } else if (conn->lastRead < 0 && mprIsSocketEof(conn->sock)) { if (conn->state < HTTP_STATE_PARSED) { conn->error = 1; conn->rx->eof = 1; } conn->errorMsg = conn->sock->errorMsg ? conn->sock->errorMsg : sclone("Connection reset"); conn->keepAliveCount = 0; conn->lastRead = 0; httpTrace(conn, "connection.close", "context", "msg:'%s'", conn->errorMsg); } } }
char *mprGets(MprFile *file, char *buf, uint size) { MprBuf *bp; int count, len, c; mprAssert(file); if (file == 0) { return 0; } if (file->buf == 0) { file->buf = mprCreateBuf(file, MPR_DEFAULT_ALLOC, MPR_MAX_STRING); } bp = file->buf; /* * Must leave room for null */ count = 0; while (--size > 0) { if (mprGetBufLength(bp) == 0) { mprFlushBuf(bp); len = mprRead(file, mprGetBufEnd(bp), mprGetBufLinearSpace(bp)); if (len <= 0) { return 0; } mprAdjustBufEnd(bp, len); mprAddNullToBuf(bp); } if ((c = mprGetCharFromBuf(bp)) == '\n') { buf[count] = '\0'; return buf; } buf[count++] = c; } buf[count] = '\0'; return buf; }
/* Scan for a pattern. Trim the pattern from the token. Return 1 if the pattern was found, return 0 if not found. Return < 0 on errors. */ static int scanFor(MprXml *xp, char *pattern) { MprBuf *tokBuf; char *start, *p, *cp; int c; mprAssert(pattern); tokBuf = xp->tokBuf; mprAssert(tokBuf); start = mprGetBufStart(tokBuf); while (1) { cp = start; for (p = pattern; *p; p++) { if (cp >= (char*) tokBuf->end) { if ((c = getNextChar(xp)) < 0) { return 0; } if (mprPutCharToBuf(tokBuf, c) < 0) { return -1; } } if (*cp++ != *p) { break; } } if (*p == '\0') { /* Remove the pattern from the tokBuf */ mprAdjustBufEnd(tokBuf, - (int) slen(pattern)); trimToken(xp); return 1; } start++; } }
static int getPostData(MprCtx ctx, MprBuf *buf) { char *contentLength; int bytes, len, space; if ((contentLength = getenv("CONTENT_LENGTH")) != 0) { len = atoi(contentLength); } else { len = MAXINT; } while (len > 0) { space = mprGetBufSpace(buf); if (space < MPR_BUFSIZE) { if (mprGrowBuf(buf, MPR_BUFSIZE) < 0) { error(mpr, "Couldn't allocate memory to read post data"); return -1; } } space = mprGetBufSpace(buf); bytes = (int) read(0, mprGetBufEnd(buf), space); if (bytes < 0) { error(mpr, "Couldn't read CGI input %d", errno); return -1; } else if (bytes == 0) { /* EOF */ if (len > 0) { error(mpr, "Missing content (length %s)", contentLength); } break; } mprAdjustBufEnd(buf, bytes); len -= bytes; } mprAddNullToBuf(buf); return 0; }
/* Get another character. We read and buffer blocks of data if we need more data to parse. */ static int getNextChar(MprXml *xp) { MprBuf *inBuf; ssize l; int c; inBuf = xp->inBuf; if (mprGetBufLength(inBuf) <= 0) { /* Flush to reset the servp/endp pointers to the start of the buffer so we can do a maximal read */ mprFlushBuf(inBuf); l = (xp->readFn)(xp, xp->inputArg, mprGetBufStart(inBuf), mprGetBufSpace(inBuf)); if (l <= 0) { return -1; } mprAdjustBufEnd(inBuf, l); } c = mprGetCharFromBuf(inBuf); if (c == '\n') { xp->lineNumber++; } return c; }
static void readFromCgi(Cgi *cgi, int channel) { HttpConn *conn; HttpPacket *packet; HttpTx *tx; HttpQueue *q, *writeq; MprCmd *cmd; ssize nbytes; int err; cmd = cgi->cmd; conn = cgi->conn; tx = conn->tx; q = cgi->readq; writeq = conn->writeq; assert(conn->sock); assert(conn->state > HTTP_STATE_BEGIN); if (tx->finalized) { mprCloseCmdFd(cmd, channel); } while (mprGetCmdFd(cmd, channel) >= 0 && !tx->finalized && writeq->count < writeq->max) { if ((packet = cgi->headers) != 0) { if (mprGetBufSpace(packet->content) < ME_MAX_BUFFER && mprGrowBuf(packet->content, ME_MAX_BUFFER) < 0) { break; } } else if ((packet = httpCreateDataPacket(ME_MAX_BUFFER)) == 0) { break; } nbytes = mprReadCmd(cmd, channel, mprGetBufEnd(packet->content), ME_MAX_BUFFER); if (nbytes < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprCloseCmdFd(cmd, channel); break; } else if (nbytes == 0) { mprCloseCmdFd(cmd, channel); break; } else { traceData(cmd, mprGetBufEnd(packet->content), nbytes); mprAdjustBufEnd(packet->content, nbytes); } if (channel == MPR_CMD_STDERR) { mprLog("error cgi", 0, "CGI failed uri=\"%s\", details: %s", conn->rx->uri, mprGetBufStart(packet->content)); httpSetStatus(conn, HTTP_CODE_SERVICE_UNAVAILABLE); cgi->seenHeader = 1; } if (!cgi->seenHeader) { if (!parseCgiHeaders(cgi, packet)) { cgi->headers = packet; return; } cgi->headers = 0; cgi->seenHeader = 1; } if (!tx->finalizedOutput && httpGetPacketLength(packet) > 0) { /* Put the data to the CGI readq, then cgiToBrowserService will take care of it */ httpPutPacket(q, packet); } } }
static void readFromCgi(Cgi *cgi, int channel) { HttpConn *conn; HttpPacket *packet; HttpTx *tx; HttpQueue *q, *writeq; MprCmd *cmd; ssize nbytes; int err; cmd = cgi->cmd; conn = cgi->conn; tx = conn->tx; q = cgi->readq; writeq = conn->writeq; assert(conn->sock); assert(conn->state > HTTP_STATE_BEGIN); if (tx->finalized) { mprCloseCmdFd(cmd, channel); } while (mprGetCmdFd(cmd, channel) >= 0 && !tx->finalized && writeq->count < writeq->max) { if ((packet = cgi->headers) != 0) { if (mprGetBufSpace(packet->content) < ME_MAX_BUFFER && mprGrowBuf(packet->content, ME_MAX_BUFFER) < 0) { break; } } else if ((packet = httpCreateDataPacket(ME_MAX_BUFFER)) == 0) { break; } nbytes = mprReadCmd(cmd, channel, mprGetBufEnd(packet->content), ME_MAX_BUFFER); if (nbytes < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprTrace(6, "CGI: Gateway read error %d for %s", err, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprCloseCmdFd(cmd, channel); break; } else if (nbytes == 0) { mprTrace(6, "CGI: Gateway EOF for %s, pid %d", (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr", cmd->pid); mprCloseCmdFd(cmd, channel); break; } else { mprTrace(6, "CGI: Gateway read %d bytes from %s", nbytes, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); traceData(cmd, mprGetBufEnd(packet->content), nbytes); mprAdjustBufEnd(packet->content, nbytes); } if (channel == MPR_CMD_STDERR) { // FUTURE - should be an option to keep going despite stderr output mprError("CGI: Error for \"%s\"\n\n%s", conn->rx->uri, mprGetBufStart(packet->content)); httpSetStatus(conn, HTTP_CODE_SERVICE_UNAVAILABLE); cgi->seenHeader = 1; } if (!cgi->seenHeader) { if (!parseCgiHeaders(cgi, packet)) { cgi->headers = packet; return; } cgi->headers = 0; cgi->seenHeader = 1; } if (!tx->finalizedOutput && httpGetPacketLength(packet) > 0) { /* Put the data to the CGI readq, then cgiToBrowserService will take care of it */ httpPutPacket(q, packet); } } }
/* Get the next ESP input token. input points to the next input token. parse->token will hold the parsed token. The function returns the token id. */ static int getEspToken(EspParse *parse) { char *start, *end, *next; int tid, done, c; start = next = parse->next; end = &start[slen(start)]; mprFlushBuf(parse->token); tid = ESP_TOK_LITERAL; for (done = 0; !done && next < end; next++) { c = *next; switch (c) { case '<': if (next[1] == '%' && ((next == start) || next[-1] != '\\')) { next += 2; if (mprGetBufLength(parse->token) > 0) { next -= 3; } else { next = eatSpace(parse, next); if (*next == '=') { /* <%= directive */ tid = ESP_TOK_EXPR; next = eatSpace(parse, ++next); while (next < end && !(*next == '%' && next[1] == '>' && next[-1] != '\\')) { if (*next == '\n') parse->lineNumber++; if (!addChar(parse, *next++)) { return ESP_TOK_ERR; } } } else if (*next == '@') { tid = ESP_TOK_CONTROL; next = eatSpace(parse, ++next); while (next < end && !(*next == '%' && next[1] == '>' && next[-1] != '\\')) { if (*next == '\n') parse->lineNumber++; if (!addChar(parse, *next++)) { return ESP_TOK_ERR; } } } else { tid = ESP_TOK_CODE; while (next < end && !(*next == '%' && next[1] == '>' && next[-1] != '\\')) { if (*next == '\n') parse->lineNumber++; if (!addChar(parse, *next++)) { return ESP_TOK_ERR; } } } if (*next && next > start && next[-1] == '-') { /* Remove "-" */ mprAdjustBufEnd(parse->token, -1); mprAddNullToBuf(parse->token); next = eatNewLine(parse, next + 2) - 1; } else { next++; } } done++; } else { if (!addChar(parse, c)) { return ESP_TOK_ERR; } } break; case '@': if ((next == start) || next[-1] != '\\') { if (next[1] == '@' || next[1] == '*') { next += 2; if (mprGetBufLength(parse->token) > 0) { next -= 3; } else { tid = next[-1] == '@' ? ESP_TOK_VAR : ESP_TOK_FIELD; next = eatSpace(parse, next); while (isalnum((int) *next) || *next == '_') { if (*next == '\n') parse->lineNumber++; if (!addChar(parse, *next++)) { return ESP_TOK_ERR; } } next--; } done++; } } break; case '\n': parse->lineNumber++; /* Fall through */ case '\r': default: if (c == '\"' || c == '\\') { if (!addChar(parse, '\\')) { return ESP_TOK_ERR; } } if (!addChar(parse, c)) { return ESP_TOK_ERR; } break; } } if (mprGetBufLength(parse->token) == 0) { tid = ESP_TOK_EOF; } parse->next = next; return tid; }
static void cgiEvent(MaQueue *q, MprCmd *cmd, int channel) { MaConn *conn; MaResponse *resp; MprBuf *buf; int space, nbytes, err; mprLog(cmd, 6, "CGI callback channel %d", channel); buf = 0; conn = q->conn; resp = conn->response; mprAssert(resp); cmd->lastActivity = mprGetTime(cmd); switch (channel) { case MPR_CMD_STDIN: writeToCGI(q->pair); return; case MPR_CMD_STDOUT: buf = cmd->stdoutBuf; break; case MPR_CMD_STDERR: buf = cmd->stderrBuf; break; } mprAssert(buf); mprResetBufIfEmpty(buf); /* Come here for CGI stdout, stderr events. ie. reading data from the CGI program. */ while (mprGetCmdFd(cmd, channel) >= 0) { /* Read as much data from the CGI as possible */ do { if ((space = mprGetBufSpace(buf)) == 0) { mprGrowBuf(buf, MA_BUFSIZE); if ((space = mprGetBufSpace(buf)) == 0) { break; } } nbytes = mprReadCmdPipe(cmd, channel, mprGetBufEnd(buf), space); mprLog(q, 5, "CGI: read from gateway %d on channel %d. errno %d", nbytes, channel, nbytes >= 0 ? 0 : mprGetOsError()); if (nbytes < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprLog(cmd, 5, "CGI read error %d for %", mprGetError(), (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprCloseCmdFd(cmd, channel); } else if (nbytes == 0) { /* This may reap the terminated child and thus clear cmd->process if both stderr and stdout are closed. */ mprLog(cmd, 5, "CGI EOF for %s", (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprCloseCmdFd(cmd, channel); break; } else { mprLog(cmd, 5, "CGI read %d bytes from %s", nbytes, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprAdjustBufEnd(buf, nbytes); traceData(cmd, mprGetBufStart(buf), nbytes); } } while ((space = mprGetBufSpace(buf)) > 0); if (mprGetBufLength(buf) == 0) { return; } if (channel == MPR_CMD_STDERR) { /* If we have an error message, send that to the client */ if (mprGetBufLength(buf) > 0) { mprAddNullToBuf(buf); mprLog(conn, 4, mprGetBufStart(buf)); if (writeToClient(q, cmd, buf, channel) < 0) { return; } maSetResponseCode(conn, MPR_HTTP_CODE_SERVICE_UNAVAILABLE); cmd->userFlags |= MA_CGI_SEEN_HEADER; cmd->status = 0; } } else { if (!(cmd->userFlags & MA_CGI_SEEN_HEADER) && !parseHeader(conn, cmd)) { return; } if (cmd->userFlags & MA_CGI_SEEN_HEADER) { if (writeToClient(q, cmd, buf, channel) < 0) { return; } } } } }