static HttpUri *toHttpUri(Ejs *ejs, EjsObj *arg, int dup) { HttpUri *uri; if (ejsIs(ejs, arg, String)) { uri = httpCreateUri(ejsToMulti(ejs, arg), 0); } else if (ejsIs(ejs, arg, Uri)) { if (dup) { uri = httpCloneUri(((EjsUri*) arg)->uri, 0); } else { uri = ((EjsUri*) arg)->uri; } } else if (ejsIs(ejs, arg, Path)) { uri = httpCreateUri(((EjsPath*) arg)->value, 0); } else if (ejsGetLength(ejs, arg) > 0) { uri = createHttpUriFromHash(ejs, arg, 0); } else { arg = (EjsObj*) ejsToString(ejs, arg); uri = httpCreateUri(ejsToMulti(ejs, arg), 0); } return uri; }
PUBLIC int httpConnect(HttpConn *conn, cchar *method, cchar *uri, struct MprSsl *ssl) { assert(conn); assert(method && *method); assert(uri && *uri); if (httpServerConn(conn)) { httpError(conn, HTTP_CODE_BAD_GATEWAY, "Cannot call connect in a server"); return MPR_ERR_BAD_STATE; } if (conn->tx == 0 || conn->state != HTTP_STATE_BEGIN) { /* WARNING: this will erase headers */ httpPrepClientConn(conn, 0); } assert(conn->state == HTTP_STATE_BEGIN); conn->tx->parsedUri = httpCreateUri(uri, HTTP_COMPLETE_URI_PATH); if (openConnection(conn, ssl) == 0) { return MPR_ERR_CANT_OPEN; } conn->authRequested = 0; conn->tx->method = supper(method); conn->startMark = mprGetHiResTicks(); /* The receive pipeline is created when parsing the response in parseIncoming() */ httpCreateTxPipeline(conn, conn->http->clientRoute); httpSetState(conn, HTTP_STATE_CONNECTED); setDefaultHeaders(conn); return 0; }
/* 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); } }
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; }
/* Convert an arg to a URI. Can handle strings, paths, URIs and object hashes. Will cast all else to strings and then parse. */ static EjsUri *castToUri(Ejs *ejs, EjsObj *arg) { EjsUri *up; up = ejsCreateObj(ejs, ESV(Uri), 0); if (ejsIs(ejs, arg, String)) { up->uri = httpCreateUri(up, ejsToMulti(ejs, arg), 0); } else if (ejsIs(ejs, arg, Uri)) { up->uri = httpCloneUri(((EjsUri*) arg)->uri, 0); } else if (ejsIs(ejs, arg, Path)) { ustr = ((EjsPath*) arg)->path; up->uri = httpCreateUri(up, ustr, 0); } else if (ejsGetLength(ejs, arg) > 0) { up->uri = createHttpUriFromHash(ejs, up, arg, 0); } else { arg = (EjsObj) ejsToString(ejs, arg); up->uri = httpCreateUri(up, ejsToMulti(ejs, arg), 0); } return up; }
PUBLIC int httpConnect(HttpStream *stream, cchar *method, cchar *url, MprSsl *ssl) { HttpNet *net; HttpTx *tx; HttpUri *uri; cchar *ip, *protocol; int port; assert(stream); assert(method && *method); assert(url && *url); net = stream->net; if (httpServerStream(stream)) { mprLog("client error", 0, "Cannot call httpConnect() in a server"); return MPR_ERR_BAD_STATE; } if (net->protocol <= 0) { mprLog("client error", 0, "HTTP protocol to use has not been defined"); return MPR_ERR_BAD_STATE; } if (stream->tx == 0 || stream->state != HTTP_STATE_BEGIN) { httpResetClientStream(stream, 0); } tx = stream->tx; tx->method = supper(method); stream->authRequested = 0; stream->startMark = mprGetHiResTicks(); if ((uri = tx->parsedUri = httpCreateUri(url, HTTP_COMPLETE_URI_PATH)) == 0) { return MPR_ERR_BAD_ARGS; } ssl = uri->secure ? (ssl ? ssl : mprCreateSsl(0)) : 0; httpGetUriAddress(uri, &ip, &port); if (net->sock) { if (net->error) { mprCloseSocket(net->sock, 0); net->sock = 0; } else if (canUse(net, stream, uri, ssl, ip, port)) { httpLog(net->trace, "client.connection.reuse", "context", "reuse:%d", stream->keepAliveCount); } else { if (net->protocol >= 2) { if (mprGetListLength(net->streams) > 1) { httpError(stream, HTTP_CODE_COMMS_ERROR, "Cannot use network for %s due to other existing requests", ip); return MPR_ERR_CANT_FIND; } } else { mprCloseSocket(net->sock, 0); net->sock = 0; } } } if (!net->sock) { if (httpConnectNet(net, ip, port, ssl) < 0) { return MPR_ERR_CANT_CONNECT; } stream->net = net; stream->sock = net->sock; stream->ip = net->ip; stream->port = net->port; stream->keepAliveCount = (net->protocol >= 2) ? 0 : stream->limits->keepAliveMax; #if ME_HTTP_WEB_SOCKETS if (net->protocol == 1 && uri->webSockets && httpUpgradeWebSocket(stream) < 0) { stream->errorMsg = net->errorMsg = net->sock->errorMsg; return 0; } #endif } httpCreatePipeline(stream); setDefaultHeaders(stream); httpSetState(stream, HTTP_STATE_CONNECTED); protocol = net->protocol < 2 ? "HTTP/1.1" : "HTTP/2"; httpLog(net->trace, "client.request", "request", "method='%s', url='%s', protocol='%s'", tx->method, url, protocol); return 0; }
PUBLIC HttpUri *httpLinkUri(HttpConn *conn, cchar *target, MprHash *options) { HttpRoute *route, *lroute; HttpRx *rx; HttpUri *uri; cchar *routeName, *action, *controller, *originalAction, *tplate; char *rest; assert(conn); rx = conn->rx; route = rx->route; controller = 0; if (target == 0) { target = ""; } if (*target == '@') { target = sjoin("{action: '", target, "'}", NULL); } if (*target != '{') { tplate = target; if (!options) { options = route->vars; } } else { if (options) { options = mprBlendHash(httpGetOptions(target), options); } else { options = httpGetOptions(target); } options = mprBlendHash(options, route->vars); /* Prep the action. Forms are: . @action # Use the current controller . @controller/ # Use "index" as the action . @controller/action */ if ((action = httpGetOption(options, "action", 0)) != 0) { originalAction = action; if (*action == '@') { action = &action[1]; } if (strchr(action, '/')) { controller = stok((char*) action, "/", (char**) &action); action = stok((char*) action, "/", &rest); } if (controller) { httpSetOption(options, "controller", controller); } else { controller = httpGetParam(conn, "controller", 0); } if (action == 0 || *action == '\0') { action = "list"; } if (action != originalAction) { httpSetOption(options, "action", action); } } /* Find the template to use. Strategy is this order: . options.template . options.route.template . options.action mapped to a route.template, via: . /app/STAR/action . /app/controller/action . /app/STAR/default . /app/controller/default */ if ((tplate = httpGetOption(options, "template", 0)) == 0) { if ((routeName = httpGetOption(options, "route", 0)) != 0) { routeName = expandRouteName(conn, routeName); lroute = httpLookupRoute(conn->host, routeName); } else { lroute = 0; } if (!lroute) { if ((lroute = httpLookupRoute(conn->host, actionRoute(route, controller, action))) == 0) { if ((lroute = httpLookupRoute(conn->host, actionRoute(route, "{controller}", action))) == 0) { if ((lroute = httpLookupRoute(conn->host, actionRoute(route, controller, "default"))) == 0) { lroute = httpLookupRoute(conn->host, actionRoute(route, "{controller}", "default")); } } } } if (lroute) { tplate = lroute->tplate; } } if (!tplate) { mprLog("error http", 0, "Cannot find template for URI %s", target); target = "/"; } } target = httpTemplate(conn, tplate, options); if ((uri = httpCreateUri(target, 0)) == 0) { return 0; } return uri; }
PUBLIC HttpUri *httpLinkUri(HttpConn *conn, cchar *target, MprHash *options) { HttpRoute *route, *lroute; HttpRx *rx; HttpUri *uri; cchar *routeName, *action, *controller, *originalAction, *tplate; char *rest; rx = conn->rx; route = rx->route; controller = 0; if (target == 0) { target = ""; } if (*target == '@') { target = sjoin("{action: '", target, "'}", NULL); } if (*target != '{') { tplate = target; if (!options) { options = route->vars; } } else { if (options) { options = mprBlendHash(httpGetOptions(target), options); } else { options = httpGetOptions(target); } options = mprBlendHash(options, route->vars); /* Prep the action. Forms are: . @action # Use the current controller . @controller/ # Use "index" as the action . @controller/action */ if ((action = httpGetOption(options, "action", 0)) != 0) { originalAction = action; if (*action == '@') { action = &action[1]; } if (strchr(action, '/')) { controller = stok((char*) action, "/", (char**) &action); action = stok((char*) action, "/", &rest); } if (controller) { httpSetOption(options, "controller", controller); } else { controller = httpGetParam(conn, "controller", 0); } if (action == 0 || *action == '\0') { action = "list"; } if (action != originalAction) { httpSetOption(options, "action", action); } } /* Find the template to use. Strategy is this order: . options.template . options.route.template . options.action mapped to a route.template, via: . /app/STAR/action . /app/controller/action . /app/STAR/default . /app/controller/default */ if ((tplate = httpGetOption(options, "template", 0)) == 0) { if ((routeName = httpGetOption(options, "route", 0)) != 0) { routeName = expandRouteName(conn, routeName); lroute = httpLookupRoute(conn->host, routeName); } else { lroute = 0; } if (!lroute) { if ((lroute = httpLookupRoute(conn->host, actionRoute(route, controller, action))) == 0) { if ((lroute = httpLookupRoute(conn->host, actionRoute(route, "{controller}", action))) == 0) { if ((lroute = httpLookupRoute(conn->host, actionRoute(route, controller, "default"))) == 0) { lroute = httpLookupRoute(conn->host, actionRoute(route, "{controller}", "default")); } } } } if (lroute) { tplate = lroute->tplate; } } if (!tplate) { mprLog("error http", 0, "Cannot find template for URI %s", target); target = "/"; } } target = httpTemplate(conn, tplate, options); uri = httpCreateUri(target, 0); /* This was changed from: httpCreateUri(rx->uri) to rx->parsedUri. The use case was appweb: /auth/form/login which redirects using: https:///auth/form/login on localhost:4443 This must extract the existing host and port from the prior request */ uri = httpResolveUri(rx->parsedUri, 1, &uri, 0); return httpNormalizeUri(uri); }
static int parseArgs(int argc, char **argv) { char *argp, *key, *value; int i, setWorkers, nextArg, ssl; setWorkers = 0; ssl = 0; for (nextArg = 1; nextArg < argc; nextArg++) { argp = argv[nextArg]; if (*argp != '-') { break; } if (smatch(argp, "--auth")) { if (nextArg >= argc) { return showUsage(); } else { app->authType = slower(argv[++nextArg]); } } else if (smatch(argp, "--benchmark") || smatch(argp, "-b")) { app->benchmark++; } else if (smatch(argp, "--ca")) { if (nextArg >= argc) { return showUsage(); } else { app->ca = sclone(argv[++nextArg]); if (!mprPathExists(app->ca, R_OK)) { mprError("Cannot find ca file %s", app->ca); return MPR_ERR_BAD_ARGS; } } ssl = 1; } else if (smatch(argp, "--cert")) { if (nextArg >= argc) { return showUsage(); } else { app->cert = sclone(argv[++nextArg]); if (!mprPathExists(app->cert, R_OK)) { mprError("Cannot find cert file %s", app->cert); return MPR_ERR_BAD_ARGS; } } ssl = 1; } else if (smatch(argp, "--chunk")) { if (nextArg >= argc) { return showUsage(); } else { value = argv[++nextArg]; app->chunkSize = atoi(value); if (app->chunkSize < 0) { mprError("Bad chunksize %d", app->chunkSize); return MPR_ERR_BAD_ARGS; } } } else if (smatch(argp, "--ciphers")) { if (nextArg >= argc) { return showUsage(); } else { app->ciphers = sclone(argv[++nextArg]); } ssl = 1; } else if (smatch(argp, "--continue") || smatch(argp, "-c")) { app->continueOnErrors++; } else if (smatch(argp, "--cookie")) { if (nextArg >= argc) { return showUsage(); } else { mprAddItem(app->headers, mprCreateKeyPair("Cookie", argv[++nextArg], 0)); } } else if (smatch(argp, "--data")) { if (nextArg >= argc) { return showUsage(); } else { if (app->bodyData == 0) { app->bodyData = mprCreateBuf(-1, -1); } mprPutStringToBuf(app->bodyData, argv[++nextArg]); } } else if (smatch(argp, "--debugger") || smatch(argp, "-D")) { mprSetDebugMode(1); app->retries = 0; app->timeout = MAXINT; } else if (smatch(argp, "--delete")) { app->method = "DELETE"; } else if (smatch(argp, "--form") || smatch(argp, "-f")) { if (nextArg >= argc) { return showUsage(); } else { if (app->formData == 0) { app->formData = mprCreateList(-1, 0); } addFormVars(argv[++nextArg]); } } else if (smatch(argp, "--header")) { if (nextArg >= argc) { return showUsage(); } else { key = argv[++nextArg]; if ((value = strchr(key, ':')) == 0) { mprError("Bad header format. Must be \"key: value\""); return MPR_ERR_BAD_ARGS; } *value++ = '\0'; while (isspace((uchar) *value)) { value++; } mprAddItem(app->headers, mprCreateKeyPair(key, value, 0)); } } else if (smatch(argp, "--host")) { if (nextArg >= argc) { return showUsage(); } else { app->host = argv[++nextArg]; if (*app->host == ':') { app->host = &app->host[1]; } if (isPort(app->host)) { app->host = sfmt("http://127.0.0.1:%s", app->host); } else { app->host = sclone(app->host); } } } else if (smatch(argp, "--iterations") || smatch(argp, "-i")) { if (nextArg >= argc) { return showUsage(); } else { app->iterations = atoi(argv[++nextArg]); } } else if (smatch(argp, "--key")) { if (nextArg >= argc) { return showUsage(); } else { app->key = sclone(argv[++nextArg]); if (!mprPathExists(app->key, R_OK)) { mprError("Cannot find key file %s", app->key); return MPR_ERR_BAD_ARGS; } } ssl = 1; } else if (smatch(argp, "--log") || smatch(argp, "-l")) { if (nextArg >= argc) { return showUsage(); } else { mprStartLogging(argv[++nextArg], 0); } } else if (smatch(argp, "--method") || smatch(argp, "-m")) { if (nextArg >= argc) { return showUsage(); } else { app->method = argv[++nextArg]; } } else if (smatch(argp, "--out") || smatch(argp, "-o")) { if (nextArg >= argc) { return showUsage(); } else { app->outFilename = sclone(argv[++nextArg]); } } else if (smatch(argp, "--noout") || smatch(argp, "-n") || smatch(argp, "--quiet") || smatch(argp, "-q")) { app->noout++; } else if (smatch(argp, "--nofollow")) { app->nofollow++; } else if (smatch(argp, "--password") || smatch(argp, "-p")) { if (nextArg >= argc) { return showUsage(); } else { app->password = sclone(argv[++nextArg]); } } else if (smatch(argp, "--post")) { app->method = "POST"; } else if (smatch(argp, "--printable")) { app->printable++; } else if (smatch(argp, "--protocol")) { if (nextArg >= argc) { return showUsage(); } else { app->protocol = supper(argv[++nextArg]); } } else if (smatch(argp, "--provider")) { /* Undocumented SSL provider selection */ if (nextArg >= argc) { return showUsage(); } else { app->provider = sclone(argv[++nextArg]); } ssl = 1; } else if (smatch(argp, "--put")) { app->method = "PUT"; } else if (smatch(argp, "--range")) { if (nextArg >= argc) { return showUsage(); } else { if (app->ranges == 0) { app->ranges = sfmt("bytes=%s", argv[++nextArg]); } else { app->ranges = srejoin(app->ranges, ",", argv[++nextArg], NULL); } } } else if (smatch(argp, "--retries") || smatch(argp, "-r")) { if (nextArg >= argc) { return showUsage(); } else { app->retries = atoi(argv[++nextArg]); } } else if (smatch(argp, "--self")) { /* Undocumented. Allow self-signed certs. Users should just not set --verify */ app->verifyIssuer = 0; ssl = 1; } else if (smatch(argp, "--sequence")) { app->sequence++; } else if (smatch(argp, "--showHeaders") || smatch(argp, "--show") || smatch(argp, "-s")) { app->showHeaders++; } else if (smatch(argp, "--showStatus") || smatch(argp, "--showCode")) { app->showStatus++; } else if (smatch(argp, "--single") || smatch(argp, "-s")) { app->singleStep++; } else if (smatch(argp, "--text")) { app->text++; } else if (smatch(argp, "--threads") || smatch(argp, "-t")) { if (nextArg >= argc) { return showUsage(); } else { app->loadThreads = atoi(argv[++nextArg]); } } else if (smatch(argp, "--timeout")) { if (nextArg >= argc) { return showUsage(); } else { app->timeout = atoi(argv[++nextArg]) * MPR_TICKS_PER_SEC; } } else if (smatch(argp, "--upload") || smatch(argp, "-u")) { app->upload++; } else if (smatch(argp, "--user") || smatch(argp, "--username")) { if (nextArg >= argc) { return showUsage(); } else { app->username = argv[++nextArg]; } // DEPRECATE validate. Preserve verify. } else if (smatch(argp, "--validate") || smatch(argp, "--verify")) { app->verifyPeer = 1; ssl = 1; } else if (smatch(argp, "--verbose") || smatch(argp, "-v")) { app->verbose++; } else if (smatch(argp, "--version") || smatch(argp, "-V")) { mprEprintf("%s %s\n" "Copyright (C) Embedthis Software 2003-2013\n" "Copyright (C) Michael O'Brien 2003-2013\n", BIT_TITLE, BIT_VERSION); exit(0); } else if (smatch(argp, "--workerTheads") || smatch(argp, "-w")) { if (nextArg >= argc) { return showUsage(); } else { app->workers = atoi(argv[++nextArg]); } setWorkers++; } else if (smatch(argp, "--zero")) { app->zeroOnErrors++; } else if (smatch(argp, "--")) { nextArg++; break; } else if (smatch(argp, "-")) { break; } else { return showUsage(); } } if (argc == nextArg) { return showUsage(); } app->nextArg = nextArg; argc = argc - nextArg; argv = &argv[nextArg]; app->target = argv[argc - 1]; if (--argc > 0) { /* Files present on command line */ app->files = mprCreateList(argc, MPR_LIST_STATIC_VALUES); for (i = 0; i < argc; i++) { mprAddItem(app->files, argv[i]); } } if (!setWorkers) { app->workers = app->loadThreads + 2; } if (app->method == 0) { if (app->bodyData || app->formData || app->upload) { app->method = "POST"; } else if (app->files) { app->method = "PUT"; } else { app->method = "GET"; } } #if BIT_PACK_SSL { HttpUri *uri = httpCreateUri(app->target, 0); if (uri->secure || ssl) { app->ssl = mprCreateSsl(0); if (app->provider) { mprSetSslProvider(app->ssl, app->provider); } if (app->cert) { if (!app->key) { mprError("Must specify key file"); return 0; } mprSetSslCertFile(app->ssl, app->cert); mprSetSslKeyFile(app->ssl, app->key); } if (app->ca) { mprLog(4, "Using CA: \"%s\"", app->ca); mprSetSslCaFile(app->ssl, app->ca); } if (app->verifyIssuer == -1) { app->verifyIssuer = app->verifyPeer ? 1 : 0; } mprVerifySslPeer(app->ssl, app->verifyPeer); mprVerifySslIssuer(app->ssl, app->verifyIssuer); if (app->ciphers) { mprSetSslCiphers(app->ssl, app->ciphers); } } else { mprVerifySslPeer(NULL, 0); } } #else /* Suppress comp warning */ mprNop(&ssl); #endif return 0; }
/* function set uri(value: String): Void */ static EjsObj *uri_set_uri(Ejs *ejs, EjsUri *up, int argc, EjsObj **argv) { up->uri = httpCreateUri(ejsToMulti(ejs, argv[0]), 0); return 0; }
static HttpConn *openConnection(HttpConn *conn, cchar *url, struct MprSsl *ssl) { Http *http; HttpUri *uri; MprSocket *sp; char *ip; int port, rc, level; mprAssert(conn); http = conn->http; uri = httpCreateUri(url, HTTP_COMPLETE_URI); conn->tx->parsedUri = uri; if (*url == '/') { 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) { httpCloseConn(conn); } else { mprLog(4, "Http: reusing keep-alive socket on: %s:%d", ip, port); } } if (conn->sock) { return conn; } if ((sp = mprCreateSocket()) == 0) { httpError(conn, HTTP_CODE_COMMS_ERROR, "Can't create socket for %s", url); return 0; } if ((rc = mprConnectSocket(sp, ip, port, 0)) < 0) { httpError(conn, HTTP_CODE_COMMS_ERROR, "Can't open socket on %s:%d", ip, port); return 0; } #if BIT_PACK_SSL /* Must be done even if using keep alive for repeat SSL requests */ if (uri->secure) { if (ssl == 0) { ssl = mprCreateSsl(); } if (mprUpgradeSocket(sp, ssl, 0) < 0) { conn->errorMsg = sp->errorMsg; return 0; } } #endif conn->sock = sp; conn->ip = sclone(ip); conn->port = port; conn->secure = uri->secure; conn->keepAliveCount = (conn->limits->keepAliveMax) ? conn->limits->keepAliveMax : -1; if ((level = httpShouldTrace(conn, HTTP_TRACE_RX, HTTP_TRACE_CONN, NULL)) >= 0) { mprLog(level, "### Outgoing connection from %s:%d to %s:%d", conn->ip, conn->port, conn->sock->ip, conn->sock->port); } return conn; }
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); }