/*JSON{ "type":"idle", "generate" : "jswrap_waveform_idle", "ifndef" : "SAVE_ON_FLASH" }*/ bool jswrap_waveform_idle() { JsVar *waveforms = jsvObjectGetChild(execInfo.hiddenRoot, JSI_WAVEFORM_NAME, 0); if (waveforms) { JsvArrayIterator it; jsvArrayIteratorNew(&it, waveforms); while (jsvArrayIteratorHasElement(&it)) { JsVar *waveform = jsvArrayIteratorGetElement(&it); bool running = jsvGetBoolAndUnLock(jsvObjectGetChild(waveform, "running", 0)); if (running) { JsVar *buffer = jswrap_waveform_getBuffer(waveform,0,0); UtilTimerTask task; // Search for a timer task if (!jstGetLastBufferTimerTask(buffer, &task)) { // if the timer task is now gone... JsVar *arrayBuffer = jsvObjectGetChild(waveform, "buffer", 0); jsiQueueObjectCallbacks(waveform, "#onfinish", &arrayBuffer, 1); jsvUnLock(arrayBuffer); running = false; jsvUnLock(jsvObjectSetChild(waveform, "running", jsvNewFromBool(running))); } else { // If the timer task is still there... if (task.data.buffer.nextBuffer && task.data.buffer.nextBuffer != task.data.buffer.currentBuffer) { // if it is a double-buffered task int currentBuffer = (jsvGetRef(buffer)==task.data.buffer.currentBuffer) ? 0 : 1; JsVar *oldBuffer = jsvObjectGetChild(waveform, "currentBuffer", JSV_INTEGER); if (jsvGetInteger(oldBuffer) !=currentBuffer) { // buffers have changed - fire off a 'buffer' event with the buffer that needs to be filled jsvSetInteger(oldBuffer, currentBuffer); JsVar *arrayBuffer = jsvObjectGetChild(waveform, (currentBuffer==0) ? "buffer" : "buffer2", 0); jsiQueueObjectCallbacks(waveform, "#onbuffer", &arrayBuffer, 1); jsvUnLock(arrayBuffer); } jsvUnLock(oldBuffer); } } jsvUnLock(buffer); } jsvUnLock(waveform); // if not running, remove waveform from this list if (!running) jsvArrayIteratorRemoveAndGotoNext(&it, waveforms); else jsvArrayIteratorNext(&it); } jsvArrayIteratorFree(&it); jsvUnLock(waveforms); } return false; // no need to stay awake - an IRQ will wake us }
bool socketSendData(JsNetwork *net, JsVar *connection, int sckt, JsVar **sendData) { char buf[64]; int a=1; if (!jsvIsEmptyString(*sendData)) { size_t bufLen = httpStringGet(*sendData, buf, sizeof(buf)); a = net->send(net, sckt, buf, bufLen); // Now cut what we managed to send off the beginning of sendData if (a>0) { JsVar *newSendData = 0; if (a < (int)jsvGetStringLength(*sendData)) { // we didn't send all of it... cut out what we did send newSendData = jsvNewFromStringVar(*sendData, (size_t)a, JSVAPPENDSTRINGVAR_MAXLENGTH); } else { // we sent all of it! Issue a drain event jsiQueueObjectCallbacks(connection, "#ondrain", &connection, 1); } jsvUnLock(*sendData); *sendData = newSendData; } } if (a<0) { // could just be busy which is ok jsError("Socket error %d while sending", a); return false; } return true; }
// Fire error events on up to two objects if there is an error, returns true if there is an error // The error events have a code field and a message field. static bool fireErrorEvent(int error, JsVar *obj1, JsVar *obj2) { bool hadError = error < 0 && error != SOCKET_ERR_CLOSED; JsVar *params[1]; if (hadError) { params[0] = jsvNewObject(); jsvObjectSetChildAndUnLock(params[0], "code", jsvNewFromInteger(error)); jsvObjectSetChildAndUnLock(params[0], "message", jsvNewFromString(socketErrorString(error))); if (obj1 != NULL) jsiQueueObjectCallbacks(obj1, HTTP_NAME_ON_ERROR, params, 1); if (obj2 != NULL) jsiQueueObjectCallbacks(obj2, HTTP_NAME_ON_ERROR, params, 1); jsvUnLock(params[0]); } return hadError; }
/*JSON{ "type" : "method", "class" : "Object", "name" : "emit", "generate" : "jswrap_object_emit", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["args","JsVarArray","Optional arguments"] ] } Call the event listeners for this object, for instance ```http.emit('data', 'Foo')```. See Node.js's EventEmitter. */ void jswrap_object_emit(JsVar *parent, JsVar *event, JsVar *argArray) { if (!jsvHasChildren(parent)) { jsWarn("Parent must be an object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.emit(..) must be a string"); return; } char eventName[16]; if (!jswrap_object_get_event_name(eventName, event)) return; // extract data const unsigned int MAX_ARGS = 4; JsVar *args[MAX_ARGS]; unsigned int n = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, argArray); while (jsvObjectIteratorHasValue(&it)) { if (n>=MAX_ARGS) { jsWarn("Too many arguments"); break; } args[n++] = jsvObjectIteratorGetValue(&it); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsiQueueObjectCallbacks(parent, eventName, args, (int)n); // unlock jsvUnLockMany(n, args); }
/*JSON{ "type" : "method", "class" : "Object", "name" : "emit", "generate" : "jswrap_object_emit", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["args","JsVarArray","Optional arguments"] ] } Call the event listeners for this object, for instance ```http.emit('data', 'Foo')```. See Node.js's EventEmitter. */ void jswrap_object_emit(JsVar *parent, JsVar *event, JsVar *argArray) { if (!jsvIsObject(parent)) { jsWarn("Parent must be a proper object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.emit(..) must be a string"); return; } char eventName[16] = "#on"; jsvGetString(event, &eventName[3], sizeof(eventName)-4); // extract data const int MAX_ARGS = 4; JsVar *args[MAX_ARGS]; int n = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, argArray); while (jsvObjectIteratorHasValue(&it)) { if (n>=MAX_ARGS) { jsWarn("Too many arguments"); break; } args[n++] = jsvObjectIteratorGetValue(&it); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsiQueueObjectCallbacks(parent, eventName, args, n); // unlock while (n--) jsvUnLock(args[n]); }
// returns 0 on success and a (negative) error number on failure int socketSendData(JsNetwork *net, JsVar *connection, int sckt, JsVar **sendData) { char *buf = alloca(net->chunkSize); // allocate on stack assert(!jsvIsEmptyString(*sendData)); size_t bufLen = httpStringGet(*sendData, buf, net->chunkSize); int num = netSend(net, sckt, buf, bufLen); if (num < 0) return num; // an error occurred // Now cut what we managed to send off the beginning of sendData if (num > 0) { JsVar *newSendData = 0; if (num < (int)jsvGetStringLength(*sendData)) { // we didn't send all of it... cut out what we did send newSendData = jsvNewFromStringVar(*sendData, (size_t)num, JSVAPPENDSTRINGVAR_MAXLENGTH); } else { // we sent all of it! Issue a drain event, unless we want to close, then we shouldn't // callback for more data bool wantClose = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_CLOSE,0)); if (!wantClose) { jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_DRAIN, &connection, 1); } newSendData = jsvNewFromEmptyString(); } jsvUnLock(*sendData); *sendData = newSendData; } return 0; }
static void cc3000_state_change(const char *data) { JsVar *wlanObj = jsvObjectGetChild(jsiGetParser()->root, CC3000_OBJ_NAME, 0); JsVar *dataVar = jsvNewFromString(data); if (wlanObj) jsiQueueObjectCallbacks(wlanObj, CC3000_ON_STATE_CHANGE, dataVar, 0); jsvUnLock(dataVar); jsvUnLock(wlanObj);
/*JSON{ "type":"method", "class": "Object", "name" : "emit", "description" : ["Call the event listeners for this object, for instance ```http.emit('data', 'Foo')```. See Node.js's EventEmitter."], "generate" : "jswrap_object_emit", "params" : [ [ "event", "JsVar", "The name of the event, for instance 'data'"], [ "v1", "JsVar", "Optional argument 1"], [ "v2", "JsVar", "Optional argument 2"] ] }*/ void jswrap_object_emit(JsVar *parent, JsVar *event, JsVar *v1, JsVar *v2) { if (!jsvIsObject(parent)) { jsWarn("Parent must be a proper object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.emit(..) must be a string"); return; } char eventName[16] = "#on"; jsvGetString(event, &eventName[3], sizeof(eventName)-4); jsiQueueObjectCallbacks(parent, eventName, v1, v2); }
/*JSON{ "type" : "method", "class" : "Object", "name" : "on", "generate" : "jswrap_object_on", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["listener","JsVar","The listener to call when this event is received"] ] } Register an event listener for this object, for instance ```http.on('data', function(d) {...})```. See Node.js's EventEmitter. */ void jswrap_object_on(JsVar *parent, JsVar *event, JsVar *listener) { if (!jsvHasChildren(parent)) { jsWarn("Parent must be an object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.on(..) must be a string"); return; } if (!jsvIsFunction(listener) && !jsvIsString(listener)) { jsWarn("Second argument to EventEmitter.on(..) must be a function or a String (containing code)"); return; } JsVar *eventName = jsvVarPrintf(JS_EVENT_PREFIX"%s",event); if (!eventName) return; // no memory JsVar *eventList = jsvFindChildFromVar(parent, eventName, true); jsvUnLock(eventName); JsVar *eventListeners = jsvSkipName(eventList); if (jsvIsUndefined(eventListeners)) { // just add jsvSetValueOfName(eventList, listener); } else { if (jsvIsArray(eventListeners)) { // we already have an array, just add to it jsvArrayPush(eventListeners, listener); } else { // not an array - we need to make it an array JsVar *arr = jsvNewEmptyArray(); jsvArrayPush(arr, eventListeners); jsvArrayPush(arr, listener); jsvSetValueOfName(eventList, arr); jsvUnLock(arr); } } jsvUnLock2(eventListeners, eventList); /* Special case if we're a data listener and data has already arrived then * we queue an event immediately. */ if (jsvIsStringEqual(event, "data")) { JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0); if (jsvIsString(buf)) { jsiQueueObjectCallbacks(parent, STREAM_CALLBACK_NAME, &buf, 1); jsvRemoveNamedChild(parent, STREAM_BUFFER_NAME); } jsvUnLock(buf); } }
/*JSON{ "type" : "method", "class" : "Object", "name" : "on", "generate" : "jswrap_object_on", "params" : [ ["event","JsVar","The name of the event, for instance 'data'"], ["listener","JsVar","The listener to call when this event is received"] ] } Register an event listener for this object, for instance ```http.on('data', function(d) {...})```. See Node.js's EventEmitter. */ void jswrap_object_on(JsVar *parent, JsVar *event, JsVar *listener) { if (!jsvIsObject(parent)) { jsWarn("Parent must be a proper object - not a String, Integer, etc."); return; } if (!jsvIsString(event)) { jsWarn("First argument to EventEmitter.on(..) must be a string"); return; } if (!jsvIsFunction(listener) && !jsvIsString(listener)) { jsWarn("Second argument to EventEmitter.on(..) must be a function or a String (containing code)"); return; } char eventName[16] = "#on"; jsvGetString(event, &eventName[3], sizeof(eventName)-4); JsVar *eventList = jsvFindChildFromString(parent, eventName, true); JsVar *eventListeners = jsvSkipName(eventList); if (jsvIsUndefined(eventListeners)) { // just add jsvSetValueOfName(eventList, listener); } else { if (jsvIsArray(eventListeners)) { // we already have an array, just add to it jsvArrayPush(eventListeners, listener); } else { // not an array - we need to make it an array JsVar *arr = jsvNewWithFlags(JSV_ARRAY); jsvArrayPush(arr, eventListeners); jsvArrayPush(arr, listener); jsvSetValueOfName(eventList, arr); jsvUnLock(arr); } } jsvUnLock(eventListeners); jsvUnLock(eventList); /* Special case if we're a data listener and data has already arrived then * we queue an event immediately. */ if (jsvIsStringEqual(event, "data")) { JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0); if (jsvIsString(buf)) { jsiQueueObjectCallbacks(parent, "#ondata", &buf, 1); jsvRemoveNamedChild(parent, STREAM_BUFFER_NAME); } jsvUnLock(buf); } }
static void handlePipeClose(JsVar *arr, JsvObjectIterator *it, JsVar* pipe) { jsiQueueObjectCallbacks(pipe, "#oncomplete", &pipe, 1); // also call 'end' if 'end' was passed as an initialisation option if (jsvGetBoolAndUnLock(jsvObjectGetChild(pipe,"end",0))) { // call destination.end if available JsVar *destination = jsvObjectGetChild(pipe,"destination",0); if (destination) { // remove our drain and close listeners. // TODO: This removes ALL listeners. Maybe we should just remove ours? jswrap_object_removeAllListeners_cstr(destination, "drain"); jswrap_object_removeAllListeners_cstr(destination, "close"); // execute the 'end' function JsVar *endFunc = jspGetNamedField(destination, "end", false); if (endFunc) { jsvUnLock(jspExecuteFunction(endFunc, destination, 0, 0)); jsvUnLock(endFunc); } // execute the 'close' function JsVar *closeFunc = jspGetNamedField(destination, "close", false); if (closeFunc) { jsvUnLock(jspExecuteFunction(closeFunc, destination, 0, 0)); jsvUnLock(closeFunc); } jsvUnLock(destination); } /* call source.close if available - probably not what node does but seems very sensible in this case. If you don't want it, set end:false */ JsVar *source = jsvObjectGetChild(pipe,"source",0); if (source) { // TODO: This removes ALL listeners. Maybe we should just remove ours? jswrap_object_removeAllListeners_cstr(source, "close"); // execute the 'close' function JsVar *closeFunc = jspGetNamedField(source, "close", false); if (closeFunc) { jsvUnLock(jspExecuteFunction(closeFunc, source, 0, 0)); jsvUnLock(closeFunc); } } jsvUnLock(source); } JsVar *idx = jsvObjectIteratorGetKey(it); jsvRemoveChild(arr,idx); jsvUnLock(idx); }
// Connect this connection/socket void clientRequestConnect(JsNetwork *net, JsVar *httpClientReqVar) { SocketType socketType = socketGetType(httpClientReqVar); clientRequestWrite(httpClientReqVar, 0); // force sendData to be made JsVar *options = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_OPTIONS_VAR, false); unsigned short port = (unsigned short)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "port", 0)); if (port==0) port=80; char hostName[128]; JsVar *hostNameVar = jsvObjectGetChild(options, "host", 0); if (jsvIsUndefined(hostNameVar)) strncpy(hostName, "localhost", sizeof(hostName)); else jsvGetString(hostNameVar, hostName, sizeof(hostName)); jsvUnLock(hostNameVar); uint32_t host_addr = 0; networkGetHostByName(net, hostName, &host_addr); if(!host_addr) { jsError("Unable to locate host"); jsvUnLock(jsvObjectSetChild(httpClientReqVar, HTTP_NAME_CLOSENOW, jsvNewFromBool(true))); jsvUnLock(options); net->checkError(net); return; } int sckt = net->createsocket(net, host_addr, port); if (sckt<0) { jsError("Unable to create socket\n"); jsvUnLock(jsvObjectSetChild(httpClientReqVar, HTTP_NAME_CLOSENOW, jsvNewFromBool(true))); } else { jsvUnLock(jsvObjectSetChild(httpClientReqVar, HTTP_NAME_SOCKET, jsvNewFromInteger(sckt+1))); // For HTTP we get the connection callback when we've got a header back // Otherwise we just call back on success if (socketType != ST_HTTP) { jsiQueueObjectCallbacks(httpClientReqVar, HTTP_NAME_ON_CONNECT, &httpClientReqVar, 1); } } jsvUnLock(options); net->checkError(net); }
void httpClientConnectionsIdle() { char buf[64]; JsVar *arr = httpGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS,false); if (!arr) return; JsArrayIterator it; jsvArrayIteratorNew(&it, arr); while (jsvArrayIteratorHasElement(&it)) { JsVar *connection = jsvArrayIteratorGetElement(&it); bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); SOCKET sckt = (SOCKET)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); /* We do this up here because we want to wait until we have been once * around the idle loop (=callbacks have been executed) before we run this */ if (hadHeaders && receiveData) { JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); jsiQueueObjectCallbacks(resVar, HTTP_NAME_ON_DATA, receiveData, 0); jsvUnLock(resVar); // clear - because we have issued a callback jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,0); } if (sckt!=INVALID_SOCKET) { JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); // send data if possible if (sendData) { // this will wait to see if we can write any more, but ALSO // will wait for connection fd_set writefds; FD_ZERO(&writefds); FD_SET(sckt, &writefds); struct timeval time; time.tv_sec = 0; time.tv_usec = 0; int n = select(sckt+1, 0, &writefds, 0, &time); if (n==SOCKET_ERROR ) { // we probably disconnected so just get rid of this closeConnectionNow = true; } else if (FD_ISSET(sckt, &writefds)) { if (!_http_send(sckt, &sendData)) closeConnectionNow = true; jsvObjectSetChild(connection, HTTP_NAME_SEND_DATA, sendData); // _http_send prob updated sendData } #ifdef USE_CC3000 } else { // When in CC3000, write then read (FIXME) #else } // When in Linux, just read and write at the same time { #endif // Now receive data fd_set s; FD_ZERO(&s); FD_SET(sckt,&s); // check for waiting clients struct timeval timeout; timeout.tv_sec = 0; #ifdef USE_CC3000 timeout.tv_usec = 5000; // 5 millisec #else timeout.tv_usec = 0; #endif int n = select(sckt+1,&s,NULL,NULL,&timeout); if (n==SOCKET_ERROR) { // we probably disconnected so just get rid of this closeConnectionNow = true; } else if (n>0) { // receive data int num = (int)recv(sckt,buf,sizeof(buf),0); // add it to our request string if (num>0) { if (!receiveData) { receiveData = jsvNewFromEmptyString(); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } if (receiveData) { // could be out of memory jsvAppendStringBuf(receiveData, buf, num); if (!hadHeaders) { JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); if (httpParseHeaders(&receiveData, resVar, false)) { hadHeaders = true; jsvUnLock(jsvObjectSetChild(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders))); jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, resVar, 0); } jsvUnLock(resVar); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } } } else if (num==0) { // select says data, but recv says 0 means connection is closed closeConnectionNow = true; } } else { #ifdef USE_CC3000 // Nothing to send or receive, and closed if (!sendData && cc3000_socket_has_closed(sckt)) closeConnectionNow = true; #endif } } jsvUnLock(sendData); } jsvUnLock(receiveData); if (closeConnectionNow) { JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); jsiQueueObjectCallbacks(resVar, HTTP_NAME_ON_CLOSE, 0, 0); jsvUnLock(resVar); _httpClientConnectionKill(connection); JsVar *connectionName = jsvArrayIteratorGetIndex(&it); jsvArrayIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); } else jsvArrayIteratorNext(&it); jsvUnLock(connection); } jsvUnLock(arr); }
bool socketIdle(JsNetwork *net) { if (networkState != NETWORKSTATE_ONLINE) { // clear all clients and servers _socketCloseAllConnections(net); return false; } bool hadSockets = false; JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVERS,false); if (arr) { JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; JsVar *server = jsvObjectIteratorGetValue(&it); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(server,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined int theClient = netAccept(net, sckt); if (theClient >= 0) { SocketType socketType = socketGetType(server); if ((socketType&ST_TYPE_MASK) == ST_HTTP) { JsVar *req = jspNewObject(0, "httpSRq"); JsVar *res = jspNewObject(0, "httpSRs"); if (res && req) { // out of memory? socketSetType(req, ST_HTTP); JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVER_CONNECTIONS, true); if (arr) { jsvArrayPush(arr, req); jsvUnLock(arr); } jsvObjectSetChild(req, HTTP_NAME_RESPONSE_VAR, res); jsvObjectSetChild(req, HTTP_NAME_SERVER_VAR, server); jsvObjectSetChildAndUnLock(req, HTTP_NAME_SOCKET, jsvNewFromInteger(theClient+1)); } jsvUnLock2(req, res); } else { // Normal sockets JsVar *sock = jspNewObject(0, "Socket"); if (sock) { // out of memory? socketSetType(sock, ST_NORMAL); JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS, true); if (arr) { jsvArrayPush(arr, sock); jsvUnLock(arr); } jsvObjectSetChildAndUnLock(sock, HTTP_NAME_SOCKET, jsvNewFromInteger(theClient+1)); jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, &sock, 1); jsvUnLock(sock); } } } jsvUnLock(server); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); jsvUnLock(arr); } if (socketServerConnectionsIdle(net)) hadSockets = true; if (socketClientConnectionsIdle(net)) hadSockets = true; netCheckError(net); return hadSockets; }
bool socketClientConnectionsIdle(JsNetwork *net) { char buf[64]; JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS,false); if (!arr) return false; bool hadSockets = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; // Get connection, socket, and socket type // For normal sockets, socket==connection, but for HTTP we split it into a request and a response JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); JsVar *socket = (socketType==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined if (sckt<0) closeConnectionNow = true; bool hadHeaders = true; if (socketType==ST_HTTP) hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); /* We do this up here because we want to wait until we have been once * around the idle loop (=callbacks have been executed) before we run this */ if (hadHeaders) socketClientPushReceiveData(connection, socket, &receiveData); if (!closeConnectionNow) { JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); // send data if possible if (sendData) { bool b = socketSendData(net, connection, sckt, &sendData); if (!b) closeConnectionNow = true; jsvObjectSetChild(connection, HTTP_NAME_SEND_DATA, sendData); // _http_send prob updated sendData } else { if (jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSE, false))) closeConnectionNow = true; } // Now read data if possible int num = net->recv(net, sckt, buf, sizeof(buf)); if (num<0) { // we probably disconnected so just get rid of this closeConnectionNow = true; } else { // add it to our request string if (num>0) { if (!receiveData) { receiveData = jsvNewFromEmptyString(); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } if (receiveData) { // could be out of memory jsvAppendStringBuf(receiveData, buf, (size_t)num); if (socketType==ST_HTTP && !hadHeaders) { JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); if (httpParseHeaders(&receiveData, resVar, false)) { hadHeaders = true; jsvUnLock(jsvObjectSetChild(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders))); jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &resVar, 1); } jsvUnLock(resVar); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } } } } jsvUnLock(sendData); } if (closeConnectionNow) { socketClientPushReceiveData(connection, socket, &receiveData); if (socketType != ST_HTTP) jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_END, 0, 0); jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, 0, 0); _socketConnectionKill(net, connection); JsVar *connectionName = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); } else { jsvObjectIteratorNext(&it); } jsvUnLock(receiveData); jsvUnLock(connection); jsvUnLock(socket); } jsvUnLock(arr); return hadSockets; }
bool socketServerConnectionsIdle(JsNetwork *net) { char *buf = alloca(net->chunkSize); // allocate on stack JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVER_CONNECTIONS,false); if (!arr) return false; bool hadSockets = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; // Get connection, socket, and socket type // For normal sockets, socket==connection, but for HTTP we split it into a request and a response JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); JsVar *socket = ((socketType&ST_TYPE_MASK)==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); int error = 0; if (!closeConnectionNow) { int num = netRecv(net, sckt, buf, net->chunkSize); if (num<0) { // we probably disconnected so just get rid of this closeConnectionNow = true; error = num; } else { // add it to our request string if (num>0) { JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); JsVar *oldReceiveData = receiveData; if (!receiveData) receiveData = jsvNewFromEmptyString(); if (receiveData) { jsvAppendStringBuf(receiveData, buf, (size_t)num); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); if (!hadHeaders && httpParseHeaders(&receiveData, connection, true)) { hadHeaders = true; jsvObjectSetChildAndUnLock(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders)); JsVar *server = jsvObjectGetChild(connection,HTTP_NAME_SERVER_VAR,0); JsVar *args[2] = { connection, socket }; jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, args, ((socketType&ST_TYPE_MASK)==ST_HTTP) ? 2 : 1); jsvUnLock(server); } if (hadHeaders && !jsvIsEmptyString(receiveData)) { // Keep track of how much we received (so we can close once we have it) if ((socketType&ST_TYPE_MASK)==ST_HTTP) { jsvObjectSetChildAndUnLock(connection, HTTP_NAME_RECEIVE_COUNT, jsvNewFromInteger( jsvGetIntegerAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_RECEIVE_COUNT, JSV_INTEGER)) + jsvGetStringLength(receiveData) )); } // execute 'data' callback or save data if (jswrap_stream_pushData(connection, receiveData, false)) { // clear received data jsvUnLock(receiveData); receiveData = 0; } } // if received data changed, update it if (receiveData != oldReceiveData) jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,receiveData); jsvUnLock(receiveData); } } } // send data if possible JsVar *sendData = jsvObjectGetChild(socket,HTTP_NAME_SEND_DATA,0); if (sendData && !jsvIsEmptyString(sendData)) { int sent = socketSendData(net, socket, sckt, &sendData); // FIXME? checking for errors is a bit iffy. With the esp8266 network that returns // varied error codes we'd want to skip SOCKET_ERR_CLOSED and let the recv side deal // with normal closing so we don't miss the tail of what's received, but other drivers // return -1 (which is the same value) for all errors. So we rely on the check ~12 lines // down if(num>0)closeConnectionNow=false instead. if (sent < 0) { closeConnectionNow = true; error = sent; } jsvObjectSetChild(socket, HTTP_NAME_SEND_DATA, sendData); // socketSendData prob updated sendData } // only close if we want to close, have no data to send, and aren't receiving data bool wantClose = jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_CLOSE,0)); if (wantClose && (!sendData || jsvIsEmptyString(sendData)) && num<=0) { bool reallyCloseNow = true; if ((socketType&ST_TYPE_MASK)==ST_HTTP) { // Check if we had a Content-Length header - if so, we need to wait until we have received that amount JsVar *headers = jsvObjectGetChild(connection,"headers",0); if (headers) { JsVarInt contentLength = jsvGetIntegerAndUnLock(jsvObjectGetChild(headers,"Content-Length",0)); JsVarInt contentReceived = jsvGetIntegerAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_RECEIVE_COUNT, 0)); if (contentLength > contentReceived) { reallyCloseNow = false; } jsvUnLock(headers); } } closeConnectionNow = reallyCloseNow; } else if (num > 0) closeConnectionNow = false; // guarantee that anything received is processed jsvUnLock(sendData); } if (closeConnectionNow) { // send out any data that we were POSTed JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); if (hadHeaders && !jsvIsEmptyString(receiveData)) { // execute 'data' callback or save data jswrap_stream_pushData(connection, receiveData, true); } jsvUnLock(receiveData); // fire error events bool hadError = fireErrorEvent(error, connection, socket); // fire the close listeners JsVar *params[1] = { jsvNewFromBool(hadError) }; jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CLOSE, params, 1); jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, params, 1); jsvUnLock(params[0]); _socketConnectionKill(net, connection); JsVar *connectionName = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); } else jsvObjectIteratorNext(&it); jsvUnLock2(connection, socket); } jsvObjectIteratorFree(&it); jsvUnLock(arr); return hadSockets; }
void httpServerConnectionsIdle() { char buf[64]; JsVar *arr = httpGetArray(HTTP_ARRAY_HTTP_SERVER_CONNECTIONS,false); if (!arr) return; JsArrayIterator it; jsvArrayIteratorNew(&it, arr); while (jsvArrayIteratorHasElement(&it)) { JsVar *connection = jsvArrayIteratorGetElement(&it); JsVar *connectReponse = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); SOCKET sckt = (SOCKET)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); bool hadData = false; // TODO: look for unreffed connections? fd_set s; FD_ZERO(&s); FD_SET(sckt,&s); // check for waiting clients struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; int n = select(sckt+1,&s,NULL,NULL,&timeout); if (n==SOCKET_ERROR) { // we probably disconnected so just get rid of this closeConnectionNow = true; } else if (n>0) { hadData = true; // receive data int num = (int)recv(sckt,buf,sizeof(buf),0); // add it to our request string if (num>0) { JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); if (!receiveData) { receiveData = jsvNewFromEmptyString(); jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,receiveData); } if (receiveData) { jsvAppendStringBuf(receiveData, buf, num); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); if (!hadHeaders && httpParseHeaders(&receiveData, connection, true)) { hadHeaders = true; jsvUnLock(jsvObjectSetChild(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders))); JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); JsVar *server = jsvObjectGetChild(connection,HTTP_NAME_SERVER_VAR,0); jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, connection, resVar); jsvUnLock(server); jsvUnLock(resVar); } jsvUnLock(receiveData); } } } // send data if possible JsVar *sendData = jsvObjectGetChild(connectReponse,HTTP_NAME_SEND_DATA,0); if (sendData) { fd_set writefds; FD_ZERO(&writefds); FD_SET(sckt, &writefds); struct timeval time; time.tv_sec = 0; time.tv_usec = 0; int n = select(sckt+1, 0, &writefds, 0, &time); if (n==SOCKET_ERROR ) { // we probably disconnected so just get rid of this closeConnectionNow = true; } else if (FD_ISSET(sckt, &writefds)) { if (!_http_send(sckt, &sendData)) closeConnectionNow = true; } jsvObjectSetChild(connectReponse, HTTP_NAME_SEND_DATA, sendData); // _http_send prob updated sendData } else { #ifdef USE_CC3000 // nothing to send, nothing to receive, and closed... if (!hadData && cc3000_socket_has_closed(sckt)) closeConnectionNow = true; #endif } if (jsvGetBoolAndUnLock(jsvObjectGetChild(connectReponse,HTTP_NAME_CLOSE,0)) && !sendData) closeConnectionNow = true; jsvUnLock(sendData); if (closeConnectionNow) { JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); jsiQueueObjectCallbacks(resVar, HTTP_NAME_ON_CLOSE, 0, 0); jsvUnLock(resVar); _httpServerConnectionKill(connection); JsVar *connectionName = jsvArrayIteratorGetIndex(&it); jsvArrayIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); } else jsvArrayIteratorNext(&it); jsvUnLock(connection); jsvUnLock(connectReponse); } jsvArrayIteratorFree(&it); jsvUnLock(arr); }
static void handlePipeClose(JsVar *arr, JsvObjectIterator *it, JsVar* pipe) { jsiQueueObjectCallbacks(pipe, "#oncomplete", &pipe, 1); // Check the source to see if there was more data... It may not be a stream, // but if it is and it has data it should have a a STREAM_BUFFER_NAME field JsVar *source = jsvObjectGetChild(pipe,"source",0); JsVar *destination = jsvObjectGetChild(pipe,"destination",0); if (source && destination) { JsVar *buffer = jsvObjectGetChild(source, STREAM_BUFFER_NAME, 0); if (buffer && jsvGetStringLength(buffer)) { jsvObjectSetChild(source, STREAM_BUFFER_NAME, 0); // remove outstanding data /* call write fn - we ignore drain/etc here because the source has just closed and we want to get this sorted quickly */ JsVar *writeFunc = jspGetNamedField(destination, "write", false); if (jsvIsFunction(writeFunc)) // do the objects have the necessary methods on them? jsvUnLock(jspExecuteFunction(writeFunc, destination, 1, &buffer)); jsvUnLock(writeFunc); // update position JsVar *position = jsvObjectGetChild(pipe,"position",0); jsvSetInteger(position, jsvGetInteger(position) + (JsVarInt)jsvGetStringLength(buffer)); jsvUnLock(position); } jsvUnLock(buffer); } // also call 'end' if 'end' was passed as an initialisation option if (jsvGetBoolAndUnLock(jsvObjectGetChild(pipe,"end",0))) { // call destination.end if available if (destination) { // remove our drain and close listeners. // TODO: This removes ALL listeners. Maybe we should just remove ours? jswrap_object_removeAllListeners_cstr(destination, "drain"); jswrap_object_removeAllListeners_cstr(destination, "close"); // execute the 'end' function JsVar *endFunc = jspGetNamedField(destination, "end", false); if (endFunc) { jsvUnLock(jspExecuteFunction(endFunc, destination, 0, 0)); jsvUnLock(endFunc); } // execute the 'close' function JsVar *closeFunc = jspGetNamedField(destination, "close", false); if (closeFunc) { jsvUnLock(jspExecuteFunction(closeFunc, destination, 0, 0)); jsvUnLock(closeFunc); } } /* call source.close if available - probably not what node does but seems very sensible in this case. If you don't want it, set end:false */ if (source) { // TODO: This removes ALL listeners. Maybe we should just remove ours? jswrap_object_removeAllListeners_cstr(source, "close"); // execute the 'close' function JsVar *closeFunc = jspGetNamedField(source, "close", false); if (closeFunc) { jsvUnLock(jspExecuteFunction(closeFunc, source, 0, 0)); jsvUnLock(closeFunc); } } } jsvUnLock(source); jsvUnLock(destination); JsVar *idx = jsvObjectIteratorGetKey(it); jsvRemoveChild(arr,idx); jsvUnLock(idx); }
/**@brief Function for the application's SoftDevice event handler. * * @param[in] p_ble_evt SoftDevice event. */ static void on_ble_evt(ble_evt_t * p_ble_evt) { uint32_t err_code; switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_TIMEOUT: // the timeout for sd_ble_gap_adv_start expired - kick it off again jswrap_nrf_bluetooth_startAdvertise(); break; case BLE_GAP_EVT_CONNECTED: m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; bleStatus &= ~BLE_IS_SENDING; // reset state - just in case jsiSetConsoleDevice( EV_BLUETOOTH ); break; case BLE_GAP_EVT_DISCONNECTED: m_conn_handle = BLE_CONN_HANDLE_INVALID; jsiSetConsoleDevice( DEFAULT_CONSOLE_DEVICE ); // restart advertising after disconnection jswrap_nrf_bluetooth_startAdvertise(); break; case BLE_GAP_EVT_SEC_PARAMS_REQUEST: // Pairing not supported err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); APP_ERROR_CHECK(err_code); break; case BLE_GATTS_EVT_SYS_ATTR_MISSING: // No system attributes have been stored. err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0); APP_ERROR_CHECK(err_code); break; case BLE_EVT_TX_COMPLETE: // UART Transmit finished - we can try and send more data bleStatus &= ~BLE_IS_SENDING; jswrap_nrf_transmit_string(); break; case BLE_GAP_EVT_ADV_REPORT: { // Advertising data received const ble_gap_evt_adv_report_t *p_adv = &p_ble_evt->evt.gap_evt.params.adv_report; JsVar *evt = jsvNewObject(); if (evt) { jsvObjectSetChildAndUnLock(evt, "rssi", jsvNewFromInteger(p_adv->rssi)); jsvObjectSetChildAndUnLock(evt, "addr", jsvVarPrintf("%02x:%02x:%02x:%02x:%02x:%02x", p_adv->peer_addr.addr[5], p_adv->peer_addr.addr[4], p_adv->peer_addr.addr[3], p_adv->peer_addr.addr[2], p_adv->peer_addr.addr[1], p_adv->peer_addr.addr[0])); JsVar *data = jsvNewStringOfLength(p_adv->dlen); if (data) { jsvSetString(data, p_adv->data, p_adv->dlen); JsVar *ab = jsvNewArrayBufferFromString(data, p_adv->dlen); jsvUnLock(data); jsvObjectSetChildAndUnLock(evt, "data", ab); } jsiQueueObjectCallbacks(execInfo.root, BLE_SCAN_EVENT, &evt, 1); jsvUnLock(evt); } break; } default: // No implementation needed. break; } }
bool socketClientConnectionsIdle(JsNetwork *net) { char *buf = alloca(net->chunkSize); // allocate on stack JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS,false); if (!arr) return false; bool hadSockets = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; // Get connection, socket, and socket type // For normal sockets, socket==connection, but for HTTP connection is httpCRq and socket is httpCRs JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); JsVar *socket = ((socketType&ST_TYPE_MASK)==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); bool socketClosed = false; JsVar *receiveData = 0; bool hadHeaders = false; int error = 0; // error code received from netXxxx functions bool isHttp = (socketType&ST_TYPE_MASK) == ST_HTTP; bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); bool alreadyConnected = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CONNECTED, false)); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined if (sckt>=0) { if (isHttp) hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); else hadHeaders = true; receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); /* We do this up here because we want to wait until we have been once * around the idle loop (=callbacks have been executed) before we run this */ if (hadHeaders) socketClientPushReceiveData(connection, socket, &receiveData); JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); if (!closeConnectionNow) { // send data if possible if (sendData && !jsvIsEmptyString(sendData)) { // don't try to send if we're already in error state int num = 0; if (error == 0) num = socketSendData(net, connection, sckt, &sendData); if (num > 0 && !alreadyConnected && !isHttp) { // whoa, we sent something, must be connected! jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &connection, 1); jsvObjectSetChildAndUnLock(connection, HTTP_NAME_CONNECTED, jsvNewFromBool(true)); alreadyConnected = true; } if (num < 0) { closeConnectionNow = true; error = num; } jsvObjectSetChild(connection, HTTP_NAME_SEND_DATA, sendData); // _http_send prob updated sendData } else { // no data to send, do we want to close? do so. if (jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSE, false))) closeConnectionNow = true; } // Now read data if possible (and we have space for it) if (!receiveData || !hadHeaders) { int num = netRecv(net, sckt, buf, net->chunkSize); //if (num != 0) printf("recv returned %d\r\n", num); if (!alreadyConnected && num == SOCKET_ERR_NO_CONN) { ; // ignore... it's just telling us we're not connected yet } else if (num < 0) { closeConnectionNow = true; error = num; // disconnected without headers? error. if (!hadHeaders && error == SOCKET_ERR_CLOSED) error = SOCKET_ERR_NO_RESP; } else { // did we just get connected? if (!alreadyConnected && !isHttp) { jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &connection, 1); jsvObjectSetChildAndUnLock(connection, HTTP_NAME_CONNECTED, jsvNewFromBool(true)); alreadyConnected = true; // if we do not have any data to send, issue a drain event if (!sendData || (int)jsvGetStringLength(sendData) == 0) jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_DRAIN, &connection, 1); } // got data add it to our receive buffer if (num > 0) { if (!receiveData) { receiveData = jsvNewFromEmptyString(); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } if (receiveData) { // could be out of memory jsvAppendStringBuf(receiveData, buf, (size_t)num); if ((socketType&ST_TYPE_MASK)==ST_HTTP && !hadHeaders) { // for HTTP see whether we now have full response headers JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); if (httpParseHeaders(&receiveData, resVar, false)) { hadHeaders = true; jsvObjectSetChildAndUnLock(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders)); jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &resVar, 1); } jsvUnLock(resVar); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } } } } } jsvUnLock(sendData); } } if (closeConnectionNow) { socketClientPushReceiveData(connection, socket, &receiveData); if (!receiveData) { if ((socketType&ST_TYPE_MASK) != ST_HTTP) jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_END, &socket, 1); // If we had data to send but the socket closed, this is an error JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); if (sendData && jsvGetStringLength(sendData) > 0 && error == SOCKET_ERR_CLOSED) error = SOCKET_ERR_UNSENT_DATA; jsvUnLock(sendData); _socketConnectionKill(net, connection); JsVar *connectionName = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); socketClosed = true; // fire error event, if there is an error bool hadError = fireErrorEvent(error, connection, NULL); // close callback must happen after error callback JsVar *params[1] = { jsvNewFromBool(hadError) }; jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, params, 1); jsvUnLock(params[0]); } } if (!socketClosed) { jsvObjectIteratorNext(&it); } jsvUnLock3(receiveData, connection, socket); } jsvUnLock(arr); return hadSockets; }
bool socketServerConnectionsIdle(JsNetwork *net) { char buf[64]; JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_SERVER_CONNECTIONS,false); if (!arr) return false; bool hadSockets = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; // Get connection, socket, and socket type // For normal sockets, socket==connection, but for HTTP we split it into a request and a response JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); JsVar *socket = (socketType==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); if (!closeConnectionNow) { int num = net->recv(net, sckt, buf,sizeof(buf)); if (num<0) { // we probably disconnected so just get rid of this closeConnectionNow = true; } else { // add it to our request string if (num>0) { JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); JsVar *oldReceiveData = receiveData; if (!receiveData) receiveData = jsvNewFromEmptyString(); if (receiveData) { jsvAppendStringBuf(receiveData, buf, (size_t)num); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); if (!hadHeaders && httpParseHeaders(&receiveData, connection, true)) { hadHeaders = true; jsvUnLock(jsvObjectSetChild(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders))); JsVar *server = jsvObjectGetChild(connection,HTTP_NAME_SERVER_VAR,0); JsVar *args[2] = { connection, socket }; jsiQueueObjectCallbacks(server, HTTP_NAME_ON_CONNECT, args, (socketType==ST_HTTP) ? 2 : 1); jsvUnLock(server); } if (hadHeaders && !jsvIsEmptyString(receiveData)) { // execute 'data' callback or save data jswrap_stream_pushData(connection, receiveData); // clear received data jsvUnLock(receiveData); receiveData = 0; } // if received data changed, update it if (receiveData != oldReceiveData) jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,receiveData); jsvUnLock(receiveData); } } } // send data if possible JsVar *sendData = jsvObjectGetChild(socket,HTTP_NAME_SEND_DATA,0); if (sendData) { if (!socketSendData(net, socket, sckt, &sendData)) closeConnectionNow = true; jsvObjectSetChild(socket, HTTP_NAME_SEND_DATA, sendData); // socketSendData prob updated sendData } // only close if we want to close, have no data to send, and aren't receiving data if (jsvGetBoolAndUnLock(jsvObjectGetChild(socket,HTTP_NAME_CLOSE,0)) && !sendData && num<=0) closeConnectionNow = true; jsvUnLock(sendData); } if (closeConnectionNow) { // send out any data that we were POSTed JsVar *receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); bool hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); if (hadHeaders && !jsvIsEmptyString(receiveData)) { // execute 'data' callback or save data jswrap_stream_pushData(connection, receiveData); } jsvUnLock(receiveData); // fire the close listeners jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CLOSE, 0, 0); jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, 0, 0); _socketConnectionKill(net, connection); JsVar *connectionName = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); } else jsvObjectIteratorNext(&it); jsvUnLock(connection); jsvUnLock(socket); } jsvObjectIteratorFree(&it); jsvUnLock(arr); return hadSockets; }