PUBLIC char *httpGetDateString(MprPath *sbuf) { MprTicks when; if (sbuf == 0) { when = mprGetTime(); } else { when = (MprTicks) sbuf->mtime * TPS; } return mprFormatUniversalTime(HTTP_DATE_FORMAT, when); }
/* 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; }
/* Set lifespan < 0 to delete the cookie in the client. Set lifespan == 0 for no expiry. WARNING: Some browsers (Chrome, Firefox) do not delete session cookies when you exit the browser. */ PUBLIC void httpSetCookie(HttpConn *conn, cchar *name, cchar *value, cchar *path, cchar *cookieDomain, MprTicks lifespan, int flags) { HttpRx *rx; char *cp, *expiresAtt, *expires, *domainAtt, *domain, *secure, *httponly; rx = conn->rx; if (path == 0) { path = "/"; } domain = (char*) cookieDomain; if (!domain) { domain = sclone(rx->hostHeader); if ((cp = strchr(domain, ':')) != 0) { *cp = '\0'; } if (*domain && domain[strlen(domain) - 1] == '.') { domain[strlen(domain) - 1] = '\0'; } } domainAtt = domain ? "; domain=" : ""; if (domain && !strchr(domain, '.')) { if (smatch(domain, "localhost")) { domainAtt = domain = ""; } else { domain = sjoin(".", domain, NULL); } } if (lifespan) { expiresAtt = "; expires="; expires = mprFormatUniversalTime(MPR_HTTP_DATE, mprGetTime() + lifespan); } else { expires = expiresAtt = ""; } secure = (conn->secure & (flags & HTTP_COOKIE_SECURE)) ? "; secure" : ""; httponly = (flags & HTTP_COOKIE_HTTP) ? "; httponly" : ""; /* Allow multiple cookie headers. Even if the same name. Later definitions take precedence. */ httpAppendHeader(conn, "Set-Cookie", sjoin(name, "=", value, "; path=", path, domainAtt, domain, expiresAtt, expires, secure, httponly, NULL)); httpAppendHeader(conn, "Cache-Control", "no-cache=\"set-cookie\""); }
ssize httpWriteCached(HttpConn *conn) { MprTime modified; cchar *cacheKey, *data, *content; if (!conn->tx->cache) { return MPR_ERR_CANT_FIND; } cacheKey = makeCacheKey(conn); if ((content = mprReadCache(conn->host->responseCache, cacheKey, &modified, 0)) == 0) { mprLog(3, "No cached data for ", cacheKey); return 0; } mprLog(5, "Used cached ", cacheKey); data = setHeadersFromCache(conn, content); httpSetHeader(conn, "Etag", mprGetMD5(cacheKey)); httpSetHeader(conn, "Last-Modified", mprFormatUniversalTime(MPR_HTTP_DATE, modified)); conn->tx->cacheBuffer = 0; httpWriteString(conn->writeq, data); httpFinalize(conn); return slen(data); }