/* Test if a request has expired relative to the default inactivity and request timeout limits. Set timeout to a non-zero value to apply an overriding smaller timeout Set timeout to a value in msec. If timeout is zero, override default limits and wait forever. If timeout is < 0, use default inactivity and duration timeouts. If timeout is > 0, then use this timeout as an additional timeout. */ PUBLIC bool httpRequestExpired(HttpConn *conn, MprTicks timeout) { HttpLimits *limits; MprTicks inactivityTimeout, requestTimeout; limits = conn->limits; if (mprGetDebugMode() || timeout == 0) { inactivityTimeout = requestTimeout = MPR_MAX_TIMEOUT; } else if (timeout < 0) { inactivityTimeout = limits->inactivityTimeout; requestTimeout = limits->requestTimeout; } else { inactivityTimeout = min(limits->inactivityTimeout, timeout); requestTimeout = min(limits->requestTimeout, timeout); } if (mprGetRemainingTicks(conn->started, requestTimeout) < 0) { if (requestTimeout != timeout) { httpTrace(conn, "timeout.duration", "error", "msg:'Request cancelled exceeded max duration',timeout:%lld", requestTimeout / 1000); } return 1; } if (mprGetRemainingTicks(conn->lastActivity, inactivityTimeout) < 0) { if (inactivityTimeout != timeout) { httpTrace(conn, "timeout.inactivity", "error", "msg:'Request cancelled due to inactivity',timeout:%lld", inactivityTimeout / 1000); } return 1; } return 0; }
/* static function run(timeout: Number = -1, oneEvent: Boolean = false): Boolean */ static EjsObj *app_run(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { MprTicks mark, remaining; int64 dispatcherMark; int rc, oneEvent, timeout; timeout = (argc > 0) ? ejsGetInt(ejs, argv[0]) : MAXINT; oneEvent = (argc > 1) ? ejsGetInt(ejs, argv[1]) : 0; if (ejs->hosted) { return ESV(true); } if (timeout < 0) { timeout = MAXINT; } mark = mprGetTicks(); remaining = timeout; dispatcherMark = mprGetEventMark(ejs->dispatcher); do { rc = mprWaitForEvent(ejs->dispatcher, remaining, dispatcherMark); remaining = mprGetRemainingTicks(mark, timeout); dispatcherMark = mprGetEventMark(ejs->dispatcher); } while (!ejs->exception && !oneEvent && !ejs->exiting && remaining > 0 && !mprIsStopping()); return (rc == 0) ? ESV(true) : ESV(false); }
/* function waitForMessage(timeout: Number = -1): Boolean */ static EjsBoolean *workerWaitForMessage(Ejs *ejs, EjsWorker *worker, int argc, EjsObj **argv) { MprTicks mark, remaining; int timeout; timeout = (argc > 0) ? ejsGetInt(ejs, argv[0]): MAXINT; if (timeout < 0) { timeout = MAXINT; } mark = mprGetTicks(); remaining = timeout; worker->gotMessage = 0; do { mprWaitForEvent(ejs->dispatcher, (int) remaining); remaining = mprGetRemainingTicks(mark, timeout); } while (!worker->gotMessage && remaining > 0 && !ejs->exception); if (worker->gotMessage) { worker->gotMessage = 0; return ESV(true); } else { return ESV(true); } }
static int join(Ejs *ejs, EjsObj *workers, int timeout) { MprTicks mark; int result, remaining; mprTrace(5, "Worker.join: joining %d", ejs->joining); mark = mprGetTicks(); remaining = timeout; do { ejs->joining = !reapJoins(ejs, workers); if (!ejs->joining) { break; } if (mprShouldAbortRequests()) { ejsThrowStateError(ejs, "Program instructed to exit"); break; } mprWaitForEvent(ejs->dispatcher, remaining); remaining = (int) mprGetRemainingTicks(mark, timeout); } while (remaining > 0 && !ejs->exception); if (ejs->exception) { return 0; } result = (ejs->joining) ? MPR_ERR_TIMEOUT: 0; ejs->joining = 0; mprTrace(7, "Worker.join: result %d", result); return result; }
PUBLIC bool httpConfigure(HttpConfigureProc proc, void *data, MprTicks timeout) { Http *http; MprTicks mark; http = HTTP; mark = mprGetTicks(); if (timeout < 0) { timeout = http->serverLimits->requestTimeout; } else if (timeout == 0) { timeout = MAXINT; } do { lock(http->connections); /* Own request will count as 1 */ if (mprGetListLength(http->connections) == 0) { (proc)(data); unlock(http->connections); return 1; } unlock(http->connections); mprSleep(10); /* Defaults to 10 secs */ } while (mprGetRemainingTicks(mark, timeout) > 0); return 0; }
/* This is called when unloading a view or controller module All of ESP must be quiesced. */ static bool espUnloadModule(cchar *module, MprTicks timeout) { MprModule *mp; MprTicks mark; if ((mp = mprLookupModule(module)) != 0) { mark = mprGetTicks(); esp->reloading = 1; do { lock(esp); /* Own request will count as 1 */ if (esp->inUse <= 1) { if (mprUnloadModule(mp) < 0) { mprLog("error esp", 0, "Cannot unload module %s", mp->name); unlock(esp); } esp->reloading = 0; unlock(esp); return 1; } unlock(esp); mprSleep(10); } while (mprGetRemainingTicks(mark, timeout) > 0); esp->reloading = 0; } return 0; }
static int doRequest(HttpConn *conn, cchar *url, MprList *files) { MprTicks mark, remaining; HttpLimits *limits; MprFile *outFile; cchar *path; assert(url && *url); limits = conn->limits; mprTrace(4, "fetch: %s %s", app->method, url); mark = mprGetTicks(); if (issueRequest(conn, url, files) < 0) { return MPR_ERR_CANT_CONNECT; } remaining = limits->requestTimeout; if (app->outFilename) { path = app->loadThreads > 1 ? sfmt("%s-%s.tmp", app->outFilename, mprGetCurrentThreadName()): app->outFilename; if ((outFile = mprOpenFile(path, O_CREAT | O_WRONLY | O_TRUNC | O_TEXT, 0664)) == 0) { mprError("Cannot open %s", path); return MPR_ERR_CANT_OPEN; } } else { outFile = mprGetStdout(); } mprAddRoot(outFile); while (!conn->tx->finalized && conn->state < HTTP_STATE_COMPLETE && remaining > 0) { remaining = mprGetRemainingTicks(mark, limits->requestTimeout); readBody(conn, outFile); httpWait(conn, 0, remaining); } if (conn->state < HTTP_STATE_COMPLETE && !conn->error) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TIMEOUT, "Inactive request timed out, exceeded request timeout %d", app->timeout); } else { readBody(conn, outFile); } if (app->outFilename) { mprCloseFile(outFile); } mprRemoveRoot(outFile); reportResponse(conn, url, mprGetTicks() - mark); httpDestroyRx(conn->rx); httpDestroyTx(conn->tx); return 0; }
/* Windows only routine to wait for I/O on the channels to the gateway and the child process. This will queue events on the dispatcher queue when I/O occurs or the process dies. NOTE: NamedPipes cannot use WaitForMultipleEvents, so we dedicate a thread to polling. WARNING: this should not be called from a dispatcher other than cmd->dispatcher. */ static void pollWinCmd(MprCmd *cmd, MprTicks timeout) { MprTicks mark, delay; MprWaitHandler *wp; int i, rc, nbytes; mark = mprGetTicks(); if (cmd->stopped) { timeout = 0; } slock(cmd); for (i = MPR_CMD_STDOUT; i < MPR_CMD_MAX_PIPE; i++) { if (cmd->files[i].handle) { wp = cmd->handlers[i]; if (wp && wp->desiredMask & MPR_READABLE) { rc = PeekNamedPipe(cmd->files[i].handle, NULL, 0, NULL, &nbytes, NULL); if (rc && nbytes > 0 || cmd->process == 0) { wp->presentMask |= MPR_READABLE; mprQueueIOEvent(wp); } } } } if (cmd->files[MPR_CMD_STDIN].handle) { wp = cmd->handlers[MPR_CMD_STDIN]; if (wp && wp->desiredMask & MPR_WRITABLE) { wp->presentMask |= MPR_WRITABLE; mprQueueIOEvent(wp); } } if (cmd->process) { delay = (cmd->eofCount == cmd->requiredEof && cmd->files[MPR_CMD_STDIN].handle == 0) ? timeout : 0; do { mprYield(MPR_YIELD_STICKY); if (WaitForSingleObject(cmd->process, (DWORD) delay) == WAIT_OBJECT_0) { mprResetYield(); reapCmd(cmd, 0); break; } else { mprResetYield(); } delay = mprGetRemainingTicks(mark, timeout); } while (cmd->eofCount == cmd->requiredEof); } sunlock(cmd); }
/* Pause the application. This services events while asleep. static function sleep(delay: Number = -1): void MOB - sleep currently throws if an exception is generated in an event callback (worker). It should not. */ static EjsObj *app_sleep(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { MprTicks mark, remaining; int timeout; timeout = (argc > 0) ? ejsGetInt(ejs, argv[0]) : MAXINT; if (timeout < 0) { timeout = MAXINT; } mark = mprGetTicks(); remaining = timeout; do { mprWaitForEvent(ejs->dispatcher, (int) remaining); remaining = mprGetRemainingTicks(mark, timeout); } while (!ejs->exiting && remaining > 0 && !mprIsStopping()); return 0; }
/* This routine does not yield */ PUBLIC void mprNap(MprTicks timeout) { MprTicks remaining, mark; struct timespec t; int rc; assert(timeout >= 0); mark = mprGetTicks(); remaining = timeout; do { /* MAC OS X corrupts the timeout if using the 2nd paramater, so recalc each time */ t.tv_sec = ((int) (remaining / 1000)); t.tv_nsec = ((int) ((remaining % 1000) * 1000000)); rc = nanosleep(&t, NULL); remaining = mprGetRemainingTicks(mark, timeout); } while (rc < 0 && errno == EINTR && remaining > 0); }
/* This is called when unloading a view or controller module */ PUBLIC bool espUnloadModule(cchar *module, MprTicks timeout) { MprModule *mp; MprTicks mark; Esp *esp; if ((mp = mprLookupModule(module)) != 0) { esp = MPR->espService; mark = mprGetTicks(); do { lock(esp); /* Own request will count as 1 */ if (esp->inUse <= 1) { mprUnloadModule(mp); unlock(esp); return 1; } unlock(esp); mprSleep(10); /* Defaults to 10 secs */ } while (mprGetRemainingTicks(mark, timeout) > 0); } return 0; }
/* Wait for the connection to reach a given state. Should only be used on the client side. @param state Desired state. Set to zero if you want to wait for one I/O event. @param timeout Timeout in msec. If timeout is zero, wait forever. If timeout is < 0, use default inactivity and duration timeouts. */ PUBLIC int httpWait(HttpConn *conn, int state, MprTicks timeout) { HttpLimits *limits; MprTicks delay, start; int64 dispatcherMark; int justOne; limits = conn->limits; if (conn->endpoint) { assert(!conn->endpoint); return MPR_ERR_BAD_STATE; } if (conn->state <= HTTP_STATE_BEGIN) { return MPR_ERR_BAD_STATE; } if (state == 0) { /* Wait for just one I/O event */ state = HTTP_STATE_FINALIZED; justOne = 1; } else { justOne = 0; } if (conn->error) { if (conn->state >= state) { return 0; } return MPR_ERR_BAD_STATE; } if (timeout < 0) { timeout = limits->requestTimeout; } else if (timeout == 0) { timeout = MPR_MAX_TIMEOUT; } if (state > HTTP_STATE_CONTENT) { httpFinalizeOutput(conn); } start = conn->http->now; dispatcherMark = mprGetEventMark(conn->dispatcher); while (conn->state < state && !conn->error && !mprIsSocketEof(conn->sock)) { if (httpRequestExpired(conn, -1)) { return MPR_ERR_TIMEOUT; } httpEnableConnEvents(conn); delay = min(limits->inactivityTimeout, mprGetRemainingTicks(start, timeout)); delay = max(delay, 0); mprWaitForEvent(conn->dispatcher, delay, dispatcherMark); if (justOne || (mprGetRemainingTicks(start, timeout) <= 0)) { break; } dispatcherMark = mprGetEventMark(conn->dispatcher); } if (conn->error) { return MPR_ERR_NOT_READY; } if (conn->state < state) { if (mprGetRemainingTicks(start, timeout) <= 0) { return MPR_ERR_TIMEOUT; } if (!justOne) { return MPR_ERR_CANT_READ; } } conn->lastActivity = conn->http->now; return 0; }
/* Read data. If sync mode, this will block. If async, will never block. Will return what data is available up to the requested size. Timeout in milliseconds to wait. Set to -1 to use the default inactivity timeout. Set to zero to wait forever. Returns a count of bytes read. Returns zero if no data. EOF if returns zero and conn->state is > HTTP_STATE_CONTENT. */ PUBLIC ssize httpReadBlock(HttpConn *conn, char *buf, ssize size, MprTicks timeout, int flags) { HttpPacket *packet; HttpQueue *q; HttpLimits *limits; MprBuf *content; MprTicks start, delay; ssize nbytes, len; int64 dispatcherMark; q = conn->readq; assert(q->count >= 0); assert(size >= 0); limits = conn->limits; if (flags == 0) { flags = conn->async ? HTTP_NON_BLOCK : HTTP_BLOCK; } if (timeout < 0) { timeout = limits->inactivityTimeout; } else if (timeout == 0) { timeout = MPR_MAX_TIMEOUT; } if (flags & HTTP_BLOCK) { start = conn->http->now; dispatcherMark = mprGetEventMark(conn->dispatcher); while (q->count <= 0 && !conn->error && (conn->state <= HTTP_STATE_CONTENT)) { if (httpRequestExpired(conn, -1)) { break; } delay = min(limits->inactivityTimeout, mprGetRemainingTicks(start, timeout)); httpEnableConnEvents(conn); mprWaitForEvent(conn->dispatcher, delay, dispatcherMark); if (mprGetRemainingTicks(start, timeout) <= 0) { break; } dispatcherMark = mprGetEventMark(conn->dispatcher); } } for (nbytes = 0; size > 0 && q->count > 0; ) { if ((packet = q->first) == 0) { break; } content = packet->content; len = mprGetBufLength(content); len = min(len, size); assert(len <= q->count); if (len > 0) { len = mprGetBlockFromBuf(content, buf, len); assert(len <= q->count); } buf += len; size -= len; q->count -= len; assert(q->count >= 0); nbytes += len; if (mprGetBufLength(content) == 0) { httpGetPacket(q); } if (flags & HTTP_NON_BLOCK) { break; } } assert(q->count >= 0); if (nbytes < size) { buf[nbytes] = '\0'; } return nbytes; }
/* Wait for the connection to reach a given state. Should only be used on the client side. @param state Desired state. Set to zero if you want to wait for one I/O event. @param timeout Timeout in msec. If timeout is zero, wait forever. If timeout is < 0, use default inactivity and duration timeouts. */ PUBLIC int httpWait(HttpStream *stream, int state, MprTicks timeout) { HttpLimits *limits; MprTicks delay, start; int64 dispatcherMark; int justOne; limits = stream->limits; if (httpServerStream(stream)) { return MPR_ERR_BAD_STATE; } if (stream->state <= HTTP_STATE_BEGIN) { return MPR_ERR_BAD_STATE; } if (state == 0) { /* Wait for just one I/O event */ state = HTTP_STATE_FINALIZED; justOne = 1; } else { justOne = 0; } if (stream->error) { if (stream->state >= state) { return 0; } return MPR_ERR_BAD_STATE; } if (timeout < 0) { timeout = limits->requestTimeout; } else if (timeout == 0) { timeout = MPR_MAX_TIMEOUT; } if (state > HTTP_STATE_CONTENT) { httpFinalizeOutput(stream); } start = stream->http->now; dispatcherMark = mprGetEventMark(stream->dispatcher); // TODO - how does this work with http2? while (stream->state < state && !stream->error && !mprIsSocketEof(stream->sock)) { if (httpRequestExpired(stream, -1)) { return MPR_ERR_TIMEOUT; } // TODO - review httpEnableNetEvents(stream->net); delay = min(limits->inactivityTimeout, mprGetRemainingTicks(start, timeout)); delay = max(delay, 0); mprWaitForEvent(stream->dispatcher, delay, dispatcherMark); if (justOne || (mprGetRemainingTicks(start, timeout) <= 0)) { break; } dispatcherMark = mprGetEventMark(stream->dispatcher); } if (stream->error) { return MPR_ERR_NOT_READY; } if (stream->state < state) { if (mprGetRemainingTicks(start, timeout) <= 0) { return MPR_ERR_TIMEOUT; } if (!justOne) { return MPR_ERR_CANT_READ; } } stream->lastActivity = stream->http->now; return 0; }