/*JSON{ "type" : "function", "name" : "getSerial", "generate" : "jswrap_interface_getSerial", "return" : ["JsVar","The board's serial number"] } Get the serial number of this board */ JsVar *jswrap_interface_getSerial() { char buf[8]; unsigned char serial[32]; int i, serialSize = jshGetSerialNumber(serial, sizeof(serial)); JsVar *str = jsvNewFromEmptyString(); if (!str) return 0; for (i=0;i<serialSize;i++) { if ((i&3)==0 && i) jsvAppendString(str, "-"); itostr(serial[i] | 0x100, buf, 16); jsvAppendString(str, &buf[1]); } return str; }
void serverResponseWrite(JsVar *httpServerResponseVar, JsVar *data) { // Append data to sendData JsVar *sendData = jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_SEND_DATA, 0); if (!sendData) { // no sendData, so no headers - add them! JsVar *sendHeaders = jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_HEADERS, 0); if (sendHeaders) { sendData = jsvVarPrintf("HTTP/1.0 %d OK\r\nServer: Espruino "JS_VERSION"\r\n", jsvGetIntegerAndUnLock(jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_CODE, 0))); httpAppendHeaders(sendData, sendHeaders); jsvObjectSetChild(httpServerResponseVar, HTTP_NAME_HEADERS, 0); jsvUnLock(sendHeaders); // finally add ending newline jsvAppendString(sendData, "\r\n"); } else if (!jsvIsUndefined(data)) { // we have already sent headers, but want to send more sendData = jsvNewFromEmptyString(); } jsvObjectSetChild(httpServerResponseVar, HTTP_NAME_SEND_DATA, sendData); } if (sendData && !jsvIsUndefined(data)) { JsVar *s = jsvAsString(data, false); if (s) jsvAppendStringVarComplete(sendData,s); jsvUnLock(s); } jsvUnLock(sendData); }
/*JSON{ "type":"function", "name" : "require", "description" : "Load the given module, and return the exported functions", "generate" : "jswrap_require", "params" : [ [ "moduleName", "JsVar", "A String containing the name of the given module"] ], "return" : ["JsVar", "The result of evaluating the string"] }*/ JsVar *jswrap_require(JsVar *moduleName) { if (!jsvIsString(moduleName)) { jsWarn("Expecting a module name as a string, but got %t", moduleName); return 0; } // Search to see if we have already loaded this module JsVar *moduleList = jswrap_modules_getModuleList(); if (!moduleList) return 0; // out of memory JsVar *moduleExportName = jsvFindChildFromVar(moduleList, moduleName, true); jsvUnLock(moduleList); if (!moduleExportName) return 0; // out of memory JsVar *moduleExport = jsvSkipName(moduleExportName); if (moduleExport) { // Found the module! jsvUnLock(moduleExportName); return moduleExport; } // Now check if it is built-in char moduleNameBuf[32]; jsvGetString(moduleName, moduleNameBuf, sizeof(moduleNameBuf)); if (jswIsBuiltInLibrary(moduleNameBuf)) { // create a 'fake' module that Espruino can use to map its built-in functions against moduleExport = jspNewBuiltin(moduleNameBuf); } else { // Now try and load it JsVar *fileContents = 0; //if (jsvIsStringEqual(moduleName,"http")) {} //if (jsvIsStringEqual(moduleName,"fs")) {} #ifdef USE_FILESYSTEM JsVar *modulePath = jsvNewFromString( #ifdef LINUX "node_modules/" #else "NODE_M~1/" #endif ); if (!modulePath) { jsvUnLock(moduleExportName); return 0; } // out of memory jsvAppendStringVarComplete(modulePath, moduleName); jsvAppendString(modulePath,".js"); fileContents = wrap_fat_readFile(modulePath); jsvUnLock(modulePath); #endif if (!fileContents || jsvIsStringEqual(fileContents,"")) { jsvUnLock(moduleExportName); jsvUnLock(fileContents); jsWarn("Module not found"); return 0; } moduleExport = jspEvaluateModule(jsiGetParser(), fileContents); jsvUnLock(fileContents); } assert(moduleExport); jsvSetValueOfName(moduleExportName, moduleExport); // save in cache jsvUnLock(moduleExportName); return moduleExport; }
static void httpAppendHeaders(JsVar *string, JsVar *headerObject) { // append headers JsvObjectIterator it; jsvObjectIteratorNew(&it, headerObject); while (jsvObjectIteratorHasValue(&it)) { JsVar *k = jsvAsString(jsvObjectIteratorGetKey(&it), true); JsVar *v = jsvAsString(jsvObjectIteratorGetValue(&it), true); jsvAppendStringVarComplete(string, k); jsvAppendString(string, ": "); jsvAppendStringVarComplete(string, v); jsvAppendString(string, "\r\n"); jsvUnLock2(k, v); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); // free headers }
/*JSON{ "type":"function", "name" : "edit", "description" : ["Fill the console with the contents of the given function, so you can edit it.", "NOTE: This is a convenience function - it will not edit 'inner functions'. For that, you must edit the 'outer function' and re-execute it."], "generate" : "jswrap_interface_edit", "params" : [ [ "funcName", "JsVarName", "The name of the function to edit (either a string or just the unquoted name)"] ] }*/ void jswrap_interface_edit(JsVar *funcName) { if (jsvIsString(funcName)) { JsVar *func = 0; if (jsvIsName(funcName)) func = jsvSkipName(funcName); else func = jsvSkipNameAndUnLock(jsvFindChildFromVar(jsiGetParser()->root, funcName, 0)); if (jsvIsFunction(func)) { JsVar *scopeVar = jsvFindChildFromString(func, JSPARSE_FUNCTION_SCOPE_NAME, false); JsVarRef scope = jsvGetRef(scopeVar); jsvUnLock(scopeVar); JsVar *newLine = jsvNewFromEmptyString(); if (newLine) { // could be out of memory jsvAppendStringVarComplete(newLine, funcName); if (scope) { // If we have a scope, it's an internal function so we will need to write different code jsvAppendString(newLine, ".replaceWith("); } else { jsvAppendString(newLine, " = "); } JsVar *funcData = jsvAsString(func, false); if (funcData) jsvAppendStringVarComplete(newLine, funcData); jsvUnLock(funcData); if (scope) { jsvAppendString(newLine, ");"); } else { jsvAppendString(newLine, ";"); } jsiReplaceInputLine(newLine); jsvUnLock(newLine); } } else { jsError("Edit should be called with the name of a function"); } jsvUnLock(func); } else { jsError("Edit should be called with edit(funcName) or edit('funcName')"); } }
void clientRequestWrite(JsVar *httpClientReqVar, JsVar *data) { SocketType socketType = socketGetType(httpClientReqVar); // Append data to sendData JsVar *sendData = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, 0); if (!sendData) { JsVar *options = 0; // Only append a header if we're doing HTTP if (socketType == ST_HTTP) options = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_OPTIONS_VAR, 0); if (options) { JsVar *method = jsvObjectGetChild(options, "method", 0); JsVar *path = jsvObjectGetChild(options, "path", 0); sendData = jsvVarPrintf("%v %v HTTP/1.0\r\nUser-Agent: Espruino "JS_VERSION"\r\nConnection: close\r\n", method, path); jsvUnLock(method); jsvUnLock(path); JsVar *headers = jsvObjectGetChild(options, "headers", 0); bool hasHostHeader = false; if (jsvIsObject(headers)) { JsVar *hostHeader = jsvObjectGetChild(headers, "Host", 0); hasHostHeader = hostHeader!=0; jsvUnLock(hostHeader); httpAppendHeaders(sendData, headers); } jsvUnLock(headers); if (!hasHostHeader) { JsVar *host = jsvObjectGetChild(options, "host", 0); int port = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "port", 0)); if (port>0 && port!=80) jsvAppendPrintf(sendData, "Host: %v:%d\r\n", host, port); else jsvAppendPrintf(sendData, "Host: %v\r\n", host); jsvUnLock(host); } // finally add ending newline jsvAppendString(sendData, "\r\n"); } else { sendData = jsvNewFromString(""); } jsvObjectSetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, sendData); jsvUnLock(options); } if (data && sendData) { JsVar *s = jsvAsString(data, false); if (s) jsvAppendStringVarComplete(sendData,s); jsvUnLock(s); } jsvUnLock(sendData); }
void serverResponseWriteHead(JsVar *httpServerResponseVar, int statusCode, JsVar *headers) { if (!jsvIsUndefined(headers) && !jsvIsObject(headers)) { jsError("Headers sent to writeHead should be an object"); return; } JsVar *sendData = jsvObjectGetChild(httpServerResponseVar, HTTP_NAME_SEND_DATA, 0); if (sendData) { // If sendData!=0 then we were already called jsError("Headers have already been sent"); jsvUnLock(sendData); return; } sendData = jsvVarPrintf("HTTP/1.0 %d OK\r\nServer: Espruino "JS_VERSION"\r\n", statusCode); if (headers) httpAppendHeaders(sendData, headers); // finally add ending newline jsvAppendString(sendData, "\r\n"); jsvObjectSetChildAndUnLock(httpServerResponseVar, HTTP_NAME_SEND_DATA, sendData); }
/*JSON{ "type" : "staticmethod", "class" : "url", "name" : "parse", "generate" : "jswrap_url_parse", "params" : [ ["urlStr","JsVar","A URL to be parsed"], ["parseQuery","bool","Whether to parse the query string into an object not (default = false)"] ], "return" : ["JsVar","An object containing options for ```http.request``` or ```http.get```. Contains `method`, `host`, `path`, `pathname`, `search`, `port` and `query`"] } A utility function to split a URL into parts This is useful in web servers for instance when handling a request. For instance `url.parse("/a?b=c&d=e",true)` returns `{"method":"GET","host":"","path":"/a?b=c&d=e","pathname":"/a","search":"?b=c&d=e","port":80,"query":{"b":"c","d":"e"}}` */ JsVar *jswrap_url_parse(JsVar *url, bool parseQuery) { if (!jsvIsString(url)) return 0; JsVar *obj = jsvNewWithFlags(JSV_OBJECT); if (!obj) return 0; // out of memory // scan string to try and pick stuff out JsvStringIterator it; jsvStringIteratorNew(&it, url, 0); int slashes = 0; int colons = 0; int addrStart = -1; int portStart = -1; int pathStart = -1; int searchStart = -1; int charIdx = 0; int portNumber = 0; while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch == '/') { slashes++; if (pathStart<0) pathStart = charIdx; if (colons==1 && slashes==2 && addrStart<0) { addrStart = charIdx; pathStart = -1; searchStart = -1; } } if (ch == ':') { colons++; if (addrStart>=0 && pathStart<0) portStart = charIdx; } if (portStart>=0 && charIdx>portStart && pathStart<0 && ch >= '0' && ch <= '9') { portNumber = portNumber*10 + (ch-'0'); } if (ch == '?' && pathStart>=0) { searchStart = charIdx; } jsvStringIteratorNext(&it); charIdx++; } jsvStringIteratorFree(&it); // try and sort stuff out if (pathStart<0) pathStart = charIdx; if (pathStart<0) pathStart = charIdx; int addrEnd = (portStart>=0) ? portStart : pathStart; // pull out details if (addrStart>0) jsvObjectSetChildAndUnLock(obj, "protocol", jsvNewFromStringVar(url, 0, (size_t)addrStart-1)); jsvObjectSetChildAndUnLock(obj, "method", jsvNewFromString("GET")); jsvObjectSetChildAndUnLock(obj, "host", jsvNewFromStringVar(url, (size_t)(addrStart+1), (size_t)(addrEnd-(addrStart+1)))); JsVar *v; v = jsvNewFromStringVar(url, (size_t)pathStart, JSVAPPENDSTRINGVAR_MAXLENGTH); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvObjectSetChildAndUnLock(obj, "path", v); v = jsvNewFromStringVar(url, (size_t)pathStart, (size_t)((searchStart>=0)?(searchStart-pathStart):JSVAPPENDSTRINGVAR_MAXLENGTH)); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvObjectSetChildAndUnLock(obj, "pathname", v); jsvObjectSetChildAndUnLock(obj, "search", (searchStart>=0)?jsvNewFromStringVar(url, (size_t)searchStart, JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull()); jsvObjectSetChildAndUnLock(obj, "port", (portNumber<=0 || portNumber>65535) ? jsvNewWithFlags(JSV_NULL) : jsvNewFromInteger(portNumber)); JsVar *query = (searchStart>=0)?jsvNewFromStringVar(url, (size_t)(searchStart+1), JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull(); if (parseQuery && !jsvIsNull(query)) { JsVar *queryStr = query; jsvStringIteratorNew(&it, query, 0); query = jsvNewWithFlags(JSV_OBJECT); JsVar *key = jsvNewFromEmptyString(); JsVar *val = jsvNewFromEmptyString(); bool hadEquals = false; while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch=='&') { if (jsvGetStringLength(key)>0 || jsvGetStringLength(val)>0) { key = jsvAsArrayIndexAndUnLock(key); // make sure "0" gets made into 0 jsvMakeIntoVariableName(key, val); jsvAddName(query, key); jsvUnLock2(key, val); key = jsvNewFromEmptyString(); val = jsvNewFromEmptyString(); hadEquals = false; } } else if (!hadEquals && ch=='=') { hadEquals = true; } else { // decode percent escape chars if (ch=='%') { jsvStringIteratorNext(&it); ch = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); ch = (char)((chtod(ch)<<4) | chtod(jsvStringIteratorGetChar(&it))); } if (hadEquals) jsvAppendCharacter(val, ch); else jsvAppendCharacter(key, ch); } jsvStringIteratorNext(&it); charIdx++; } jsvStringIteratorFree(&it); jsvUnLock(queryStr); if (jsvGetStringLength(key)>0 || jsvGetStringLength(val)>0) { key = jsvAsArrayIndexAndUnLock(key); // make sure "0" gets made into 0 jsvMakeIntoVariableName(key, val); jsvAddName(query, key); } jsvUnLock2(key, val); } jsvObjectSetChildAndUnLock(obj, "query", query); return obj; }
void clientRequestWrite(JsNetwork *net, JsVar *httpClientReqVar, JsVar *data) { SocketType socketType = socketGetType(httpClientReqVar); // Append data to sendData JsVar *sendData = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, 0); if (!sendData) { JsVar *options = 0; // Only append a header if we're doing HTTP AND we haven't already connected if ((socketType&ST_TYPE_MASK) == ST_HTTP) if (jsvGetIntegerAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SOCKET, 0))==0) options = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_OPTIONS_VAR, 0); if (options) { // We're an HTTP client - make a header JsVar *method = jsvObjectGetChild(options, "method", 0); JsVar *path = jsvObjectGetChild(options, "path", 0); sendData = jsvVarPrintf("%v %v HTTP/1.0\r\nUser-Agent: Espruino "JS_VERSION"\r\nConnection: close\r\n", method, path); jsvUnLock2(method, path); JsVar *headers = jsvObjectGetChild(options, "headers", 0); bool hasHostHeader = false; if (jsvIsObject(headers)) { JsVar *hostHeader = jsvObjectGetChild(headers, "Host", 0); hasHostHeader = hostHeader!=0; jsvUnLock(hostHeader); httpAppendHeaders(sendData, headers); // if Transfer-Encoding:chunked was set, subsequent writes need to 'chunk' the data that is sent if (jsvIsStringEqualAndUnLock(jsvObjectGetChild(headers, "Transfer-Encoding", 0), "chunked")) { jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CHUNKED, jsvNewFromBool(true)); } } jsvUnLock(headers); if (!hasHostHeader) { JsVar *host = jsvObjectGetChild(options, "host", 0); int port = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "port", 0)); if (port>0 && port!=80) jsvAppendPrintf(sendData, "Host: %v:%d\r\n", host, port); else jsvAppendPrintf(sendData, "Host: %v\r\n", host); jsvUnLock(host); } // finally add ending newline jsvAppendString(sendData, "\r\n"); } else { // !options // We're not HTTP (or were already connected), so don't send any header sendData = jsvNewFromString(""); } jsvObjectSetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, sendData); jsvUnLock(options); } // We have data and aren't out of memory... if (data && sendData) { // append the data to what we want to send JsVar *s = jsvAsString(data, false); if (s) { if ((socketType&ST_TYPE_MASK) == ST_HTTP && jsvGetBoolAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_CHUNKED, 0))) { // If we asked to send 'chunked' data, we need to wrap it up, // prefixed with the length jsvAppendPrintf(sendData, "%x\r\n%v\r\n", jsvGetStringLength(s), s); } else { jsvAppendStringVarComplete(sendData,s); } jsvUnLock(s); } } jsvUnLock(sendData); if ((socketType&ST_TYPE_MASK) == ST_HTTP) { // on HTTP we connect after the first write clientRequestConnect(net, httpClientReqVar); } }