/* * Public routine to set the error message. Caller MUST NOT free. */ char *ejsGetErrorMsg(Ejs *ejs, int withStack) { EjsVar *message, *stack, *error; cchar *name; char *buf; if (ejs->flags & EJS_FLAG_EMPTY) { return ""; } error = (EjsVar*) ejs->exception; message = stack = 0; name = 0; if (error) { name = error->type->qname.name; if (ejsIsA(ejs, error, ejs->errorType)) { message = ejsGetProperty(ejs, error, ES_Error_message); stack = ejsGetProperty(ejs, error, ES_Error_stack); } else if (ejsIsString(error)) { name = "Error"; message = error; } else if (ejsIsNumber(error)) { name = "Error"; message = error; } else if (error == (EjsVar*) ejs->stopIterationType) { name = "StopIteration"; message = (EjsVar*) ejsCreateString(ejs, "Uncaught StopIteration exception"); } } if (!withStack) { stack = 0; } if (stack && ejsIsString(stack) && message && ejsIsString(message)){ buf = mprAsprintf(ejs, -1, "%s Exception: %s\nStack:\n%s", name, ((EjsString*) message)->value, ((EjsString*) stack)->value); } else if (message && ejsIsString(message)){ buf = mprAsprintf(ejs, -1, "%s: %s", name, ((EjsString*) message)->value); } else if (message && ejsIsNumber(message)){ buf = mprAsprintf(ejs, -1, "%s: %d", name, ((EjsNumber*) message)->value); } else { if (error) { buf = mprStrdup(ejs, "Unknown exception object type"); } else { buf = mprStrdup(ejs, ""); } } mprFree(ejs->errorMsg); ejs->errorMsg = buf; return buf; }
static void setHttpPipeline(Ejs *ejs, EjsHttpServer *sp) { EjsString *vs; HttpHost *host; HttpRoute *route; Http *http; HttpStage *stage; cchar *name; int i; assure(sp->endpoint); http = sp->endpoint->http; host = mprGetFirstItem(sp->endpoint->hosts); route = mprGetFirstItem(host->routes); if (sp->outgoingStages) { httpClearRouteStages(route, HTTP_STAGE_TX); for (i = 0; i < sp->outgoingStages->length; i++) { vs = ejsGetProperty(ejs, sp->outgoingStages, i); if (vs && ejsIs(ejs, vs, String)) { name = vs->value; if (httpLookupStage(http, name) == 0) { ejsThrowArgError(ejs, "Cannot find pipeline stage name %s", name); return; } httpAddRouteFilter(route, name, NULL, HTTP_STAGE_TX); } } } if (sp->incomingStages) { httpClearRouteStages(route, HTTP_STAGE_RX); for (i = 0; i < sp->incomingStages->length; i++) { vs = ejsGetProperty(ejs, sp->incomingStages, i); if (vs && ejsIs(ejs, vs, String)) { name = vs->value; if (httpLookupStage(http, name) == 0) { ejsThrowArgError(ejs, "Cannot find pipeline stage name %s", name); return; } httpAddRouteFilter(route, name, NULL, HTTP_STAGE_RX); } } } if (sp->connector) { if ((stage = httpLookupStage(http, sp->connector)) == 0) { ejsThrowArgError(ejs, "Cannot find pipeline stage name %s", sp->connector); return; } route->connector = stage; } }
/** Get a serialized string representation of a variable using JSON encoding. @return Returns a string variable or null if an exception is thrown. */ EjsString *ejsToJson(Ejs *ejs, EjsVar *vp) { EjsFunction *fn; EjsString *result; if (vp == 0) { return ejsCreateString(ejs, "undefined"); } if (vp->jsonVisited) { return ejsCreateString(ejs, "this"); } vp->jsonVisited = 1; /* * Types can provide a toJSON method, a serializeVar helper. If neither are provided, toString is used as a fall-back. */ fn = (EjsFunction*) ejsGetProperty(ejs, (EjsVar*) vp->type, ES_Object_toJSON); if (ejsIsFunction(fn)) { result = (EjsString*) ejsRunFunction(ejs, fn, vp, 0, NULL); } else { result = ejsToString(ejs, vp); } vp->jsonVisited = 0; return result; }
/** Get a string representation of a variable. @return Returns a string variable or null if an exception is thrown. */ EjsString *ejsToString(Ejs *ejs, EjsVar *vp) { EjsFunction *fn; if (vp == 0) { return ejsCreateString(ejs, "undefined"); } else if (ejsIsString(vp)) { return (EjsString*) vp; } /* * Types can provide a toString method or a castVar helper */ if (vp->type->helpers->getProperty != getProperty) { fn = (EjsFunction*) ejsGetProperty(ejs, (EjsVar*) vp->type, ES_Object_toString); if (ejsIsFunction(fn)) { return (EjsString*) ejsRunFunction(ejs, fn, vp, 0, NULL); } } if (vp->type->helpers->castVar) { return (EjsString*) (vp->type->helpers->castVar)(ejs, vp, ejs->stringType); } ejsThrowInternalError(ejs, "CastVar helper not defined for type \"%s\"", vp->type->qname.name); return 0; }
EjsVar *ejsCreateException(Ejs *ejs, int slot, cchar *fmt, va_list fmtArgs) { EjsType *type; EjsVar *error; char *buf; if (ejs->exception) { buf = mprVasprintf(ejs, 0, fmt, fmtArgs); mprError(ejs, "Double exception: %s", buf); mprFree(buf); return ejs->exception; } if (!ejs->initialized || (ejs->flags & EJS_FLAG_EMPTY)) { buf = mprVasprintf(ejs, 0, fmt, fmtArgs); mprError(ejs, "Exception: %s", buf); mprFree(buf); return ejs->exception; } type = (EjsType*) ejsGetProperty(ejs, ejs->global, slot); if (type == 0) { type = ejs->errorType; } error = createException(ejs, type, fmt, fmtArgs); if (error) { ejsThrowException(ejs, error); } return error; }
EjsVar *ejsCreateInstance(Ejs *ejs, EjsType *type, int argc, EjsVar **argv) { EjsFunction *fun; EjsVar *vp; int slotNum; mprAssert(type); vp = ejsCreateVar(ejs, type, 0); if (vp == 0) { ejsThrowMemoryError(ejs); return 0; } if (type->hasConstructor) { slotNum = type->block.numInherited; fun = (EjsFunction*) ejsGetProperty(ejs, (EjsVar*) type, slotNum); if (fun == 0) { return 0; } if (!ejsIsFunction(fun)) { return 0; } vp->permanent = 1; ejsRunFunction(ejs, fun, vp, argc, argv); vp->permanent = 0; } return vp; }
/* * Get a variable by name. If vp is specified, it contains an explicit object in which to search for the variable name. * Otherwise, the full execution scope is consulted. The lookup fields will be set as residuals. */ EjsVar *ejsGetVarByName(Ejs *ejs, EjsVar *vp, EjsName *name, EjsLookup *lookup) { EjsVar *result; int slotNum; mprAssert(ejs); mprAssert(name); // OPT - really nice to remove this if (vp && vp->type->helpers->getPropertyByName) { result = (*vp->type->helpers->getPropertyByName)(ejs, vp, name); if (result) { return result; } } if (vp) { slotNum = ejsLookupVar(ejs, vp, name, lookup); } else { slotNum = ejsLookupScope(ejs, name, lookup); } if (slotNum < 0) { return 0; } return ejsGetProperty(ejs, lookup->obj, slotNum); }
/* function send(...content): Number */ static EjsNumber *ws_send(Ejs *ejs, EjsWebSocket *ws, int argc, EjsObj **argv) { EjsArray *args; EjsByteArray *ba; EjsAny *arg; ssize nbytes; int i; args = (EjsArray*) argv[0]; if (ws->conn->state < HTTP_STATE_PARSED && !waitForHttpState(ws, HTTP_STATE_PARSED, -1, 1)) { return ESV(null); } nbytes = 0; for (i = 0; i < args->length; i++) { if ((arg = ejsGetProperty(ejs, args, i)) != 0) { if (ejsIs(ejs, arg, ByteArray)) { ba = (EjsByteArray*) arg; nbytes = ejsGetByteArrayAvailableData(ba); nbytes = httpSendBlock(ws->conn, WS_MSG_BINARY, (cchar*) &ba->value[ba->readPosition], nbytes, HTTP_BLOCK); } else { nbytes = httpSend(ws->conn, ejsToMulti(ejs, arg)); } if (nbytes < 0) { return ESV(null); } } } return ejsCreateNumber(ejs, (MprNumber) nbytes); }
/* function setLimits(limits: Object): Void */ static EjsObj *hs_setLimits(Ejs *ejs, EjsHttpServer *sp, int argc, EjsObj **argv) { EjsObj *vp, *app, *cache, *cacheLimits; HttpLimits *limits; if (sp->limits == 0) { sp->limits = ejsCreateEmptyPot(ejs); limits = (sp->endpoint) ? sp->endpoint->limits : ejs->http->serverLimits; assure(limits); ejsGetHttpLimits(ejs, sp->limits, limits, 1); } ejsBlendObject(ejs, sp->limits, argv[0], EJS_BLEND_OVERWRITE); if (sp->endpoint) { limits = (sp->endpoint) ? sp->endpoint->limits : ejs->http->serverLimits; ejsSetHttpLimits(ejs, limits, sp->limits, 1); } if ((vp = ejsGetPropertyByName(ejs, sp->limits, EN("sessionTimeout"))) != 0) { app = ejsGetPropertyByName(ejs, ejs->global, N("ejs", "App")); cache = ejsGetProperty(ejs, app, ES_App_cache); if (cache && cache != ESV(null)) { cacheLimits = ejsCreateEmptyPot(ejs); ejsSetPropertyByName(ejs, cacheLimits, EN("lifespan"), vp); ejsCacheSetLimits(ejs, cache, cacheLimits); } } return 0; }
static void setupTrace(Ejs *ejs, HttpTrace *trace, int dir, EjsObj *options) { EjsArray *extensions; EjsObj *ext; HttpTrace *tp; int i, level, *levels; tp = &trace[dir]; levels = tp->levels; if ((level = getNumOption(ejs, options, "all")) >= 0) { for (i = 0; i < HTTP_TRACE_MAX_ITEM; i++) { levels[i] = level; } } else { levels[HTTP_TRACE_CONN] = getNumOption(ejs, options, "conn"); levels[HTTP_TRACE_FIRST] = getNumOption(ejs, options, "first"); levels[HTTP_TRACE_HEADER] = getNumOption(ejs, options, "headers"); levels[HTTP_TRACE_BODY] = getNumOption(ejs, options, "body"); } tp->size = getNumOption(ejs, options, "size"); if ((extensions = (EjsArray*) ejsGetPropertyByName(ejs, options, EN("include"))) != 0) { if (!ejsIs(ejs, extensions, Array)) { ejsThrowArgError(ejs, "include is not an array"); return; } tp->include = mprCreateHash(0, 0); for (i = 0; i < extensions->length; i++) { if ((ext = ejsGetProperty(ejs, extensions, i)) != 0) { mprAddKey(tp->include, ejsToMulti(ejs, ejsToString(ejs, ext)), ""); } } } if ((extensions = (EjsArray*) ejsGetPropertyByName(ejs, options, EN("exclude"))) != 0) { if (!ejsIs(ejs, extensions, Array)) { ejsThrowArgError(ejs, "exclude is not an array"); return; } tp->exclude = mprCreateHash(0, 0); for (i = 0; i < extensions->length; i++) { if ((ext = ejsGetProperty(ejs, extensions, i)) != 0) { mprAddKey(tp->exclude, ejsToMulti(ejs, ejsToString(ejs, ext)), MPR->emptyString); } } } }
/* function secure(keyFile: Path, certFile: Path!, protocols: Array? = null, ciphers: Array? = null): Void */ static EjsObj *hs_secure(Ejs *ejs, EjsHttpServer *sp, int argc, EjsObj **argv) { #if ME_COM_SSL EjsArray *protocols; cchar *token; int mask, protoMask, i; if (sp->ssl == 0 && ((sp->ssl = mprCreateSsl(1)) == 0)) { return 0; } if (!ejsIs(ejs, argv[0], Null)) { mprSetSslKeyFile(sp->ssl, ejsToMulti(ejs, argv[0])); } if (!ejsIs(ejs, argv[1], Null)) { mprSetSslCertFile(sp->ssl, ejsToMulti(ejs, argv[1])); } if (argc >= 3 && ejsIs(ejs, argv[2], Array)) { protocols = (EjsArray*) argv[2]; protoMask = 0; for (i = 0; i < protocols->length; i++) { token = ejsToMulti(ejs, ejsGetProperty(ejs, protocols, i)); mask = -1; if (*token == '-') { token++; mask = 0; } else if (*token == '+') { token++; } if (scaselesscmp(token, "SSLv2") == 0) { protoMask &= ~(MPR_PROTO_SSLV2 & ~mask); protoMask |= (MPR_PROTO_SSLV2 & mask); } else if (scaselesscmp(token, "SSLv3") == 0) { protoMask &= ~(MPR_PROTO_SSLV3 & ~mask); protoMask |= (MPR_PROTO_SSLV3 & mask); } else if (scaselesscmp(token, "TLSv1") == 0) { protoMask &= ~(MPR_PROTO_TLSV1 & ~mask); protoMask |= (MPR_PROTO_TLSV1 & mask); } else if (scaselesscmp(token, "ALL") == 0) { protoMask &= ~(MPR_PROTO_ALL & ~mask); protoMask |= (MPR_PROTO_ALL & mask); } } mprSetSslProtocols(sp->ssl, protoMask); } if (argc >= 4 && ejsIs(ejs, argv[3], Array)) { mprSetSslCiphers(sp->ssl, ejsToMulti(ejs, argv[3])); } #else ejsThrowReferenceError(ejs, "SSL support was not included in the build"); #endif return 0; }
/* Write data to the file function write(data: Object): Number */ PUBLIC EjsObj *writeFile(Ejs *ejs, EjsFile *fp, int argc, EjsObj **argv) { EjsArray *args; EjsByteArray *ap; EjsObj *vp; EjsString *str; cchar *buf; ssize len, written; int i; assert(argc == 1 && ejsIs(ejs, argv[0], Array)); args = (EjsArray*) argv[0]; if (!(fp->mode & EJS_FILE_WRITE)) { ejsThrowStateError(ejs, "File not opened for writing"); return 0; } written = 0; for (i = 0; i < args->length; i++) { vp = ejsGetProperty(ejs, (EjsObj*) args, i); assert(vp); switch (TYPE(vp)->sid) { case S_ByteArray: ap = (EjsByteArray*) vp; // TODO UNICODE ENCODING buf = (cchar*) &ap->value[ap->readPosition]; len = ap->writePosition - ap->readPosition; break; case S_String: // UNICODE #if UNICODE && FUTURE buf = awtom(((EjsString*) vp)->value, &len); #else buf = ((EjsString*) vp)->value; len = ((EjsString*) vp)->length; #endif break; default: str = ejsToString(ejs, vp); buf = awtom(((EjsString*) str)->value, &len); break; } if (mprWriteFile(fp->file, buf, len) != len) { ejsThrowIOError(ejs, "Cannot write to %s", fp->path); return 0; } written += len; /* Use GC to free buf as it may not be allocated */ } return (EjsObj*) ejsCreateNumber(ejs, (MprNumber) written); }
/* function emit(level: Number, ...data): Number */ static EjsNumber *lf_emit(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { EjsArray *args; EjsByteArray *ap; EjsObj *vp; EjsString *str; char *msg, *arg; ssize len, written; int i, level, paused; assure(argc >= 2 && ejsIs(ejs, argv[1], Array)); level = ejsGetInt(ejs, argv[0]); args = (EjsArray*) argv[1]; written = 0; msg = 0; paused = ejsBlockGC(ejs); for (i = 0; i < args->length; i++) { vp = ejsGetProperty(ejs, args, i); assure(vp); switch (TYPE(vp)->sid) { case S_ByteArray: ap = (EjsByteArray*) vp; // TODO ENCODING arg = (char*) &ap->value[ap->readPosition]; len = ap->writePosition - ap->readPosition; break; case S_String: // MOB - use NULL instead of &len arg = awtom(((EjsString*) vp)->value, &len); break; default: str = ejsToString(ejs, vp); // MOB - use NULL instead of &len arg = awtom(((EjsString*) str)->value, &len); break; } msg = srejoin(msg, arg, NULL); } if (msg) { mprRawLog(level, "%s", msg); written += slen(msg); } ejsUnblockGC(ejs, paused); return ejsCreateNumber(ejs, (MprNumber) slen(msg)); }
static EjsVar *booleanConstructor(Ejs *ejs, EjsBoolean *bp, int argc, EjsVar **argv) { EjsArray *args; EjsVar *vp; mprAssert(argc == 0 || (argc == 1 && ejsIsArray(argv[0]))); if (argc == 1) { args = (EjsArray*) argv[0]; vp = ejsGetProperty(ejs, (EjsVar*) args, 0); if (vp) { /* Change the bp value */ bp = ejsToBoolean(ejs, vp); } } return (EjsVar*) bp; }
EjsAny *ejsCreateException(Ejs *ejs, int slot, cchar *fmt, va_list fmtArgs) { EjsType *type; EjsAny *error; if (ejs->exception) { mprLog("ejs vm", 0, "Double exception: %s", sfmtv(fmt, fmtArgs)); return ejs->exception; } type = (ejs->initialized) ? ejsGetProperty(ejs, ejs->global, slot) : NULL; if (type == 0) { type = EST(Error); } error = createException(ejs, type, fmt, fmtArgs); if (error) { ejsThrowException(ejs, error); } return error; }
/* Prepare form data as a series of key-value pairs. Data is formatted according to www-url-encoded specs by mprSetHttpFormData. Objects are flattened into a one level key/value pairs. Keys can have embedded "." separators. E.g. name=value&address=77%20Park%20Lane */ static void prepForm(Ejs *ejs, EjsHttp *hp, cchar *prefix, EjsObj *data) { EjsName qname; EjsObj *vp; EjsString *value; cchar *key, *sep, *vstr; char *encodedKey, *encodedValue, *newPrefix, *newKey; int i, count; count = ejsGetLength(ejs, data); for (i = 0; i < count; i++) { if (ejsIs(ejs, data, Array)) { key = itos(i); } else { qname = ejsGetPropertyName(ejs, data, i); key = ejsToMulti(ejs, qname.name); } vp = ejsGetProperty(ejs, data, i); if (vp == 0) { continue; } if (ejsGetLength(ejs, vp) > 0) { if (prefix) { newPrefix = sfmt("%s.%s", prefix, key); prepForm(ejs, hp, newPrefix, vp); } else { prepForm(ejs, hp, key, vp); } } else { value = ejsToString(ejs, vp); sep = (mprGetBufLength(hp->requestContent) > 0) ? "&" : ""; if (prefix) { newKey = sjoin(prefix, ".", key, NULL); encodedKey = mprUriEncode(newKey, MPR_ENCODE_URI_COMPONENT); } else { encodedKey = mprUriEncode(key, MPR_ENCODE_URI_COMPONENT); } vstr = ejsToMulti(ejs, value); encodedValue = mprUriEncode(vstr, MPR_ENCODE_URI_COMPONENT); mprPutToBuf(hp->requestContent, "%s%s=%s", sep, encodedKey, encodedValue); } } }
/* * Get a property given a name. */ EjsVar *ejsGetPropertyByName(Ejs *ejs, EjsVar *vp, EjsName *name) { int slotNum; mprAssert(ejs); mprAssert(vp); mprAssert(name); if (vp->type->helpers->getPropertyByName) { return (vp->type->helpers->getPropertyByName)(ejs, vp, name); } /* * Fall back and use a two-step lookup and get */ slotNum = ejsLookupProperty(ejs, vp, name); if (slotNum < 0) { return 0; } return ejsGetProperty(ejs, vp, slotNum); }
/* Join uri segments function join(...others): Uri */ static EjsUri *uri_join(Ejs *ejs, EjsUri *up, int argc, EjsObj **argv) { EjsUri *result; EjsObj *arg; EjsArray *args; HttpUri *uri, *other, *oldUri; int i; args = (EjsArray*) argv[0]; result = cloneUri(ejs, up, 0); uri = result->uri; for (i = 0; i < args->length; i++) { arg = ejsGetProperty(ejs, args, i); if ((other = toHttpUri(ejs, arg, 0)) == NULL) { return 0; } oldUri = uri; uri = httpJoinUri(oldUri, 1, &other); } result->uri = uri; return result; }
EjsVar *ejsCreateException(Ejs *ejs, int slot, cchar *fmt, va_list fmtArgs) { EjsType *type; EjsVar *error; char *buf; #if DEBUG_IDE mprBreakpoint(0, 0); #endif mprAssert(ejs->exception == 0); if (ejs->exception) { mprAllocVsprintf(ejs, &buf, 0, fmt, fmtArgs); mprError(ejs, "Double exception: %s", buf); return ejs->exception; } if (!ejs->initialized || (ejs->flags & EJS_FLAG_EMPTY)) { mprAllocVsprintf(ejs, &buf, 0, fmt, fmtArgs); mprError(ejs, "Exception: %s", buf); return ejs->exception; } type = (EjsType*) ejsGetProperty(ejs, ejs->global, slot); if (type == 0) { type = ejs->errorType; } error = createException(ejs, type, fmt, fmtArgs); if (error) { ejsThrowException(ejs, error); } return error; }
/* Cast the operand to the specified type function cast(type: Type) : Object */ static EjsAny *castError(Ejs *ejs, EjsError *error, EjsType *type) { EjsString *stack, *msg; EjsString *us; char *buf; switch (type->sid) { case S_Boolean: return ejsCreateBoolean(ejs, 1); case S_String: stack = (EjsString*) ejsRunFunctionBySlot(ejs, error, ES_Error_formatStack, 0, NULL); us = ejsIs(ejs, stack, String) ? stack : ESV(empty); msg = ejsGetProperty(ejs, error, ES_Error_message); if ((buf = sfmt("%@ Exception: %@\nStack:\n%@\n", TYPE(error)->qname.name, msg, us)) == NULL) { ejsThrowMemoryError(ejs); } return ejsCreateStringFromAsc(ejs, buf); default: ejsThrowTypeError(ejs, "Unknown type"); } return 0; }
/* Get a variable by name. If obj is specified, it contains an explicit object in which to search for the variable name. Otherwise, the full execution scope is consulted. The lookup fields will be set as residuals. */ EjsAny *ejsGetVarByName(Ejs *ejs, EjsAny *obj, EjsName name, EjsLookup *lookup) { EjsObj *result; int slotNum; assert(ejs); // OPT - really nice to remove this // OPT -- perhaps delegate the logic below down into a getPropertyByName? if (obj && TYPE(obj)->helpers.getPropertyByName) { if ((result = (TYPE(obj)->helpers.getPropertyByName)(ejs, obj, name)) != 0) { return result; } } if (obj) { slotNum = ejsLookupVar(ejs, obj, name, lookup); } else { slotNum = ejsLookupScope(ejs, name, lookup); } if (slotNum < 0) { return 0; } return ejsGetProperty(ejs, lookup->obj, slotNum); }
static EjsRequest *createRequest(EjsHttpServer *sp, HttpConn *conn) { Ejs *ejs; EjsRequest *req; EjsPath *documents; cchar *dir; ejs = sp->ejs; documents = ejsGetProperty(ejs, sp, ES_ejs_web_HttpServer_documents); if (ejsIs(ejs, documents, Path)) { dir = documents->value; } else { /* Safety fall back */ dir = conn->rx->route->home; } req = ejsCreateRequest(ejs, sp, conn, dir); httpSetConnContext(conn, req); #if FUTURE if (sp->pipe) { def = ejsRunFunction(ejs, sp->createPipeline, if ((vp = ejsGetPropertyByName(ejs, def, ejsName(&name, "", "handler"))) != 0) { handler = ejsToMulti(ejs, vp); }
static void receiveRequest(EjsRequest *req, MprEvent *event) { Ejs *ejs; EjsAny *argv[1]; EjsFunction *onrequest; HttpConn *conn; conn = req->conn; ejs = req->ejs; assert(ejs); onrequest = ejsGetProperty(ejs, req->server, ES_ejs_web_HttpServer_onrequest); if (!ejsIsFunction(ejs, onrequest)) { ejsThrowStateError(ejs, "HttpServer.onrequest is not a function"); return; } argv[0] = req; ejsRunFunction(ejs, onrequest, req->server, 1, argv); if (conn->state == HTTP_STATE_BEGIN) { conn->ejs = 0; httpUsePrimary(conn); } httpEnableConnEvents(conn); }
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; }
/* Process a message sent from postMessage. This may run inside the worker or outside in the parent depending on the direction of the message. But it ALWAYS runs in the appropriate thread for the interpreter. */ static int doMessage(Message *msg, MprEvent *mprEvent) { Ejs *ejs; EjsObj *event, *frame; EjsWorker *worker; EjsFunction *callback; EjsObj *argv[1]; worker = msg->worker; worker->gotMessage = 1; ejs = worker->ejs; assert(!ejs->exception); event = 0; ejsBlockGC(ejs); callback = ejsGetProperty(ejs, worker, msg->callbackSlot); switch (msg->callbackSlot) { case ES_Worker_onerror: event = ejsCreateObj(ejs, ESV(ErrorEvent), 0); break; case ES_Worker_onclose: case ES_Worker_onmessage: event = ejsCreateObj(ejs, ESV(Event), 0); break; default: assert(msg->callbackSlot == 0); return 0; } worker->event = event; if (msg->data) { ejsSetProperty(ejs, event, ES_Event_data, ejsCreateStringFromAsc(ejs, msg->data)); } if (msg->message) { ejsSetProperty(ejs, event, ES_ErrorEvent_message, msg->message); } if (msg->stack) { ejsSetProperty(ejs, event, ES_ErrorEvent_stack, msg->stack); if ((frame = ejsGetProperty(ejs, msg->stack, 0)) != 0 && !ejsIs(ejs, frame, Void)) { ejsSetProperty(ejs, event, ES_ErrorEvent_filename, ejsGetPropertyByName(ejs, frame, EN("filename"))); ejsSetProperty(ejs, event, ES_ErrorEvent_lineno, ejsGetPropertyByName(ejs, frame, EN("lineno"))); } } assert(!ejs->exception); if (callback == 0 || ejsIs(ejs, callback, Null)) { if (msg->callbackSlot == ES_Worker_onmessage) { mprTrace(6, "Discard message as no onmessage handler defined for worker"); } else if (msg->callbackSlot == ES_Worker_onerror) { if (ejsIs(ejs, msg->message, String)) { ejsThrowError(ejs, "Exception in Worker: %@", ejsToString(ejs, msg->message)); } else { ejsThrowError(ejs, "Exception in Worker: %s", ejsGetErrorMsg(worker->pair->ejs, 1)); } } else { /* Ignore onclose message */ } } else if (!ejsIsFunction(ejs, callback)) { ejsThrowTypeError(ejs, "Worker callback %s is not a function", msg->callback); } else { assert(!ejs->exception); argv[0] = event; ejsRunFunction(ejs, callback, worker, 1, argv); } if (msg->callbackSlot == ES_Worker_onclose) { assert(!worker->inside); worker->state = EJS_WORKER_COMPLETE; mprTrace(5, "Worker.doMessage: complete"); /* Worker and insider interpreter are now eligible for garbage collection */ removeWorker(worker); } mprSignalDispatcher(ejs->dispatcher); worker->event = 0; return 0; }
/* Get the current exception error. May be an Error object or may be any other object that is thrown. Caller must NOT free. */ cchar *ejsGetErrorMsg(Ejs *ejs, int withStack) { EjsString *str, *tag, *msg, *message; EjsAny *stack, *error, *saveException; char *buf, *stackStr; error = ejs->exception; message = 0; stack = 0; tag = NULL; if (error) { tag = TYPE(error)->qname.name; if (ejsIs(ejs, error, Error)) { message = ejsGetProperty(ejs, error, ES_Error_message); if (withStack && ejs->initialized && ejs->state) { saveException = ejs->exception; ejsClearException(ejs); stack = ejsRunFunctionBySlot(ejs, error, ES_Error_formatStack, 0, NULL); ejsThrowException(ejs, saveException); } } else if (ejsIs(ejs, error, String)) { tag = ejsCreateStringFromAsc(ejs, "Error"); message = (EjsString*) error; } else if (ejsIs(ejs, error, Number)) { tag = ejsCreateStringFromAsc(ejs, "Error"); message = (EjsString*) error; } else if (error == EST(StopIteration)) { message = ejsCreateStringFromAsc(ejs, "Uncaught StopIteration exception"); } } if (message == ESV(null) || message == 0) { msg = ejsCreateStringFromAsc(ejs, "Exception"); } else{ msg = ejsToString(ejs, message); } if (ejsIs(ejs, error, Error)) { stackStr = (stack) ? ejsToMulti(ejs, stack) : 0; if (stackStr && *stackStr) { buf = sfmt("%@: %@\nStack:\n%s", tag, msg, (stack) ? ejsToMulti(ejs, stack) : ""); } else { buf = sfmt("%@: %@", tag, msg); } } else if (message && ejsIs(ejs, message, String)){ buf = sfmt("%@: %@", tag, msg); } else if (message && ejsIs(ejs, message, Number)){ buf = sfmt("%@: %g", tag, ((EjsNumber*) msg)->value); } else if (error) { EjsObj *saveException = ejs->exception; ejs->exception = 0; str = ejsToString(ejs, error); buf = sclone(ejsToMulti(ejs, str)); ejs->exception = saveException; } else { buf = sclone(""); } ejs->errorMsg = buf; return buf; }
/* function listen(endpoint): Void An endpoint can be either a "port" or "ip:port", or null. If hosted, this call does little -- just add to the ejs->httpServers list. */ static EjsVoid *hs_listen(Ejs *ejs, EjsHttpServer *sp, int argc, EjsObj **argv) { HttpEndpoint *endpoint; HttpHost *host; HttpRoute *route; EjsString *address; EjsObj *loc; EjsPath *documents; if (!sp->hosted) { loc = (argc >= 1) ? argv[0] : ESV(null); if (loc != ESV(null)) { address = ejsToString(ejs, loc); // TODO should permit https://IP:PORT mprParseSocketAddress(address->value, &sp->ip, &sp->port, NULL, 0); } else { address = 0; } if (address == 0) { ejsThrowArgError(ejs, "Missing listen endpoint"); return 0; } if (sp->endpoint) { httpDestroyEndpoint(sp->endpoint); sp->endpoint = 0; } /* The endpoint uses the ejsDispatcher. This is VERY important. All connections will inherit this also. This serializes all activity on one dispatcher. */ if ((endpoint = httpCreateEndpoint(sp->ip, sp->port, ejs->dispatcher)) == 0) { ejsThrowIOError(ejs, "Cannot create Http endpoint object"); return 0; } sp->endpoint = endpoint; host = httpCreateHost(NULL); httpSetHostName(host, sfmt("%s:%d", sp->ip, sp->port)); route = httpCreateConfiguredRoute(host, 1); httpAddRouteMethods(route, "DELETE, HEAD, OPTIONS, PUT"); httpAddRouteHandler(route, "ejsHandler", ""); httpSetRouteTarget(route, "run", 0); httpFinalizeRoute(route); httpSetHostDefaultRoute(host, route); httpAddHostToEndpoint(endpoint, host); if (sp->limits) { ejsSetHttpLimits(ejs, endpoint->limits, sp->limits, 1); } if (sp->incomingStages || sp->outgoingStages || sp->connector) { setHttpPipeline(ejs, sp); } if (sp->ssl) { httpSecureEndpoint(endpoint, sp->ssl); } if (sp->name) { httpSetHostName(host, sp->name); } httpSetSoftware(EJS_HTTPSERVER_NAME); httpSetEndpointAsync(endpoint, sp->async); httpSetEndpointContext(endpoint, sp); httpSetEndpointNotifier(endpoint, stateChangeNotifier); /* This is only required when http is using non-ejs handlers and/or filters */ documents = ejsGetProperty(ejs, sp, ES_ejs_web_HttpServer_documents); if (ejsIs(ejs, documents, Path)) { httpSetRouteDocuments(route, documents->value); } #if KEEP // TODO -- what to do with home? // TODO - if removed, then the "home" property should be removed? home = ejsGetProperty(ejs, sp, ES_ejs_web_HttpServer_home); if (ejsIs(ejs, home, Path)) { httpSetRoutDir(host, home->value); } #endif if (httpStartEndpoint(endpoint) < 0) { httpDestroyEndpoint(sp->endpoint); sp->endpoint = 0; ejsThrowIOError(ejs, "Cannot listen on %s", address->value); } } if (ejs->httpServers == 0) { ejs->httpServers = mprCreateList(-1, MPR_LIST_STATIC_VALUES); } /* Remove to make sure old listening() registrations are removed */ mprRemoveItem(ejs->httpServers, sp); mprAddItem(ejs->httpServers, sp); return 0; }
PUBLIC EjsAny *ejsGetItem(Ejs *ejs, EjsArray *ap, int index) { return ejsGetProperty(ejs, ap, index); }
static void onWebSocketEvent(EjsWebSocket *ws, int event, EjsAny *data, HttpPacket *packet) { Ejs *ejs; EjsAny *eobj; EjsFunction *fn; HttpRx *rx; cchar *eventName, *reason; int slot, status; ejs = ws->ejs; rx = ws->conn->rx; eobj = ejsCreateObj(ejs, ESV(Object), 0); slot = -1; eventName = 0; switch(event) { case HTTP_EVENT_READABLE: slot = ES_WebSocket_onmessage; eventName = "readable"; assert(data); ejsSetPropertyByName(ejs, eobj, EN("data"), data); ejsSetPropertyByName(ejs, eobj, EN("last"), ejsCreateBoolean(ejs, packet->last)); ejsSetPropertyByName(ejs, eobj, EN("type"), ejsCreateNumber(ejs, packet->type)); break; case HTTP_EVENT_ERROR: eventName = "error"; slot = ES_WebSocket_onerror; break; case HTTP_EVENT_APP_OPEN: slot = ES_WebSocket_onopen; eventName = "headers"; if (rx->webSocket) { httpSetWebSocketPreserveFrames(ws->conn, ws->frames); } break; case HTTP_EVENT_DESTROY: if (ws->closed) { break; } ws->closed = 1; /* Fall through to close */ case HTTP_EVENT_APP_CLOSE: eventName = "complete"; slot = ES_WebSocket_onclose; status = rx ? rx->webSocket->closeStatus: WS_STATUS_COMMS_ERROR; reason = rx ? rx->webSocket->closeReason: 0; ejsSetPropertyByName(ejs, eobj, EN("code"), ejsCreateNumber(ejs, status)); ejsSetPropertyByName(ejs, eobj, EN("reason"), ejsCreateStringFromAsc(ejs, reason)); ejsSetPropertyByName(ejs, eobj, EN("wasClean"), ejsCreateBoolean(ejs, status != WS_STATUS_COMMS_ERROR)); break; } if (slot >= 0) { if (ws->emitter) { ejsSendEvent(ejs, ws->emitter, eventName, ws, data); } fn = ejsGetProperty(ejs, ws, slot); if (ejsIsFunction(ejs, fn) && !ejs->exception) { ejsRunFunction(ejs, fn, ws, 1, &eobj); } } }
/* Find a variable in an object considering namespaces. If the space is "", then search for the property name using the set of open namespaces. */ int ejsLookupVarWithNamespaces(Ejs *ejs, EjsAny *obj, EjsName name, EjsLookup *lookup) { EjsNamespace *nsp; EjsName qname, target; EjsString *space; EjsBlock *b; MprList *globalSpaces; int next, slotNum; assert(obj); assert(name.name); assert(name.space); assert(lookup); b = (EjsBlock*) ejs->global; globalSpaces = &b->namespaces; if (name.space->value[0]) { /* Lookup with an explicit namespace */ slotNum = ejsLookupProperty(ejs, obj, name); lookup->name = name; } else { /* Lookup with the set of open namespaces in the current scope Special lookup with space == NULL. Means lookup only match if there is only one property of this name */ qname.space = NULL; qname.name = name.name; if ((slotNum = ejsLookupProperty(ejs, obj, qname)) >= 0) { if (TYPE(obj)->virtualSlots) { lookup->name = name; } else { target = ejsGetPropertyName(ejs, obj, slotNum); lookup->name = target; space = name.space; if (space->value[0] && space != target.space) { /* Unique name match. Name matches, but namespace does not */ slotNum = -1; } else if (target.space && target.space->value[0]) { /* OPT - Look at global spaces first */ for (next = -1; (nsp = mprGetPrevItem(globalSpaces, &next)) != 0; ) { if (nsp->value == target.space) { goto done; } } // OPT -- need a fast way to know if the space is a standard reserved namespace or not */ /* Verify namespace is open */ for (b = ejs->state->bp; b; b = b->scope) { // OPT. Doing some namespaces multiple times. Fix in compiler. for (next = -1; (nsp = mprGetPrevItem(&b->namespaces, &next)) != 0; ) { if (nsp->value == target.space) { goto done; } } } slotNum = -1; } } } else { qname = name; for (b = ejs->state->bp; b; b = b->scope) { for (next = -1; (nsp = (EjsNamespace*) mprGetPrevItem(&b->namespaces, &next)) != 0; ) { qname.space = nsp->value; if ((slotNum = ejsLookupProperty(ejs, obj, qname)) >= 0) { lookup->name = qname; goto done; } } } for (next = -1; (nsp = mprGetPrevItem(globalSpaces, &next)) != 0; ) { qname.space = nsp->value; if ((slotNum = ejsLookupProperty(ejs, obj, qname)) >= 0) { lookup->name = qname; goto done; } } } } done: if (slotNum >= 0) { // OPT MUST GET RID OF THIS. Means that every store does a get lookup->ref = ejsGetProperty(ejs, obj, slotNum); if (ejs->exception) { slotNum = -1; } else { lookup->obj = obj; lookup->slotNum = slotNum; lookup->trait = ejsGetPropertyTraits(ejs, lookup->obj, lookup->slotNum); } } return slotNum; }