/* Get a character from the file. This will put the file into buffered mode. */ PUBLIC int mprGetFileChar(MprFile *file) { MprBuf *bp; ssize len; assert(file); if (file == 0) { return MPR_ERR; } if (file->buf == 0) { file->buf = mprCreateBuf(ME_MAX_BUFFER, ME_MAX_BUFFER); } bp = file->buf; if (mprGetBufLength(bp) == 0) { len = fillBuf(file); if (len <= 0) { return -1; } } if (mprGetBufLength(bp) == 0) { return 0; } file->pos++; return mprGetCharFromBuf(bp); }
/* * Add a packet to the io vector. Return the number of bytes added to the vector. */ static void addPacketForNet(MaQueue *q, MaPacket *packet) { MaResponse *resp; MaConn *conn; MprIOVec *iovec; int index, mask; conn = q->conn; resp = conn->response; iovec = q->iovec; index = q->ioIndex; mprAssert(q->count >= 0); mprAssert(q->ioIndex < (MA_MAX_IOVEC - 2)); if (packet->prefix) { addToNetVector(q, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix)); } if (maGetPacketLength(packet) > 0) { addToNetVector(q, mprGetBufStart(packet->content), mprGetBufLength(packet->content)); } mask = MA_TRACE_RESPONSE | ((packet->flags & MA_PACKET_HEADER) ? MA_TRACE_HEADERS : MA_TRACE_BODY); if (maShouldTrace(conn, mask)) { maTraceContent(conn, packet, 0, resp->bytesWritten, mask); } }
/* 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)); }
static void freeNetPackets(HttpQueue *q, ssize bytes) { HttpPacket *packet; ssize len; mprAssert(q->count >= 0); mprAssert(bytes >= 0); while (bytes > 0 && (packet = q->first) != 0) { if (packet->prefix) { len = mprGetBufLength(packet->prefix); len = min(len, bytes); mprAdjustBufStart(packet->prefix, len); bytes -= len; /* Prefixes don't count in the q->count. No need to adjust */ if (mprGetBufLength(packet->prefix) == 0) { packet->prefix = 0; } } if (packet->content) { len = mprGetBufLength(packet->content); len = min(len, bytes); mprAdjustBufStart(packet->content, len); bytes -= len; q->count -= len; mprAssert(q->count >= 0); } if (packet->content == 0 || mprGetBufLength(packet->content) == 0) { /* This will remove the packet from the queue and will re-enable upstream disabled queues. */ httpGetPacket(q); } } }
/* * Add a packet to the io vector. Return the number of bytes added to the vector. */ static void addPacketForSend(MaQueue *q, MaPacket *packet) { MaResponse *resp; MaConn *conn; MprIOVec *iovec; int mask; conn = q->conn; resp = conn->response; iovec = q->iovec; mprAssert(q->count >= 0); mprAssert(q->ioIndex < (MA_MAX_IOVEC - 2)); if (packet->prefix) { addToSendVector(q, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix)); } if (packet->esize > 0) { mprAssert(q->ioFile == 0); q->ioFile = 1; q->ioCount += packet->esize; } else if (maGetPacketLength(packet) > 0) { /* * Header packets have actual content. File data packets are virtual and only have a count. */ addToSendVector(q, mprGetBufStart(packet->content), mprGetBufLength(packet->content)); mask = (packet->flags & MA_PACKET_HEADER) ? MA_TRACE_HEADERS : MA_TRACE_BODY; if (maShouldTrace(conn, mask)) { maTraceContent(conn, packet, 0, resp->bytesWritten, mask); } } }
void maTraceContent(MaConn *conn, MaPacket *packet, int64 size, int64 offset, int mask) { MaHost *host; int len; mprAssert(conn->trace); host = conn->host; if (offset >= host->traceMaxLength) { mprLog(conn, conn->host->traceLevel, "Abbreviating response trace for conn %d", conn->seqno); conn->trace = 0; return; } if (size <= 0) { size = INT_MAX; } if (packet->prefix) { len = mprGetBufLength(packet->prefix); len = (int) min(len, size); traceBuf(conn, mprGetBufStart(packet->prefix), len, mask); } if (packet->content) { len = mprGetBufLength(packet->content); len = (int) min(len, size); traceBuf(conn, mprGetBufStart(packet->content), len, mask); } }
PUBLIC int mprFlushFile(MprFile *file) { MprFileSystem *fs; MprBuf *bp; ssize len, rc; assert(file); if (file == 0) { return MPR_ERR_BAD_HANDLE; } if (file->buf == 0) { return 0; } if (file->mode & (O_WRONLY | O_RDWR)) { fs = file->fileSystem; bp = file->buf; while (mprGetBufLength(bp) > 0) { len = mprGetBufLength(bp); rc = fs->writeFile(file, mprGetBufStart(bp), len); if (rc < 0) { return (int) rc; } mprAdjustBufStart(bp, rc); } mprFlushBuf(bp); } return 0; }
/* Peek at a character from the file without disturbing the read position. This will put the file into buffered mode. */ PUBLIC int mprPeekFileChar(MprFile *file) { MprBuf *bp; ssize len; assert(file); if (file == 0) { return MPR_ERR; } if (file->buf == 0) { file->buf = mprCreateBuf(ME_MAX_BUFFER, ME_MAX_BUFFER); } bp = file->buf; if (mprGetBufLength(bp) == 0) { len = fillBuf(file); if (len <= 0) { return -1; } } if (mprGetBufLength(bp) == 0) { return 0; } return ((uchar*) mprGetBufStart(bp))[0]; }
/* Read a line from the file. This will put the file into buffered mode. Return NULL on eof. */ PUBLIC char *mprReadLine(MprFile *file, ssize maxline, ssize *lenp) { MprBuf *bp; MprFileSystem *fs; ssize size, len, nlen, consumed; cchar *eol, *newline, *start; char *result; assert(file); if (file == 0) { return NULL; } if (lenp) { *lenp = 0; } if (maxline <= 0) { maxline = ME_MAX_BUFFER; } fs = file->fileSystem; newline = fs->newline; if (file->buf == 0) { file->buf = mprCreateBuf(maxline, maxline); } bp = file->buf; result = NULL; size = 0; do { if (mprGetBufLength(bp) == 0) { if (fillBuf(file) <= 0) { return result; } } start = mprGetBufStart(bp); len = mprGetBufLength(bp); if ((eol = findNewline(start, newline, len, &nlen)) != 0) { len = eol - start; consumed = len + nlen; } else { consumed = len; } file->pos += (MprOff) consumed; if (lenp) { *lenp += len; } if ((result = mprRealloc(result, size + len + 1)) == 0) { return NULL; } memcpy(&result[size], start, len); size += len; result[size] = '\0'; mprAdjustBufStart(bp, consumed); } while (!eol); return result; }
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); } }
static void freeSendPackets(HttpQueue *q, MprOff bytes) { HttpPacket *packet; ssize len; assert(q->first); assert(q->count >= 0); assert(bytes >= 0); /* Loop while data to be accounted for and we have not hit the end of data packet There should be 2-3 packets on the queue. A header packet for the HTTP response headers, an optional data packet with packet->esize set to the size of the file, and an end packet with no content. Must leave this routine with the end packet still on the queue and all bytes accounted for. */ while ((packet = q->first) != 0 && !(packet->flags & HTTP_PACKET_END) && bytes > 0) { if (packet->prefix) { len = mprGetBufLength(packet->prefix); len = (ssize) min(len, bytes); mprAdjustBufStart(packet->prefix, len); bytes -= len; /* Prefixes don't count in the q->count. No need to adjust */ if (mprGetBufLength(packet->prefix) == 0) { packet->prefix = 0; } } if (packet->esize) { len = (ssize) min(packet->esize, bytes); packet->esize -= len; packet->epos += len; bytes -= len; assert(packet->esize >= 0); } else if ((len = httpGetPacketLength(packet)) > 0) { /* Header packets come here */ len = (ssize) min(len, bytes); mprAdjustBufStart(packet->content, len); bytes -= len; q->count -= len; assert(q->count >= 0); } if (packet->esize == 0 && httpGetPacketLength(packet) == 0) { /* Done with this packet - consume it */ assert(!(packet->flags & HTTP_PACKET_END)); httpGetPacket(q); } else { break; } } assert(bytes == 0); }
int mprPuts(MprFile *file, const char *writeBuf, uint count) { MprBuf *bp; char *buf; int total, bytes, len; mprAssert(file); /* * Buffer output and flush when full. */ if (file->buf == 0) { file->buf = mprCreateBuf(file, MPR_BUFSIZE, 0); if (file->buf == 0) { return MPR_ERR_CANT_ALLOCATE; } } bp = file->buf; if (mprGetBufLength(bp) > 0 && mprGetBufSpace(bp) < (int) count) { len = mprGetBufLength(bp); if (mprWrite(file, mprGetBufStart(bp), len) != len) { return MPR_ERR_CANT_WRITE; } mprFlushBuf(bp); } total = 0; buf = (char*) writeBuf; while (count > 0) { bytes = mprPutBlockToBuf(bp, buf, count); if (bytes <= 0) { return MPR_ERR_CANT_ALLOCATE; } count -= bytes; buf += bytes; total += bytes; mprAddNullToBuf(bp); if (count > 0) { len = mprGetBufLength(bp); if (mprWrite(file, mprGetBufStart(bp), len) != len) { return MPR_ERR_CANT_WRITE; } mprFlushBuf(bp); } } return total; }
/* * Clear entries from the IO vector that have actually been transmitted. This supports partial writes due to the socket * being full. Don't come here if we've seen all the packets and all the data has been completely written. ie. small files * don't come here. */ static void freeSentPackets(MaQueue *q, int64 bytes) { MaPacket *packet; int64 len; mprAssert(q->first); mprAssert(q->count >= 0); mprAssert(bytes >= 0); while ((packet = q->first) != 0) { if (packet->prefix) { len = mprGetBufLength(packet->prefix); len = min(len, bytes); mprAdjustBufStart(packet->prefix, (int) len); bytes -= len; /* Prefixes don't count in the q->count. No need to adjust */ if (mprGetBufLength(packet->prefix) == 0) { mprFree(packet->prefix); packet->prefix = 0; } } if (packet->esize) { len = min(packet->esize, bytes); packet->esize -= len; packet->epos += len; bytes -= len; mprAssert(packet->esize >= 0); mprAssert(bytes == 0); if (packet->esize > 0) { break; } } else if ((len = maGetPacketLength(packet)) > 0) { len = min(len, bytes); mprAdjustBufStart(packet->content, (int) len); bytes -= len; q->count -= (int) len; mprAssert(q->count >= 0); } if (maGetPacketLength(packet) == 0) { if ((packet = maGet(q)) != 0) { maFreePacket(q, packet); } } mprAssert(bytes >= 0); if (bytes == 0) { break; } } }
/* * Return true if the packet is too large to be accepted by the downstream queue. */ bool maPacketTooBig(MaQueue *q, MaPacket *packet) { int size; size = mprGetBufLength(packet->content); return size > q->max || size > q->packetSize; }
static void printBodyData(MaQueue *q) { MprBuf *buf; char **keys, *value; int i, numKeys; if (q->pair == 0 || q->pair->first == 0) { return; } buf = q->pair->first->content; mprAddNullToBuf(buf); numKeys = getVars(q, &keys, mprGetBufStart(buf), mprGetBufLength(buf)); if (numKeys == 0) { maWrite(q, "<H2>No Body Data Found</H2>\r\n"); } else { maWrite(q, "<H2>Decoded Body Data</H2>\r\n"); for (i = 0; i < (numKeys * 2); i += 2) { value = keys[i+1]; maWrite(q, "<p>PVAR %s=%s</p>\r\n", keys[i], value ? value: ""); } } maWrite(q, "\r\n"); mprFree(keys); mprFree(buf); }
/* Write data back to the client (browser). Must be locked when called. */ static int writeToClient(MaQueue *q, MprCmd *cmd, MprBuf *buf, int channel) { MaConn *conn; int rc, len; conn = q->conn; /* Write to the browser. We write as much as we can. Service queues to get the filters and connectors pumping. */ while ((len = mprGetBufLength(buf)) > 0) { if (!conn->requestFailed) { rc = maWriteBlock(q, mprGetBufStart(buf), len, 1); mprLog(q, 5, "CGI: write %d bytes to client. Rc rc %d, errno %d", len, rc, mprGetOsError()); } else { /* Request has failed so just eat the data */ rc = len; mprAssert(len > 0); } if (rc < 0) { return MPR_ERR_CANT_WRITE; } mprAssert(rc == len); mprAdjustBufStart(buf, rc); mprResetBufIfEmpty(buf); maServiceQueues(conn); } return 0; }
/* * Get the next input token. The content buffer is advanced to the next token. This routine always returns a * non-zero token. The empty string means the delimiter was not found. */ static char *getToken(MaConn *conn, cchar *delim) { MprBuf *buf; char *token, *nextToken; int len; mprAssert(mprGetParent(conn->input) == conn); buf = conn->input->content; len = mprGetBufLength(buf); if (len == 0) { return ""; } token = mprGetBufStart(buf); nextToken = mprStrnstr(mprGetBufStart(buf), delim, len); if (nextToken) { *nextToken = '\0'; len = (int) strlen(delim); nextToken += len; buf->start = nextToken; } else { buf->start = mprGetBufEnd(buf); mprAddNullToBuf(buf); } return token; }
/* Add a packet to the io vector. Return the number of bytes added to the vector. */ static void addPacketForSend(HttpQueue *q, HttpPacket *packet) { HttpConn *conn; conn = q->conn; assert(q->count >= 0); assert(q->ioIndex < (ME_MAX_IOVEC - 2)); if (packet->prefix) { addToSendVector(q, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix)); } if (packet->esize > 0) { assert(q->ioFile == 0); q->ioFile = 1; q->ioCount += packet->esize; } else if (httpGetPacketLength(packet) > 0) { /* Header packets have actual content. File data packets are virtual and only have a count. */ addToSendVector(q, mprGetBufStart(packet->content), httpGetPacketLength(packet)); if (httpTracing(conn) && packet->flags & HTTP_PACKET_DATA) { httpTraceBody(conn, 1, packet, -1); } } }
static int setContentLength(HttpConn *conn, MprList *files) { MprPath info; MprOff len; char *path, *pair; int next; len = 0; if (app->upload) { httpEnableUpload(conn); return 0; } for (next = 0; (path = mprGetNextItem(files, &next)) != 0; ) { if (strcmp(path, "-") != 0) { if (mprGetPathInfo(path, &info) < 0) { mprError("Can't access file %s", path); return MPR_ERR_CANT_ACCESS; } len += info.size; } } if (app->formData) { for (next = 0; (pair = mprGetNextItem(app->formData, &next)) != 0; ) { len += slen(pair); } len += mprGetListLength(app->formData) - 1; } if (app->bodyData) { len += mprGetBufLength(app->bodyData); } if (len > 0) { httpSetContentLength(conn, len); } return 0; }
int mprGetBlockFromBuf(MprBuf *bp, char *buf, int size) { int thisLen, bytesRead; mprAssert(buf); mprAssert(size >= 0); mprAssert(bp->buflen == (bp->endbuf - bp->data)); /* * Get the max bytes in a straight copy */ bytesRead = 0; while (size > 0) { thisLen = mprGetBufLength(bp); thisLen = min(thisLen, size); if (thisLen <= 0) { break; } memcpy(buf, bp->start, thisLen); buf += thisLen; bp->start += thisLen; size -= thisLen; bytesRead += thisLen; } return bytesRead; }
/* Trace any packet */ PUBLIC bool httpTracePacket(HttpConn *conn, cchar *event, cchar *type, HttpPacket *packet, cchar *values, ...) { va_list ap; int level; assert(conn); assert(packet); if (!conn || conn->http->traceLevel == 0 || conn->rx->skipTrace) { return 0; } level = PTOI(mprLookupKey(conn->trace->events, type)); if (level == 0 || level > conn->http->traceLevel) { \ return 0; } if (packet->prefix) { httpTraceContent(conn, event, type, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix), 0); } if (values) { va_start(ap, values); values = sfmtv(values, ap); va_end(ap); } if (packet->content) { if (values) { httpTraceContent(conn, event, type, mprGetBufStart(packet->content), httpGetPacketLength(packet), "%s", values); } else { httpTraceContent(conn, event, type, mprGetBufStart(packet->content), httpGetPacketLength(packet), 0); } } return 1; }
/* * Complete the request and return true if there is a pipelined request following. */ bool maProcessCompletion(MaConn *conn) { MaRequest *req; MaPacket *packet; bool more; mprAssert(conn->state == MPR_HTTP_STATE_COMPLETE); req = conn->request; maLogRequest(conn); #if BLD_DEBUG mprLog(req, 4, "Request complete used %,d K, conn usage %,d K, mpr usage %,d K, page usage %,d K", req->arena->allocBytes / 1024, conn->arena->allocBytes / 1024, mprGetMpr(conn)->heap.allocBytes / 1024, mprGetMpr(conn)->pageHeap.allocBytes / 1024); /* mprPrintAllocReport(mprGetMpr(conn), "Before completing request"); */ #endif packet = conn->input; more = packet && (mprGetBufLength(packet->content) > 0); if (mprGetParent(packet) != conn) { if (more) { conn->input = maSplitPacket(conn, packet, 0); mprAssert(mprGetParent(conn->input) == conn); } else { conn->input = 0; } } /* * This will free the request, response, pipeline and call maPrepConnection to reset the state. */ mprFree(req->arena); return (conn->disconnected || conn->connectionFailed) ? 0 : more; }
/* Read data. If sync mode, this will block. If async, will never block. Will return what data is available up to the requested size. Returns a count of bytes read. Returns zero if not data. EOF if returns zero and conn->state is > HTTP_STATE_CONTENT. */ PUBLIC ssize httpRead(HttpConn *conn, char *buf, ssize size) { HttpPacket *packet; HttpQueue *q; MprBuf *content; ssize nbytes, len; q = conn->readq; assert(q->count >= 0); assert(size >= 0); VERIFY_QUEUE(q); while (q->count <= 0 && !conn->async && !conn->error && conn->sock && (conn->state <= HTTP_STATE_CONTENT)) { httpServiceQueues(conn); if (conn->sock) { httpWait(conn, 0, MPR_TIMEOUT_NO_BUSY); } } conn->lastActivity = conn->http->now; for (nbytes = 0; size > 0 && q->count > 0; ) { if ((packet = q->first) == 0) { break; } content = packet->content; len = mprGetBufLength(content); len = min(len, size); assert(len <= q->count); if (len > 0) { len = mprGetBlockFromBuf(content, buf, len); assert(len <= q->count); } buf += len; size -= len; q->count -= len; assert(q->count >= 0); nbytes += len; if (mprGetBufLength(content) == 0) { httpGetPacket(q); } } assert(q->count >= 0); if (nbytes < size) { buf[nbytes] = '\0'; } return nbytes; }
/* * Get the packet into which to read data. This may be owned by the connection or if mid-request, may be owned by the * request. Also return in *bytesToRead the length of data to attempt to read. */ static inline MaPacket *getPacket(MaConn *conn, int *bytesToRead) { MaPacket *packet; MaRequest *req; MprBuf *content; MprOff remaining; int len; req = conn->request; len = MA_BUFSIZE; /* * The input packet may have pipelined headers and data left from the prior request. It may also have incomplete * chunk boundary data. */ if ((packet = conn->input) == NULL) { conn->input = packet = maCreateConnPacket(conn, len); } else { content = packet->content; mprResetBufIfEmpty(content); if (req) { /* * Don't read more than the remainingContent unless chunked. We do this to minimize requests split * across packets. */ if (req->remainingContent) { remaining = req->remainingContent; if (req->flags & MA_REQ_CHUNKED) { remaining = max(remaining, MA_BUFSIZE); } len = (int) min(remaining, MAXINT); } len = min(len, MA_BUFSIZE); mprAssert(len > 0); if (mprGetBufSpace(content) < len) { mprGrowBuf(content, len); } } else { /* * Still reading the request headers */ if (mprGetBufLength(content) >= conn->http->limits.maxHeader) { maFailConnection(conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, "Header too big"); return 0; } if (mprGetBufSpace(content) < MA_BUFSIZE) { mprGrowBuf(content, MA_BUFSIZE); } len = mprGetBufSpace(content); } } mprAssert(len > 0); *bytesToRead = len; return packet; }
static void freeNetPackets(MaQueue *q, int64 bytes) { MaPacket *packet; int len; mprAssert(q->first); mprAssert(q->count >= 0); mprAssert(bytes >= 0); while ((packet = q->first) != 0) { if (packet->prefix) { len = mprGetBufLength(packet->prefix); len = (int) min(len, bytes); mprAdjustBufStart(packet->prefix, len); bytes -= len; /* Prefixes don't count in the q->count. No need to adjust */ if (mprGetBufLength(packet->prefix) == 0) { mprFree(packet->prefix); packet->prefix = 0; } } if (packet->content) { len = mprGetBufLength(packet->content); len = (int) min(len, bytes); mprAdjustBufStart(packet->content, len); bytes -= len; q->count -= len; mprAssert(q->count >= 0); } if (packet->content == 0 || mprGetBufLength(packet->content) == 0) { /* * This will remove the packet from the queue and will re-enable upstream disabled queues. */ if ((packet = maGet(q)) != 0) { maFreePacket(q, packet); } } mprAssert(bytes >= 0); if (bytes == 0) { break; } } }
static void writeToCGI(MaQueue *q) { MaConn *conn; MaPacket *packet; MprCmd *cmd; MprBuf *buf; int len, rc, err; cmd = (MprCmd*) q->pair->queueData; mprAssert(cmd); conn = q->conn; for (packet = maGet(q); packet && !conn->requestFailed; packet = maGet(q)) { buf = packet->content; len = mprGetBufLength(buf); mprAssert(len > 0); rc = mprWriteCmdPipe(cmd, MPR_CMD_STDIN, mprGetBufStart(buf), len); mprLog(q, 5, "CGI: write %d bytes to gateway. Rc rc %d, errno %d", len, rc, mprGetOsError()); if (rc < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprLog(q, 2, "CGI: write to gateway failed for %d bytes, rc %d, errno %d", len, rc, mprGetOsError()); mprCloseCmdFd(cmd, MPR_CMD_STDIN); maFailRequest(conn, MPR_HTTP_CODE_BAD_GATEWAY, "Can't write body data to CGI gateway"); break; } else { mprLog(q, 5, "CGI: write to gateway %d bytes asked to write %d", rc, len); mprAdjustBufStart(buf, rc); if (mprGetBufLength(buf) > 0) { maPutBack(q, packet); } else { maFreePacket(q, packet); } } } }
void mprCompactBuf(MprBuf *bp) { if (mprGetBufLength(bp) == 0) { mprFlushBuf(bp); return; } if (bp->start > bp->data) { memmove(bp->data, bp->start, (bp->end - bp->start)); bp->end -= (bp->start - bp->data); bp->start = bp->data; } }
/* uncompressString(data: String): String */ static EjsString *zlib_uncompressString(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { EjsString *in; MprBuf *out; z_stream zs; uchar outbuf[ZBUFSIZE]; ssize nbytes, size; int rc; in = (EjsString*) argv[0]; if ((out = mprCreateBuf(ZBUFSIZE, -1)) == 0) { return 0; } if ((size = in->length) == 0) { return ESV(empty); } zs.zalloc = Z_NULL; zs.zfree = Z_NULL; zs.opaque = Z_NULL; zs.avail_in = 0; rc = inflateInit(&zs); zs.next_in = (uchar*) in->value; zs.avail_in = (int) size; do { if (zs.avail_in == 0) { break; } do { zs.avail_out = ZBUFSIZE; zs.next_out = outbuf; if ((rc = inflate(&zs, Z_NO_FLUSH)) == Z_NEED_DICT) { inflateEnd(&zs); return 0; } else if (rc == Z_DATA_ERROR || rc == Z_MEM_ERROR) { inflateEnd(&zs); return 0; } else { nbytes = ZBUFSIZE - zs.avail_out; } if (mprPutBlockToBuf(out, (char*) outbuf, nbytes) != nbytes) { ejsThrowIOError(ejs, "Cannot copy to byte array"); inflateEnd(&zs); return 0; } } while (zs.avail_out == 0); assure(zs.avail_in == 0); } while (rc != Z_STREAM_END); deflateEnd(&zs); return ejsCreateStringFromBytes(ejs, mprGetBufStart(out), mprGetBufLength(out)); }
/* * Parse a new request. Return true to keep going with this or subsequent request, zero means insufficient data to proceed. */ static bool parseRequest(MaConn *conn, MaPacket *packet) { MaRequest *req; char *start, *end; int len; /* * Must wait until we have the complete set of headers. */ if ((len = mprGetBufLength(packet->content)) == 0) { return 0; } start = mprGetBufStart(packet->content); if ((end = mprStrnstr(start, "\r\n\r\n", len)) == 0) { return 0; } len = (int) (end - start); if (len >= conn->host->limits->maxHeader) { maFailConnection(conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, "Header too big"); return 0; } if (parseFirstLine(conn, packet)) { parseHeaders(conn, packet); } else { return 0; } maMatchHandler(conn); /* * Have read the headers. Create the request pipeline. This calls the open() stage entry routines. */ maCreatePipeline(conn); req = conn->request; if (conn->connectionFailed) { /* Discard input data */ mprAssert(conn->keepAliveCount <= 0); conn->state = MPR_HTTP_STATE_PROCESSING; maRunPipeline(conn); } else if (req->remainingContent > 0) { conn->state = MPR_HTTP_STATE_CONTENT; } else if (req->length == -1 && (req->method == MA_REQ_POST || req->method == MA_REQ_PUT)) { conn->state = MPR_HTTP_STATE_CONTENT; } else { /* * Can run the request now if there is no incoming data. */ conn->state = MPR_HTTP_STATE_PROCESSING; maRunPipeline(conn); } return !conn->disconnected; }
/* Add a packet to the io vector. Return the number of bytes added to the vector. */ static void addPacketForNet(HttpQueue *q, HttpPacket *packet) { HttpTx *tx; HttpConn *conn; int item; conn = q->conn; tx = conn->tx; mprAssert(q->count >= 0); mprAssert(q->ioIndex < (HTTP_MAX_IOVEC - 2)); if (packet->prefix) { addToNetVector(q, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix)); } if (httpGetPacketLength(packet) > 0) { addToNetVector(q, mprGetBufStart(packet->content), mprGetBufLength(packet->content)); } item = (packet->flags & HTTP_PACKET_HEADER) ? HTTP_TRACE_HEADER : HTTP_TRACE_BODY; if (httpShouldTrace(conn, HTTP_TRACE_TX, item, tx->ext) >= 0) { httpTraceContent(conn, HTTP_TRACE_TX, item, packet, 0, (ssize) tx->bytesWritten); } }