static void browserToCgiService(HttpQueue *q) { HttpConn *conn; HttpPacket *packet; Cgi *cgi; MprCmd *cmd; MprBuf *buf; ssize rc, len; int err; if ((cgi = q->queueData) == 0) { return; } assert(q == cgi->writeq); cmd = cgi->cmd; assert(cmd); conn = cgi->conn; for (packet = httpGetPacket(q); packet; packet = httpGetPacket(q)) { if ((buf = packet->content) == 0) { /* End packet */ continue; } len = mprGetBufLength(buf); rc = mprWriteCmd(cmd, MPR_CMD_STDIN, mprGetBufStart(buf), len); if (rc < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { httpPutBackPacket(q, packet); break; } mprLog(2, "CGI: write to gateway failed for %d bytes, rc %d, errno %d", len, rc, mprGetOsError()); mprCloseCmdFd(cmd, MPR_CMD_STDIN); httpDiscardQueueData(q, 1); httpError(conn, HTTP_CODE_BAD_GATEWAY, "Cannot write body data to CGI gateway"); break; } mprTrace(6, "CGI: browserToCgiService %d/%d, qmax %d", rc, len, q->max); mprAdjustBufStart(buf, rc); if (mprGetBufLength(buf) > 0) { httpPutBackPacket(q, packet); break; } } if (q->count > 0) { /* Wait for writable event so cgiCallback can recall this routine */ mprEnableCmdEvents(cmd, MPR_CMD_STDIN); } else if (conn->rx->eof) { mprCloseCmdFd(cmd, MPR_CMD_STDIN); } else { mprDisableCmdEvents(cmd, MPR_CMD_STDIN); } }
void httpDiscardData(HttpConn *conn, int dir) { HttpTx *tx; HttpQueue *q, *qhead; tx = conn->tx; if (tx == 0) { return; } qhead = tx->queue[dir]; for (q = qhead->nextQ; q != qhead; q = q->nextQ) { httpDiscardQueueData(q, 1); } }
static void netOutgoingService(HttpQueue *q) { HttpConn *conn; HttpTx *tx; ssize written; int errCode; conn = q->conn; tx = conn->tx; conn->lastActivity = conn->http->now; mprAssert(conn->sock); if (!conn->sock || conn->connectorComplete) { return; } if (tx->flags & HTTP_TX_NO_BODY) { httpDiscardQueueData(q, 1); } if ((tx->bytesWritten + q->count) > conn->limits->transmissionBodySize) { httpError(conn, HTTP_CODE_REQUEST_TOO_LARGE | ((tx->bytesWritten) ? HTTP_ABORT : 0), "Http transmission aborted. Exceeded transmission max body of %,Ld bytes", conn->limits->transmissionBodySize); if (tx->bytesWritten) { httpConnectorComplete(conn); return; } } if (tx->flags & HTTP_TX_SENDFILE) { /* Relay via the send connector */ if (tx->file == 0) { if (tx->flags & HTTP_TX_HEADERS_CREATED) { tx->flags &= ~HTTP_TX_SENDFILE; } else { tx->connector = conn->http->sendConnector; httpSendOpen(q); } } if (tx->file) { httpSendOutgoingService(q); return; } } while (q->first || q->ioIndex) { if (q->ioIndex == 0 && buildNetVec(q) <= 0) { break; } /* Issue a single I/O request to write all the blocks in the I/O vector */ mprAssert(q->ioIndex > 0); written = mprWriteSocketVector(conn->sock, q->iovec, q->ioIndex); LOG(5, "Net connector wrote %d, written so far %Ld, q->count %d/%d", written, tx->bytesWritten, q->count, q->max); if (written < 0) { errCode = mprGetError(q); if (errCode == EAGAIN || errCode == EWOULDBLOCK) { /* Socket full, wait for an I/O event */ httpSocketBlocked(conn); break; } if (errCode != EPIPE && errCode != ECONNRESET) { LOG(5, "netOutgoingService write failed, error %d", errCode); } httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Write error %d", errCode); httpConnectorComplete(conn); break; } else if (written == 0) { /* Socket full, wait for an I/O event */ httpSocketBlocked(conn); break; } else if (written > 0) { tx->bytesWritten += written; freeNetPackets(q, written); adjustNetVec(q, written); } } if (q->ioCount == 0) { if ((q->flags & HTTP_QUEUE_EOF)) { httpConnectorComplete(conn); } else { httpNotifyWritable(conn); } } }
PUBLIC void httpWriteHeaders(HttpQueue *q, HttpPacket *packet) { Http *http; HttpConn *conn; HttpTx *tx; HttpUri *parsedUri; MprKey *kp; MprBuf *buf; int level; assert(packet->flags == HTTP_PACKET_HEADER); conn = q->conn; http = conn->http; tx = conn->tx; buf = packet->content; if (tx->flags & HTTP_TX_HEADERS_CREATED) { return; } tx->flags |= HTTP_TX_HEADERS_CREATED; tx->responded = 1; if (conn->headersCallback) { /* Must be before headers below */ (conn->headersCallback)(conn->headersCallbackArg); } if (tx->flags & HTTP_TX_USE_OWN_HEADERS && !conn->error) { conn->keepAliveCount = 0; return; } setHeaders(conn, packet); if (conn->endpoint) { mprPutStringToBuf(buf, conn->protocol); mprPutCharToBuf(buf, ' '); mprPutIntToBuf(buf, tx->status); mprPutCharToBuf(buf, ' '); mprPutStringToBuf(buf, httpLookupStatus(http, tx->status)); } else { mprPutStringToBuf(buf, tx->method); mprPutCharToBuf(buf, ' '); parsedUri = tx->parsedUri; if (http->proxyHost && *http->proxyHost) { if (parsedUri->query && *parsedUri->query) { mprPutToBuf(buf, "http://%s:%d%s?%s %s", http->proxyHost, http->proxyPort, parsedUri->path, parsedUri->query, conn->protocol); } else { mprPutToBuf(buf, "http://%s:%d%s %s", http->proxyHost, http->proxyPort, parsedUri->path, conn->protocol); } } else { if (parsedUri->query && *parsedUri->query) { mprPutToBuf(buf, "%s?%s %s", parsedUri->path, parsedUri->query, conn->protocol); } else { mprPutStringToBuf(buf, parsedUri->path); mprPutCharToBuf(buf, ' '); mprPutStringToBuf(buf, conn->protocol); } } } if ((level = httpShouldTrace(conn, HTTP_TRACE_TX, HTTP_TRACE_FIRST, tx->ext)) >= mprGetLogLevel(tx)) { mprAddNullToBuf(buf); mprLog(level, " %s", mprGetBufStart(buf)); } mprPutStringToBuf(buf, "\r\n"); /* Output headers */ kp = mprGetFirstKey(conn->tx->headers); while (kp) { mprPutStringToBuf(packet->content, kp->key); mprPutStringToBuf(packet->content, ": "); if (kp->data) { mprPutStringToBuf(packet->content, kp->data); } mprPutStringToBuf(packet->content, "\r\n"); kp = mprGetNextKey(conn->tx->headers, kp); } /* By omitting the "\r\n" delimiter after the headers, chunks can emit "\r\nSize\r\n" as a single chunk delimiter */ if (tx->length >= 0 || tx->chunkSize <= 0) { mprPutStringToBuf(buf, "\r\n"); } if (tx->altBody) { /* Error responses are emitted here */ mprPutStringToBuf(buf, tx->altBody); httpDiscardQueueData(tx->queue[HTTP_QUEUE_TX]->nextQ, 0); } tx->headerSize = mprGetBufLength(buf); tx->flags |= HTTP_TX_HEADERS_CREATED; q->count += httpGetPacketLength(packet); }
PUBLIC void httpSendOutgoingService(HttpQueue *q) { HttpConn *conn; HttpTx *tx; MprFile *file; MprOff written; int errCode; conn = q->conn; tx = conn->tx; conn->lastActivity = conn->http->now; if (tx->finalizedConnector) { return; } if (tx->flags & HTTP_TX_NO_BODY) { httpDiscardQueueData(q, 1); } if ((tx->bytesWritten + q->ioCount) > conn->limits->txBodySize && conn->limits->txBodySize < HTTP_UNLIMITED) { httpLimitError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TOO_LARGE | ((tx->bytesWritten) ? HTTP_ABORT : 0), "Http transmission aborted. Exceeded max body of %lld bytes", conn->limits->txBodySize); if (tx->bytesWritten) { httpFinalizeConnector(conn); return; } } tx->writeBlocked = 0; if (q->ioIndex == 0) { buildSendVec(q); } /* No need to loop around as send file tries to write as much of the file as possible. If not eof, will always have the socket blocked. */ file = q->ioFile ? tx->file : 0; written = mprSendFileToSocket(conn->sock, file, q->ioPos, q->ioCount, q->iovec, q->ioIndex, NULL, 0); if (written < 0) { errCode = mprGetError(); if (errCode == EAGAIN || errCode == EWOULDBLOCK) { /* Socket full, wait for an I/O event */ tx->writeBlocked = 1; } else { if (errCode != EPIPE && errCode != ECONNRESET && errCode != ECONNABORTED && errCode != ENOTCONN) { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "sendConnector: error, errCode %d", errCode); } else { httpDisconnect(conn); } httpFinalizeConnector(conn); } httpTrace(conn, "connection.io.error", "error", "msg:'Connector write error',errno:%d", errCode); } else if (written > 0) { tx->bytesWritten += written; freeSendPackets(q, written); adjustSendVec(q, written); } if (q->first && q->first->flags & HTTP_PACKET_END) { httpFinalizeConnector(conn); httpGetPacket(q); } }