/* Accept incoming body data from the client (via the pipeline) destined for the CGI gateway. This is typically POST or PUT data. */ static void incomingCgiData(MaQueue *q, MaPacket *packet) { MaConn *conn; MaResponse *resp; MaRequest *req; MprCmd *cmd; mprAssert(q); mprAssert(packet); conn = q->conn; resp = conn->response; req = conn->request; cmd = (MprCmd*) q->pair->queueData; if (cmd) { cmd->lastActivity = mprGetTime(cmd); } if (maGetPacketLength(packet) == 0) { /* End of input */ if (req->remainingContent > 0) { /* Short incoming body data. Just kill the CGI process. */ mprFree(cmd); q->queueData = 0; maFailRequest(conn, MPR_HTTP_CODE_BAD_REQUEST, "Client supplied insufficient body data"); } if (req->form) { maAddVarsFromQueue(req->formVars, q); } } else { /* No service routine, we just need it to be queued for writeToCGI */ if (req->form) { maJoinForService(q, packet, 0); } else { maPutForService(q, packet, 0); } } if (cmd) { writeToCGI(q); } }
/* This routine runs after all incoming data has been received */ static void runCgi(MaQueue *q) { MaResponse *resp; MaConn *conn; MprCmd *cmd; conn = q->conn; resp = conn->response; cmd = (MprCmd*) q->queueData; if (cmd == 0) { startCgi(q); cmd = (MprCmd*) q->queueData; if (q->pair->count > 0) { writeToCGI(q->pair); } } /* Close the CGI program's stdin. This will allow it to exit if it was expecting input data. */ mprCloseCmdFd(cmd, MPR_CMD_STDIN); if (conn->requestFailed) { maPutForService(q, maCreateEndPacket(q), 1); return; } while (mprWaitForCmd(cmd, 1000) < 0) { if (mprGetElapsedTime(cmd, cmd->lastActivity) >= conn->host->timeout) { break; } } if (cmd->pid == 0) { maPutForService(q, maCreateEndPacket(q), 1); } else { mprStopCmd(cmd); mprReapCmd(cmd, MPR_TIMEOUT_STOP_TASK); cmd->status = 255; } }
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; } } } } }