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); } }
/* * 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); } } }
/* 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; }
/* * 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); } }
/* * 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); } } }
bool httpPamVerifyUser(HttpConn *conn) { MprBuf *abilities; pam_handle_t *pamh; UserInfo info; struct pam_conv conv = { pamChat, &info }; struct group *gp; int res, i; mprAssert(conn->username); mprAssert(conn->password); mprAssert(!conn->encoded); info.name = (char*) conn->username; info.password = (char*) conn->password; pamh = NULL; if ((res = pam_start("login", info.name, &conv, &pamh)) != PAM_SUCCESS) { return 0; } if ((res = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS) { pam_end(pamh, PAM_SUCCESS); mprLog(5, "httpPamVerifyUser failed to verify %s", conn->username); return 0; } pam_end(pamh, PAM_SUCCESS); mprLog(5, "httpPamVerifyUser verified %s", conn->username); if (!conn->user) { conn->user = mprLookupKey(conn->rx->route->auth->users, conn->username); } if (!conn->user) { Gid groups[32]; int ngroups; /* Create a temporary user with a abilities set to the groups */ ngroups = sizeof(groups) / sizeof(Gid); if ((i = getgrouplist(conn->username, 99999, groups, &ngroups)) >= 0) { abilities = mprCreateBuf(0, 0); for (i = 0; i < ngroups; i++) { if ((gp = getgrgid(groups[i])) != 0) { mprPutFmtToBuf(abilities, "%s ", gp->gr_name); } } mprAddNullToBuf(abilities); mprLog(5, "Create temp user \"%s\" with abilities: %s", conn->username, mprGetBufStart(abilities)); conn->user = httpCreateUser(conn->rx->route->auth, conn->username, 0, mprGetBufStart(abilities)); } } return 1; }
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; }
/* 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; }
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]; }
/* This replace will replace the given pattern and the next word on the same line with the given replacement. */ static char *replace(cchar *str, cchar *pattern, cchar *fmt, ...) { va_list args; MprBuf *buf; cchar *s, *replacement; ssize plen; va_start(args, fmt); replacement = sfmtv(fmt, args); buf = mprCreateBuf(-1, -1); plen = slen(pattern); for (s = str; *s; s++) { if (*s == *pattern && sncmp(s, pattern, plen) == 0) { mprPutStringToBuf(buf, replacement); for (s += plen; *s && isspace((uchar) *s) && *s != '\n'; s++) ; for (; *s && !isspace((uchar) *s) && *s != '\n' && *s != '>'; s++) ; } #if BIT_WIN_LIKE if (*s == '\n' && s[-1] != '\r') { mprPutCharToBuf(buf, '\r'); } #endif mprPutCharToBuf(buf, *s); } va_end(args); mprAddNullToBuf(buf); return sclone(mprGetBufStart(buf)); }
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); }
static void incomingSimpleData(HttpQueue *q, HttpPacket *packet) { /* Do something with the incoming data in packet and then free the packet. */ mprLog(0, "Data in packet is %s", mprGetBufStart(packet->content)); }
/* 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)); }
/* 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; }
/* 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); } }
PUBLIC char *httpStatsReport(int flags) { MprTime now; MprBuf *buf; HttpStats s; double elapsed; static MprTime lastTime; static HttpStats last; double mb; mb = 1024.0 * 1024; now = mprGetTime(); elapsed = (now - lastTime) / 1000.0; httpGetStats(&s); buf = mprCreateBuf(0, 0); mprPutToBuf(buf, "\nHttp Report: at %s\n\n", mprGetDate("%D %T")); if (flags & HTTP_STATS_MEMORY) { mprPutToBuf(buf, "Memory %8.1f MB, %5.1f%% max\n", s.mem / mb, s.mem / (double) s.memMax * 100.0); mprPutToBuf(buf, "Heap %8.1f MB, %5.1f%% mem\n", s.heap / mb, s.heap / (double) s.mem * 100.0); mprPutToBuf(buf, "Heap-peak %8.1f MB\n", s.heapPeak / mb); mprPutToBuf(buf, "Heap-used %8.1f MB, %5.1f%% used\n", s.heapUsed / mb, s.heapUsed / (double) s.heap * 100.0); mprPutToBuf(buf, "Heap-free %8.1f MB, %5.1f%% free\n", s.heapFree / mb, s.heapFree / (double) s.heap * 100.0); if (s.memMax == (size_t) -1) { mprPutToBuf(buf, "Heap limit -\n"); mprPutToBuf(buf, "Heap readline -\n"); } else { mprPutToBuf(buf, "Heap limit %8.1f MB\n", s.memMax / mb); mprPutToBuf(buf, "Heap redline %8.1f MB\n", s.memRedline / mb); } } mprPutToBuf(buf, "Connections %8.1f per/sec\n", (s.totalConnections - last.totalConnections) / elapsed); mprPutToBuf(buf, "Requests %8.1f per/sec\n", (s.totalRequests - last.totalRequests) / elapsed); mprPutToBuf(buf, "Sweeps %8.1f per/sec\n", (s.totalSweeps - last.totalSweeps) / elapsed); mprPutCharToBuf(buf, '\n'); mprPutToBuf(buf, "Clients %8d active\n", s.activeClients); mprPutToBuf(buf, "Connections %8d active\n", s.activeConnections); mprPutToBuf(buf, "Processes %8d active\n", s.activeProcesses); mprPutToBuf(buf, "Requests %8d active\n", s.activeRequests); mprPutToBuf(buf, "Sessions %8d active\n", s.activeSessions); mprPutToBuf(buf, "Workers %8d busy - %d yielded, %d idle, %d max\n", s.workersBusy, s.workersYielded, s.workersIdle, s.workersMax); mprPutToBuf(buf, "Sessions %8.1f MB\n", s.memSessions / mb); mprPutCharToBuf(buf, '\n'); last = s; lastTime = now; mprAddNullToBuf(buf); return sclone(mprGetBufStart(buf)); }
/* 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; }
/* * Join two packets by pulling the content from the second into the first. */ int maJoinPacket(MaPacket *packet, MaPacket *p) { int len; mprAssert(packet->esize == 0); mprAssert(p->esize == 0); len = maGetPacketLength(p); if (mprPutBlockToBuf(packet->content, mprGetBufStart(p->content), len) != len) { return MPR_ERR_NO_MEMORY; } 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 *getCgiToken(MprBuf *buf, cchar *delim) { char *token, *nextToken; ssize len; len = mprGetBufLength(buf); if (len == 0) { return ""; } token = mprGetBufStart(buf); nextToken = sncontains(mprGetBufStart(buf), delim, len); if (nextToken) { *nextToken = '\0'; len = (int) strlen(delim); nextToken += len; buf->start = nextToken; } else { buf->start = mprGetBufEnd(buf); } return token; }
/* 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); } }
/* Map options to an attribute string. Remove all internal control specific options and transparently handle URI link options. WARNING: this returns a non-cloned reference and relies on no GC yield until the returned value is used or cloned. This is done as an optimization to reduce memeory allocations. */ static cchar *map(HttpConn *conn, MprHash *options) { Esp *esp; EspReq *req; MprHash *params; MprKey *kp; MprBuf *buf; cchar *value; char *pstr; if (options == 0 || mprGetHashLength(options) == 0) { return MPR->emptyString; } req = conn->data; if (httpGetOption(options, EDATA("refresh"), 0) && !httpGetOption(options, "id", 0)) { httpAddOption(options, "id", sfmt("id_%d", req->lastDomID++)); } esp = MPR->espService; buf = mprCreateBuf(-1, -1); for (kp = 0; (kp = mprGetNextKey(options, kp)) != 0; ) { if (kp->type != MPR_JSON_OBJ && kp->type != MPR_JSON_ARRAY && !mprLookupKey(esp->internalOptions, kp->key)) { mprPutCharToBuf(buf, ' '); value = kp->data; /* Support link template resolution for these options */ if (smatch(kp->key, EDATA("click")) || smatch(kp->key, EDATA("remote")) || smatch(kp->key, EDATA("refresh"))) { value = httpUriEx(conn, value, options); if ((params = httpGetOptionHash(options, "params")) != 0) { pstr = (char*) ""; for (kp = 0; (kp = mprGetNextKey(params, kp)) != 0; ) { pstr = sjoin(pstr, mprUriEncode(kp->key, MPR_ENCODE_URI_COMPONENT), "=", mprUriEncode(kp->data, MPR_ENCODE_URI_COMPONENT), "&", NULL); } if (pstr[0]) { /* Trim last "&" */ pstr[strlen(pstr) - 1] = '\0'; } mprPutToBuf(buf, "%s-params='%s", params); } } mprPutStringToBuf(buf, kp->key); mprPutStringToBuf(buf, "='"); mprPutStringToBuf(buf, value); mprPutCharToBuf(buf, '\''); } } mprAddNullToBuf(buf); return mprGetBufStart(buf); }
/* Expand ${token} references in a path or string. */ static char *stemplateInner(cchar *str, void *keys, int json) { MprBuf *buf; cchar *value; char *src, *result, *cp, *tok; if (str) { if (schr(str, '$') == 0) { return sclone(str); } buf = mprCreateBuf(0, 0); for (src = (char*) str; *src; ) { if (*src == '$') { if (*++src == '{') { for (cp = ++src; *cp && *cp != '}'; cp++) ; tok = snclone(src, cp - src); } else { for (cp = src; *cp && (isalnum((uchar) *cp) || *cp == '_'); cp++) ; tok = snclone(src, cp - src); } if (json) { value = mprGetJson(keys, tok); } else { value = mprLookupKey(keys, tok); } if (value != 0) { mprPutStringToBuf(buf, value); if (src > str && src[-1] == '{') { src = cp + 1; } else { src = cp; } } else { mprPutCharToBuf(buf, '$'); if (src > str && src[-1] == '{') { mprPutCharToBuf(buf, '{'); } mprPutCharToBuf(buf, *src++); } } else { mprPutCharToBuf(buf, *src++); } } mprAddNullToBuf(buf); result = sclone(mprGetBufStart(buf)); } else { result = MPR->emptyString; } return result; }
static EjsString *joinArray(Ejs *ejs, EjsArray *ap, int argc, EjsObj **argv) { EjsString *sep, *sp; MprBuf *buf; ssize len; int i, nonString; sep = (argc == 1) ? (EjsString*) argv[0] : NULL; if (sep == ESV(empty) && ap->length == 1 && ejsIs(ejs, ap->data[0], String)) { /* Optimized path for joining [string]. This happens frequently with fun(...args) */ return (EjsString*) ap->data[0]; } /* Get an estimate of the string length */ len = 0; nonString = 0; for (i = 0; i < ap->length; i++) { sp = (EjsString*) ap->data[i]; if (!ejsIs(ejs, sp, String)) { nonString = 1; continue; } len += sp->length; } if (sep) { len += (ap->length * sep->length); } if (nonString) { len += ME_MAX_BUFFER; } buf = mprCreateBuf(len + 1, -1); for (i = 0; i < ap->length; i++) { sp = (EjsString*) ap->data[i]; if (!ejsIsDefined(ejs, sp)) { continue; } sp = ejsToString(ejs, sp); if (!ejsIsDefined(ejs, sp)) { continue; } if (i > 0 && sep) { mprPutBlockToBuf(buf, sep->value, sep->length); } mprPutBlockToBuf(buf, sp->value, sp->length); } mprAddNullToBuf(buf); return ejsCreateStringFromBytes(ejs, mprGetBufStart(buf), mprGetBufLength(buf)); }
/* * Optimization to correctly size the packets to the chunk filter. */ static int getChunkPacketSize(MaConn *conn, MprBuf *buf) { MaRequest *req; char *start, *cp; int need, size; req = conn->request; need = 0; switch (req->chunkState) { case MA_CHUNK_START: start = mprGetBufStart(buf); if (mprGetBufLength(buf) < 3) { return 0; } if (start[0] != '\r' || start[1] != '\n') { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad chunk specification"); return 0; } for (cp = &start[2]; cp < (char*) buf->end && *cp != '\n'; cp++) {} if ((cp - start) < 2 || (cp[-1] != '\r' || cp[0] != '\n')) { /* Insufficient data */ if ((cp - start) > 80) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad chunk specification"); return 0; } return 0; } need = (int) (cp - start + 1); size = (int) mprAtoi(&start[2], 16); if (size == 0 && &cp[2] < buf->end && cp[1] == '\r' && cp[2] == '\n') { /* * This is the last chunk (size == 0). Now need to consume the trailing "\r\n". * We are lenient if the request does not have the trailing "\r\n" as required by the spec. */ need += 2; } break; case MA_CHUNK_DATA: need = (int) min(MAXINT, req->remainingContent); break; default: mprAssert(0); } req->remainingContent = need; return need; }
static void printPost(MprBuf *buf) { int i; if (numPostKeys) { mprPrintf(mpr, "<H2>Decoded Post Variables</H2>\r\n"); for (i = 0; i < (numPostKeys * 2); i += 2) { mprPrintf(mpr, "<p>PVAR %s=%s</p>\r\n", postKeys[i], postKeys[i+1]); } } else if (buf) { if (write(1, mprGetBufStart(buf), mprGetBufLength(buf)) != 0) {} } else { mprPrintf(mpr, "<H2>No Post Data Found</H2>\r\n"); } mprPrintf(mpr, "\r\n"); }
/* function read(buffer: ByteArray, offset: Number = 0, count: Number = -1): Number Returns a count of bytes read. Non-blocking if a callback is defined. Otherwise, blocks. Offset: -1 then read to the buffer write position, >= 0 then read to that offset count: -1 then read as much as the buffer will hold. If buffer is growable, read all content. If not growable, read the buffer size. If count >= 0, then read that number of bytes. */ static EjsNumber *http_read(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { EjsByteArray *buffer; HttpConn *conn; MprOff contentLength; ssize offset, count; conn = hp->conn; buffer = (EjsByteArray*) argv[0]; offset = (argc >= 2) ? ejsGetInt(ejs, argv[1]) : 0; count = (argc >= 3) ? ejsGetInt(ejs, argv[2]): -1; if (!waitForResponseHeaders(hp)) { return 0; } contentLength = httpGetContentLength(conn); if (conn->state >= HTTP_STATE_PARSED && contentLength == hp->readCount) { /* End of input */ return ESV(null); } if (offset < 0) { offset = buffer->writePosition; } else if (offset < buffer->size) { ejsSetByteArrayPositions(ejs, buffer, offset, offset); } else { ejsThrowOutOfBoundsError(ejs, "Bad read offset value"); return 0; } if (count < 0 && !buffer->resizable) { count = buffer->size - offset; } if ((count = readHttpData(ejs, hp, count)) < 0) { assert(ejs->exception); return 0; } else if (count == 0 && conn->state > HTTP_STATE_CONTENT) { return ESV(null); } hp->readCount += count; if (ejsCopyToByteArray(ejs, buffer, offset, (char*) mprGetBufStart(hp->responseContent), count) != count) { ejsThrowMemoryError(ejs); } ejsSetByteArrayPositions(ejs, buffer, -1, buffer->writePosition + count); mprAdjustBufStart(hp->responseContent, count); mprResetBufIfEmpty(hp->responseContent); return ejsCreateNumber(ejs, (MprNumber) count); }