/* function WebSocket(uri: Uri, protocols = null, options) options = { certificate: Path, verify: Boolean, } */ static EjsWebSocket *wsConstructor(Ejs *ejs, EjsWebSocket *ws, int argc, EjsObj **argv) { EjsAny *certificate; bool verify; assert(ejsIsPot(ejs, ws)); ejsLoadHttpService(ejs); ws->ejs = ejs; verify = 0; ws->uri = httpUriToString(((EjsUri*) argv[0])->uri, 0); if (argc >= 2) { if (ejsIs(ejs, argv[1], Array)) { ws->protocols = sclone((ejsToString(ejs, argv[1]))->value); } else if (ejsIs(ejs, argv[1], String)) { ws->protocols = sclone(((EjsString*) argv[1])->value); } else { ws->protocols = sclone("chat"); } } else { ws->protocols = sclone("chat"); } if (*ws->protocols == '\0') { ejsThrowArgError(ejs, "Bad protocol"); return 0; } if (argc >= 3) { ws->frames = ejsGetPropertyByName(ejs, argv[2], EN("frames")) == ESV(true); verify = ejsGetPropertyByName(ejs, argv[2], EN("verify")) == ESV(true); if ((certificate = ejsGetPropertyByName(ejs, argv[2], EN("certificate"))) != 0) { ws->certFile = ejsToMulti(ejs, argv[0]); } } if ((ws->conn = httpCreateConn(MPR->httpService, NULL, ejs->dispatcher)) == 0) { ejsThrowMemoryError(ejs); return 0; } httpSetAsync(ws->conn, 1); httpPrepClientConn(ws->conn, 0); httpSetConnNotifier(ws->conn, webSocketNotify); httpSetWebSocketProtocols(ws->conn, ws->protocols); httpSetConnContext(ws->conn, ws); if (sstarts(ws->uri, "wss")) { ws->ssl = mprCreateSsl(0); mprVerifySslIssuer(ws->ssl, verify); mprVerifySslPeer(ws->ssl, verify); #if FUTURE if (!hp->caFile) { //MOB - Some define for this. hp->caFile = mprJoinPath(mprGetAppDir(), "http-ca.crt"); } mprSetSslCaFile(hp->ssl, hp->caFile); mprSetSslCaFile(hp->ssl, mprJoinPath(mprGetAppDir(), "http-ca.crt")); #endif } startWebSocketRequest(ejs, ws); return ws; }
MAIN(simpleClient, int argc, char** argv) { Mpr *mpr; App *app; cchar *content; int code; /* Create the Multithreaded Portable Runtime */ mpr = mprCreate(argc, argv, MPR_USER_EVENTS_THREAD); if ((app = mprAllocObj(App, manageApp)) == 0) { return MPR_ERR_MEMORY; } mprAddRoot(app); mprAddStandardSignals(app); /* Start the Multithreaded Portable Runtime */ mprStart(); /* Create an Http service object */ app->http = httpCreate(mpr); /* Get a client http object to work with. We can issue multiple requests with this one object. */ app->conn = httpCreateConn(app->http, NULL, NULL); /* Get a URL */ if (httpConnect(app->conn, "GET", "http://www.embedthis.com/index.html") < 0) { mprError("Can't get URL"); exit(2); } /* Examine the HTTP response HTTP code. 200 is success. */ code = httpGetStatus(app->conn); if (code != 200) { mprError("Server responded with code %d\n", code); exit(1); } /* Get the actual response content */ content = httpReadString(app->conn); if (content) { mprPrintf("Server responded with: %s\n", content); } mprDestroy(MPR_EXIT_DEFAULT); return 0; }
MAIN(simpleClient, int argc, char **argv, char **envp) { Http *http; HttpConn *conn; cchar *content; int code; /* Create the Multithreaded Portable Runtime and start it. */ mprCreate(argc, argv, 0); mprStart(); /* Get a client http object to work with. We can issue multiple requests with this one object. Add the conn as a root object so the GC won't collect it while we are using it. */ http = httpCreate(HTTP_CLIENT_SIDE); conn = httpCreateConn(http, NULL, NULL); mprAddRoot(conn); /* Open a connection to issue the GET. Then finalize the request output - this forces the request out. */ if (httpConnect(conn, "GET", "http://www.embedthis.com/index.html", NULL) < 0) { mprError("Can't get URL"); exit(2); } httpFinalizeOutput(conn); /* Wait for a response */ if (httpWait(conn, HTTP_STATE_PARSED, 10000) < 0) { mprError("No response"); exit(2); } /* Examine the HTTP response HTTP code. 200 is success. */ code = httpGetStatus(conn); if (code != 200) { mprError("Server responded with code %d\n", code); exit(1); } /* Get the actual response content */ content = httpReadString(conn); if (content) { mprPrintf("Server responded with: %s\n", content); } mprDestroy(MPR_EXIT_DEFAULT); return 0; }
/* function close(): Void */ static EjsObj *http_close(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { if (hp->conn) { httpFinalize(hp->conn); sendHttpCloseEvent(ejs, hp); httpDestroyConn(hp->conn); // TODO OPT - Better to do this on demand. This consumes a conn until GC. hp->conn = httpCreateConn(ejs->http, NULL, ejs->dispatcher); httpPrepClientConn(hp->conn, 0); httpSetConnNotifier(hp->conn, httpEventChange); httpSetConnContext(hp->conn, hp); } return 0; }
/* Per-thread execution. Called for main thread and helper threads. */ static void threadMain(void *data, MprThread *tp) { ThreadData *td; HttpConn *conn; MprEvent e; td = tp->data; td->dispatcher = mprCreateDispatcher(tp->name, 1); td->conn = conn = httpCreateConn(app->http, NULL, td->dispatcher); /* Relay to processThread via the dispatcher. This serializes all activity on the conn->dispatcher */ e.mask = MPR_READABLE; e.data = tp; mprRelayEvent(conn->dispatcher, (MprEventProc) processThread, conn, &e); }
int startRequest(MprTestGroup *gp, cchar *method, cchar *uri) { Http *http; HttpConn *conn; gp->content = 0; http = getHttp(gp); if (*uri == '/') { httpSetDefaultClientPort(http, app->port); httpSetDefaultClientHost(http, app->host); } gp->conn = conn = httpCreateConn(http, NULL, gp->dispatcher); if (httpConnect(conn, method, uri, NULL) < 0) { return MPR_ERR_CANT_OPEN; } return 0; }
/* function Http(uri: Uri = null) */ static EjsHttp *httpConstructor(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { ejsLoadHttpService(ejs); hp->ejs = ejs; if ((hp->conn = httpCreateConn(ejs->http, NULL, ejs->dispatcher)) == 0) { ejsThrowMemoryError(ejs); return 0; } httpPrepClientConn(hp->conn, 0); httpSetConnNotifier(hp->conn, httpEventChange); httpSetConnContext(hp->conn, hp); if (argc == 1 && ejsIs(ejs, argv[0], Null)) { hp->uri = httpUriToString(((EjsUri*) argv[0])->uri, HTTP_COMPLETE_URI); } hp->method = sclone("GET"); hp->requestContent = mprCreateBuf(BIT_MAX_BUFFER, -1); hp->responseContent = mprCreateBuf(BIT_MAX_BUFFER, -1); return hp; }
/* Convenience method to issue a client http request. Assumes the Mpr and Http services are created and initialized. */ PUBLIC HttpConn *httpRequest(cchar *method, cchar *uri, cchar *data, char **err) { HttpConn *conn; MprDispatcher *dispatcher; ssize len; if (err) { *err = 0; } dispatcher = mprCreateDispatcher("httpRequest", MPR_DISPATCHER_AUTO); mprStartDispatcher(dispatcher); conn = httpCreateConn(NULL, dispatcher); mprAddRoot(conn); /* Open a connection to issue the request. Then finalize the request output - this forces the request out. */ if (httpConnect(conn, method, uri, NULL) < 0) { mprRemoveRoot(conn); httpDestroyConn(conn); *err = sfmt("Cannot connect to %s", uri); return 0; } if (data) { len = slen(data); if (httpWriteBlock(conn->writeq, data, len, HTTP_BLOCK) != len) { *err = sclone("Cannot write request body data"); } } httpFinalizeOutput(conn); if (httpWait(conn, HTTP_STATE_CONTENT, MPR_MAX_TIMEOUT) < 0) { mprRemoveRoot(conn); httpDestroyConn(conn); *err = sclone("No response"); return 0; } mprRemoveRoot(conn); return conn; }
/* Accept a new client connection on a new socket. If multithreaded, this will come in on a worker thread dedicated to this connection. This is called from the listen wait handler. */ HttpConn *httpAcceptConn(HttpEndpoint *endpoint, MprEvent *event) { HttpConn *conn; MprSocket *sock; MprDispatcher *dispatcher; MprEvent e; int level; mprAssert(endpoint); mprAssert(event); /* This will block in sync mode until a connection arrives */ if ((sock = mprAcceptSocket(endpoint->sock)) == 0) { if (endpoint->sock->handler) { mprEnableSocketEvents(endpoint->sock, MPR_READABLE); } return 0; } if (endpoint->ssl) { if (mprUpgradeSocket(sock, endpoint->ssl, 1) < 0) { mprCloseSocket(sock, 0); return 0; } } if (endpoint->sock->handler) { /* Re-enable events on the listen socket */ mprEnableSocketEvents(endpoint->sock, MPR_READABLE); } dispatcher = event->dispatcher; if (mprShouldDenyNewRequests()) { mprCloseSocket(sock, 0); return 0; } if ((conn = httpCreateConn(endpoint->http, endpoint, dispatcher)) == 0) { mprCloseSocket(sock, 0); return 0; } conn->notifier = endpoint->notifier; conn->async = endpoint->async; conn->endpoint = endpoint; conn->sock = sock; conn->port = sock->port; conn->ip = sclone(sock->ip); conn->secure = (endpoint->ssl != 0); if (!httpValidateLimits(endpoint, HTTP_VALIDATE_OPEN_CONN, conn)) { conn->endpoint = 0; httpDestroyConn(conn); return 0; } mprAssert(conn->state == HTTP_STATE_BEGIN); httpSetState(conn, HTTP_STATE_CONNECTED); if ((level = httpShouldTrace(conn, HTTP_TRACE_RX, HTTP_TRACE_CONN, NULL)) >= 0) { mprLog(level, "### Incoming connection from %s:%d to %s:%d %s", conn->ip, conn->port, sock->acceptIp, sock->acceptPort, conn->secure ? "(secure)" : ""); } e.mask = MPR_READABLE; e.timestamp = conn->http->now; (conn->ioCallback)(conn, &e); return conn; }
/* Accept a new client connection on a new socket. This will come in on a worker thread with a new dispatcher dedicated to this connection. */ PUBLIC HttpConn *httpAcceptConn(HttpEndpoint *endpoint, MprEvent *event) { Http *http; HttpConn *conn; HttpAddress *address; MprSocket *sock; int64 value; assert(event); assert(event->dispatcher); assert(endpoint); sock = event->sock; http = endpoint->http; if (mprShouldDenyNewRequests()) { mprCloseSocket(sock, 0); return 0; } if ((conn = httpCreateConn(endpoint, event->dispatcher)) == 0) { mprCloseSocket(sock, 0); return 0; } conn->notifier = endpoint->notifier; conn->async = endpoint->async; conn->endpoint = endpoint; conn->sock = sock; conn->port = sock->port; conn->ip = sclone(sock->ip); if ((value = httpMonitorEvent(conn, HTTP_COUNTER_ACTIVE_CONNECTIONS, 1)) > conn->limits->connectionsMax) { httpTrace(conn, "connection.accept.error", "error", "msg:'Too many concurrent connections',active:%d,max:%d", (int) value, conn->limits->connectionsMax); httpDestroyConn(conn); return 0; } if (mprGetHashLength(http->addresses) > conn->limits->clientMax) { httpTrace(conn, "connection.accept.error", "error", "msg:'Too many concurrent clients',active:%d,max:%d", mprGetHashLength(http->addresses), conn->limits->clientMax); httpDestroyConn(conn); return 0; } address = conn->address; if (address && address->banUntil) { if (address->banUntil < http->now) { httpTrace(conn, "monitor.ban.stop", "context", "client:'%s'", conn->ip); address->banUntil = 0; } else { if (address->banStatus) { httpError(conn, HTTP_CLOSE | address->banStatus, "Connection refused, client banned: %s", address->banMsg ? address->banMsg : ""); } else { httpDestroyConn(conn); return 0; } } } if (endpoint->ssl) { if (mprUpgradeSocket(sock, endpoint->ssl, 0) < 0) { httpDisconnect(conn); httpTrace(conn, "connection.upgrade.error", "error", "msg:'Cannot upgrade socket. %s'", sock->errorMsg); httpMonitorEvent(conn, HTTP_COUNTER_SSL_ERRORS, 1); httpDestroyConn(conn); return 0; } } assert(conn->state == HTTP_STATE_BEGIN); httpSetState(conn, HTTP_STATE_CONNECTED); httpTrace(conn, "connection.accept.new", "context", "peer:'%s',endpoint:'%s:%d'", conn->ip, sock->acceptIp, sock->acceptPort); event->mask = MPR_READABLE; event->timestamp = conn->http->now; (conn->ioCallback)(conn, event); return conn; }
/* Per-thread execution. Called for main thread and helper threads. */ static void threadMain(void *data, MprThread *tp) { ThreadData *td; HttpConn *conn; cchar *path; char *url; int next, count; td = tp->data; /* Create and start a dispatcher. This ensures that all activity on the connection in this thread will be serialized with respect to all I/O events and httpProtocol work. This also ensures that I/O events will be handled by this thread from httpWait. */ td->dispatcher = mprCreateDispatcher(tp->name, 0); mprStartDispatcher(td->dispatcher); td->conn = conn = httpCreateConn(NULL, td->dispatcher); httpFollowRedirects(conn, !app->nofollow); httpSetTimeout(conn, app->timeout, app->timeout); if (strcmp(app->protocol, "HTTP/1.0") == 0) { httpSetKeepAliveCount(conn, 0); httpSetProtocol(conn, "HTTP/1.0"); } if (app->iterations == 1) { conn->limits->keepAliveMax = 0; } if (app->username) { if (app->password == 0 && !strchr(app->username, ':')) { app->password = getPassword(); } httpSetCredentials(conn, app->username, app->password, app->authType); } for (count = 0; count < app->iterations; count++) { if (mprShouldDenyNewRequests(conn)) { break; } if (!app->success && !app->continueOnErrors) { break; } if (app->singleStep) waitForUser(); if (app->files && !app->upload) { for (next = 0; (path = mprGetNextItem(app->files, &next)) != 0; ) { /* If URL ends with "/", assume it is a directory on the target and append each file name */ if (app->target[strlen(app->target) - 1] == '/') { url = mprJoinPath(app->target, mprGetPathBase(path)); } else { url = app->target; } app->requestFiles = mprCreateList(-1, MPR_LIST_STATIC_VALUES | MPR_LIST_STABLE); mprAddItem(app->requestFiles, path); td->url = url = resolveUrl(conn, url); if (app->verbose) { mprPrintf("putting: %s to %s\n", path, url); } if (doRequest(conn, url, app->requestFiles) < 0) { app->success = 0; break; } } } else { td->url = url = resolveUrl(conn, app->target); if (doRequest(conn, url, app->files) < 0) { app->success = 0; break; } } if (app->verbose > 1) { mprPrintf("."); } } httpDestroyConn(conn); mprDestroyDispatcher(conn->dispatcher); finishThread(tp); }