static void readBody(HttpConn *conn, MprFile *outFile) { char buf[BIT_MAX_BUFFER]; cchar *result; ssize bytes; while (!conn->error && conn->sock && (bytes = httpRead(conn, buf, sizeof(buf))) > 0) { if (!app->noout) { result = formatOutput(conn, buf, &bytes); if (result) { mprWriteFile(outFile, result, bytes); } } #if FUTURE // This should be pushed into a range filter. // Buffer all output and then parsing can work type = httpGetHeader(conn, "Content-Type"); if (scontains(type, "multipart/byteranges")) { if ((boundary = scontains(type, "boundary=")) != 0) { boundary += 9; if (*boundary) { boundary = sfmt("--%s\r\n", boundary); } } } #endif } }
/* See if there is acceptable cached content for this request. If so, return true. Will setup tx->cacheBuffer as a side-effect if the output should be captured and cached. */ static bool fetchCachedResponse(HttpConn *conn) { HttpTx *tx; MprTime modified, when; cchar *value, *key, *tag; int status, cacheOk, canUseClientCache; tx = conn->tx; /* Transparent caching. Manual caching must manually call httpWriteCached() */ key = makeCacheKey(conn); if ((value = httpGetHeader(conn, "Cache-Control")) != 0 && (scontains(value, "max-age=0") == 0 || scontains(value, "no-cache") == 0)) { mprLog(3, "Client reload. Cache-control header '%s' rejects use of cached content.", value); } else if ((tx->cachedContent = mprReadCache(conn->host->responseCache, key, &modified, 0)) != 0) { /* See if a NotModified response can be served. This is much faster than sending the response. Observe headers: If-None-Match: "ec18d-54-4d706a63" If-Modified-Since: Fri, 04 Mar 2012 04:28:19 GMT Set status to OK when content must be transmitted. */ cacheOk = 1; canUseClientCache = 0; tag = mprGetMD5(key); if ((value = httpGetHeader(conn, "If-None-Match")) != 0) { canUseClientCache = 1; if (scmp(value, tag) != 0) { cacheOk = 0; } } if (cacheOk && (value = httpGetHeader(conn, "If-Modified-Since")) != 0) { canUseClientCache = 1; mprParseTime(&when, value, 0, 0); if (modified > when) { cacheOk = 0; } } status = (canUseClientCache && cacheOk) ? HTTP_CODE_NOT_MODIFIED : HTTP_CODE_OK; mprLog(3, "cacheHandler: Use cached content for %s, status %d", key, status); httpSetStatus(conn, status); httpSetHeader(conn, "Etag", mprGetMD5(key)); httpSetHeader(conn, "Last-Modified", mprFormatUniversalTime(MPR_HTTP_DATE, modified)); return 1; } mprLog(3, "cacheHandler: No cached content for %s", key); return 0; }
/* Test if the version is acceptable based on the supplied critera. The acceptable formats for criteria are: VER Allows prereleases 1.2.[*xX] Wild card version portion. (allows prereleases). ~VER Compatible with VER at the least significant level. ~1.2.3 == (>=1.2.3 <1.3.0) Compatible at the patch level ~1.2 == 1.2.x Compatible at the minor level ~1 == 1.x Compatible at the major level ^VER Compatible with VER at the most significant level. ^0.2.3 == 0.2.3 <= VER < 0.3.0 ^1.2.3 == 1.2.3 <= VER < 2.0.0 [>, >=, <, <=, ==, !=]VER Create range relative to given version EXPR1 - EXPR2 <=EXPR1 <=EXPR2 EXPR1 || EXPR2 ... EXPR1 OR EXPR2 EXPR1 && EXPR2 ... EXPR1 AND EXPR2 EXPR1 EXPR2 ... EXPR1 AND EXPR2 Pre-release versions will only match if the criteria contains a "-.*" prerelease suffix */ PUBLIC bool mprIsVersionObjAcceptable(MprVersion *vp, cchar *criteria) { char *expr, *exprTok, *range, *rangeTok, *low, *high; bool allMatched; if (!vp->ok) { return 0; } if (!criteria || *criteria == '\0') { return 1; } criteria = cleanVersion(criteria); for (range = (char*) criteria; stok(range, "||", &rangeTok) != 0; range = rangeTok) { range = strim(range, " \t", 0); allMatched = 1; for (expr = (char*) range; sptok(expr, "&&", &exprTok) != 0; expr = exprTok) { if (scontains(expr, " - ")) { low = sptok(expr, " - ", &high); return inRange(vp, sjoin(">=", low, NULL)) && inRange(vp, sjoin("<=", high, NULL)); } if (!inRange(vp, expr)) { allMatched = 0; break; } } if (allMatched) { return 1; } } return 0; }
PUBLIC void espDefineAction(HttpRoute *route, cchar *target, void *callback) { EspRoute *eroute; char *action, *controller; assert(route); assert(target && *target); assert(callback); eroute = ((EspRoute*) route->eroute)->top; if (target) { #if DEPRECATED || 1 /* Keep till version 6 */ if (scontains(target, "-cmd-")) { target = sreplace(target, "-cmd-", "/"); } else if (schr(target, '-')) { controller = ssplit(sclone(target), "-", (char**) &action); target = sjoin(controller, "/", action, NULL); } #endif if (!eroute->actions) { eroute->actions = mprCreateHash(-1, MPR_HASH_STATIC_VALUES); } mprAddKey(eroute->actions, target, callback); } }
/* Check if the layout has changed. Returns false if the layout does not exist. */ static bool layoutIsStale(EspRoute *eroute, cchar *source, cchar *module) { char *data, *lpath, *quote; cchar *layout, *layoutsDir; ssize len; bool stale; int recompile; stale = 0; layoutsDir = httpGetDir(eroute->route, "LAYOUTS"); if ((data = mprReadPathContents(source, &len)) != 0) { if ((lpath = scontains(data, "@ layout \"")) != 0) { lpath = strim(&lpath[10], " ", MPR_TRIM_BOTH); if ((quote = schr(lpath, '"')) != 0) { *quote = '\0'; } layout = (layoutsDir && *lpath) ? mprJoinPath(layoutsDir, lpath) : 0; } else { layout = (layoutsDir) ? mprJoinPath(layoutsDir, "default.esp") : 0; } if (layout) { stale = espModuleIsStale(layout, module, &recompile); if (stale) { mprLog("info esp", 4, "esp layout %s is newer than module %s", layout, module); } } } return stale; }
PUBLIC int httpSetGroupAccount(cchar *newGroup) { Http *http; http = HTTP; if (smatch(newGroup, "HTTP") || smatch(newGroup, "APPWEB")) { #if ME_UNIX_LIKE /* Only change group if root */ if (getuid() != 0) { return 0; } #endif #if MACOSX || FREEBSD newGroup = "_www"; #elif LINUX || ME_UNIX_LIKE { char *buf; newGroup = "nobody"; /* Debian has nogroup, Fedora has nobody. Ugh! */ if ((buf = mprReadPathContents("/etc/group", NULL)) != 0) { if (scontains(buf, "nogroup:")) { newGroup = "nogroup"; } } } #elif WINDOWS newGroup = "Administrator"; #endif } #if ME_UNIX_LIKE struct group *gp; if (snumber(newGroup)) { http->gid = atoi(newGroup); if ((gp = getgrgid(http->gid)) == 0) { mprLog("critical http", 0, "Bad group id: %d", http->gid); return MPR_ERR_CANT_ACCESS; } newGroup = gp->gr_name; } else { if ((gp = getgrnam(newGroup)) == 0) { mprLog("critical http", 0, "Bad group name: %s", newGroup); return MPR_ERR_CANT_ACCESS; } http->gid = gp->gr_gid; } http->groupChanged = 1; #endif http->group = sclone(newGroup); return 0; }
HttpHost *httpLookupHostOnEndpoint(HttpEndpoint *endpoint, cchar *name) { HttpHost *host; int next; if (name == 0 || *name == '\0') { return mprGetFirstItem(endpoint->hosts); } for (next = 0; (host = mprGetNextItem(endpoint->hosts, &next)) != 0; ) { if (smatch(host->name, name)) { return host; } if (*host->name == '*') { if (host->name[1] == '\0') { return host; } if (scontains(name, &host->name[1])) { return host; } } } return 0; }
MAIN(appweb, int argc, char **argv, char **envp) { Mpr *mpr; cchar *argp, *jail; char *logSpec, *traceSpec; int argind; jail = 0; logSpec = 0; traceSpec = 0; if ((mpr = mprCreate(argc, argv, MPR_USER_EVENTS_THREAD)) == NULL) { exit(1); } if ((app = mprAllocObj(AppwebApp, manageApp)) == NULL) { exit(2); } mprAddRoot(app); mprAddStandardSignals(); #if ME_ROM extern MprRomInode romFiles[]; mprSetRomFileSystem(romFiles); #endif if (httpCreate(HTTP_CLIENT_SIDE | HTTP_SERVER_SIDE) == 0) { exit(3); } app->mpr = mpr; app->workers = -1; app->home = sclone(ME_SERVER_ROOT); app->documents = app->home; argc = mpr->argc; argv = (char**) mpr->argv; for (argind = 1; argind < argc; argind++) { argp = argv[argind]; if (*argp != '-') { break; } if (smatch(argp, "--config") || smatch(argp, "--conf")) { if (argind >= argc) { usageError(); } app->configFile = sclone(argv[++argind]); #if ME_UNIX_LIKE } else if (smatch(argp, "--chroot")) { if (argind >= argc) { usageError(); } jail = mprGetAbsPath(argv[++argind]); #endif } else if (smatch(argp, "--debugger") || smatch(argp, "-D")) { mprSetDebugMode(1); } else if (smatch(argp, "--exe")) { if (argind >= argc) { usageError(); } mpr->argv[0] = mprGetAbsPath(argv[++argind]); mprSetAppPath(mpr->argv[0]); mprSetModuleSearchPath(NULL); } else if (smatch(argp, "--home")) { if (argind >= argc) { usageError(); } app->home = sclone(argv[++argind]); if (chdir(app->home) < 0) { mprLog("error appweb", 0, "Cannot change directory to %s", app->home); exit(4); } } else if (smatch(argp, "--log") || smatch(argp, "-l")) { if (argind >= argc) { usageError(); } logSpec = argv[++argind]; } else if (smatch(argp, "--name") || smatch(argp, "-n")) { if (argind >= argc) { usageError(); } mprSetAppName(argv[++argind], 0, 0); } else if (smatch(argp, "--threads")) { if (argind >= argc) { usageError(); } app->workers = atoi(argv[++argind]); } else if (smatch(argp, "--show") || smatch(argp, "-s")) { app->show = 1; } else if (smatch(argp, "--trace") || smatch(argp, "-t")) { if (argind >= argc) { usageError(); } traceSpec = argv[++argind]; } else if (smatch(argp, "--verbose") || smatch(argp, "-v")) { if (!logSpec) { logSpec = sfmt("stderr:2"); } if (!traceSpec) { traceSpec = sfmt("stderr:2"); } } else if (smatch(argp, "--version") || smatch(argp, "-V")) { mprPrintf("%s\n", ME_VERSION); exit(0); } else if (*argp == '-' && isdigit((uchar) argp[1])) { if (!logSpec) { logSpec = sfmt("stderr:%d", (int) stoi(&argp[1])); } if (!traceSpec) { traceSpec = sfmt("stderr:%d", (int) stoi(&argp[1])); } } else if (smatch(argp, "-?") || scontains(argp, "-help")) { usageError(); exit(5); } else if (*argp == '-') { mprLog("error appweb", 0, "Unknown switch \"%s\"", argp); usageError(); exit(5); } } app->home = mprGetAbsPath(app->home); if (logSpec) { mprStartLogging(logSpec, MPR_LOG_DETAILED | MPR_LOG_CONFIG | MPR_LOG_CMDLINE); } if (traceSpec) { httpStartTracing(traceSpec); } if (mprStart() < 0) { mprLog("error appweb", 0, "Cannot start MPR"); mprDestroy(); return MPR_ERR_CANT_INITIALIZE; } if (checkEnvironment(argv[0]) < 0) { exit(6); } if (argc == argind && !app->configFile) { if (findAppwebConf() < 0) { exit(7); } } if (jail && changeRoot(jail) < 0) { exit(8); } if (createEndpoints(argc - argind, &argv[argind]) < 0) { return MPR_ERR_CANT_INITIALIZE; } if (maStartAppweb(app->appweb) < 0) { mprLog("error appweb", 0, "Cannot start HTTP service, exiting."); exit(9); } if (app->show) { httpLogRoutes(0, 0); } mprServiceEvents(-1, 0); mprLog("info appweb", 1, "Stopping Appweb ..."); mprDestroy(); return mprGetExitStatus(); }
/* Add cache configuration to the route. This can be called multiple times. Uris, extensions and methods may optionally provide a space or comma separated list of items. If URI is NULL or "*", cache all URIs for this route. Otherwise, cache only the given URIs. The URIs may contain an ordered set of request parameters. For example: "/user/show?name=john&posts=true" Note: the URI should not include the route prefix (scriptName) The extensions should not contain ".". The methods may contain "*" for all methods. */ void httpAddCache(HttpRoute *route, cchar *methods, cchar *uris, cchar *extensions, cchar *types, MprTime clientLifespan, MprTime serverLifespan, int flags) { HttpCache *cache; char *item, *tok; cache = 0; if (!route->caching) { httpAddRouteHandler(route, "cacheHandler", ""); httpAddRouteFilter(route, "cacheFilter", "", HTTP_STAGE_TX); route->caching = mprCreateList(0, 0); } else if (flags & HTTP_CACHE_RESET) { route->caching = mprCreateList(0, 0); } else if (route->parent && route->caching == route->parent->caching) { route->caching = mprCloneList(route->parent->caching); } if ((cache = mprAllocObj(HttpCache, manageHttpCache)) == 0) { return; } if (extensions) { cache->extensions = mprCreateHash(0, 0); for (item = stok(sclone(extensions), " \t,", &tok); item; item = stok(0, " \t,", &tok)) { if (smatch(item, "*")) { extensions = 0; } else { mprAddKey(cache->extensions, item, cache); } } } else if (types) { cache->types = mprCreateHash(0, 0); for (item = stok(sclone(types), " \t,", &tok); item; item = stok(0, " \t,", &tok)) { if (smatch(item, "*")) { extensions = 0; } else { mprAddKey(cache->types, item, cache); } } } if (methods) { cache->methods = mprCreateHash(0, MPR_HASH_CASELESS); for (item = stok(sclone(methods), " \t,", &tok); item; item = stok(0, " \t,", &tok)) { if (smatch(item, "*")) { methods = 0; } else { mprAddKey(cache->methods, item, cache); } } } if (uris) { cache->uris = mprCreateHash(0, 0); for (item = stok(sclone(uris), " \t,", &tok); item; item = stok(0, " \t,", &tok)) { if (flags & HTTP_CACHE_ONLY && route->prefix && !scontains(item, sfmt("prefix=%s", route->prefix))) { /* Auto-add ?prefix=ROUTE_NAME if there is no query */ if (!schr(item, '?')) { item = sfmt("%s?prefix=%s", item, route->prefix); } } mprAddKey(cache->uris, item, cache); } } if (clientLifespan <= 0) { clientLifespan = route->lifespan; } cache->clientLifespan = clientLifespan; if (serverLifespan <= 0) { serverLifespan = route->lifespan; } cache->serverLifespan = serverLifespan; cache->flags = flags; mprAddItem(route->caching, cache); #if UNUSED && KEEP mprLog(3, "Caching route %s for methods %s, URIs %s, extensions %s, types %s, client lifespan %d, server lifespan %d", route->name, (methods) ? methods: "*", (uris) ? uris: "*", (extensions) ? extensions: "*", (types) ? types: "*", cache->clientLifespan / MPR_TICKS_PER_SEC); cache->serverLifespan / MPR_TICKS_PER_SEC); #endif }
static bool process(cchar *operation, bool quiet) { cchar *name, *off, *path; int rc, launch, update, service, upstart; /* No systemd support yet */ rc = 1; name = app->serviceName; launch = upstart = update = service = 0; if (exists("/bin/launchctl") && exists("/Library/LaunchDaemons/com.%s.%s.plist", slower(BLD_COMPANY), name)) { launch++; } else if (exists("/sbin/start") && exists("/etc/init/rc.conf") && (exists("/etc/init/%s.conf", name) || exists("/etc/init/%s.off", name))) { upstart++; } else if (exists("/usr/sbin/update-rc.d") && exists("/etc/init.d/%s", name)) { update++; } else if (exists("/sbin/service") && exists("/etc/init.d/%s", name)) { service++; } else { mprError("Can't locate system tool to manage service"); return 0; } /* Operations */ if (smatch(operation, "install")) { if (launch) { ; } else if (service) { if (!run("/sbin/chkconfig --del %s", name)) { rc = 0; } else if (!run("/sbin/chkconfig --add %s", name)) { rc = 0; } else if (!run("/sbin/chkconfig --level 5 %s", name)) { rc = 0; } } else if (update) { ; } else if (upstart) { ; } } else if (smatch(operation, "uninstall")) { process("disable", 1); if (launch) { ; } else if (service) { rc = run("/sbin/chkconfig --del %s", name); } else if (update) { ; } else if (upstart) { ; } } else if (smatch(operation, "enable")) { /* Enable service (will start on reboot) */ if (launch) { path = sfmt("/Library/LaunchDaemons/com.%s.%s.plist", slower(BLD_COMPANY), name); /* Unfortunately, there is no launchctl command to do an enable without starting. So must do a stop below. */ if (!run("/bin/launchctl load -w %s", path)) { rc = 0; } else { rc = process("stop", 1); } } else if (update) { rc = run("/usr/sbin/update-rc.d %s defaults 90 10", name); /* Not supported on older versions rc = run("/usr/sbin/update-rc.d %s enable", name); */ } else if (service) { rc = run("/sbin/chkconfig %s on", name); } else if (upstart) { off = sfmt("/etc/init/%s.off", name); if (exists(off) && !run("mv %s /etc/init/%s.conf", off, name)) { rc = 0; } } } else if (smatch(operation, "disable")) { process("stop", 1); if (launch) { rc = run("/bin/launchctl unload -w /Library/LaunchDaemons/com.%s.%s.plist", slower(BLD_COMPANY), name); } else if (update) { /* Not supported on older versions rc = run("/usr/sbin/update-rc.d %s disable", name); */ rc = run("/usr/sbin/update-rc.d -f %s remove", name); } else if (service) { rc = run("/sbin/chkconfig %s off", name); } else if (upstart) { if (exists("/etc/init/%s.conf", name)) { rc = run("mv /etc/init/%s.conf /etc/init/%s.off", name, name); } } } else if (smatch(operation, "start")) { if (launch) { rc = run("/bin/launchctl load /Library/LaunchDaemons/com.%s.%s.plist", slower(BLD_COMPANY), name); } else if (service) { rc = run("/sbin/service %s start", name); } else if (update) { rc = run("/usr/sbin/invoke-rc.d --quiet %s start", name); } else if (upstart) { rc = run("/sbin/start %s", name); if (!rc) { if (scontains(app->error, "start: Job is already running", -1)) { rc = 0; } } } } else if (smatch(operation, "stop")) { if (launch) { rc = run("/bin/launchctl unload /Library/LaunchDaemons/com.%s.%s.plist", slower(BLD_COMPANY), name); } else if (service) { if (!run("/sbin/service %s stop", name)) { rc = killPid(); } } else if (update) { if (!run("/usr/sbin/invoke-rc.d --quiet %s stop", name)) { rc = killPid(); } } else if (upstart) { if (exists("/etc/init/%s.conf", name)) { rc = run("/sbin/stop %s", name); } } } else if (smatch(operation, "reload")) { rc = process("restart", 0); } else if (smatch(operation, "restart")) { process("stop", 1); rc = process("start", 0); } else if (smatch(operation, "run")) { runService(); } if (!quiet) { if (!rc && app->error && *app->error) { mprError("Can't run command: %s\nCommand output: %s\n", app->command, app->error); } /* Logging at level one will be visible if appman -v is used */ if (app->error && *app->error) { mprLog(1, "Error: %s", app->error); } if (app->output && *app->output) { mprLog(1, "Output: %s", app->output); } } return rc; }
/* Create and initialize a URI. This accepts full URIs with schemes (http:) and partial URLs Support IPv4 and [IPv6]. Supported forms: SCHEME://[::]:PORT/URI SCHEME://HOST:PORT/URI [::]:PORT/URI :PORT/URI HOST:PORT/URI PORT/URI /URI URI NOTE: HOST/URI is not supported and requires a scheme prefix. This is because it is ambiguous with a relative uri path. Missing fields are null or zero. */ PUBLIC HttpUri *httpCreateUri(cchar *uri, int flags) { HttpUri *up; char *tok, *next; if ((up = mprAllocObj(HttpUri, manageUri)) == 0) { return 0; } tok = sclone(uri); /* [scheme://][hostname[:port]][/path[.ext]][#ref][?query] First trim query and then reference from the end */ if ((next = schr(tok, '?')) != 0) { *next++ = '\0'; up->query = sclone(next); } if ((next = schr(tok, '#')) != 0) { *next++ = '\0'; up->reference = sclone(next); } /* [scheme://][hostname[:port]][/path] */ if ((next = scontains(tok, "://")) != 0) { up->scheme = snclone(tok, (next - tok)); if (smatch(up->scheme, "http")) { if (flags & HTTP_COMPLETE_URI) { up->port = 80; } } else if (smatch(up->scheme, "ws")) { if (flags & HTTP_COMPLETE_URI) { up->port = 80; } up->webSockets = 1; } else if (smatch(up->scheme, "https")) { if (flags & HTTP_COMPLETE_URI) { up->port = 443; } up->secure = 1; } else if (smatch(up->scheme, "wss")) { if (flags & HTTP_COMPLETE_URI) { up->port = 443; } up->secure = 1; up->webSockets = 1; } tok = &next[3]; } /* [hostname[:port]][/path] */ if (*tok == '[' && ((next = strchr(tok, ']')) != 0)) { /* IPv6 [::]:port/uri */ up->host = snclone(&tok[1], (next - tok) - 1); tok = ++next; } else if (*tok && *tok != '/' && *tok != ':' && (up->scheme || strchr(tok, ':'))) { /* Supported forms: scheme://hostname hostname:port */ if ((next = spbrk(tok, ":/")) == 0) { next = &tok[slen(tok)]; } up->host = snclone(tok, next - tok); tok = next; } assert(tok); /* [:port][/path] */ if (*tok == ':') { up->port = atoi(++tok); if ((tok = schr(tok, '/')) == 0) { tok = ""; } } assert(tok); /* [/path] */ if (*tok) { up->path = sclone(tok); /* path[.ext[/extra]] */ if ((tok = srchr(up->path, '.')) != 0) { if (tok[1]) { if ((next = srchr(up->path, '/')) != 0) { if (next < tok) { up->ext = sclone(++tok); } } else { up->ext = sclone(++tok); } } } } if (flags & (HTTP_COMPLETE_URI | HTTP_COMPLETE_URI_PATH)) { if (up->path == 0 || *up->path == '\0') { up->path = sclone("/"); } } up->secure = smatch(up->scheme, "https") || smatch(up->scheme, "wss"); up->webSockets = (smatch(up->scheme, "ws") || smatch(up->scheme, "wss")); if (flags & HTTP_COMPLETE_URI) { if (!up->scheme) { up->scheme = sclone("http"); } if (!up->host) { up->host = sclone("localhost"); } if (!up->port) { up->port = up->secure ? 443 : 80; } } up->valid = httpValidUriChars(uri); return up; }
/* Format a string URI from parts */ PUBLIC char *httpFormatUri(cchar *scheme, cchar *host, int port, cchar *path, cchar *reference, cchar *query, int flags) { char *uri; cchar *portStr, *hostDelim, *portDelim, *pathDelim, *queryDelim, *referenceDelim, *cp; portDelim = ""; portStr = ""; hostDelim = ""; if (flags & HTTP_COMPLETE_URI) { if (scheme == 0 || *scheme == '\0') { scheme = "http"; } if (host == 0 || *host == '\0') { if (port || path || reference || query) { host = "localhost"; } } } if (scheme) { hostDelim = "://"; } if (!host) { host = ""; } if (mprIsIPv6(host)) { if (*host != '[') { host = sfmt("[%s]", host); } else if ((cp = scontains(host, "]:")) != 0) { port = 0; } } else if (schr(host, ':')) { port = 0; } if (port != 0 && port != getDefaultPort(scheme)) { portStr = itos(port); portDelim = ":"; } if (scheme == 0) { scheme = ""; } if (path && *path) { if (*host) { pathDelim = (*path == '/') ? "" : "/"; } else { pathDelim = ""; } } else { pathDelim = path = ""; } if (reference && *reference) { referenceDelim = "#"; } else { referenceDelim = reference = ""; } if (query && *query) { queryDelim = "?"; } else { queryDelim = query = ""; } if (*portDelim) { uri = sjoin(scheme, hostDelim, host, portDelim, portStr, pathDelim, path, referenceDelim, reference, queryDelim, query, NULL); } else { uri = sjoin(scheme, hostDelim, host, pathDelim, path, referenceDelim, reference, queryDelim, query, NULL); } return uri; }
MAIN(http, int argc, char **argv, char **envp) { Mpr *mpr; cchar *argp; char *logSpec, *traceSpec; int argind; logSpec = 0; traceSpec = 0; if ((mpr = mprCreate(argc, argv, MPR_USER_EVENTS_THREAD)) == NULL) { exit(1); } if ((app = mprAllocObj(App, manageApp)) == NULL) { exit(2); } mprAddRoot(app); mprAddStandardSignals(); if (httpCreate(HTTP_CLIENT_SIDE | HTTP_SERVER_SIDE) < 0) { exit(2); } app->mpr = mpr; app->home = mprGetCurrentPath(); argc = mpr->argc; argv = (char**) mpr->argv; for (argind = 1; argind < argc; argind++) { argp = argv[argind]; if (*argp != '-') { break; } if (smatch(argp, "--config") || smatch(argp, "--conf")) { if (argind >= argc) { usageError(); } app->configFile = sclone(argv[++argind]); } else if (smatch(argp, "--debugger") || smatch(argp, "-D")) { mprSetDebugMode(1); } else if (smatch(argp, "--log") || smatch(argp, "-l")) { if (argind >= argc) { usageError(); } logSpec = argv[++argind]; } else if (smatch(argp, "--name") || smatch(argp, "-n")) { if (argind >= argc) { usageError(); } mprSetAppName(argv[++argind], 0, 0); } else if (smatch(argp, "--show") || smatch(argp, "-s")) { app->show = 1; } else if (smatch(argp, "--trace") || smatch(argp, "-t")) { if (argind >= argc) { usageError(); } traceSpec = argv[++argind]; } else if (smatch(argp, "--verbose") || smatch(argp, "-v")) { if (!logSpec) { logSpec = sfmt("stderr:2"); } if (!traceSpec) { traceSpec = sfmt("stderr:2"); } } else if (smatch(argp, "--version") || smatch(argp, "-V")) { mprPrintf("%s\n", ME_VERSION); exit(0); } else if (*argp == '-' && isdigit((uchar) argp[1])) { if (!logSpec) { logSpec = sfmt("stderr:%d", (int) stoi(&argp[1])); } if (!traceSpec) { traceSpec = sfmt("stderr:%d", (int) stoi(&argp[1])); } } else if (smatch(argp, "-?") || scontains(argp, "-help")) { usageError(); exit(3); } else if (*argp == '-') { mprLog("error http", 0, "Unknown switch \"%s\"", argp); usageError(); exit(4); } } if (logSpec) { mprStartLogging(logSpec, MPR_LOG_CMDLINE); } if (traceSpec) { httpStartTracing(traceSpec); } if (mprStart() < 0) { mprLog("error http", 0, "Cannot start MPR"); mprDestroy(); return MPR_ERR_CANT_INITIALIZE; } if (argc == argind && !app->configFile) { if (findConfig() < 0) { exit(5); } } if (createEndpoints(argc - argind, &argv[argind]) < 0) { return MPR_ERR_CANT_INITIALIZE; } if (httpStartEndpoints() < 0) { mprLog("error http", 0, "Cannot start HTTP service, exiting."); exit(6); } if (app->show) { httpLogRoutes(0, 0); } mprServiceEvents(-1, 0); mprLog("info http", 1, "Stopping http-server ..."); mprDestroy(); return mprGetExitStatus(); }