/* 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); } }
/* function finalize(): Void */ static EjsObj *http_finalize(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { if (hp->conn) { httpFinalize(hp->conn); } return 0; }
/* The ready callback is invoked when all body data has been received */ static void readyFileHandler(HttpQueue *q) { /* The queue already contains a single data packet representing all the output data. */ httpFinalize(q->conn); }
/* Write upload data. This routine blocks. If you need non-blocking ... cut and paste. */ ssize httpWriteUploadData(HttpConn *conn, MprList *fileData, MprList *formData) { char *path, *pair, *key, *value, *name; ssize rc; int next; rc = 0; if (formData) { for (rc = next = 0; rc >= 0 && (pair = mprGetNextItem(formData, &next)) != 0; ) { key = stok(sclone(pair), "=", &value); rc += httpWrite(conn->writeq, "%s\r\nContent-Disposition: form-data; name=\"%s\";\r\n", conn->boundary, key); rc += httpWrite(conn->writeq, "Content-Type: application/x-www-form-urlencoded\r\n\r\n%s\r\n", value); } } if (fileData) { for (rc = next = 0; rc >= 0 && (path = mprGetNextItem(fileData, &next)) != 0; ) { name = mprGetPathBase(path); rc += httpWrite(conn->writeq, "%s\r\nContent-Disposition: form-data; name=\"file%d\"; filename=\"%s\"\r\n", conn->boundary, next - 1, name); rc += httpWrite(conn->writeq, "Content-Type: %s\r\n\r\n", mprLookupMime(MPR->mimeTypes, path)); rc += blockingFileCopy(conn, path); rc += httpWrite(conn->writeq, "\r\n"); } } rc += httpWrite(conn->writeq, "%s--\r\n--", conn->boundary); httpFinalize(conn); return rc; }
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 }
static void readyError(HttpQueue *q) { if (!q->conn->error) { httpError(q->conn, HTTP_CODE_SERVICE_UNAVAILABLE, "The requested resource is not available"); } httpFinalize(q->conn); }
/* function get(uri: String = null, ...data): Http The spec allows GET methods to have body data, but is rarely, if ever, used. */ static EjsHttp *http_get(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { startHttpRequest(ejs, hp, "GET", argc, argv); if (!ejs->exception && hp->conn) { httpFinalize(hp->conn); } return hp; }
static void readyError(HttpQueue *q) { if (!q->stream->error) { httpError(q->stream, HTTP_CODE_NOT_FOUND, "The requested resource is not available"); } httpFinalize(q->stream); httpScheduleQueue(q); }
void espAutoFinalize(HttpConn *conn) { EspReq *req; req = conn->data; if (req->autoFinalize) { httpFinalize(conn); } }
/* 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); }
/* function close(): Void */ static EjsObj *http_close(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv) { if (hp->conn) { httpFinalize(hp->conn); sendHttpCloseEvent(ejs, hp); httpDestroyConn(hp->conn); // TODO OPT - Better to do this on demand. This consumes a conn until GC. hp->conn = httpCreateConn(ejs->http, NULL, ejs->dispatcher); httpPrepClientConn(hp->conn, 0); httpSetConnNotifier(hp->conn, httpEventChange); httpSetConnContext(hp->conn, hp); } return 0; }
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); }
/* Read the output data from the CGI script and return it to the client. This is called by the MPR in response to I/O events from the CGI process for stdout/stderr data from the CGI script and for EOF from the CGI's stdin. IMPORTANT: This event runs on the connection's dispatcher. (ie. single threaded and safe) */ static void cgiCallback(MprCmd *cmd, int channel, void *data) { HttpConn *conn; Cgi *cgi; int suspended; if ((cgi = data) == 0) { return; } if ((conn = cgi->conn) == 0) { return; } conn->lastActivity = conn->http->now; switch (channel) { case MPR_CMD_STDIN: /* Stdin can absorb more data */ httpResumeQueue(cgi->writeq); break; case MPR_CMD_STDOUT: case MPR_CMD_STDERR: readFromCgi(cgi, channel); break; default: /* Child death notification */ if (cmd->status != 0) { httpError(cgi->conn, HTTP_CODE_BAD_GATEWAY, "Bad CGI process termination"); } break; } if (cgi->location) { httpRedirect(conn, conn->tx->status, cgi->location); } if (cmd->complete || cgi->location) { cgi->location = 0; httpFinalize(conn); mprCreateEvent(conn->dispatcher, "cgiComplete", 0, httpIOEvent, conn, 0); return; } suspended = httpIsQueueSuspended(conn->writeq); assert(!suspended || conn->tx->writeBlocked); mprEnableCmdOutputEvents(cmd, !suspended); mprCreateEvent(conn->dispatcher, "cgi", 0, httpIOEvent, conn, 0); }
/* Read the output data from the CGI script and return it to the client. This is called by the MPR in response to I/O events from the CGI process for stdout/stderr data from the CGI script and for EOF from the CGI's stdin. IMPORTANT: This event runs on the connection's dispatcher. (ie. single threaded and safe) */ static void cgiCallback(MprCmd *cmd, int channel, void *data) { HttpConn *conn; Cgi *cgi; int suspended; if ((cgi = data) == 0) { return; } if ((conn = cgi->conn) == 0) { return; } conn->lastActivity = conn->http->now; mprTrace(6, "CGI: cgiCallback event channel %d", channel); switch (channel) { case MPR_CMD_STDIN: /* Stdin can absorb more data */ httpResumeQueue(cgi->writeq); break; case MPR_CMD_STDOUT: case MPR_CMD_STDERR: readFromCgi(cgi, channel); break; default: /* Child death notification */ break; } if (cgi->location) { httpRedirect(conn, conn->tx->status, cgi->location); } if (cmd->complete || cgi->location) { cgi->location = 0; httpFinalize(conn); mprCreateEvent(conn->dispatcher, "cgiComplete", 0, httpIOEvent, conn, 0); return; } suspended = httpIsQueueSuspended(conn->writeq); mprTrace(6, "CGI: %s CGI: cgiCallback. Conn->writeq %d", suspended ? "DISABLE" : "ENABLE", conn->writeq->count); assert(!suspended || conn->tx->writeBlocked); mprEnableCmdOutputEvents(cmd, !suspended); mprCreateEvent(conn->dispatcher, "cgi", 0, httpIOEvent, conn, 0); }
static void readyCacheHandler(HttpQueue *q) { HttpConn *conn; HttpTx *tx; cchar *data; conn = q->conn; tx = conn->tx; if (tx->cachedContent) { mprLog(3, "cacheHandler: write cached content for '%s'", conn->rx->uri); if ((data = setHeadersFromCache(conn, tx->cachedContent)) != 0) { tx->length = slen(data); httpWriteString(q, data); } } httpFinalize(conn); }
static int sendRequest(HttpConn *conn, cchar *method, cchar *url, MprList *files) { if (httpConnect(conn, method, url, app->ssl) < 0) { mprError("Can't process request for \"%s\". %s.", url, httpGetError(conn)); return MPR_ERR_CANT_OPEN; } /* This program does not do full-duplex writes with reads. ie. if you have a request that sends and receives data in parallel -- http will do the writes first then read the response. */ if (app->bodyData || app->formData || files) { if (writeBody(conn, files) < 0) { mprError("Can't write body data to \"%s\". %s", url, httpGetError(conn)); return MPR_ERR_CANT_WRITE; } } mprAssert(!mprGetCurrentThread()->yielded); httpFinalize(conn); return 0; }
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); }
static void handleTraceMethod(HttpStream *stream) { HttpTx *tx; HttpQueue *q; HttpPacket *traceData; tx = stream->tx; q = stream->writeq; /* Create a dummy set of headers to use as the response body. Then reset so the connector will create the headers in the normal fashion. Need to be careful not to have a content length in the headers in the body. */ tx->flags |= HTTP_TX_NO_LENGTH; httpDiscardData(stream, HTTP_QUEUE_TX); traceData = httpCreateDataPacket(q->packetSize); httpCreateHeaders1(q, traceData); tx->flags &= ~(HTTP_TX_NO_LENGTH | HTTP_TX_HEADERS_CREATED); httpSetContentType(stream, "message/http"); httpPutPacketToNext(q, traceData); httpFinalize(stream); }
/* 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); }
void httpCreateTxPipeline(HttpConn *conn, HttpRoute *route) { Http *http; HttpTx *tx; HttpRx *rx; HttpQueue *q; HttpStage *stage, *filter; int next, hasOutputFilters; mprAssert(conn); mprAssert(route); http = conn->http; rx = conn->rx; tx = conn->tx; tx->outputPipeline = mprCreateList(-1, 0); if (tx->handler == 0) { tx->handler = http->passHandler; } mprAddItem(tx->outputPipeline, tx->handler); hasOutputFilters = 0; if (route->outputStages) { for (next = 0; (filter = mprGetNextItem(route->outputStages, &next)) != 0; ) { if (matchFilter(conn, filter, route, HTTP_STAGE_TX) == HTTP_ROUTE_OK) { mprAddItem(tx->outputPipeline, filter); mprLog(4, "Select output filter: \"%s\"", filter->name); hasOutputFilters = 1; } } } if (tx->connector == 0) { if (tx->handler == http->fileHandler && (rx->flags & HTTP_GET) && !hasOutputFilters && !conn->secure && httpShouldTrace(conn, HTTP_TRACE_TX, HTTP_TRACE_BODY, tx->ext) < 0) { tx->connector = http->sendConnector; } else if (route && route->connector) { tx->connector = route->connector; } else { tx->connector = http->netConnector; } } mprAddItem(tx->outputPipeline, tx->connector); mprLog(4, "Select connector: \"%s\"", tx->connector->name); /* Create the outgoing queue heads and open the queues */ q = tx->queue[HTTP_QUEUE_TX]; for (next = 0; (stage = mprGetNextItem(tx->outputPipeline, &next)) != 0; ) { q = httpCreateQueue(conn, stage, HTTP_QUEUE_TX, q); } conn->writeq = tx->queue[HTTP_QUEUE_TX]->nextQ; conn->connectorq = tx->queue[HTTP_QUEUE_TX]->prevQ; pairQueues(conn); /* Put the header before opening the queues incase an open routine actually services and completes the request httpHandleOptionsTrace does this when called from openFile() in fileHandler. */ httpPutForService(conn->writeq, httpCreateHeaderPacket(), HTTP_DELAY_SERVICE); openQueues(conn); /* Refinalize if httpFinalize was called before the Tx pipeline was created */ if (conn->refinalize) { conn->finalized = 0; httpFinalize(conn); } }
PUBLIC void httpHandleOptions(HttpStream *stream) { httpSetHeaderString(stream, "Allow", httpGetRouteMethods(stream->rx->route)); httpFinalize(stream); }
static void readyPass(HttpQueue *q) { httpFinalize(q->conn); }
static void readyPass(HttpQueue *q) { httpFinalize(q->stream); httpScheduleQueue(q); }
/* 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); }
void espFinalize(HttpConn *conn) { httpFinalize(conn); }
/* function [get|put|delete|post...](uri = null, ...data): Http */ static EjsHttp *startHttpRequest(Ejs *ejs, EjsHttp *hp, char *method, int argc, EjsObj **argv) { EjsArray *args; EjsByteArray *data; EjsNumber *written; EjsUri *uriObj; HttpConn *conn; ssize nbytes; conn = hp->conn; hp->responseCache = 0; hp->requestContentCount = 0; mprFlushBuf(hp->responseContent); if (argc >= 1 && !ejsIs(ejs, argv[0], Null)) { uriObj = (EjsUri*) argv[0]; hp->uri = httpUriToString(uriObj->uri, HTTP_COMPLETE_URI); } if (argc == 2 && ejsIs(ejs, argv[1], Array)) { args = (EjsArray*) argv[1]; if (args->length > 0) { data = ejsCreateByteArray(ejs, -1); written = ejsWriteToByteArray(ejs, data, 1, &argv[1]); mprPutBlockToBuf(hp->requestContent, (char*) data->value, (int) written->value); mprAddNullToBuf(hp->requestContent); assert(written > 0); } } if (hp->uri == 0) { ejsThrowArgError(ejs, "URL is not defined"); return 0; } if (method && strcmp(hp->method, method) != 0) { hp->method = sclone(method); } if (hp->method == 0) { ejsThrowArgError(ejs, "HTTP Method is not defined"); return 0; } if (hp->certFile) { if (!hp->ssl) { hp->ssl = mprCreateSsl(0); } mprSetSslCertFile(hp->ssl, hp->certFile); if (!hp->keyFile) { ejsThrowStateError(ejs, "Must define a Http.key to use with a certificate"); } mprSetSslKeyFile(hp->ssl, hp->keyFile); } if (hp->caFile) { if (!hp->ssl) { hp->ssl = mprCreateSsl(0); } mprSetSslCaFile(hp->ssl, hp->caFile); } if (httpConnect(conn, hp->method, hp->uri, hp->ssl) < 0) { ejsThrowIOError(ejs, "Cannot issue request for \"%s\"", hp->uri); return 0; } if (mprGetBufLength(hp->requestContent) > 0) { nbytes = httpWriteBlock(conn->writeq, mprGetBufStart(hp->requestContent), mprGetBufLength(hp->requestContent), HTTP_BLOCK); if (nbytes < 0) { ejsThrowIOError(ejs, "Cannot write request data for \"%s\"", hp->uri); return 0; } else if (nbytes > 0) { assert(nbytes == mprGetBufLength(hp->requestContent)); mprAdjustBufStart(hp->requestContent, nbytes); hp->requestContentCount += nbytes; } httpFinalize(conn); } httpNotify(conn, HTTP_EVENT_WRITABLE, 0); if (conn->async) { httpEnableConnEvents(hp->conn); } return hp; }
/* The current request has an error and cannot complete as normal. This call sets the Http response status and overrides the normal output with an alternate error message. If the output has alread started (headers sent), then the connection MUST be closed so the client can get some indication the request failed. */ static void errorv(HttpConn *conn, int flags, cchar *fmt, va_list args) { HttpRx *rx; HttpTx *tx; cchar *uri; int status; assert(fmt); rx = conn->rx; tx = conn->tx; if (conn == 0) { return; } status = flags & HTTP_CODE_MASK; if (status == 0) { status = HTTP_CODE_INTERNAL_SERVER_ERROR; } if (flags & (HTTP_ABORT | HTTP_CLOSE)) { conn->keepAliveCount = 0; } if (flags & HTTP_ABORT) { conn->connError = 1; if (rx) { rx->eof = 1; } } if (!conn->error) { conn->error = 1; httpOmitBody(conn); conn->errorMsg = formatErrorv(conn, status, fmt, args); mprLog(2, "Error: %s", conn->errorMsg); HTTP_NOTIFY(conn, HTTP_EVENT_ERROR, 0); if (conn->endpoint) { if (status == HTTP_CODE_NOT_FOUND) { httpMonitorEvent(conn, HTTP_COUNTER_NOT_FOUND_ERRORS, 1); } httpMonitorEvent(conn, HTTP_COUNTER_ERRORS, 1); } httpAddHeaderString(conn, "Cache-Control", "no-cache"); if (conn->endpoint && tx && rx) { if (tx->flags & HTTP_TX_HEADERS_CREATED) { /* If the response headers have been sent, must let the other side of the failure ... aborting the request is the only way as the status has been sent. */ flags |= HTTP_ABORT; } else { if (rx->route && (uri = httpLookupRouteErrorDocument(rx->route, tx->status)) && !smatch(uri, rx->uri)) { errorRedirect(conn, uri); } else { makeAltBody(conn, status); } } } httpFinalize(conn); } if (flags & HTTP_ABORT) { httpDisconnect(conn); } }