/* * Add a packet to the io vector. Return the number of bytes added to the vector. */ static void addPacketForNet(MaQueue *q, MaPacket *packet) { MaResponse *resp; MaConn *conn; MprIOVec *iovec; int index, mask; conn = q->conn; resp = conn->response; iovec = q->iovec; index = q->ioIndex; mprAssert(q->count >= 0); mprAssert(q->ioIndex < (MA_MAX_IOVEC - 2)); if (packet->prefix) { addToNetVector(q, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix)); } if (maGetPacketLength(packet) > 0) { addToNetVector(q, mprGetBufStart(packet->content), mprGetBufLength(packet->content)); } mask = MA_TRACE_RESPONSE | ((packet->flags & MA_PACKET_HEADER) ? MA_TRACE_HEADERS : MA_TRACE_BODY); if (maShouldTrace(conn, mask)) { maTraceContent(conn, packet, 0, resp->bytesWritten, mask); } }
/* * Add a packet to the io vector. Return the number of bytes added to the vector. */ static void addPacketForSend(MaQueue *q, MaPacket *packet) { MaResponse *resp; MaConn *conn; MprIOVec *iovec; int mask; conn = q->conn; resp = conn->response; iovec = q->iovec; mprAssert(q->count >= 0); mprAssert(q->ioIndex < (MA_MAX_IOVEC - 2)); if (packet->prefix) { addToSendVector(q, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix)); } if (packet->esize > 0) { mprAssert(q->ioFile == 0); q->ioFile = 1; q->ioCount += packet->esize; } else if (maGetPacketLength(packet) > 0) { /* * Header packets have actual content. File data packets are virtual and only have a count. */ addToSendVector(q, mprGetBufStart(packet->content), mprGetBufLength(packet->content)); mask = (packet->flags & MA_PACKET_HEADER) ? MA_TRACE_HEADERS : MA_TRACE_BODY; if (maShouldTrace(conn, mask)) { maTraceContent(conn, packet, 0, resp->bytesWritten, mask); } } }
/* * Process request body data (typically post or put content). Packet will be null if the client closed the * connection to signify end of data. */ static bool processContent(MaConn *conn, MaPacket *packet) { MaRequest *req; MaResponse *resp; MaQueue *q; MaHost *host; MprBuf *content; int64 remaining; int nbytes; req = conn->request; resp = conn->response; host = conn->host; q = &resp->queue[MA_QUEUE_RECEIVE]; mprAssert(packet); if (packet == 0) { return 0; } if (conn->connectionFailed) { conn->state = MPR_HTTP_STATE_PROCESSING; maPutForService(resp->queue[MA_QUEUE_SEND].nextQ, maCreateHeaderPacket(resp), 1); return 1; } content = packet->content; if (req->flags & MA_REQ_CHUNKED) { if ((remaining = getChunkPacketSize(conn, content)) == 0) { /* Need more data or bad chunk specification */ if (mprGetBufLength(content) > 0) { conn->input = packet; } return 0; } } else { remaining = req->remainingContent; } nbytes = (int) min(remaining, mprGetBufLength(content)); mprAssert(nbytes >= 0); mprLog(conn, 7, "processContent: packet of %d bytes, remaining %Ld", mprGetBufLength(content), remaining); if (maShouldTrace(conn, MA_TRACE_REQUEST | MA_TRACE_BODY)) { maTraceContent(conn, packet, 0, 0, MA_TRACE_REQUEST | MA_TRACE_BODY); } if (nbytes > 0) { mprAssert(maGetPacketLength(packet) > 0); remaining -= nbytes; req->remainingContent -= nbytes; req->receivedContent += nbytes; if (req->receivedContent >= host->limits->maxBody) { conn->keepAliveCount = 0; maFailConnection(conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, "Request content body is too big %Ld vs limit %Ld", req->receivedContent, host->limits->maxBody); return 1; } if (packet == req->headerPacket) { /* Preserve headers if more data to come. Otherwise handlers may free the packet and destory the headers */ packet = maSplitPacket(resp, packet, 0); } else { mprStealBlock(resp, packet); } conn->input = 0; if (remaining == 0 && mprGetBufLength(packet->content) > nbytes) { /* * Split excess data belonging to the next pipelined request. */ mprLog(conn, 7, "processContent: Split packet of %d at %d", maGetPacketLength(packet), nbytes); conn->input = maSplitPacket(conn, packet, nbytes); mprAssert(mprGetParent(conn->input) == conn); } if ((q->count + maGetPacketLength(packet)) > q->max) { conn->keepAliveCount = 0; maFailConnection(q->conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, "Too much body data"); return 1; } maPutNext(q, packet); } else { conn->input = 0; mprStealBlock(resp, packet); } if (req->remainingContent == 0 || conn->requestFailed) { /* * End of input. Send a zero packet EOF signal and enable the handler send queue. */ if (req->remainingContent > 0 && conn->protocol > 0 && !conn->requestFailed) { maFailConnection(conn, MPR_HTTP_CODE_COMMS_ERROR, "Insufficient content data sent with request"); } else { maPutNext(q, maCreateEndPacket(resp)); conn->state = MPR_HTTP_STATE_PROCESSING; maRunPipeline(conn); } return 1; } maServiceQueues(conn); return conn->input ? mprGetBufLength(conn->input->content) : 0; }
/* * Parse the first line of a http request. Return true if the first line parsed. This is only called once all the headers * have been read and buffered. */ static bool parseFirstLine(MaConn *conn, MaPacket *packet) { MaRequest *req; MaResponse *resp; MaHost *host; MprBuf *content; cchar *endp; char *methodName, *uri, *httpProtocol; int method, len; req = conn->request = maCreateRequest(conn); resp = conn->response = maCreateResponse(conn); host = conn->host; #if BLD_DEBUG req->startTime = mprGetTime(conn); req->startTicks = mprGetTicks(); #endif methodName = getToken(conn, " "); if (*methodName == '\0') { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad request method name"); return 0; } method = 0; switch (methodName[0]) { case 'D': if (strcmp(methodName, "DELETE") == 0) { method = MA_REQ_DELETE; } break; case 'G': if (strcmp(methodName, "GET") == 0) { method = MA_REQ_GET; } break; case 'P': if (strcmp(methodName, "POST") == 0) { method = MA_REQ_POST; } else if (strcmp(methodName, "PUT") == 0) { method = MA_REQ_PUT; } break; case 'H': if (strcmp(methodName, "HEAD") == 0) { method = MA_REQ_HEAD; resp->flags |= MA_RESP_NO_BODY; } break; case 'O': if (strcmp(methodName, "OPTIONS") == 0) { method = MA_REQ_OPTIONS; resp->flags |= MA_RESP_NO_BODY; } break; case 'T': if (strcmp(methodName, "TRACE") == 0) { method = MA_REQ_TRACE; resp->flags |= MA_RESP_NO_BODY; } break; } if (method == 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_METHOD, "Bad method"); return 0; } uri = getToken(conn, " "); if (*uri == '\0') { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad HTTP request. Bad URI."); return 0; } if ((int) strlen(uri) >= conn->http->limits.maxUrl) { maFailRequest(conn, MPR_HTTP_CODE_REQUEST_URL_TOO_LARGE, "Bad request. URI too long."); return 0; } httpProtocol = getToken(conn, "\r\n"); if (strcmp(httpProtocol, "HTTP/1.1") == 0) { conn->protocol = 1; } else if (strcmp(httpProtocol, "HTTP/1.0") == 0) { conn->protocol = 0; if (method == MA_REQ_POST || method == MA_REQ_PUT) { req->remainingContent = MAXINT; } } else { maFailConnection(conn, MPR_HTTP_CODE_NOT_ACCEPTABLE, "Unsupported HTTP protocol"); return 0; } req->method = method; req->methodName = methodName; req->httpProtocol = httpProtocol; req->url = uri; if ((conn->trace = maSetupTrace(host, conn->response->extension)) != 0) { if (maShouldTrace(conn, MA_TRACE_REQUEST | MA_TRACE_HEADERS)) { mprLog(req, host->traceLevel, "\n@@@ New request from %s:%d to %s:%d\n%s %s %s", conn->remoteIpAddr, conn->remotePort, conn->sock->ipAddr, conn->sock->port, methodName, uri, httpProtocol); content = packet->content; endp = strstr((char*) content->start, "\r\n\r\n"); len = (endp) ? (int) (endp - mprGetBufStart(content) + 4) : 0; maTraceContent(conn, packet, len, 0, MA_TRACE_REQUEST | MA_TRACE_HEADERS); } } else { mprLog(conn, 2, "%s %s %s", methodName, uri, httpProtocol); } return 1; }