/* * Manage requests to directories. This will either do an external redirect back to the browser or do an internal * (transparent) redirection and serve different content back to the browser. This routine may modify the requested * URI and/or the request handler. */ static void processDirectory(MaConn *conn, bool *rescan) { MaRequest *req; MaResponse *resp; MprFileInfo *info; char path[MPR_MAX_FNAME], urlBuf[MPR_MAX_FNAME], *index; int len; req = conn->request; resp = conn->response; info = &resp->fileInfo; mprAssert(info->isDir); index = req->dir->indexName; if (req->url[strlen(req->url) - 1] == '/') { /* * Internal directory redirections */ len = (int) strlen(resp->filename); if (resp->filename[len - 1] == '/') { resp->filename[len - 1] = '\0'; } path[0] = '\0'; mprAssert(resp->filename && *resp->filename); mprAssert(index && *index); mprStrcat(path, sizeof(path), NULL, resp->filename, "/", index, NULL); if (mprAccess(resp, path, R_OK)) { /* * Index file exists, so do an internal redirect to it. Client will not be aware of this happening. * Must rematch the handler on return. */ maSetRequestUri(conn, addIndex(conn, urlBuf, sizeof(urlBuf), index)); resp->filename = mprStrdup(resp, path); mprGetFileInfo(conn, resp->filename, &resp->fileInfo); resp->extension = getExtension(conn); if ((resp->mimeType = (char*) maLookupMimeType(conn->host, resp->extension)) == 0) { resp->mimeType = (char*) "text/html"; } *rescan = 1; } return; } /* * External redirect. Ask the client to re-issue a request for a new location. See if an index exists and if so, * construct a new location for the index. If the index can't be accessed, just append a "/" to the URI and redirect. */ if (req->parsedUri->query && req->parsedUri->query[0]) { mprSprintf(path, sizeof(path), "%s/%s?%s", req->url, index, req->parsedUri->query); } else { mprSprintf(path, sizeof(path), "%s/%s", req->url, index); } if (!mprAccess(resp, path, R_OK)) { mprSprintf(path, sizeof(path), "%s/", req->url); } maRedirect(conn, MPR_HTTP_CODE_MOVED_PERMANENTLY, path); resp->handler = conn->http->passHandler; }
static void upload(MaQueue *q) { MaConn *conn; char *sw; char *newLocation; int responseStatus; conn = q->conn; newLocation = 0; responseStatus = 0; sw = (char*) strstr(maGetFormVar(conn, "QUERY_STRING", ""), "SWITCHES="); if (sw) { sw = mprStrdup(q, sw + 9); mprUrlDecode(sw, (int) strlen(sw) + 1, sw); if (*sw == '-') { if (sw[1] == 'l') { newLocation = sw + 3; } else if (sw[1] == 's') { responseStatus = atoi(sw + 3); } } } maSetResponseCode(conn, 200); maSetResponseMimeType(conn, "text/html"); maDontCacheResponse(conn); /* * Test writing headers. The Server header overwrote the "Server" header * * maSetHeader(conn, "MyCustomHeader: true"); * maSetHeader(conn, "Server: private"); */ if (maGetCookies(conn) == 0) { maSetCookie(conn, "appwebTest", "Testing can be fun", 43200, "/", 0); } if (newLocation) { maRedirect(conn, 302, newLocation); } else if (responseStatus) { maFailRequest(conn, responseStatus, "Custom Status"); } else { maWrite(q, "<HTML><TITLE>egiProgram: EGI Output</TITLE><BODY>\r\n"); printRequestHeaders(q); printQueryData(q); printBodyData(q); maWrite(q, "</BODY></HTML>\r\n"); } if (sw) { mprFree(sw); } }
static void myEgi(MaQueue *q) { MaConn *conn; conn = q->conn; maSetResponseCode(conn, 200); maWrite(q, "<HTML><TITLE>simpleEgi</TITLE><BODY>\r\n"); maWrite(q, "<p>Name: %s</p>\n", maGetFormVar(conn, "name", "-")); maWrite(q, "<p>Address: %s</p>\n", maGetFormVar(conn, "address", "-")); maWrite(q, "</BODY></HTML>\r\n"); #if POSSIBLE /* * Useful things to do in egi forms */ maSetResponseCode(conn, 200); maSetResponseMimeType(conn, "text/plain"); maDontCacheResponse(conn); maRedirect(conn, 302, "/myURl"); maFailRequest(conn, 409, "My message : %d", 5); #endif }
static void reportFailure(MaConn *conn, int code, cchar *fmt, va_list args) { MaResponse *resp; MaRequest *req; cchar *url, *status; char *emsg, *msg, *filename; mprAssert(fmt); if (conn->requestFailed) { return; } conn->requestFailed = 1; if (fmt == 0) { fmt = ""; } req = conn->request; resp = conn->response; maDontCacheResponse(conn); msg = mprVasprintf(conn, MA_BUFSIZE, fmt, args); if (resp == 0 || req == 0) { mprLog(conn, 2, "\"%s\", code %d: %s.", mprGetHttpCodeString(conn, code), code, msg); } else { resp->code = code; filename = resp->filename ? resp->filename : 0; /* 711 is a custom error used by the test suite. */ if (code != 711) { mprLog(resp, 2, "Error: \"%s\", code %d for URI \"%s\", file \"%s\": %s.", mprGetHttpCodeString(conn, code), code, req->url ? req->url : "", filename ? filename : "", msg); } /* * Use an error document rather than standard error boilerplate. */ if (req->location) { url = maLookupErrorDocument(req->location, code); if (url && *url) { maRedirect(conn, 302, url); mprFree(msg); return; } } /* * If the headers have already been filled, this alternate response body will be ignored. */ if (resp->altBody == 0) { status = mprGetHttpCodeString(conn, code); /* * For security, escape the message */ emsg = mprEscapeHtml(resp, msg); resp->altBody = mprAsprintf(resp, -1, "<!DOCTYPE html>\r\n" "<html><head><title>Document Error: %s</title></head>\r\n" "<body><h2>Access Error: %d -- %s</h2>\r\n<p>%s</p>\r\n</body>\r\n</html>\r\n", status, code, status, emsg); } resp->flags |= MA_RESP_NO_BODY; } mprFree(msg); }
/* * Find the matching handler for a request. If any errors occur, the pass handler is used to pass errors onto the * net/sendfile connectors to send to the client. This routine may rewrite the request URI and may redirect the request. */ void maMatchHandler(MaConn *conn) { MaRequest *req; MaResponse *resp; MaHost *host; MaAlias *alias; MaStage *handler; bool rescan; int loopCount; req = conn->request; resp = conn->response; host = req->host; /* * Find the alias that applies for this url. There is always a catch-all alias for the document root. */ alias = req->alias = maGetAlias(host, req->url); mprAssert(alias); if (alias->redirectCode) { // TODO - what about internal redirects? maRedirect(conn, alias->redirectCode, alias->uri); return; } if (conn->requestFailed || conn->request->method & (MA_REQ_OPTIONS | MA_REQ_TRACE)) { handler = conn->http->passHandler; return; } /* * Get the best (innermost) location block and see if a handler is explicitly set for that location block. * Possibly rewrite the url and retry. */ loopCount = MA_MAX_REWRITE; do { rescan = 0; if ((handler = findLocationHandler(conn)) == 0) { /* * Didn't find a location block handler, so try to match by extension and by handler match() routines. * This may invoke processDirectory which may redirect and thus require reprocessing -- hence the loop. */ handler = findHandlerByExtension(conn); } if (handler && !(handler->flags & MA_STAGE_VIRTUAL)) { if (!mapToFile(conn, &rescan)) { return; } } } while (handler && rescan && loopCount-- > 0); if (handler == 0) { maFailRequest(conn, MPR_HTTP_CODE_BAD_METHOD, "Requested method %s not supported for URL: %s", req->methodName, req->url); handler = conn->http->passHandler; } resp->handler = handler; mprLog(resp, 4, "Select handler: \"%s\" for \"%s\"", handler->name, req->url); setEnv(conn); }
/* Parse the CGI output headers. Sample CGI program: Content-type: text/html <html..... */ static bool parseHeader(MaConn *conn, MprCmd *cmd) { MaResponse *resp; MaQueue *q; MprBuf *buf; char *endHeaders, *headers, *key, *value, *location; int fd, len; resp = conn->response; location = 0; value = 0; buf = mprGetCmdBuf(cmd, MPR_CMD_STDOUT); mprAddNullToBuf(buf); headers = mprGetBufStart(buf); /* Split the headers from the body. */ len = 0; fd = mprGetCmdFd(cmd, MPR_CMD_STDOUT); if ((endHeaders = strstr(headers, "\r\n\r\n")) == NULL) { if ((endHeaders = strstr(headers, "\n\n")) == NULL) { if (fd >= 0 && strlen(headers) < MA_MAX_HEADERS) { /* Not EOF and less than max headers and have not yet seen an end of headers delimiter */ return 0; } } else len = 2; } else { len = 4; } if (endHeaders) { endHeaders[len - 1] = '\0'; endHeaders += len; } /* Want to be tolerant of CGI programs that omit the status line. */ if (strncmp((char*) buf->start, "HTTP/1.", 7) == 0) { if (!parseFirstCgiResponse(conn, cmd)) { /* maFailConnection already called */ return 0; } } if (endHeaders && strchr(mprGetBufStart(buf), ':')) { mprLog(conn, 4, "CGI: parseHeader: header\n%s", headers); while (mprGetBufLength(buf) > 0 && buf->start[0] && (buf->start[0] != '\r' && buf->start[0] != '\n')) { if ((key = getCgiToken(buf, ":")) == 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad header format"); return 0; } value = getCgiToken(buf, "\n"); while (isspace((int) *value)) { value++; } len = (int) strlen(value); while (len > 0 && (value[len - 1] == '\r' || value[len - 1] == '\n')) { value[len - 1] = '\0'; len--; } mprStrLower(key); if (strcmp(key, "location") == 0) { location = value; } else if (strcmp(key, "status") == 0) { maSetResponseCode(conn, atoi(value)); } else if (strcmp(key, "content-type") == 0) { maSetResponseMimeType(conn, value); } else if (strcmp(key, "content-length") == 0) { maSetEntityLength(conn, (MprOff) mprAtoi(value, 10)); resp->chunkSize = 0; } else { /* Now pass all other headers back to the client */ maSetHeader(conn, 0, key, "%s", value); } } buf->start = endHeaders; } if (location) { maRedirect(conn, resp->code, location); q = resp->queue[MA_QUEUE_SEND].nextQ; maPutForService(q, maCreateEndPacket(q), 1); } cmd->userFlags |= MA_CGI_SEEN_HEADER; return 1; }
static void redirect(void *handle, int code, cchar *url) { maRedirect(handle, code, url); }