static void setDefaultHeaders(HttpConn *conn) { HttpAuthType *ap; assert(conn); if (smatch(conn->protocol, "HTTP/1.0")) { conn->http10 = 1; } if (conn->username && conn->authType) { if ((ap = httpLookupAuthType(conn->authType)) != 0) { if ((ap->setAuth)(conn, conn->username, conn->password)) { conn->authRequested = 1; } } } if (conn->port != 80 && conn->port != 443) { httpAddHeader(conn, "Host", "%s:%d", conn->ip, conn->port); } else { httpAddHeaderString(conn, "Host", conn->ip); } httpAddHeaderString(conn, "Accept", "*/*"); if (conn->keepAliveCount > 0) { httpSetHeaderString(conn, "Connection", "Keep-Alive"); } else { httpSetHeaderString(conn, "Connection", "close"); } }
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 void setDefaultHeaders(HttpStream *stream) { HttpAuthType *ap; assert(stream); if (stream->username && stream->authType && ((ap = httpLookupAuthType(stream->authType)) != 0)) { if ((ap->setAuth)(stream, stream->username, stream->password)) { stream->authRequested = 1; } } if (stream->net->protocol < 2) { if (stream->port != 80 && stream->port != 443) { if (schr(stream->ip, ':')) { httpAddHeader(stream, "Host", "[%s]:%d", stream->ip, stream->port); } else { httpAddHeader(stream, "Host", "%s:%d", stream->ip, stream->port); } } else { httpAddHeaderString(stream, "Host", stream->ip); } if (stream->keepAliveCount > 0) { httpSetHeaderString(stream, "Connection", "Keep-Alive"); } else { httpSetHeaderString(stream, "Connection", "close"); } } httpAddHeaderString(stream, "Accept", "*/*"); }
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")) { httpSetHeaderString(conn, header->key, header->value); } else { httpAppendHeaderString(conn, header->key, header->value); } } if (app->text) { httpSetHeader(conn, "Accept", "text/plain"); } if (app->sequence) { static int next = 0; seq = itos(next++); httpSetHeaderString(conn, "X-Http-Seq", seq); } if (app->ranges) { httpSetHeaderString(conn, "Range", app->ranges); } if (app->formData) { httpSetContentType(conn, "application/x-www-form-urlencoded"); } if (setContentLength(conn, files) < 0) { return MPR_ERR_CANT_OPEN; } return 0; }
/* Define headers and create an empty header packet that will be filled later by the pipeline. */ static int setClientHeaders(HttpConn *conn) { HttpAuthType *authType; mprAssert(conn); if (smatch(conn->protocol, "HTTP/1.0")) { conn->http10 = 1; } if (conn->authType && (authType = httpLookupAuthType(conn->authType)) != 0) { (authType->setAuth)(conn); conn->setCredentials = 1; } if (conn->port != 80) { httpAddHeader(conn, "Host", "%s:%d", conn->ip, conn->port); } else { httpAddHeaderString(conn, "Host", conn->ip); } #if UNUSED if (conn->http10 && !smatch(conn->tx->method, "GET")) { conn->keepAliveCount = 0; } #endif if (conn->keepAliveCount > 0) { httpSetHeaderString(conn, "Connection", "Keep-Alive"); } else { httpSetHeaderString(conn, "Connection", "close"); } return 0; }
/* Start the request (and complete it) */ static void startDir(HttpQueue *q) { HttpConn *conn; HttpTx *tx; HttpRx *rx; MprList *list; MprDirEntry *dp; HttpDir *dir; cchar *path; uint nameSize; int next; conn = q->conn; rx = conn->rx; tx = conn->tx; if ((dir = conn->reqData) == 0) { httpError(conn, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot get directory listing"); return; } assert(tx->filename); if (!(rx->flags & (HTTP_GET | HTTP_HEAD))) { httpError(conn, HTTP_CODE_BAD_METHOD, "Bad method"); return; } httpSetContentType(conn, "text/html"); httpSetHeaderString(conn, "Cache-Control", "no-cache"); httpSetHeaderString(conn, "Last-Modified", conn->http->currentDate); parseQuery(conn); if ((list = mprGetPathFiles(tx->filename, MPR_PATH_RELATIVE)) == 0) { httpWrite(q, "<h2>Cannot get file list</h2>\r\n"); outputFooter(q); return; } if (dir->pattern) { filterDirList(conn, list); } sortList(conn, list); /* Get max filename size */ nameSize = 0; for (next = 0; (dp = mprGetNextItem(list, &next)) != 0; ) { nameSize = max((int) strlen(dp->name), nameSize); } nameSize = max(nameSize, 22); path = rx->route->prefix ? sjoin(rx->route->prefix, rx->pathInfo, NULL) : rx->pathInfo; outputHeader(q, path, nameSize); for (next = 0; (dp = mprGetNextItem(list, &next)) != 0; ) { outputLine(q, dp, tx->filename, nameSize); } outputFooter(q); httpFinalize(conn); }
static void processDir(HttpQueue *q) { HttpConn *conn; HttpTx *tx; HttpRx *rx; MprList *list; MprDirEntry *dp; Dir *dir; uint nameSize; int next; conn = q->conn; rx = conn->rx; tx = conn->tx; dir = conn->data; mprLog(5, "processDir"); mprAssert(tx->filename); httpSetHeaderString(conn, "Cache-Control", "no-cache"); httpSetHeaderString(conn, "Last-Modified", conn->http->currentDate); parseQuery(conn); list = mprGetPathFiles(tx->filename, 1); if (list == 0) { httpWrite(q, "<h2>Can't get file list</h2>\r\n"); outputFooter(q); return; } if (dir->pattern) { filterDirList(conn, list); } sortList(conn, list); /* Get max filename */ nameSize = 0; for (next = 0; (dp = mprGetNextItem(list, &next)) != 0; ) { nameSize = max((int) strlen(dp->name), nameSize); } nameSize = max(nameSize, 22); outputHeader(q, rx->pathInfo, nameSize); for (next = 0; (dp = mprGetNextItem(list, &next)) != 0; ) { outputLine(q, dp, tx->filename, nameSize); } outputFooter(q); httpFinalize(conn); }
static void myaction(HttpConn *conn) { HttpQueue *q; q = conn->writeq; /* Set the HTTP response status */ httpSetStatus(conn, 200); /* Add desired headers. "Set" will overwrite, add will create if not already defined. */ httpAddHeaderString(conn, "Content-Type", "text/plain"); httpSetHeaderString(conn, "Cache-Control", "no-cache"); httpWrite(q, "<html><title>simpleAction</title><body>\r\n"); httpWrite(q, "<p>Name: %s</p>\n", httpGetParam(conn, "name", "-")); httpWrite(q, "<p>Address: %s</p>\n", httpGetParam(conn, "address", "-")); httpWrite(q, "</body></html>\r\n"); /* Call finalize output and close the request. Delay closing if you want to do asynchronous output and close later. */ httpFinalize(conn); #if POSSIBLE /* Useful things to do in actions */ httpRedirect(conn, 302, "/other-uri"); httpError(conn, 409, "My message : %d", 5); #endif }
/* Set a http header. Overwrite if present. */ PUBLIC void espSetHeader(HttpConn *conn, cchar *key, cchar *fmt, ...) { va_list vargs; assert(key && *key); assert(fmt && *fmt); va_start(vargs, fmt); httpSetHeaderString(conn, key, sfmtv(fmt, vargs)); va_end(vargs); }
/* 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; cchar *path; assert(q->pair->queueData == 0); conn = q->conn; tx = conn->tx; assert(tx->filename); assert(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, "Cannot 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, "Cannot create the put URI"); return; } } if (!tx->fileInfo.isReg) { httpSetHeaderString(conn, "Location", conn->rx->uri); } httpSetStatus(conn, tx->fileInfo.isReg ? HTTP_CODE_NO_CONTENT : HTTP_CODE_CREATED); q->pair->queueData = (void*) file; }
/* function setHeader(key: String, value: String, overwrite: Boolean = true): Void */ static EjsObj *http_setHeader(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { HttpConn *conn; cchar *key, *value; bool overwrite; assert(argc >= 2); conn = hp->conn; if (conn->state >= HTTP_STATE_CONNECTED) { ejsThrowArgError(ejs, "Cannot update request headers once the request has started"); return 0; } key = ejsToMulti(ejs, argv[0]); value = ejsToMulti(ejs, argv[1]); overwrite = (argc == 3) ? ejsGetBoolean(ejs, argv[2]) : 1; if (overwrite) { httpSetHeaderString(hp->conn, key, value); } else { httpAppendHeaderString(hp->conn, key, value); } return 0; }
void espSetHeaderString(HttpConn *conn, cchar *key, cchar *value) { httpSetHeaderString(conn, key, value); }
PUBLIC void httpHandleOptions(HttpStream *stream) { httpSetHeaderString(stream, "Allow", httpGetRouteMethods(stream->rx->route)); httpFinalize(stream); }
/* Parse the CGI output headers. Sample CGI program output: Content-type: text/html <html..... */ static bool parseCgiHeaders(Cgi *cgi, HttpPacket *packet) { HttpConn *conn; MprBuf *buf; char *endHeaders, *headers, *key, *value; ssize blen; int len; conn = cgi->conn; value = 0; buf = packet->content; headers = mprGetBufStart(buf); blen = mprGetBufLength(buf); /* Split the headers from the body. Add null to ensure we can search for line terminators. */ len = 0; if ((endHeaders = sncontains(headers, "\r\n\r\n", blen)) == NULL) { if ((endHeaders = sncontains(headers, "\n\n", blen)) == NULL) { if (mprGetCmdFd(cgi->cmd, MPR_CMD_STDOUT) >= 0 && strlen(headers) < ME_MAX_HEADERS) { /* Not EOF and less than max headers and have not yet seen an end of headers delimiter */ return 0; } } len = 2; } else { len = 4; } if (endHeaders > buf->end) { assert(endHeaders <= buf->end); return 0; } 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(cgi, packet)) { /* httpError already called */ return 0; } } if (endHeaders && strchr(mprGetBufStart(buf), ':')) { while (mprGetBufLength(buf) > 0 && buf->start[0] && (buf->start[0] != '\r' && buf->start[0] != '\n')) { if ((key = getCgiToken(buf, ":")) == 0) { key = "Bad Header"; } value = getCgiToken(buf, "\n"); while (isspace((uchar) *value)) { value++; } len = (int) strlen(value); while (len > 0 && (value[len - 1] == '\r' || value[len - 1] == '\n')) { value[len - 1] = '\0'; len--; } key = slower(key); if (strcmp(key, "location") == 0) { cgi->location = value; } else if (strcmp(key, "status") == 0) { httpSetStatus(conn, atoi(value)); } else if (strcmp(key, "content-type") == 0) { httpSetHeaderString(conn, "Content-Type", value); } else if (strcmp(key, "content-length") == 0) { httpSetContentLength(conn, (MprOff) stoi(value)); httpSetChunkSize(conn, 0); } else { /* Now pass all other headers back to the client */ key = ssplit(key, ":\r\n\t ", NULL); httpSetHeaderString(conn, key, value); } } buf->start = endHeaders; } return 1; }
void espShowRequest(HttpConn *conn) { MprHash *env; MprKey *kp; MprBuf *buf; HttpRx *rx; HttpQueue *q; cchar *query; char qbuf[MPR_MAX_STRING], **keys, *value; int i, numKeys; rx = conn->rx; httpSetHeaderString(conn, "Cache-Control", "no-cache"); httpCreateCGIParams(conn); espRender(conn, "\r\n"); /* Query */ if ((query = espGetQueryString(conn)) != 0) { scopy(qbuf, sizeof(qbuf), query); if ((numKeys = getParams(&keys, qbuf, (int) strlen(qbuf))) > 0) { for (i = 0; i < (numKeys * 2); i += 2) { value = keys[i+1]; espRender(conn, "QUERY %s=%s\r\n", keys[i], value ? value: "null"); } } espRender(conn, "\r\n"); } /* Http Headers */ env = espGetHeaderHash(conn); for (ITERATE_KEYS(env, kp)) { espRender(conn, "HEADER %s=%s\r\n", kp->key, kp->data ? kp->data: "null"); } espRender(conn, "\r\n"); /* Server vars */ for (ITERATE_KEYS(conn->rx->svars, kp)) { espRender(conn, "SERVER %s=%s\r\n", kp->key, kp->data ? kp->data: "null"); } espRender(conn, "\r\n"); /* Form vars */ if ((env = espGetParams(conn)) != 0) { for (ITERATE_KEYS(env, kp)) { espRender(conn, "FORM %s=%s\r\n", kp->key, kp->data ? kp->data: "null"); } espRender(conn, "\r\n"); } /* Body */ q = conn->readq; if (q->first && rx->bytesRead > 0 && scmp(rx->mimeType, "application/x-www-form-urlencoded") == 0) { buf = q->first->content; mprAddNullToBuf(buf); if ((numKeys = getParams(&keys, mprGetBufStart(buf), (int) mprGetBufLength(buf))) > 0) { for (i = 0; i < (numKeys * 2); i += 2) { value = keys[i+1]; espRender(conn, "BODY %s=%s\r\n", keys[i], value ? value: "null"); } } espRender(conn, "\r\n"); } }
/* 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); } } } }
static int openFileHandler(HttpQueue *q) { HttpRx *rx; HttpTx *tx; HttpConn *conn; MprPath *info; char *date, dbuf[16]; MprHash *dateCache; conn = q->conn; tx = conn->tx; rx = conn->rx; info = &tx->fileInfo; if (conn->error) { return MPR_ERR_CANT_OPEN; } if (rx->flags & (HTTP_GET | HTTP_HEAD | HTTP_POST)) { if (!(info->valid || info->isDir)) { httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot find document"); return 0; } if (!tx->etag) { /* Set the etag for caching in the client */ tx->etag = sfmt("\"%llx-%llx-%llx\"", (int64) info->inode, (int64) info->size, (int64) info->mtime); } if (info->mtime) { dateCache = conn->http->dateCache; if ((date = mprLookupKey(dateCache, itosbuf(dbuf, sizeof(dbuf), (int64) info->mtime, 10))) == 0) { if (!dateCache || mprGetHashLength(dateCache) > 128) { conn->http->dateCache = dateCache = mprCreateHash(0, 0); } date = httpGetDateString(&tx->fileInfo); mprAddKey(dateCache, itosbuf(dbuf, sizeof(dbuf), (int64) info->mtime, 10), date); } httpSetHeaderString(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) { httpTrace(conn, "request.document.error", "error", "msg: 'Document is not a regular file', filename: '%s'", tx->filename); httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot serve document"); } 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 %'lld 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 && *rx->referrer) { httpTrace(conn, "request.document.error", "error", "msg: 'Cannot open document', filename: '%s', referrer: '%s'", tx->filename, rx->referrer); } else { httpTrace(conn, "request.document.error", "error", "msg: 'Cannot open document', filename: '%s'", tx->filename); } httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot open document"); } } } } else if (rx->flags & (HTTP_DELETE | HTTP_OPTIONS | HTTP_PUT)) { ; } else { httpError(conn, HTTP_CODE_BAD_METHOD, "Unsupported method"); } return 0; }
PUBLIC void httpSetContentType(HttpConn *conn, cchar *mimeType) { httpSetHeaderString(conn, "Content-Type", mimeType); }