static void startProxy(HttpQueue *q) { #if UNUSED HttpConn *conn, *target; MprHash *hp; conn = q->conn; rx = conn->rx; loc = rx->loc; httpo uri = httpJoinUriPath(NULL, rx->parsedUri, loc->prefix); pp->target = getConnection(conn); - how to hook into the pipeline for the target if (httpConnect(pp->target, rx->method, httpUriToString(uri)) < 0) { for (mprGetFirstHash(rx->headers); hp; hp = mprGetNextHash(rx->headers, hp)) { httpSetHeader(target, hp->key, hp->data); } httpSetHeader(target, "X-Forwarded-Host", ); httpSetHeader(target, "Host", ); httpSetHeader(target, "X-Forwarded-Server", ); httpSetHeader(target, "X-Forwarded-For", ); } // Push headers out - this may not work httpFlush(conn); #endif }
/* Form login service routine. Called in response to a form-based login request. Only used when httpSetAuthForm is utilized. The password is clear-text so this must be used over SSL to be secure. */ static void loginServiceProc(HttpConn *conn) { HttpAuth *auth; cchar *username, *password, *referrer; auth = conn->rx->route->auth; username = httpGetParam(conn, "username", 0); password = httpGetParam(conn, "password", 0); if (httpLogin(conn, username, password)) { if ((referrer = httpGetSessionVar(conn, "referrer", 0)) != 0) { /* Preserve protocol scheme from existing connection */ HttpUri *where = httpCreateUri(referrer, 0); httpCompleteUri(where, conn->rx->parsedUri); referrer = httpUriToString(where, 0); httpRedirect(conn, HTTP_CODE_MOVED_TEMPORARILY, referrer); } else { if (auth->loggedIn) { httpRedirect(conn, HTTP_CODE_MOVED_TEMPORARILY, auth->loggedIn); } else { httpRedirect(conn, HTTP_CODE_MOVED_TEMPORARILY, "~"); } } } else { httpRedirect(conn, HTTP_CODE_MOVED_TEMPORARILY, auth->loginPage); } }
/* function toString(): String */ static EjsString *uri_toString(Ejs *ejs, EjsUri *up, int argc, EjsObj **argv) { HttpUri *uri; uri = up->uri; return ejsCreateStringFromAsc(ejs, httpUriToString(uri, 0)); }
/* 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; }
static int issueRequest(HttpConn *conn, cchar *url, MprList *files) { HttpRx *rx; HttpUri *target, *location; char *redirect; cchar *msg, *sep; int count, redirectCount, rc; httpSetRetries(conn, app->retries); httpSetTimeout(conn, app->timeout, app->timeout); for (redirectCount = count = 0; count <= conn->retries && redirectCount < 16 && !mprShouldAbortRequests(conn); count++) { if (prepRequest(conn, files, count) < 0) { return MPR_ERR_CANT_OPEN; } if (sendRequest(conn, app->method, url, files) < 0) { return MPR_ERR_CANT_WRITE; } if ((rc = httpWait(conn, HTTP_STATE_PARSED, conn->limits->requestTimeout)) == 0) { if (httpNeedRetry(conn, &redirect)) { if (redirect) { location = httpCreateUri(redirect, 0); target = httpJoinUri(conn->tx->parsedUri, 1, &location); url = httpUriToString(target, HTTP_COMPLETE_URI); count = 0; } /* Count redirects and auth retries */ redirectCount++; count--; } else { break; } } else if (!conn->error) { if (rc == MPR_ERR_TIMEOUT) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TIMEOUT, "Inactive request timed out, exceeded request timeout %d", app->timeout); } else { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Connection I/O error"); } } if ((rx = conn->rx) != 0) { if (rx->status == HTTP_CODE_REQUEST_TOO_LARGE || rx->status == HTTP_CODE_REQUEST_URL_TOO_LARGE || (rx->status == HTTP_CODE_UNAUTHORIZED && conn->authUser == 0)) { /* No point retrying */ break; } } mprLog(MPR_DEBUG, "retry %d of %d for: %s %s", count, conn->retries, app->method, url); } // MOB - comment out errorMsg as auth errors were returning errors here. if (conn->error /* || conn->errorMsg */) { msg = (conn->errorMsg) ? conn->errorMsg : ""; sep = (msg && *msg) ? "\n" : ""; mprError("http: failed \"%s\" request for %s after %d attempt(s).%s%s", app->method, url, count, sep, msg); return MPR_ERR_CANT_CONNECT; } 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; }
static HttpConn *openConnection(HttpConn *conn, struct MprSsl *ssl) { Http *http; HttpUri *uri; MprSocket *sp; char *ip; int port, rc; assert(conn); http = conn->http; uri = conn->tx->parsedUri; if (!uri->host) { ip = (http->proxyHost) ? http->proxyHost : http->defaultClientHost; port = (http->proxyHost) ? http->proxyPort : http->defaultClientPort; } else { ip = (http->proxyHost) ? http->proxyHost : uri->host; port = (http->proxyHost) ? http->proxyPort : uri->port; } if (port == 0) { port = (uri->secure) ? 443 : 80; } if (conn && conn->sock) { if (conn->keepAliveCount-- <= 0 || port != conn->port || strcmp(ip, conn->ip) != 0 || uri->secure != (conn->sock->ssl != 0) || conn->sock->ssl != ssl) { /* Cannot reuse current socket. Close and open a new one below. */ mprCloseSocket(conn->sock, 0); conn->sock = 0; } else { httpTrace(conn, "connection.reuse", "context", "keepAlive:%d", conn->keepAliveCount); } } if (conn->sock) { return conn; } /* New socket */ if ((sp = mprCreateSocket()) == 0) { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Cannot create socket for %s", httpUriToString(uri, 0)); return 0; } if ((rc = mprConnectSocket(sp, ip, port, MPR_SOCKET_NODELAY)) < 0) { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Cannot open socket on %s:%d", ip, port); return 0; } conn->sock = sp; conn->ip = sclone(ip); conn->port = port; conn->keepAliveCount = (conn->limits->keepAliveMax) ? conn->limits->keepAliveMax : 0; #if ME_COM_SSL /* Must be done even if using keep alive for repeat SSL requests */ if (uri->secure) { char *peerName; if (ssl == 0) { ssl = mprCreateSsl(0); } peerName = isdigit(uri->host[0]) ? 0 : uri->host; if (mprUpgradeSocket(sp, ssl, peerName) < 0) { conn->errorMsg = sp->errorMsg; httpTrace(conn, "connection.upgrade.error", "error", "msg:'Cannot perform SSL upgrade. %s'", conn->errorMsg); return 0; } if (sp->peerCert) { httpTrace(conn, "context", "connection.ssl", "msg:'Connection secured with peer certificate', " \ "secure:true,cipher:'%s',peerName:'%s',subject:'%s',issuer:'%s'", sp->cipher, sp->peerName, sp->peerCert, sp->peerCertIssuer); } } #endif #if ME_HTTP_WEB_SOCKETS if (uri->webSockets && httpUpgradeWebSocket(conn) < 0) { conn->errorMsg = sp->errorMsg; return 0; } #endif httpTrace(conn, "connection.peer", "context", "peer:'%s:%d'", conn->ip, conn->port); return conn; }
/* function set uri(newUri: Uri): Void */ static EjsObj *http_set_uri(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { hp->uri = httpUriToString(((EjsUri*) argv[0])->uri, HTTP_COMPLETE_URI); return 0; }
/* function [get|put|delete|post...](uri = null, ...data): Http */ static EjsHttp *startHttpRequest(Ejs *ejs, EjsHttp *hp, char *method, int argc, EjsObj **argv) { EjsArray *args; EjsByteArray *data; EjsNumber *written; EjsUri *uriObj; HttpConn *conn; ssize nbytes; conn = hp->conn; hp->responseCache = 0; hp->requestContentCount = 0; mprFlushBuf(hp->responseContent); if (argc >= 1 && !ejsIs(ejs, argv[0], Null)) { uriObj = (EjsUri*) argv[0]; hp->uri = httpUriToString(uriObj->uri, HTTP_COMPLETE_URI); } if (argc == 2 && ejsIs(ejs, argv[1], Array)) { args = (EjsArray*) argv[1]; if (args->length > 0) { data = ejsCreateByteArray(ejs, -1); written = ejsWriteToByteArray(ejs, data, 1, &argv[1]); mprPutBlockToBuf(hp->requestContent, (char*) data->value, (int) written->value); mprAddNullToBuf(hp->requestContent); assert(written > 0); } } if (hp->uri == 0) { ejsThrowArgError(ejs, "URL is not defined"); return 0; } if (method && strcmp(hp->method, method) != 0) { hp->method = sclone(method); } if (hp->method == 0) { ejsThrowArgError(ejs, "HTTP Method is not defined"); return 0; } if (hp->certFile) { if (!hp->ssl) { hp->ssl = mprCreateSsl(0); } mprSetSslCertFile(hp->ssl, hp->certFile); if (!hp->keyFile) { ejsThrowStateError(ejs, "Must define a Http.key to use with a certificate"); } mprSetSslKeyFile(hp->ssl, hp->keyFile); } if (hp->caFile) { if (!hp->ssl) { hp->ssl = mprCreateSsl(0); } mprSetSslCaFile(hp->ssl, hp->caFile); } if (httpConnect(conn, hp->method, hp->uri, hp->ssl) < 0) { ejsThrowIOError(ejs, "Cannot issue request for \"%s\"", hp->uri); return 0; } if (mprGetBufLength(hp->requestContent) > 0) { nbytes = httpWriteBlock(conn->writeq, mprGetBufStart(hp->requestContent), mprGetBufLength(hp->requestContent), HTTP_BLOCK); if (nbytes < 0) { ejsThrowIOError(ejs, "Cannot write request data for \"%s\"", hp->uri); return 0; } else if (nbytes > 0) { assert(nbytes == mprGetBufLength(hp->requestContent)); mprAdjustBufStart(hp->requestContent, nbytes); hp->requestContentCount += nbytes; } httpFinalize(conn); } httpNotify(conn, HTTP_EVENT_WRITABLE, 0); if (conn->async) { httpEnableConnEvents(hp->conn); } return hp; }
PUBLIC char *httpLinkAbs(HttpConn *conn, cchar *target) { return httpUriToString(httpResolveUri(conn, conn->rx->parsedUri, httpLinkUri(conn, target, 0)), 0); }
PUBLIC char *httpLinkEx(HttpConn *conn, cchar *target, MprHash *options) { return httpUriToString(httpLinkUri(conn, target, options), 0); }
static int issueRequest(HttpConn *conn, cchar *url, MprList *files) { HttpRx *rx; HttpUri *target, *location; char *redirect; cchar *msg, *sep, *authType; int count, redirectCount, rc; httpSetRetries(conn, app->retries); httpSetTimeout(conn, app->timeout, app->timeout); authType = conn->authType; for (redirectCount = count = 0; count <= conn->retries && redirectCount < 10 && !mprShouldAbortRequests(conn); count++) { if (prepRequest(conn, files, count) < 0) { return MPR_ERR_CANT_OPEN; } if (sendRequest(conn, app->method, url, files) < 0) { return MPR_ERR_CANT_WRITE; } if ((rc = httpWait(conn, HTTP_STATE_PARSED, conn->limits->requestTimeout)) == 0) { if (httpNeedRetry(conn, &redirect)) { if (redirect) { httpRemoveHeader(conn, "Host"); location = httpCreateUri(redirect, 0); if (!location || !location->valid) { httpError(conn, HTTP_ABORT, "Invalid location URI"); break; } target = httpJoinUri(conn->tx->parsedUri, 1, &location); url = httpUriToString(target, HTTP_COMPLETE_URI); count = 0; } if (conn->rx && conn->rx->status == HTTP_CODE_UNAUTHORIZED && authType && smatch(authType, conn->authType)) { /* Supplied authentication details and failed */ break; } redirectCount++; count--; } else { break; } } else if (!conn->error) { if (rc == MPR_ERR_TIMEOUT) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TIMEOUT, "Inactive request timed out, exceeded request timeout %lld", app->timeout); } else { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Connection I/O error"); } } if ((rx = conn->rx) != 0) { if (rx->status == HTTP_CODE_REQUEST_TOO_LARGE || rx->status == HTTP_CODE_REQUEST_URL_TOO_LARGE || rx->status == HTTP_CODE_NOT_ACCEPTABLE || (rx->status == HTTP_CODE_UNAUTHORIZED && conn->username == 0)) { /* No point retrying */ break; } if (conn->sock->flags & MPR_SOCKET_CERT_ERROR) { break; } } mprDebug("http", 4, "retry %d of %d for: %s %s", count, conn->retries, app->method, url); } if (conn->error) { msg = (conn->errorMsg) ? conn->errorMsg : ""; sep = (msg && *msg) ? "\n" : ""; mprLog("error http", 0, "Failed \"%s\" request for %s%s%s", app->method, url, sep, msg); return MPR_ERR_CANT_CONNECT; } return 0; }
/* Redirect the user to another web page. The targetUri may or may not have a scheme. */ PUBLIC void httpRedirect(HttpConn *conn, int status, cchar *targetUri) { HttpTx *tx; HttpRx *rx; HttpUri *target, *base; HttpEndpoint *endpoint; cchar *msg; char *dir, *cp; assert(targetUri); rx = conn->rx; tx = conn->tx; if (tx->finalized) { /* A response has already been formulated */ return; } tx->status = status; if (schr(targetUri, '$')) { targetUri = httpExpandUri(conn, targetUri); } mprLog(3, "redirect %d %s", status, targetUri); msg = httpLookupStatus(conn->http, status); if (300 <= status && status <= 399) { if (targetUri == 0) { targetUri = "/"; } target = httpCreateUri(targetUri, 0); base = rx->parsedUri; /* Support URIs without a host: https:///path. This is used to redirect onto the same host but with a different scheme. So find a suitable local endpoint to supply the port for the scheme. */ if (!target->port && (!target->host || smatch(base->host, target->host)) && (target->scheme && !smatch(target->scheme, base->scheme))) { endpoint = smatch(target->scheme, "https") ? conn->host->secureEndpoint : conn->host->defaultEndpoint; if (endpoint) { target->port = endpoint->port; } else { httpError(conn, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot find endpoint for scheme %s", target->scheme); return; } } if (target->path && target->path[0] != '/') { /* Relative file redirection to a file in the same directory as the previous request. */ dir = sclone(rx->pathInfo); if ((cp = strrchr(dir, '/')) != 0) { /* Remove basename */ *cp = '\0'; } target->path = sjoin(dir, "/", target->path, NULL); } target = httpCompleteUri(target, base); targetUri = httpUriToString(target, 0); httpSetHeader(conn, "Location", "%s", targetUri); httpFormatResponse(conn, "<!DOCTYPE html>\r\n" "<html><head><title>%s</title></head>\r\n" "<body><h1>%s</h1>\r\n<p>The document has moved <a href=\"%s\">here</a>.</p></body></html>\r\n", msg, msg, targetUri); } else { httpFormatResponse(conn, "<!DOCTYPE html>\r\n" "<html><head><title>%s</title></head>\r\n" "<body><h1>%s</h1>\r\n</body></html>\r\n", msg, msg); } httpFinalize(conn); }