static int prepRequest(HttpConn *conn, MprList *files, int retry) { MprKeyValue *header; char *seq; int next; httpPrepClientConn(conn, retry); for (next = 0; (header = mprGetNextItem(app->headers, &next)) != 0; ) { if (scaselessmatch(header->key, "User-Agent")) { httpSetHeader(conn, header->key, header->value); } else { httpAppendHeader(conn, header->key, header->value); } } if (app->text) { httpSetHeader(conn, "Accept", "text/plain"); } if (app->sequence) { static int next = 0; seq = itos(next++); httpSetHeader(conn, "X-Http-Seq", seq); } if (app->ranges) { httpSetHeader(conn, "Range", app->ranges); } if (app->formData) { httpSetHeader(conn, "Content-Type", "application/x-www-form-urlencoded"); } if (setContentLength(conn, files) < 0) { return MPR_ERR_CANT_OPEN; } return 0; }
/* 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 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); } }
/* Handle Trace and Options requests. Handlers can do this themselves if they desire, but typically all Trace/Options requests come here. */ void httpHandleOptionsTrace(HttpConn *conn) { HttpRx *rx; HttpTx *tx; int flags; tx = conn->tx; rx = conn->rx; if (rx->flags & HTTP_TRACE) { /* The trace method is disabled by default unless 'TraceMethod on' is specified */ if (!conn->limits->enableTraceMethod) { tx->status = HTTP_CODE_NOT_ACCEPTABLE; httpFormatResponseBody(conn, "Trace Request Denied", "The TRACE method is disabled on this server."); } else { httpFormatResponse(conn, "%s %s %s\r\n", rx->method, rx->uri, conn->protocol); } /* This finalizes output and indicates the request is now complete */ httpFinalize(conn); } else if (rx->flags & HTTP_OPTIONS) { flags = tx->traceMethods; httpSetHeader(conn, "Allow", "OPTIONS%s%s%s%s%s%s", (conn->limits->enableTraceMethod) ? ",TRACE" : "", (flags & HTTP_STAGE_GET) ? ",GET" : "", (flags & HTTP_STAGE_HEAD) ? ",HEAD" : "", (flags & HTTP_STAGE_POST) ? ",POST" : "", (flags & HTTP_STAGE_PUT) ? ",PUT" : "", (flags & HTTP_STAGE_DELETE) ? ",DELETE" : ""); httpOmitBody(conn); httpSetContentLength(conn, 0); httpFinalize(conn); } }
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 }
void ReturnTypeEnd(HTTPCONNECTION hConnection, LIST *pList, XML *pReturnXML) { const char *pcName; LISTNODE *pNode; const char *pcMimeType = "text/plain"; if (pReturnXML != NULL) { pcName = httpGetString(pReturnXML->plAttrib, "Name"); if (strcmp(pReturnXML->pcName, "Txt") == 0) { DumpJsVar(pReturnXML, &hConnection, FALSE, TRUE); } else if (strcmp(pReturnXML->pcName, "JsVar") == 0) { const char *pcFun; httpAddBodyString(hConnection, pcName); httpAddBodyString(hConnection, "=\""); for (pNode = pReturnXML->plSubXML->pFirstNode; pNode != pReturnXML->plSubXML->pLastNode; pNode = pNode->pNextNode) { DumpJsVar(pNode->pValue, &hConnection, TRUE, TRUE); } httpAddBodyString(hConnection, "\";\n"); pcFun = httpGetString(pList, "OnJs"); if (pcFun != NULL && pcFun[0] != '\0') { httpAddBodyString(hConnection, pcFun); httpAddBodyString(hConnection, "(this);\n"); } } else if (strcmp(pReturnXML->pcName, "JsObj") == 0) { const char *pcFun; httpAddBodyString(hConnection, pcName); httpAddBodyString(hConnection, "=\n{\n"); for (pNode = pReturnXML->plSubXML->pFirstNode; pNode != pReturnXML->plSubXML->pLastNode; pNode = pNode->pNextNode) { DumpJsObj(pNode->pValue, &hConnection, 1, 0); } httpAddBodyString(hConnection, "};\n"); pcFun = httpGetString(pList, "OnJs"); if (pcFun != NULL && pcFun[0] != '\0') { httpAddBodyString(hConnection, pcFun); httpAddBodyString(hConnection, "(this);\n"); } } else if (strcmp(pReturnXML->pcName, "XML") == 0) { httpDumpXML(pReturnXML, Dump_HttpAdd, (void *)&hConnection); pcMimeType = "text/xml"; } httpDeleteXML(pReturnXML); } httpSetHeader(hConnection, 200, "OK", "", G_PC_NO_CACHE_HEADER, pcMimeType, TRUE); }
PUBLIC ssize espRenderError(HttpConn *conn, int status, cchar *fmt, ...) { va_list args; HttpRx *rx; ssize written; cchar *msg, *title, *text; va_start(args, fmt); rx = conn->rx; written = 0; if (!httpIsFinalized(conn)) { if (status == 0) { status = HTTP_CODE_INTERNAL_SERVER_ERROR; } title = sfmt("Request Error for \"%s\"", rx->pathInfo); msg = mprEscapeHtml(sfmtv(fmt, args)); if (rx->route->flags & HTTP_ROUTE_SHOW_ERRORS) { text = sfmt(\ "<!DOCTYPE html>\r\n<html>\r\n<head><title>%s</title></head>\r\n" \ "<body>\r\n<h1>%s</h1>\r\n" \ " <pre>%s</pre>\r\n" \ " <p>To prevent errors being displayed in the browser, " \ " set <b>ShowErrors off</b> in the appweb.conf file.</p>\r\n", \ "</body>\r\n</html>\r\n", title, title, msg); httpSetHeader(conn, "Content-Type", "text/html"); written += espRenderString(conn, text); espFinalize(conn); mprTrace(4, "Request error (%d) for: \"%s\"", status, rx->pathInfo); } } va_end(args); return written; }
/* function form(uri: String = null, formData: Object = null): Http Issue a POST method with form data */ static EjsHttp *http_form(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { EjsObj *data; if (argc == 2 && !ejsIs(ejs, argv[1], Null)) { /* Prep here to reset the state. The ensures the current headers will be preserved. Users may have called setHeader to define custom headers. Users must call reset if they want to clear prior headers. */ httpPrepClientConn(hp->conn, 1); mprFlushBuf(hp->requestContent); data = argv[1]; if (ejsGetLength(ejs, data) > 0) { prepForm(ejs, hp, NULL, data); } else { mprPutStringToBuf(hp->requestContent, ejsToMulti(ejs, data)); } mprAddNullToBuf(hp->requestContent); httpSetHeader(hp->conn, "Content-Type", "application/x-www-form-urlencoded"); /* Ensure this gets recomputed */ httpRemoveHeader(hp->conn, "Content-Length"); } return startHttpRequest(ejs, hp, "POST", argc, argv); }
/* This is called twice: once for TX and once for RX */ static int matchRange(HttpStream *stream, HttpRoute *route, int dir) { assert(stream->rx); httpSetHeader(stream, "Accept-Ranges", "bytes"); if ((dir & HTTP_STAGE_TX) && stream->tx->outputRanges) { return HTTP_ROUTE_OK; } return HTTP_ROUTE_OMIT_FILTER; }
PUBLIC void httpSetContentLength(HttpConn *conn, MprOff length) { HttpTx *tx; tx = conn->tx; if (tx->flags & HTTP_TX_HEADERS_CREATED) { return; } tx->length = length; httpSetHeader(conn, "Content-Length", "%Ld", tx->length); }
/* Set a http header. Overwrite if present. */ void espSetHeader(HttpConn *conn, cchar *key, cchar *fmt, ...) { va_list vargs; mprAssert(key && *key); mprAssert(fmt && *fmt); va_start(vargs, fmt); httpSetHeader(conn, key, sfmt(fmt, vargs)); va_end(vargs); }
/* Respond to the request by asking for a client login */ PUBLIC void httpDigestLogin(HttpConn *conn) { HttpAuth *auth; char *nonce, *opaque; auth = conn->rx->route->auth; nonce = createDigestNonce(conn, conn->http->secret, auth->realm); /* Opaque is unused, set to anything */ opaque = "799d5"; if (smatch(auth->qop, "none")) { httpSetHeader(conn, "WWW-Authenticate", "Digest realm=\"%s\", nonce=\"%s\"", auth->realm, nonce); } else { /* Value of null defaults to "auth" */ httpSetHeader(conn, "WWW-Authenticate", "Digest realm=\"%s\", domain=\"%s\", " "qop=\"auth\", nonce=\"%s\", opaque=\"%s\", algorithm=\"MD5\", stale=\"FALSE\"", auth->realm, conn->host->name, nonce, opaque); } httpSetContentType(conn, "text/plain"); httpError(conn, HTTP_CODE_UNAUTHORIZED, "Access Denied. Login required"); }
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); }
int Http_TestSpeed(HTTPCONNECTION hConnection, void *pParam) { LIST *pList; httpSetSendDataOverFun(hConnection, FillTestSpeedData, NULL); httpSetKeepAliveMode(hConnection, FALSE); httpSetHeader(hConnection, 200, "OK", "", g_cNoCacheHeader, "application/octet-stream", FALSE); //diag_printf("Fill data end\n"); return 0; }
int Http_SendRedirectRequest(HTTPCONNECTION hConnection, LIST *pQueryList) { char acExtraHeader[256]; const char *pcUrl; pcUrl = httpGetString(pQueryList, "RedirectUrl"); if (pcUrl != NULL && pcUrl[0] != '\0') { ClearHttpSendData(hConnection); sprintf(acExtraHeader, "Location: %s\r\n", pcUrl); httpSetHeader(hConnection, 302, "OK", "", acExtraHeader, "text/html", TRUE); return 0; } else return 1; }
bool simpleForm(MprTestGroup *gp, char *uri, char *formData, int expectStatus) { HttpConn *conn; MprOff contentLen; ssize len; int status; contentLen = 0; if (expectStatus <= 0) { expectStatus = 200; } if (startRequest(gp, "POST", uri) < 0) { return 0; } conn = getConn(gp); if (formData) { httpSetHeader(conn, "Content-Type", "application/x-www-form-urlencoded"); len = slen(formData); if (httpWrite(conn->writeq, formData, len) != len) { return MPR_ERR_CANT_WRITE; } } httpFinalizeOutput(conn); if (httpWait(conn, HTTP_STATE_COMPLETE, -1) < 0) { return MPR_ERR_CANT_READ; } status = httpGetStatus(conn); if (status != expectStatus) { mprLog("appweb test form", 0, "Client failed for %s, response code: %d, msg %s", uri, status, httpGetStatusMessage(conn)); return 0; } gp->content = httpReadString(conn); contentLen = httpGetContentLength(conn); if (! tassert(gp->content != 0 && contentLen > 0)) { return 0; } return 1; }
/* This is called to setup for a HTTP PUT request. It is called before receiving the post data via incomingFileData */ static void handlePutRequest(HttpQueue *q) { HttpConn *conn; HttpTx *tx; MprFile *file; char *path; assure(q->pair->queueData == 0); conn = q->conn; tx = conn->tx; assure(tx->filename); assure(tx->fileInfo.checked); path = tx->filename; if (tx->outputRanges) { /* Open an existing file with fall-back to create */ if ((file = mprOpenFile(path, O_BINARY | O_WRONLY, 0644)) == 0) { if ((file = mprOpenFile(path, O_CREAT | O_TRUNC | O_BINARY | O_WRONLY, 0644)) == 0) { httpError(conn, HTTP_CODE_INTERNAL_SERVER_ERROR, "Can't create the put URI"); return; } } else { mprSeekFile(file, SEEK_SET, 0); } } else { if ((file = mprOpenFile(path, O_CREAT | O_TRUNC | O_BINARY | O_WRONLY, 0644)) == 0) { httpError(conn, HTTP_CODE_INTERNAL_SERVER_ERROR, "Can't create the put URI"); return; } } if (!tx->fileInfo.isReg) { httpSetHeader(conn, "Location", conn->rx->uri); } httpSetStatus(conn, tx->fileInfo.isReg ? HTTP_CODE_NO_CONTENT : HTTP_CODE_CREATED); q->pair->queueData = (void*) file; }
ssize espRenderError(HttpConn *conn, int status, cchar *fmt, ...) { va_list args; HttpRx *rx; EspReq *req; EspRoute *eroute; ssize written; cchar *msg, *title, *text; va_start(args, fmt); rx = conn->rx; req = conn->data; eroute = req->eroute; written = 0; if (!httpIsFinalized(conn)) { if (status == 0) { status = HTTP_CODE_INTERNAL_SERVER_ERROR; } title = sfmt("Request Error for \"%s\"", rx->pathInfo); msg = mprEscapeHtml(sfmtv(fmt, args)); if (eroute->showErrors) { text = sfmt(\ "<!DOCTYPE html>\r\n<html>\r\n<head><title>%s</title></head>\r\n" \ "<body>\r\n<h1>%s</h1>\r\n" \ " <pre>%s</pre>\r\n" \ " <p>To prevent errors being displayed in the browser, " \ " set <b>log.showErrors</b> to false in the ejsrc file.</p>\r\n", \ "</body>\r\n</html>\r\n", title, title, msg); httpSetHeader(conn, "Content-Type", "text/html"); written += espRenderString(conn, text); espFinalize(conn); mprLog(4, "Request error (%d) for: \"%s\"", status, rx->pathInfo); } } va_end(args); return written; }
/* Run the handler. This is called when all input data has been received. */ static void processSimple(HttpQueue *q) { HttpConn *conn; conn = q->conn; httpSetHeader(conn, 0, "Last-Modified", conn->http->currentDate); /* Create the empty header packet. This will be filled in by the downstream network connector stage. */ httpPutForService(q, httpCreateHeaderPacket(conn), 0); /* Generate some dynamic data. If you generate a lot, this will buffer up to a configured maximum. If that limit is exceeded, the packet will be sent downstream and the response headers will be created. */ httpWrite(q, "Hello World"); /* Send an end of data packet */ httpPutForService(q, httpCreateEndPacket(conn), 1); }
int Http_PostDataGet(HTTPCONNECTION hConnection, int *piPostState, char **ppcPostBuf, int *piPostBufLen, int *piPostDataLen, char *pcFillData, int iFillDataLen, int iIsMoreData/*bool*/, void *pParam/*other parameter for extend use*/) { if (*piPostBufLen == 0) {//初始状态, 尚未接收任何post数据 *piPostBufLen = httpGetContentLength(hConnection) + 8; if (*piPostBufLen < 8) *piPostBufLen = 8; if (*piPostBufLen < MAX_POST_LENGTH) { if ((*ppcPostBuf = (char *)malloc(*piPostBufLen)) == NULL) { PRINT_MEM_OUT; } *piPostDataLen = 0; } } if (*ppcPostBuf == NULL) {//非初始状态, 分配失败或数据太长 if (iIsMoreData == 0) { httpAddBodyString(hConnection, "Too many post data or not enmough memory."); httpSetHeader(hConnection, 200, "OK", NULL, NULL, "text/plain", TRUE); return 1; } } else {//非初始状态, 分配成功 if (*piPostDataLen + iFillDataLen < *piPostBufLen) { memcpy(*ppcPostBuf + *piPostDataLen, pcFillData, iFillDataLen); *piPostDataLen += iFillDataLen; } if (iIsMoreData == 0) { (*ppcPostBuf)[*piPostDataLen] = '\0'; if (*piPostDataLen >= 1 && (*ppcPostBuf)[*piPostDataLen - 1] == '\n') { (*ppcPostBuf)[--*piPostDataLen] = '\0'; if (*piPostDataLen >= 1 && (*ppcPostBuf)[*piPostDataLen - 1] == '\r') (*ppcPostBuf)[--*piPostDataLen] = '\0'; } if (pParam != NULL) { CO_PARAM_T *pCoParam = (CO_PARAM_T *)pParam; (*pCoParam->funCmdCaller)(hConnection, *ppcPostBuf, *piPostDataLen, (pCoParam->pParam)); free(pCoParam); httpSetPostDataFun(hConnection, NULL, NULL); } free(*ppcPostBuf); *ppcPostBuf = NULL; *piPostDataLen = *piPostBufLen = 0; return 1; } } return 1; }
/* Set headers for httpWriteHeaders. This defines standard headers. */ static void setHeaders(HttpConn *conn, HttpPacket *packet) { HttpRx *rx; HttpTx *tx; HttpRoute *route; HttpRange *range; MprKeyValue *item; MprOff length; cchar *mimeType; int next; assert(packet->flags == HTTP_PACKET_HEADER); rx = conn->rx; tx = conn->tx; route = rx->route; /* Mandatory headers that must be defined here use httpSetHeader which overwrites existing values. */ httpAddHeaderString(conn, "Date", conn->http->currentDate); if (tx->ext && route) { if ((mimeType = (char*) mprLookupMime(route->mimeTypes, tx->ext)) != 0) { if (conn->error) { httpAddHeaderString(conn, "Content-Type", "text/html"); } else { httpAddHeaderString(conn, "Content-Type", mimeType); } } } if (tx->etag) { httpAddHeader(conn, "ETag", "%s", tx->etag); } length = tx->length > 0 ? tx->length : 0; if (rx->flags & HTTP_HEAD) { conn->tx->flags |= HTTP_TX_NO_BODY; httpDiscardData(conn, HTTP_QUEUE_TX); if (tx->chunkSize <= 0) { httpAddHeader(conn, "Content-Length", "%Ld", length); } } else if (tx->length < 0 && tx->chunkSize > 0) { httpSetHeaderString(conn, "Transfer-Encoding", "chunked"); } else if (conn->endpoint) { /* Server must not emit a content length header for 1XX, 204 and 304 status */ if (!((100 <= tx->status && tx->status <= 199) || tx->status == 204 || tx->status == 304 || tx->flags & HTTP_TX_NO_LENGTH)) { httpAddHeader(conn, "Content-Length", "%Ld", length); } } else if (tx->length > 0) { /* client with body */ httpAddHeader(conn, "Content-Length", "%Ld", length); } if (tx->outputRanges) { if (tx->outputRanges->next == 0) { range = tx->outputRanges; if (tx->entityLength > 0) { httpSetHeader(conn, "Content-Range", "bytes %Ld-%Ld/%Ld", range->start, range->end - 1, tx->entityLength); } else { httpSetHeader(conn, "Content-Range", "bytes %Ld-%Ld/*", range->start, range->end - 1); } } else { httpSetHeader(conn, "Content-Type", "multipart/byteranges; boundary=%s", tx->rangeBoundary); } httpSetHeader(conn, "Accept-Ranges", "bytes"); } if (conn->endpoint) { if (!(route->flags & HTTP_ROUTE_STEALTH)) { httpAddHeaderString(conn, "Server", conn->http->software); } /* If keepAliveCount == 1 */ if (--conn->keepAliveCount > 0) { assert(conn->keepAliveCount >= 1); httpAddHeaderString(conn, "Connection", "Keep-Alive"); httpAddHeader(conn, "Keep-Alive", "timeout=%Ld, max=%d", conn->limits->inactivityTimeout / 1000, conn->keepAliveCount); } else { /* Tell the peer to close the connection */ httpAddHeaderString(conn, "Connection", "close"); } if (route->flags & HTTP_ROUTE_CORS) { setCorsHeaders(conn); } /* Apply response headers */ for (ITERATE_ITEMS(route->headers, item, next)) { if (item->flags == HTTP_ROUTE_ADD_HEADER) { httpAddHeaderString(conn, item->key, item->value); } else if (item->flags == HTTP_ROUTE_APPEND_HEADER) { httpAppendHeaderString(conn, item->key, item->value); } else if (item->flags == HTTP_ROUTE_REMOVE_HEADER) { httpRemoveHeader(conn, item->key); } else if (item->flags == HTTP_ROUTE_SET_HEADER) { httpSetHeaderString(conn, item->key, item->value); } } } }
/* Test if the request matches. This may delegate the request to the dirHandler if a directory listing is required. */ static int matchFileHandler(HttpConn *conn, HttpRoute *route, int dir) { HttpRx *rx; HttpTx *tx; HttpUri *prior; MprPath *info, zipInfo; cchar *index; char *path, *pathInfo, *uri, *zipfile; int next; printf("\n matchFileHandler \n"); rx = conn->rx; tx = conn->tx; rx = conn->rx; prior = rx->parsedUri; info = &tx->fileInfo; httpMapFile(conn, route); assure(info->checked); if (rx->flags & (HTTP_DELETE | HTTP_PUT)) { return HTTP_ROUTE_OK; } if (info->isDir) { /* Manage requests for directories */ if (!sends(rx->pathInfo, "/")) { /* Append "/" and do an external redirect */ pathInfo = sjoin(rx->pathInfo, "/", NULL); uri = httpFormatUri(prior->scheme, prior->host, prior->port, pathInfo, prior->reference, prior->query, 0); httpRedirect(conn, HTTP_CODE_MOVED_PERMANENTLY, uri); return HTTP_ROUTE_OK; } if (route->indicies) { /* Ends with a "/" so do internal redirection to an index file */ for (ITERATE_ITEMS(route->indicies, index, next)) { /* Internal directory redirections. Transparently append index. Test indicies in order. */ path = mprJoinPath(tx->filename, index); if (mprPathExists(path, R_OK)) { pathInfo = sjoin(rx->scriptName, rx->pathInfo, index, NULL); uri = httpFormatUri(prior->scheme, prior->host, prior->port, pathInfo, prior->reference, prior->query, 0); httpSetUri(conn, uri); tx->filename = path; tx->ext = httpGetExt(conn); mprGetPathInfo(tx->filename, info); return HTTP_ROUTE_REROUTE; } } } /* If a directory, test if a directory listing should be rendered. If so, delegate to the dirHandler. Cannot use the sendFile handler and must use the netConnector. */ if (info->isDir && maRenderDirListing(conn)) { tx->handler = conn->http->dirHandler; tx->connector = conn->http->netConnector; return HTTP_ROUTE_OK; } } if (!info->valid && (route->flags & HTTP_ROUTE_GZIP) && rx->acceptEncoding && strstr(rx->acceptEncoding, "gzip") != 0) { /* If the route accepts zipped data and a zipped file exists, then transparently respond with it. */ zipfile = sfmt("%s.gz", tx->filename); if (mprGetPathInfo(zipfile, &zipInfo) == 0) { tx->filename = zipfile; tx->fileInfo = zipInfo; httpSetHeader(conn, "Content-Encoding", "gzip"); } } if (rx->flags & (HTTP_GET | HTTP_HEAD | HTTP_POST) && info->valid && !info->isDir && tx->length < 0) { /* The sendFile connector is optimized on some platforms to use the sendfile() system call. Set the entity length for the sendFile connector to utilize. */ httpSetEntityLength(conn, tx->fileInfo.size); } printf("\n-------------------------\n"); printf("tx->filename:\t%s",tx->filename); printf("\n-------------------------\n"); return HTTP_ROUTE_OK; }
/* Set the request as being a multipart mime upload. This defines the content type and defines a multipart mime boundary */ PUBLIC void httpEnableUpload(HttpStream *stream) { stream->boundary = sfmt("--BOUNDARY--%lld", stream->http->now); httpSetHeader(stream, "Content-Type", "multipart/form-data; boundary=%s", &stream->boundary[2]); }
static void openFileHandler(HttpQueue *q) { HttpRx *rx; HttpTx *tx; HttpRoute *route; HttpConn *conn; MprPath *info; char *date; conn = q->conn; tx = conn->tx; rx = conn->rx; route = rx->route; info = &tx->fileInfo; if (rx->flags & (HTTP_PUT | HTTP_DELETE)) { if (!(route->flags & HTTP_ROUTE_PUT_DELETE_METHODS)) { httpError(q->conn, HTTP_CODE_BAD_METHOD, "The \"%s\" method is not supported by file handler", rx->method); } } else { if (rx->flags & (HTTP_GET | HTTP_HEAD | HTTP_POST)) { if (!(info->valid || info->isDir)) { if (rx->referrer) { mprLog(2, "fileHandler: Cannot find filename %s from referrer %s", tx->filename, rx->referrer); } else { mprLog(2, "fileHandler: Cannot find filename %s", tx->filename); } httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot find %s", rx->uri); } else if (info->valid) { if (!tx->etag) { /* Set the etag for caching in the client */ tx->etag = sfmt("\"%Lx-%Lx-%Lx\"", (int64) info->inode, (int64) info->size, (int64) info->mtime); } } } if (rx->flags & (HTTP_GET | HTTP_HEAD | HTTP_POST) && !conn->error) { if (tx->fileInfo.valid && tx->fileInfo.mtime) { // TODO - OPT could cache this date = httpGetDateString(&tx->fileInfo); httpSetHeader(conn, "Last-Modified", date); } if (httpContentNotModified(conn)) { httpSetStatus(conn, HTTP_CODE_NOT_MODIFIED); httpOmitBody(conn); tx->length = -1; } if (!tx->fileInfo.isReg && !tx->fileInfo.isLink) { httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot locate document: %s", rx->uri); } else if (tx->fileInfo.size > conn->limits->transmissionBodySize) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TOO_LARGE, "Http transmission aborted. File size exceeds max body of %,Ld bytes", conn->limits->transmissionBodySize); } else if (!(tx->connector == conn->http->sendConnector)) { /* If using the net connector, open the file if a body must be sent with the response. The file will be automatically closed when the request completes. */ if (!(tx->flags & HTTP_TX_NO_BODY)) { tx->file = mprOpenFile(tx->filename, O_RDONLY | O_BINARY, 0); if (tx->file == 0) { 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 from %s", tx->filename); } } } } } else if (rx->flags & (HTTP_OPTIONS | HTTP_TRACE)) { if (route->flags & HTTP_ROUTE_PUT_DELETE_METHODS) { httpHandleOptionsTrace(q->conn, "DELETE,GET,HEAD,POST,PUT"); } else { httpHandleOptionsTrace(q->conn, "GET,HEAD,POST"); } } } }
/* Set the request as being a multipart mime upload. This defines the content type and defines a multipart mime boundary */ PUBLIC void httpEnableUpload(HttpConn *conn) { conn->boundary = sfmt("--BOUNDARY--%lld", conn->http->now); httpSetHeader(conn, "Content-Type", "multipart/form-data; boundary=%s", &conn->boundary[2]); }
/* 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); }