/* 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; }
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 } }
static void setCorsHeaders(HttpConn *conn) { HttpRoute *route; cchar *origin; route = conn->rx->route; /* Cannot use wildcard origin response if allowing credentials */ if (*route->corsOrigin && !route->corsCredentials) { httpSetHeaderString(conn, "Access-Control-Allow-Origin", route->corsOrigin); } else { origin = httpGetHeader(conn, "Origin"); httpSetHeaderString(conn, "Access-Control-Allow-Origin", origin ? origin : "*"); } if (route->corsCredentials) { httpSetHeaderString(conn, "Access-Control-Allow-Credentials", "true"); } if (route->corsHeaders) { httpSetHeaderString(conn, "Access-Control-Allow-Headers", route->corsHeaders); } if (route->corsMethods) { httpSetHeaderString(conn, "Access-Control-Allow-Methods", route->corsMethods); } if (route->corsAge) { httpSetHeader(conn, "Access-Control-Max-Age", "%d", route->corsAge); } }
static EjsString *getStringHeader(Ejs *ejs, EjsHttp *hp, cchar *key) { cchar *value; if (!waitForResponseHeaders(hp)) { return 0; } value = httpGetHeader(hp->conn, key); if (value == 0) { return ESV(null); } return ejsCreateStringFromAsc(ejs, value); }
static EjsDate *getDateHeader(Ejs *ejs, EjsHttp *hp, cchar *key) { MprTime when; cchar *value; if (!waitForResponseHeaders(hp)) { return 0; } value = httpGetHeader(hp->conn, key); if (value == 0) { return ESV(null); } if (mprParseTime(&when, value, MPR_UTC_TIMEZONE, NULL) < 0) { value = 0; } return ejsCreateDate(ejs, when); }
/* function header(key: String): String */ static EjsString *http_header(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { EjsString *result; cchar *value; char *str; if (!waitForResponseHeaders(hp)) { return 0; } str = slower(ejsToMulti(ejs, argv[0])); value = httpGetHeader(hp->conn, str); if (value) { result = ejsCreateStringFromAsc(ejs, value); } else { result = ESV(null); } return result; }
/* Connection callback */ static void webSocketNotify(HttpConn *conn, int event, int arg) { Ejs *ejs; EjsWebSocket *ws; EjsByteArray *ba; EjsAny *data; HttpPacket *packet; MprBuf *content; ssize len; if ((ws = httpGetConnContext(conn)) == 0) { return; } ejs = ws->ejs; if (!ejs->service) { /* Shutting down */ return; } switch (event) { case HTTP_EVENT_STATE: if (arg == HTTP_STATE_CONTENT) { ws->protocol = (char*) httpGetHeader(conn, "Sec-WebSocket-Protocol"); mprTrace(3, "Web socket protocol %s", ws->protocol); onWebSocketEvent(ws, HTTP_EVENT_APP_OPEN, 0, 0); } break; case HTTP_EVENT_READABLE: packet = httpGetPacket(conn->readq); content = packet->content; if (packet->type == WS_MSG_TEXT) { data = ejsCreateStringFromBytes(ejs, mprGetBufStart(content), mprGetBufLength(content)); } else { len = httpGetPacketLength(packet); assert(len > 0); if ((ba = ejsCreateByteArray(ejs, len)) == 0) { return; } memcpy(ba->value, mprGetBufStart(content), len); ejsSetByteArrayPositions(ejs, ba, -1, len); data = ba; } onWebSocketEvent(ws, event, data, packet); break; case HTTP_EVENT_ERROR: if (!ws->error && !ws->closed) { ws->error = 1; onWebSocketEvent(ws, event, 0, 0); ws->closed = 1; onWebSocketEvent(ws, HTTP_EVENT_APP_CLOSE, 0, 0); } break; case HTTP_EVENT_APP_CLOSE: if (!ws->closed) { ws->closed = 1; onWebSocketEvent(ws, event, 0, 0); } break; } }
cchar *espGetHeader(HttpConn *conn, cchar *key) { return httpGetHeader(conn, key); }
/* Run the request. This is invoked when all the input data has been received and buffered. This routine completely services the request. */ static void readyPhp(HttpQueue *q) { HttpConn *conn; HttpRx *rx; HttpTx *tx; MaPhp *php; FILE *fp; cchar *value; char shebang[MPR_MAX_STRING]; zend_file_handle file_handle; TSRMLS_FETCH(); conn = q->conn; rx = conn->rx; tx = conn->tx; if ((php = q->queueData) == 0) { return; } /* Set the request context */ zend_first_try { php->var_array = 0; SG(server_context) = conn; if (conn->username) { SG(request_info).auth_user = estrdup(conn->username); } if (conn->password) { SG(request_info).auth_password = estrdup(conn->password); } if ((value = httpGetHeader(conn, "Authorization")) != 0) { SG(request_info).auth_digest = estrdup(value); } SG(request_info).content_type = rx->mimeType; SG(request_info).path_translated = tx->filename; SG(request_info).content_length = (long) (ssize) rx->length; SG(sapi_headers).http_response_code = HTTP_CODE_OK; SG(request_info).query_string = rx->parsedUri->query; SG(request_info).request_method = rx->method; SG(request_info).request_uri = rx->uri; /* Workaround on MAC OS X where the SIGPROF is given to the wrong thread */ PG(max_input_time) = -1; EG(timeout_seconds) = 0; /* The readBodyData callback may be invoked during startup */ php_request_startup(TSRMLS_C); CG(zend_lineno) = 0; } zend_catch { mprError("Cannot start PHP request"); zend_try { php_request_shutdown(0); } zend_end_try(); httpError(conn, HTTP_CODE_INTERNAL_SERVER_ERROR, "PHP initialization failed"); return; } zend_end_try(); /* Execute the script file */ file_handle.filename = tx->filename; file_handle.free_filename = 0; file_handle.opened_path = 0; #if LOAD_FROM_FILE file_handle.type = ZEND_HANDLE_FILENAME; #else file_handle.type = ZEND_HANDLE_FP; if ((fp = fopen(tx->filename, "r")) == NULL) { if (rx->referrer) { httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot open document: %s from %s", tx->filename, rx->referrer); } else { httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot open document: %s", tx->filename); } return; } /* Check for shebang and skip */ file_handle.handle.fp = fp; shebang[0] = '\0'; if (fgets(shebang, sizeof(shebang), file_handle.handle.fp)) {} if (shebang[0] != '#' || shebang[1] != '!') { fseek(fp, 0L, SEEK_SET); } #endif zend_try { php_execute_script(&file_handle TSRMLS_CC); if (!SG(headers_sent)) { sapi_send_headers(TSRMLS_C); } } zend_catch { php_request_shutdown(NULL); httpError(conn, HTTP_CODE_INTERNAL_SERVER_ERROR, "PHP script execution failed"); return; } zend_end_try(); zend_try { php_request_shutdown(NULL); SG(server_context) = NULL; } zend_catch { httpError(conn, HTTP_CODE_INTERNAL_SERVER_ERROR, "PHP script shutdown failed"); } zend_end_try(); httpFinalize(conn); }