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)); }
int mprPutStringToBuf(MprBuf *bp, cchar *str) { if (str) { return mprPutBlockToBuf(bp, str, (int) strlen(str)); } return 0; }
/* * 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; }
/* * Write data to the client. Will buffer and flush as required. will create headers if required. */ static int writeBlock(void *handle, cchar *buf, int size) { int len, rc; len = mprGetBufLength(output); if ((len + size) < EJS_CGI_MAX_BUF) { rc = mprPutBlockToBuf(output, buf, size); } else { flushOutput(output); if (size < EJS_CGI_MAX_BUF) { rc = mprPutBlockToBuf(output, buf, size); } else { rc = write(1, (char*) buf, size); } } return rc; }
static ssize doOutput(HttpQueue *q, cchar *data, ssize len) { HttpPacket *packet; ssize count; count = min(len, q->max - q->count); count = min(count, q->packetSize); packet = httpCreateDataPacket(count); mprPutBlockToBuf(packet->content, data, len); httpPutForService(q, packet, HTTP_SCHEDULE_QUEUE); return count; }
/* 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)); }
int mprPutSubStringToBuf(MprBuf *bp, cchar *str, int count) { int len; if (str) { len = (int) strlen(str); len = min(len, count); if (len > 0) { return mprPutBlockToBuf(bp, str, len); } } return 0; }
/* * 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; }
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; }
/* Put a string to the file. This will put the file into buffered mode. */ PUBLIC ssize mprPutFileString(MprFile *file, cchar *str) { MprBuf *bp; ssize total, bytes, count; char *buf; assert(file); count = slen(str); /* Buffer output and flush when full. */ if (file->buf == 0) { file->buf = mprCreateBuf(ME_MAX_BUFFER, 0); if (file->buf == 0) { return MPR_ERR_CANT_ALLOCATE; } } bp = file->buf; if (mprGetBufLength(bp) > 0 && mprGetBufSpace(bp) < count) { mprFlushFile(file); } total = 0; buf = (char*) str; while (count > 0) { bytes = mprPutBlockToBuf(bp, buf, count); if (bytes < 0) { return MPR_ERR_CANT_ALLOCATE; } else if (bytes == 0) { if (mprFlushFile(file) < 0) { return MPR_ERR_CANT_WRITE; } continue; } count -= bytes; buf += bytes; total += bytes; file->pos += (MprOff) bytes; } return total; }
/* compressString(data: String): String */ static EjsString *zlib_compressString(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { EjsString *in; MprBuf *out; z_stream zs; uchar outbuf[ZBUFSIZE]; ssize size, nbytes; int level, flush; in = (EjsString*) argv[0]; if ((size = in->length) == 0) { return ESV(empty); } if ((out = mprCreateBuf(in->length, 0)) == 0) { return 0; } zs.zalloc = Z_NULL; zs.zfree = Z_NULL; zs.opaque = Z_NULL; level = Z_DEFAULT_COMPRESSION; deflateInit(&zs, level); zs.next_in = (uchar*) in->value; zs.avail_in = (int) size; do { flush = (zs.avail_in == 0) ? Z_FINISH : Z_NO_FLUSH; do { zs.avail_out = ZBUFSIZE; zs.next_out = outbuf; deflate(&zs, flush); nbytes = ZBUFSIZE - zs.avail_out; if (mprPutBlockToBuf(out, (char*) outbuf, nbytes) != nbytes) { ejsThrowIOError(ejs, "Cannot copy to output buffer"); deflateEnd(&zs); return 0; } } while (zs.avail_out == 0); assure(zs.avail_in == 0); } while (flush != Z_FINISH); deflateEnd(&zs); return ejsCreateStringFromBytes(ejs, mprGetBufStart(out), mprGetBufLength(out)); }
/* * Write a block of data. This is the lowest level write routine for dynamic data. If block is true, this routine will * block until all the block is written. If block is false, then it may return without having written all the data. */ int maWriteBlock(MaQueue *q, cchar *buf, int size, bool block) { MaPacket *packet; MaConn *conn; MaResponse *resp; int bytes, written, packetSize; mprAssert(q->stage->flags & MA_STAGE_HANDLER); conn = q->conn; resp = conn->response; packetSize = (resp->chunkSize > 0) ? resp->chunkSize : q->max; packetSize = min(packetSize, size); if ((q->flags & MA_QUEUE_DISABLED) || (q->count > 0 && (q->count + size) >= q->max)) { if (!drain(q, block)) { return 0; } } for (written = 0; size > 0; ) { if (q->count >= q->max && !drain(q, block)) { maCheckQueueCount(q); break; } if (conn->disconnected) { return MPR_ERR_CANT_WRITE; } if ((packet = maCreateDataPacket(q, packetSize)) == 0) { return MPR_ERR_NO_MEMORY; } if ((bytes = mprPutBlockToBuf(packet->content, buf, size)) == 0) { return MPR_ERR_NO_MEMORY; } buf += bytes; size -= bytes; written += bytes; maPutForService(q, packet, 1); maCheckQueueCount(q); } maCheckQueueCount(q); return written; }
PUBLIC ssize mprWriteFile(MprFile *file, cvoid *buf, ssize count) { MprFileSystem *fs; MprBuf *bp; ssize bytes, written; assert(file); if (file == 0) { return MPR_ERR_BAD_HANDLE; } fs = file->fileSystem; bp = file->buf; written = 0; while (count > 0) { if (bp == 0) { if ((bytes = fs->writeFile(file, buf, count)) < 0) { return bytes; } } else { if ((bytes = mprPutBlockToBuf(bp, buf, count)) < 0) { return bytes; } if (bytes != count) { mprFlushFile(file); } } count -= bytes; written += bytes; buf = (char*) buf + bytes; } file->pos += (MprOff) written; if (file->pos > file->size) { file->size = file->pos; } return written; }
/* Write a block of data. This is the lowest level write routine for data. This will buffer the data and flush if the queue buffer is full. Flushing is done by calling httpFlushQueue which will service queues as required. This may call the queue outgoing service routine and disable downstream queues if they are overfull. This routine will always accept the data and never return "short". */ PUBLIC ssize httpWriteBlock(HttpQueue *q, cchar *buf, ssize len, int flags) { HttpPacket *packet; HttpConn *conn; HttpTx *tx; ssize totalWritten, packetSize, thisWrite; assert(q == q->conn->writeq); conn = q->conn; tx = conn->tx; if (flags == 0) { flags = HTTP_BUFFER; } if (tx == 0 || tx->finalizedOutput) { return MPR_ERR_CANT_WRITE; } tx->responded = 1; for (totalWritten = 0; len > 0; ) { mprTrace(7, "httpWriteBlock q_count %d, q_max %d", q->count, q->max); if (conn->state >= HTTP_STATE_FINALIZED) { return MPR_ERR_CANT_WRITE; } if (q->last && q->last != q->first && q->last->flags & HTTP_PACKET_DATA && mprGetBufSpace(q->last->content) > 0) { packet = q->last; } else { packetSize = (tx->chunkSize > 0) ? tx->chunkSize : q->packetSize; if ((packet = httpCreateDataPacket(packetSize)) == 0) { return MPR_ERR_MEMORY; } httpPutForService(q, packet, HTTP_DELAY_SERVICE); } assert(mprGetBufSpace(packet->content) > 0); thisWrite = min(len, mprGetBufSpace(packet->content)); if (flags & (HTTP_BLOCK | HTTP_NON_BLOCK)) { thisWrite = min(thisWrite, q->max - q->count); } if (thisWrite > 0) { if ((thisWrite = mprPutBlockToBuf(packet->content, buf, thisWrite)) == 0) { return MPR_ERR_MEMORY; } buf += thisWrite; len -= thisWrite; q->count += thisWrite; totalWritten += thisWrite; } if (q->count >= q->max) { httpFlushQueue(q, 0); if (q->count >= q->max) { if (flags & HTTP_NON_BLOCK) { break; } else if (flags & HTTP_BLOCK) { while (q->count >= q->max && !tx->finalized) { if (!mprWaitForSingleIO((int) conn->sock->fd, MPR_WRITABLE, conn->limits->inactivityTimeout)) { return MPR_ERR_TIMEOUT; } httpResumeQueue(conn->connectorq); httpServiceQueues(conn); } } } } } if (conn->error) { return MPR_ERR_CANT_WRITE; } return totalWritten; }
/* This will be enabled when caching is enabled for the route and there is no acceptable cache data to use. OR - manual caching has been enabled. */ static void outgoingCacheFilterService(HttpQueue *q) { HttpPacket *packet, *data; HttpConn *conn; HttpTx *tx; MprKey *kp; cchar *cachedData; ssize size; int foundDataPacket; conn = q->conn; tx = conn->tx; foundDataPacket = 0; cachedData = 0; if (tx->status < 200 || tx->status > 299) { tx->cacheBuffer = 0; } /* This routine will save cached responses to tx->cacheBuffer. It will also send cached data if the X-SendCache header is present. Normal caching is done by cacheHandler */ if (mprLookupKey(conn->tx->headers, "X-SendCache") != 0) { if (fetchCachedResponse(conn)) { mprLog(3, "cacheFilter: write cached content for '%s'", conn->rx->uri); cachedData = setHeadersFromCache(conn, tx->cachedContent); tx->length = slen(cachedData); } } for (packet = httpGetPacket(q); packet; packet = httpGetPacket(q)) { if (!httpWillNextQueueAcceptPacket(q, packet)) { httpPutBackPacket(q, packet); return; } if (packet->flags & HTTP_PACKET_HEADER) { if (!cachedData && tx->cacheBuffer) { /* Add defined headers to the start of the cache buffer. Separate with a double newline. */ mprPutFmtToBuf(tx->cacheBuffer, "X-Status: %d\n", tx->status); for (kp = 0; (kp = mprGetNextKey(tx->headers, kp)) != 0; ) { mprPutFmtToBuf(tx->cacheBuffer, "%s: %s\n", kp->key, kp->data); } mprPutCharToBuf(tx->cacheBuffer, '\n'); } } else if (packet->flags & HTTP_PACKET_DATA) { if (cachedData) { /* Using X-SendCache. Replace the data with the cached response. */ mprFlushBuf(packet->content); mprPutBlockToBuf(packet->content, cachedData, (ssize) tx->length); } else if (tx->cacheBuffer) { /* Save the response packet to the cache buffer. Will write below in saveCachedResponse. */ size = mprGetBufLength(packet->content); if ((tx->cacheBufferLength + size) < conn->limits->cacheItemSize) { mprPutBlockToBuf(tx->cacheBuffer, mprGetBufStart(packet->content), mprGetBufLength(packet->content)); tx->cacheBufferLength += size; } else { tx->cacheBuffer = 0; mprLog(3, "cacheFilter: Item too big to cache %d bytes, limit %d", tx->cacheBufferLength + size, conn->limits->cacheItemSize); } } foundDataPacket = 1; } else if (packet->flags & HTTP_PACKET_END) { if (cachedData && !foundDataPacket) { /* Using X-SendCache but there was no data packet to replace. So do the write here */ data = httpCreateDataPacket((ssize) tx->length); mprPutBlockToBuf(data->content, cachedData, (ssize) tx->length); httpPutPacketToNext(q, data); } else if (tx->cacheBuffer) { /* Save the cache buffer to the cache store */ saveCachedResponse(conn); } } httpPutPacketToNext(q, packet); } }
static EjsString *serialize(Ejs *ejs, EjsAny *vp, Json *json) { EjsName qname; EjsFunction *fn; EjsString *result, *sv; EjsTrait *trait; EjsObj *pp, *obj, *replacerArgs[2]; wchar *cp; cchar *key; int c, isArray, i, count, slotNum, quotes; /* The main code below can handle Arrays, Objects, objects derrived from Object and also native classes with properties. All others just use toString. */ count = ejsIsPot(ejs, vp) ? ejsGetLength(ejs, vp) : 0; if (count == 0 && TYPE(vp) != ESV(Object) && TYPE(vp) != ESV(Array)) { // OPT - need some flag for this test. if (!ejsIsDefined(ejs, vp) || ejsIs(ejs, vp, Boolean) || ejsIs(ejs, vp, Number)) { return ejsToString(ejs, vp); } else if (json->regexp) { return ejsToString(ejs, vp); } else { return ejsToLiteralString(ejs, vp); } } obj = vp; json->nest++; if (json->buf == 0) { json->buf = mprCreateBuf(0, 0); mprAddRoot(json->buf); } isArray = ejsIs(ejs, vp, Array); mprPutCharToWideBuf(json->buf, isArray ? '[' : '{'); if (json->pretty) { mprPutCharToWideBuf(json->buf, '\n'); } if (++ejs->serializeDepth <= json->depth && !VISITED(obj)) { SET_VISITED(obj, 1); for (slotNum = 0; slotNum < count && !ejs->exception; slotNum++) { trait = ejsGetPropertyTraits(ejs, obj, slotNum); if (trait && (trait->attributes & (EJS_TRAIT_HIDDEN | EJS_TRAIT_DELETED | EJS_FUN_INITIALIZER | EJS_FUN_MODULE_INITIALIZER)) && !json->hidden) { continue; } pp = ejsGetProperty(ejs, obj, slotNum); if (ejs->exception) { SET_VISITED(obj, 0); json->nest--; return 0; } if (pp == 0) { continue; } if (isArray) { key = itos(slotNum); qname.name = ejsCreateStringFromAsc(ejs, key); qname.space = ESV(empty); } else { qname = ejsGetPropertyName(ejs, vp, slotNum); } quotes = json->quotes; if (!quotes) { // UNICODE for (cp = qname.name->value; cp < &qname.name->value[qname.name->length]; cp++) { if (!isalnum((uchar) *cp) && *cp != '_') { quotes = 1; break; } } } if (json->pretty) { for (i = 0; i < ejs->serializeDepth; i++) { mprPutStringToWideBuf(json->buf, json->indent); } } if (!isArray) { if (json->namespaces) { if (qname.space != ESV(empty)) { mprPutToBuf(json->buf, "\"%@\"::", qname.space); } } if (quotes) { mprPutCharToWideBuf(json->buf, '"'); } for (cp = qname.name->value; cp && *cp; cp++) { c = *cp; if (c == '"' || c == '\\') { mprPutCharToWideBuf(json->buf, '\\'); mprPutCharToWideBuf(json->buf, c); } else { mprPutCharToWideBuf(json->buf, c); } } if (quotes) { mprPutCharToWideBuf(json->buf, '"'); } mprPutCharToWideBuf(json->buf, ':'); if (json->pretty) { mprPutCharToWideBuf(json->buf, ' '); } } fn = (EjsFunction*) ejsGetPropertyByName(ejs, TYPE(pp)->prototype, N(NULL, "toJSON")); // OPT - check that this is going directly to serialize most of the time if (!ejsIsFunction(ejs, fn) || (fn->isNativeProc && fn->body.proc == (EjsProc) ejsObjToJSON)) { sv = serialize(ejs, pp, json); } else { sv = (EjsString*) ejsRunFunction(ejs, fn, pp, 1, &json->options); } if (sv == 0 || !ejsIs(ejs, sv, String)) { if (ejs->exception) { ejsThrowTypeError(ejs, "Cannot serialize property %@", qname.name); SET_VISITED(obj, 0); return 0; } } else { if (json->replacer) { replacerArgs[0] = (EjsObj*) qname.name; replacerArgs[1] = (EjsObj*) sv; /* function replacer(key: String, value: String): String */ sv = ejsRunFunction(ejs, json->replacer, obj, 2, (EjsObj**) replacerArgs); } mprPutBlockToBuf(json->buf, sv->value, sv->length * sizeof(wchar)); } if ((slotNum + 1) < count || json->commas) { mprPutCharToWideBuf(json->buf, ','); } if (json->pretty) { mprPutCharToWideBuf(json->buf, '\n'); } } SET_VISITED(obj, 0); } --ejs->serializeDepth; if (json->pretty) { for (i = ejs->serializeDepth; i > 0; i--) { mprPutStringToWideBuf(json->buf, json->indent); } } mprPutCharToWideBuf(json->buf, isArray ? ']' : '}'); mprAddNullToWideBuf(json->buf); if (--json->nest == 0) { result = ejsCreateString(ejs, mprGetBufStart(json->buf), mprGetBufLength(json->buf) / sizeof(wchar)); mprRemoveRoot(json->buf); } else { result = 0; } return result; }
Token getNextJsonToken(MprBuf *buf, wchar **token, JsonState *js) { wchar *start, *cp, *end, *next; wchar *src, *dest; int quote, tid, c, isReg; if (buf) { mprFlushBuf(buf); } cp = js->next; end = js->end; cp = skipComments(cp, end); next = cp + 1; quote = -1; isReg = 0; if (*cp == '\0') { tid = TOK_EOF; } else if (*cp == '{') { tid = TOK_LBRACE; } else if (*cp == '[') { tid = TOK_LBRACKET; } else if (*cp == '}' || *cp == ']') { tid = *cp == '}' ? TOK_RBRACE: TOK_RBRACKET; while (*++cp && isspace((uchar) *cp)) ; #if NEW || 1 /* Detect missing comma after closing brace/bracket */ if (*cp && *cp != ',' && *cp != '}' && *cp != ']' && *cp != '/') { js->error = cp; return TOK_ERR; } #endif if (*cp == ',' || *cp == ':') { cp++; } next = cp; } else { if (*cp == '"' || *cp == '\'') { tid = TOK_QID; quote = *cp++; for (start = cp; cp < end; cp++) { if (*cp == '\\' && &cp[1] < end) { cp++; } else if (*cp == quote) { break; } } if (*cp != quote) { js->error = cp; return TOK_ERR; } if (buf) { mprPutBlockToBuf(buf, (char*) start, (cp - start)); } cp++; } else if (*cp == '/') { tid = TOK_ID; isReg = 1; for (start = cp++; cp < end; cp++) { if (*cp == '\\') { if (cp[1] == '/') { cp++; } continue; } if (*cp == '/') { break; } } if (*cp != '/') { js->error = cp; return TOK_ERR; } if (buf) { mprPutBlockToBuf(buf, (char*) start, (cp - start + 1)); } cp++; } else { /* Note: this is for keys and values */ tid = TOK_ID; for (start = cp; cp < end; cp++) { if (*cp == '\\') { continue; } /* Not an allowable character outside quotes (TODO - removed space Should really keep state for parsing keys or values and not allow -,+,. in keys */ if (!(isalnum((uchar) *cp) || *cp == '_' || *cp == '-' || *cp == '+' || *cp == '.')) { break; } } if (buf) { mprPutBlockToBuf(buf, (char*) start, (int) (cp - start)); } } if (buf) { mprAddNullToBuf(buf); } while (*cp && isspace((int) *cp)) cp++; if (*cp == ',' || *cp == ':') { cp++; } else if (*cp != '}' && *cp != ']' && *cp != '\0' && *cp != '\n' && *cp != '\r' && *cp != ' ') { js->error = cp; return TOK_ERR; } next = cp; if (buf) { for (dest = src = (wchar*) buf->start; src < (wchar*) buf->end; ) { c = *src++; if (c == '\\' && !isReg) { c = *src++; if (c == 'r') { c = '\r'; } else if (c == 'n') { c = '\n'; } else if (c == 'b') { c = '\b'; } } *dest++ = c; } *dest = '\0'; *token = (wchar*) mprGetBufStart(buf); } } js->next = next; return tid; }
Token getNextJsonToken(MprBuf *buf, MprChar **token, JsonState *js) { MprChar *start, *cp, *end, *next; MprChar *src, *dest; int quote, tid, c; if (buf) { mprFlushBuf(buf); } cp = js->next; end = js->end; cp = skipComments(cp, end); next = cp + 1; quote = -1; if (*cp == '\0') { tid = TOK_EOF; } else if (*cp == '{') { tid = TOK_LBRACE; } else if (*cp == '[') { tid = TOK_LBRACKET; } else if (*cp == '}' || *cp == ']') { tid = *cp == '}' ? TOK_RBRACE: TOK_RBRACKET; while (*++cp && isspace((uchar) *cp)) ; if (*cp == ',' || *cp == ':') { cp++; } next = cp; } else { if (*cp == '"' || *cp == '\'') { tid = TOK_QID; quote = *cp++; for (start = cp; cp < end; cp++) { if (*cp == '\\') { if (cp[1] == quote) { cp++; } continue; } if (*cp == quote) { break; } } if (*cp != quote) { js->error = cp; return TOK_ERR; } if (buf) { mprPutBlockToBuf(buf, (char*) start, (cp - start)); } cp++; } else if (*cp == '/') { tid = TOK_ID; for (start = cp++; cp < end; cp++) { if (*cp == '\\') { if (cp[1] == '/') { cp++; } continue; } if (*cp == '/') { break; } } if (*cp != '/') { js->error = cp; return TOK_ERR; } if (buf) { mprPutBlockToBuf(buf, (char*) start, (cp - start)); } cp++; } else { tid = TOK_ID; for (start = cp; cp < end; cp++) { if (*cp == '\\') { continue; } /* Not an allowable character outside quotes */ if (!(isalnum((uchar) *cp) || *cp == '_' || *cp == ' ' || *cp == '-' || *cp == '+' || *cp == '.')) { break; } } if (buf) { mprPutBlockToBuf(buf, (char*) start, (int) (cp - start)); } } if (buf) { mprAddNullToBuf(buf); } if (*cp == ',' || *cp == ':') { cp++; } else if (*cp != '}' && *cp != ']' && *cp != '\0' && *cp != '\n' && *cp != '\r' && *cp != ' ') { js->error = cp; return TOK_ERR; } next = cp; if (buf) { for (dest = src = (MprChar*) buf->start; src < (MprChar*) buf->end; ) { c = *src++; if (c == '\\') { c = *src++; if (c == 'r') { c = '\r'; } else if (c == 'n') { c = '\n'; } else if (c == 'b') { c = '\b'; } } *dest++ = c; } *dest = '\0'; *token = (MprChar*) mprGetBufStart(buf); } } js->next = next; return tid; }
/* function [get|put|delete|post...](uri = null, ...data): Http */ static EjsHttp *startHttpRequest(Ejs *ejs, EjsHttp *hp, char *method, int argc, EjsObj **argv) { EjsArray *args; EjsByteArray *data; EjsNumber *written; EjsUri *uriObj; HttpConn *conn; ssize nbytes; conn = hp->conn; hp->responseCache = 0; hp->requestContentCount = 0; mprFlushBuf(hp->responseContent); if (argc >= 1 && !ejsIs(ejs, argv[0], Null)) { uriObj = (EjsUri*) argv[0]; hp->uri = httpUriToString(uriObj->uri, HTTP_COMPLETE_URI); } if (argc == 2 && ejsIs(ejs, argv[1], Array)) { args = (EjsArray*) argv[1]; if (args->length > 0) { data = ejsCreateByteArray(ejs, -1); written = ejsWriteToByteArray(ejs, data, 1, &argv[1]); mprPutBlockToBuf(hp->requestContent, (char*) data->value, (int) written->value); mprAddNullToBuf(hp->requestContent); assert(written > 0); } } if (hp->uri == 0) { ejsThrowArgError(ejs, "URL is not defined"); return 0; } if (method && strcmp(hp->method, method) != 0) { hp->method = sclone(method); } if (hp->method == 0) { ejsThrowArgError(ejs, "HTTP Method is not defined"); return 0; } if (hp->certFile) { if (!hp->ssl) { hp->ssl = mprCreateSsl(0); } mprSetSslCertFile(hp->ssl, hp->certFile); if (!hp->keyFile) { ejsThrowStateError(ejs, "Must define a Http.key to use with a certificate"); } mprSetSslKeyFile(hp->ssl, hp->keyFile); } if (hp->caFile) { if (!hp->ssl) { hp->ssl = mprCreateSsl(0); } mprSetSslCaFile(hp->ssl, hp->caFile); } if (httpConnect(conn, hp->method, hp->uri, hp->ssl) < 0) { ejsThrowIOError(ejs, "Cannot issue request for \"%s\"", hp->uri); return 0; } if (mprGetBufLength(hp->requestContent) > 0) { nbytes = httpWriteBlock(conn->writeq, mprGetBufStart(hp->requestContent), mprGetBufLength(hp->requestContent), HTTP_BLOCK); if (nbytes < 0) { ejsThrowIOError(ejs, "Cannot write request data for \"%s\"", hp->uri); return 0; } else if (nbytes > 0) { assert(nbytes == mprGetBufLength(hp->requestContent)); mprAdjustBufStart(hp->requestContent, nbytes); hp->requestContentCount += nbytes; } httpFinalize(conn); } httpNotify(conn, HTTP_EVENT_WRITABLE, 0); if (conn->async) { httpEnableConnEvents(hp->conn); } return hp; }