// 'end' this connection void clientRequestEnd(JsNetwork *net, JsVar *httpClientReqVar) { SocketType socketType = socketGetType(httpClientReqVar); if (socketType == ST_HTTP) { // on HTTP, this actually means we connect clientRequestConnect(net, httpClientReqVar); } else { // on normal sockets, we actually request close after all data sent jsvUnLock(jsvObjectSetChild(httpClientReqVar, HTTP_NAME_CLOSE, jsvNewFromBool(true))); } }
// Connect this connection/socket void clientRequestConnect(JsNetwork *net, JsVar *httpClientReqVar) { // Have we already connected? If so, don't go further if (jsvGetIntegerAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SOCKET, 0))>0) return; SocketType socketType = socketGetType(httpClientReqVar); JsVar *options = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_OPTIONS_VAR, false); unsigned short port = (unsigned short)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "port", 0)); 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\n"); // As this is already in the list of connections, an error will be thrown on idle anyway jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CLOSENOW, jsvNewFromBool(true)); jsvUnLock(options); netCheckError(net); return; } NetCreateFlags flags = NCF_NORMAL; #ifdef USE_TLS if (socketType & ST_TLS) { flags |= NCF_TLS; if (port==0) port = 443; } #endif if (port==0) port = 80; int sckt = netCreateSocket(net, host_addr, port, flags, options); if (sckt<0) { jsError("Unable to create socket\n"); // As this is already in the list of connections, an error will be thrown on idle anyway jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CLOSENOW, jsvNewFromBool(true)); } else { jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_SOCKET, jsvNewFromInteger(sckt+1)); } jsvUnLock(options); netCheckError(net); }
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); }
// 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); }
// 'end' this connection void clientRequestEnd(JsNetwork *net, JsVar *httpClientReqVar) { SocketType socketType = socketGetType(httpClientReqVar); if ((socketType&ST_TYPE_MASK) == ST_HTTP) { JsVar *finalData = 0; if (jsvGetBoolAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_CHUNKED, 0))) { // If we were asked to send 'chunked' data, we need to finish up finalData = jsvNewFromString(""); } // on HTTP, this actually means we connect // force sendData to be made clientRequestWrite(net, httpClientReqVar, finalData); jsvUnLock(finalData); } else { // on normal sockets, we actually request close after all data sent jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CLOSE, jsvNewFromBool(true)); // if we never sent any data, make sure we close 'now' JsVar *sendData = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, 0); if (!sendData || jsvIsEmptyString(sendData)) jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CLOSENOW, jsvNewFromBool(true)); jsvUnLock(sendData); } }
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); } }
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 = 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 = 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; }
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[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; }