/* * 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; }
PUBLIC int espEmail(HttpConn *conn, cchar *to, cchar *from, cchar *subject, MprTime date, cchar *mime, cchar *message, MprList *files) { MprList *lines; MprCmd *cmd; cchar *body, *boundary, *contents, *encoded, *file; char *out, *err; ssize length; int i, next, status; if (!from || !*from) { from = "anonymous"; } if (!subject || !*subject) { subject = "Mail message"; } if (!mime || !*mime) { mime = "text/plain"; } if (!date) { date = mprGetTime(); } boundary = sjoin("esp.mail=", mprGetMD5("BOUNDARY"), NULL); lines = mprCreateList(0, 0); mprAddItem(lines, sfmt("To: %s", to)); mprAddItem(lines, sfmt("From: %s", from)); mprAddItem(lines, sfmt("Date: %s", mprFormatLocalTime(0, date))); mprAddItem(lines, sfmt("Subject: %s", subject)); mprAddItem(lines, "MIME-Version: 1.0"); mprAddItem(lines, sfmt("Content-Type: multipart/mixed; boundary=%s", boundary)); mprAddItem(lines, ""); boundary = sjoin("--", boundary, NULL); mprAddItem(lines, boundary); mprAddItem(lines, sfmt("Content-Type: %s", mime)); mprAddItem(lines, ""); mprAddItem(lines, ""); mprAddItem(lines, message); for (ITERATE_ITEMS(files, file, next)) { mprAddItem(lines, boundary); if ((mime = mprLookupMime(NULL, file)) == 0) { mime = "application/octet-stream"; } mprAddItem(lines, "Content-Transfer-Encoding: base64"); mprAddItem(lines, sfmt("Content-Disposition: inline; filename=\"%s\"", mprGetPathBase(file))); mprAddItem(lines, sfmt("Content-Type: %s; name=\"%s\"", mime, mprGetPathBase(file))); mprAddItem(lines, ""); contents = mprReadPathContents(file, &length); encoded = mprEncode64Block(contents, length); for (i = 0; i < length; i += 76) { mprAddItem(lines, snclone(&encoded[i], i + 76)); } } mprAddItem(lines, sfmt("%s--", boundary)); body = mprListToString(lines, "\n"); httpTraceContent(conn, "esp.email", "context", body, slen(body), 0); cmd = mprCreateCmd(conn->dispatcher); if (mprRunCmd(cmd, "sendmail -t", NULL, body, &out, &err, -1, 0) < 0) { mprDestroyCmd(cmd); return MPR_ERR_CANT_OPEN; } if (mprWaitForCmd(cmd, ME_ESP_EMAIL_TIMEOUT) < 0) { httpTrace(conn, "esp.email.error", "error", "msg=\"Timeout waiting for command to complete\", timeout=%d, command=\"%s\"", ME_ESP_EMAIL_TIMEOUT, cmd->argv[0]); mprDestroyCmd(cmd); return MPR_ERR_CANT_COMPLETE; } if ((status = mprGetCmdExitStatus(cmd)) != 0) { httpTrace(conn, "esp.email.error", "error", "msg=\"Sendmail failed\", status=%d, error=\"%s\"", status, err); mprDestroyCmd(cmd); return MPR_ERR_CANT_WRITE; } mprDestroyCmd(cmd); return 0; }
/* * Add a response cookie */ static void setCookie(void *handle, cchar *name, cchar *value, cchar *path, cchar *cookieDomain, int lifetime, bool isSecure) { struct tm tm; cchar *userAgent, *hostName; char dateStr[64], *cp, *expiresAtt, *expires, *domainAtt, *domain, *secure; if (path == 0) { path = "/"; } userAgent = getHeader(handle, "HTTP_USER_AGENT"); hostName = getHeader(handle, "HTTP_HOST"); /* * Fix for Safari and Bonjour addresses with a trailing ".". Safari discards cookies without a domain specifier * or with a domain that includes a trailing ".". Solution: include an explicit domain and trim the trailing ".". * * User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) * AppleWebKit/530.0+ (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1 */ if (cookieDomain == 0 && userAgent && strstr(userAgent, "AppleWebKit") != 0) { domain = mprStrdup(mpr, hostName); if ((cp = strchr(domain, ':')) != 0) { *cp = '\0'; } if (*domain && domain[strlen(domain) - 1] == '.') { domain[strlen(domain) - 1] = '\0'; } else { domain = 0; } } else { domain = 0; } if (domain) { domainAtt = "; domain="; } else { domainAtt = ""; } if (lifetime > 0) { mprDecodeUniversalTime(mpr, &tm, mprGetTime(mpr) + (lifetime * MPR_TICKS_PER_SEC)); mprFormatTime(mpr, MPR_HTTP_DATE, &tm); expiresAtt = "; expires="; expires = dateStr; } else { expires = expiresAtt = ""; } if (isSecure) { secure = "; secure"; } else { secure = ";"; } /* * Allow multiple cookie headers. Even if the same name. Later definitions take precedence */ setHeader(handle, 1, "Set-Cookie", mprStrcat(mpr, -1, name, "=", value, "; path=", path, domainAtt, domain, expiresAtt, expires, secure, NULL)); setHeader(handle, 0, "Cache-control", "no-cache=\"set-cookie\""); }
MAIN(httpMain, int argc, char *argv[]) { MprTime start; double elapsed; /* * Explicit initialization of globals for re-entrancy on Vxworks */ activeLoadThreads = benchmark = continueOnErrors = fetchCount = iterations = isBinary = httpVersion = 0; success = loadThreads = nextArg = noout = nofollow = showHeaders = printable = workers = 0; retries = singleStep = timeout = verbose = 0; chunkSize = host = method = password = ranges = 0; username = 0; mpr = 0; headers = 0; formData = 0; mpr = mprCreate(argc, argv, NULL); initSettings(mpr); if (!parseArgs(mpr, argc, argv)) { showUsage(mpr); return MPR_ERR_BAD_ARGS; } #if BLD_FEATURE_MULTITHREAD mprSetMaxWorkers(mpr, workers); #endif #if BLD_FEATURE_SSL if (!mprLoadSsl(mpr, 1)) { mprError(mpr, "Can't load SSL"); exit(1); } #endif /* * Start the Timer, Socket and Worker services */ if (mprStart(mpr, 0) < 0) { mprError(mpr, "Can't start MPR for %s", mprGetAppTitle(mpr)); exit(2); } start = mprGetTime(mpr); processing(); /* * Wait for all the threads to complete (simple but effective). Keep servicing events as we wind down. */ while (activeLoadThreads > 0) { mprServiceEvents(mprGetDispatcher(mpr), 250, MPR_SERVICE_EVENTS | MPR_SERVICE_IO); } if (benchmark) { elapsed = (double) (mprGetTime(mpr) - start); if (fetchCount == 0) { elapsed = 0; fetchCount = 1; } mprPrintf(mpr, "\nRequest Count: %13d\n", fetchCount); mprPrintf(mpr, "Time elapsed: %13.4f sec\n", elapsed / 1000.0); mprPrintf(mpr, "Time per request: %13.4f sec\n", elapsed / 1000.0 / fetchCount); mprPrintf(mpr, "Requests per second: %13.4f\n", fetchCount * 1.0 / (elapsed / 1000.0)); mprPrintf(mpr, "Load threads: %13d\n", loadThreads); mprPrintf(mpr, "Worker threads: %13d\n", workers); } if (!success && verbose) { mprError(mpr, "Request failed"); } return (success) ? 0 : 255; }
int MaClient::sendRequest(char *host, int port, MprBuf* hdrBuf, char *postData, int postLen) { int len, rc; lock(); reset(); mprLog(3, tMod, "sendRequest: %s:%d\n", host, port); timestamp = mprGetTime(0); if (timeoutPeriod < 0) { timeoutPeriod = MPR_HTTP_CLIENT_TIMEOUT; } if (timeoutPeriod > 0) { if (!mprGetDebugMode()) { timer = new MprTimer(MPR_HTTP_TIMER_PERIOD, timeoutWrapper, (void *) this); } } if (sock == 0) { sock = new MprSocket(); mprLog(3, tMod, "Opening new socket on: %s:%d\n", host, port); rc = sock->openClient(host, port, MPR_SOCKET_NODELAY); if (rc < 0) { mprLog(MPR_ERROR, tMod, "Can't open socket on %s:%d, %d\n", host, port, rc); unlock(); sock->dispose(); sock = 0; return rc; } sock->setBufSize(-1, MPR_HTTP_CLIENT_BUFSIZE); } else { mprLog(3, tMod, "Reusing Keep-Alive socket on: %s:%d\n", host, port); } // // Remove this flush when pipelining is supported // inBuf->flush(); fd = sock->getFd(); // // Flush to the socket with any post data. Writes can fail because the // server prematurely closes a keep-alive connection. // len = hdrBuf->getLength(); if ((rc = sock->write(hdrBuf->getStart(), len)) != len) { flags |= MPR_HTTP_TERMINATED; unlock(); mprLog(MPR_ERROR, tMod, "Can't write to socket on %s:%d, %d\n", host, port, rc); return rc; } hdrBuf->addNull(); if (postData) { sock->setBlockingMode(1); if ((rc = sock->write(postData, postLen)) != postLen) { flags |= MPR_HTTP_TERMINATED; unlock(); mprLog(MPR_ERROR, tMod, "Can't write post data to socket on %s:%d, %d\n", host, port, rc); return rc; } sock->setBlockingMode(0); } sock->setCallback(readEventWrapper, (void*) this, 0, MPR_READABLE); // // If no callback, then we must block // if (callback == 0) { unlock(); while (state != MPR_HTTP_CLIENT_DONE) { // // If multithreaded and the events thread is not yet running, // we still want to work. // #if BLD_FEATURE_MULTITHREAD if (mprGetMpr()->isRunningEventsThread()) { completeCond->waitForCond(250); } else #endif mprGetMpr()->serviceEvents(1, 100); } } else { unlock(); } return 0; }
int MaClient::processResponseData() { char *line, *cp; int nbytes; mprLog(6, tMod, "READ DATA: %s\n", inBuf->getStart()); timestamp = mprGetTime(0); line = 0; while (state != MPR_HTTP_CLIENT_DONE && inBuf->getLength() > 0) { line = inBuf->getStart(); if (state != MPR_HTTP_CLIENT_CONTENT) { if ((cp = strchr(line, '\n')) == 0) { // Wait for more data return 0; } *cp = '\0'; if (cp[-1] == '\r') { nbytes = cp - line; *--cp = '\0'; } else { nbytes = cp - line; } inBuf->adjustStart(nbytes + 1); } else { if (contentLength <= 0) { nbytes = inBuf->getLength(); } else { nbytes = min(contentRemaining, inBuf->getLength()); } inBuf->adjustStart(nbytes); } switch(state) { case MPR_HTTP_CLIENT_START: mprLog(3, tMod, "processResponseData: %s\n", line); if (line[0] == '\0') { return 0; } responseHeader->put(line); responseHeader->put('\n'); if (parseFirst(line) < 0) { return MPR_ERR_BAD_STATE; } state = MPR_HTTP_CLIENT_HEADER; break; case MPR_HTTP_CLIENT_HEADER: if (nbytes > 1) { mprLog(3, tMod, "processResponseData: %s\n", line); responseHeader->put(line); responseHeader->put('\n'); if (parseHeader(line) < 0) { return MPR_ERR_BAD_STATE; } } else { // // Blank line means end of headers // if (flags & MPR_HTTP_INPUT_CHUNKED) { if (flags & MPR_HTTP_END_CHUNK_DATA) { finishRequest(0); } else { state = MPR_HTTP_CLIENT_CHUNK; } } else { state = MPR_HTTP_CLIENT_CONTENT; // // This means there was an explicit zero content length // if (contentRemaining == 0) { finishRequest(0); } else if (mprStrCmpAnyCase(method, "HEAD") == 0) { finishRequest(0); } } } break; case MPR_HTTP_CLIENT_CHUNK: mprLog(3, tMod, "processResponseData: %s\n", line); contentRemaining = contentLength = mprAtoi(line, 16); if (contentLength <= 0) { flags |= MPR_HTTP_END_CHUNK_DATA; state = MPR_HTTP_CLIENT_HEADER; } else { state = MPR_HTTP_CLIENT_CONTENT; } if (contentLength > MPR_HTTP_CLIENT_BUFSIZE) { delete responseContent; responseContent = new MprBuf(contentLength + 1, -1); } break; case MPR_HTTP_CLIENT_CONTENT: responseContent->put((uchar*) line, nbytes); responseContent->addNull(); mprLog(3, tMod, "processResponseData: %d bytes, %d remaining, %d sofar\n", nbytes, contentRemaining, responseContent->getLength()); if (contentRemaining > 0 || nbytes <= 0) { contentRemaining -= nbytes; if (contentRemaining <= 0) { /* if (!(flags & MPR_HTTP_INPUT_CHUNKED)) */ finishRequest(0); } } break; default: formatError("Bad state"); responseCode = MPR_HTTP_CLIENT_ERROR; finishRequest(1); return MPR_ERR_BAD_STATE; } } return 0; }
/* * Collect the child's exit status. The initiating thread must do this on uClibc. * Return zero if the exit status is successfully reaped. Return -1 if an error * and return > 0 if process still running. */ int mprReapCmd(MprCmd *cmd, int timeout) { MprTime mark; mprAssert(cmd->pid); if (timeout < 0) { timeout = MAXINT; } mark = mprGetTime(cmd); while (cmd->pid) { #if BLD_UNIX_LIKE int status, waitrc; status = 0; if ((waitrc = waitpid(cmd->pid, &status, WNOHANG | __WALL)) < 0) { mprAssert(0); mprLog(cmd, 0, "waitpid failed for pid %d, errno %d", cmd->pid, errno); return MPR_ERR_CANT_READ; } else if (waitrc == cmd->pid) { if (!WIFSTOPPED(status)) { if (WIFEXITED(status)) { cmd->status = WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { cmd->status = WTERMSIG(status); } cmd->pid = 0; } break; } else { mprAssert(waitrc == 0); } #endif #if VXWORKS /* * The command exit status (cmd->status) is set in cmdTaskEntry */ if (semTake(cmd->exitCond, MPR_TIMEOUT_STOP_TASK) != OK) { mprError(cmd, "cmd: child %s did not exit, errno %d", cmd->program); return MPR_ERR_CANT_CREATE; } semDelete(cmd->exitCond); cmd->exitCond = 0; cmd->pid = 0; #endif #if BLD_WIN_LIKE int status, rc; if ((rc = WaitForSingleObject(cmd->process, 10)) != WAIT_OBJECT_0) { if (rc == WAIT_TIMEOUT) { return -MPR_ERR_TIMEOUT; } mprLog(cmd, 6, "cmd: WaitForSingleObject no child to reap rc %d, %d", rc, GetLastError()); return MPR_ERR_CANT_READ; } if (GetExitCodeProcess(cmd->process, (ulong*) &status) == 0) { mprLog(cmd, 7, "cmd: GetExitProcess error"); return MPR_ERR_CANT_READ; } if (status != STILL_ACTIVE) { cmd->status = status; CloseHandle(cmd->process); CloseHandle(cmd->thread); cmd->process = 0; cmd->pid = 0; break; } #endif if (mprGetElapsedTime(cmd, mark) > timeout) { break; } /* Prevent busy waiting */ mprSleep(cmd, 10); } return (cmd->pid == 0) ? 0 : 1; }
int MaClient::sendCore(char *method, char *requestUrl, char *postData, int postLen) { char abuf[MPR_HTTP_MAX_PASS * 2], encDetails[MPR_HTTP_MAX_PASS * 2]; char *host; int port, len, rc, nbytes; mprAssert(requestUrl && *requestUrl); lock(); reset(); mprLog(3, tMod, "sendCore: %s %s\n", method, requestUrl); this->method = mprStrdup(method); timestamp = mprGetTime(0); if (timeoutPeriod < 0) { timeoutPeriod = MPR_HTTP_CLIENT_TIMEOUT; } if (timeoutPeriod > 0) { if (!mprGetDebugMode()) { timer = new MprTimer(MPR_HTTP_TIMER_PERIOD, timeoutWrapper, (void *) this); } } if (*requestUrl == '/') { url.parse(requestUrl); host = (proxyHost) ? proxyHost : defaultHost; port = (proxyHost) ? proxyPort : defaultPort; } else { url.parse(requestUrl); host = (proxyHost) ? proxyHost : url.host; port = (proxyHost) ? proxyPort : url.port; } if (sock) { if (port != currentPort || strcmp(host, currentHost) != 0) { // // This request is for a different host or port. Must close socket. // sock->close(0); sock->dispose(); sock = 0; } } if (sock == 0) { sock = new MprSocket(); mprLog(3, tMod, "Opening new socket on: %s:%d\n", host, port); rc = sock->openClient(host, port, MPR_SOCKET_NODELAY); if (rc < 0) { mprLog(MPR_ERROR, tMod, "Can't open socket on %s:%d, %d\n", host, port, rc); unlock(); sock->dispose(); sock = 0; return rc; } sock->setBufSize(-1, MPR_HTTP_CLIENT_BUFSIZE); currentHost = mprStrdup(host); currentPort = port; } else { mprLog(3, tMod, "Reusing Keep-Alive socket on: %s:%d\n", host, port); } // // Remove this flush when pipelining is supported // inBuf->flush(); fd = sock->getFd(); if (proxyHost && *proxyHost) { if (url.query && *url.query) { outBuf->putFmt("%s http://%s:%d%s?%s HTTP/1.1\r\n", method, proxyHost, proxyPort, url.uri, url.query); } else { outBuf->putFmt("%s http://%s:%d%s HTTP/1.1\r\n", method, proxyHost, proxyPort, url.uri); } } else { if (url.query && *url.query) { outBuf->putFmt("%s %s?%s HTTP/1.1\r\n", method, url.uri, url.query); } else { outBuf->putFmt("%s %s HTTP/1.1\r\n", method, url.uri); } } if (serverAuthType) { if (strcmp(serverAuthType, "basic") == 0) { mprSprintf(abuf, sizeof(abuf), "%s:%s", user, password); maEncode64(encDetails, sizeof(encDetails), abuf); outBuf->putFmt("Authorization: %s %s\r\n", serverAuthType, encDetails); #if BLD_FEATURE_DIGEST } else if (strcmp(serverAuthType, "digest") == 0) { char a1Buf[256], a2Buf[256], digestBuf[256]; char *ha1, *ha2, *digest, *qop; authNc++; if (secret == 0) { if (createSecret() < 0) { mprLog(MPR_ERROR, tMod, "Can't create secret\n"); return MPR_ERR_CANT_INITIALIZE; } } mprFree(authCnonce); maCalcNonce(&authCnonce, secret, 0, realm); mprSprintf(a1Buf, sizeof(a1Buf), "%s:%s:%s", user, realm, password); ha1 = maMD5(a1Buf); mprSprintf(a2Buf, sizeof(a2Buf), "%s:%s", method, url.uri); ha2 = maMD5(a2Buf); qop = (serverQop) ? serverQop : (char*) ""; if (mprStrCmpAnyCase(serverQop, "auth") == 0) { mprSprintf(digestBuf, sizeof(digestBuf), "%s:%s:%08x:%s:%s:%s", ha1, serverNonce, authNc, authCnonce, serverQop, ha2); } else if (mprStrCmpAnyCase(serverQop, "auth-int") == 0) { mprSprintf(digestBuf, sizeof(digestBuf), "%s:%s:%08x:%s:%s:%s", ha1, serverNonce, authNc, authCnonce, serverQop, ha2); } else { qop = ""; mprSprintf(digestBuf, sizeof(digestBuf), "%s:%s:%s", ha1, serverNonce, ha2); } mprFree(ha1); mprFree(ha2); digest = maMD5(digestBuf); if (*qop == '\0') { outBuf->putFmt("Authorization: Digest " "username=\"%s\", realm=\"%s\", nonce=\"%s\", " "uri=\"%s\", response=\"%s\"\r\n", user, realm, serverNonce, url.uri, digest); } else if (strcmp(qop, "auth") == 0) { outBuf->putFmt("Authorization: Digest " "username=\"%s\", realm=\"%s\", domain=\"%s\", " "algorithm=\"MD5\", qop=\"%s\", cnonce=\"%s\", " "nc=\"%08x\", nonce=\"%s\", opaque=\"%s\", " "stale=\"FALSE\", uri=\"%s\", response=\"%s\"\r\n", user, realm, serverDomain, serverQop, authCnonce, authNc, serverNonce, serverOpaque, url.uri, digest); } else if (strcmp(qop, "auth-int") == 0) { ; } mprFree(digest); #endif // BLD_FEATURE_HTTP_DIGEST } } outBuf->putFmt("Host: %s\r\n", host); outBuf->putFmt("User-Agent: %s\r\n", MPR_HTTP_CLIENT_NAME); if (userFlags & MPR_HTTP_KEEP_ALIVE) { outBuf->putFmt("Connection: Keep-Alive\r\n"); } else { outBuf->putFmt("Connection: close\r\n"); } if (postLen > 0) { outBuf->putFmt("Content-Length: %d\r\n", postLen); } if (postData) { outBuf->putFmt("Content-Type: application/x-www-form-urlencoded\r\n"); } if (userHeaders) { outBuf->put(userHeaders); } outBuf->put("\r\n"); outBuf->addNull(); // // Flush to the socket with any post data. Writes can fail because the // server prematurely closes a keep-alive connection. // len = outBuf->getLength(); if ((rc = sock->write(outBuf->getStart(), len)) != len) { flags |= MPR_HTTP_TERMINATED; unlock(); mprLog(MPR_ERROR, tMod, "Can't write to socket on %s:%d, %d\n", host, port, rc); return rc; } #if BLD_DEBUG mprLog(3, MPR_RAW, tMod, "Request >>>>\n%s\n", outBuf->getStart()); #endif if (postData) { sock->setBlockingMode(1); for (len = 0; len < postLen; ) { nbytes = postLen - len; rc = sock->write(&postData[len], nbytes); #if BLD_DEBUG mprLog(3, MPR_RAW, tMod, "POST DATA %s\n", &postData[len]); #endif if (rc < 0) { unlock(); mprLog(MPR_ERROR, tMod, "Can't write post data to socket on %s:%d, %d\n", host, port, rc); flags |= MPR_HTTP_TERMINATED; sock->dispose(); sock = 0; return rc; } len += rc; } sock->setBlockingMode(0); } sock->setCallback(readEventWrapper, (void*) this, 0, MPR_READABLE); // // If no callback, then we must block // if (callback == 0) { unlock(); while (state != MPR_HTTP_CLIENT_DONE) { // // If multithreaded and the events thread is not yet running, // we still want to work. // #if BLD_FEATURE_MULTITHREAD if (mprGetMpr()->isRunningEventsThread() && mprGetMpr()->poolService->getMaxPoolThreads() > 0) { completeCond->waitForCond(250); } else #endif mprGetMpr()->serviceEvents(1, 100); } } else { unlock(); } return 0; }
static int makeChannel(MprCmd *cmd, int index) { SECURITY_ATTRIBUTES clientAtt, serverAtt, *att; HANDLE readHandle, writeHandle; MprCmdFile *file; MprTime now; char *pipeBuf; int openMode, pipeMode, readFd, writeFd; static int tempSeed = 0; memset(&clientAtt, 0, sizeof(clientAtt)); clientAtt.nLength = sizeof(SECURITY_ATTRIBUTES); clientAtt.bInheritHandle = 1; /* * Server fds are not inherited by the child */ memset(&serverAtt, 0, sizeof(serverAtt)); serverAtt.nLength = sizeof(SECURITY_ATTRIBUTES); serverAtt.bInheritHandle = 0; file = &cmd->files[index]; now = ((int) mprGetTime(cmd) & 0xFFFF) % 64000; pipeBuf = mprAsprintf(cmd, -1, "\\\\.\\pipe\\MPR_%d_%d_%d.tmp", getpid(), (int) now, ++tempSeed); /* * Pipes are always inbound. The file below is outbound. we swap whether the client or server * inherits the pipe or file. MPR_CMD_STDIN is the clients input pipe. * Pipes are blocking since both ends share the same blocking mode. Client must be blocking. */ openMode = PIPE_ACCESS_INBOUND; pipeMode = 0; att = (index == MPR_CMD_STDIN) ? &clientAtt : &serverAtt; readHandle = CreateNamedPipe(pipeBuf, openMode, pipeMode, 1, 0, 256 * 1024, 1, att); if (readHandle == INVALID_HANDLE_VALUE) { mprError(cmd, "Can't create stdio pipes %s. Err %d\n", pipeBuf, mprGetOsError()); return MPR_ERR_CANT_CREATE; } readFd = (int) (int64) _open_osfhandle((long) readHandle, 0); att = (index == MPR_CMD_STDIN) ? &serverAtt: &clientAtt; writeHandle = CreateFile(pipeBuf, GENERIC_WRITE, 0, att, OPEN_EXISTING, openMode, 0); writeFd = (int) _open_osfhandle((long) writeHandle, 0); if (readFd < 0 || writeFd < 0) { mprError(cmd, "Can't create stdio pipes %s. Err %d\n", pipeBuf, mprGetOsError()); return MPR_ERR_CANT_CREATE; } if (index == MPR_CMD_STDIN) { file->clientFd = readFd; file->fd = writeFd; file->handle = writeHandle; } else { file->clientFd = writeFd; file->fd = readFd; file->handle = readHandle; } mprFree(pipeBuf); return 0; }
PUBLIC void maLogRequest(HttpConn *conn) { HttpHost *host; HttpRx *rx; HttpTx *tx; HttpRoute *route; MprBuf *buf; char keyBuf[80], *timeText, *fmt, *cp, *qualifier, *value, c; int len; rx = conn->rx; tx = conn->tx; route = rx->route; host = httpGetConnContext(conn); if (host == 0) { return; } fmt = route->logFormat; if (fmt == 0) { return; } if (rx->method == 0) { return; } len = BIT_MAX_URI + 256; buf = mprCreateBuf(len, len); while ((c = *fmt++) != '\0') { if (c != '%' || (c = *fmt++) == '%') { mprPutCharToBuf(buf, c); continue; } switch (c) { case 'a': /* Remote IP */ mprPutStringToBuf(buf, conn->ip); break; case 'A': /* Local IP */ mprPutStringToBuf(buf, conn->sock->listenSock->ip); break; case 'b': if (tx->bytesWritten == 0) { mprPutCharToBuf(buf, '-'); } else { mprPutIntToBuf(buf, tx->bytesWritten); } break; case 'B': /* Bytes written (minus headers) */ mprPutIntToBuf(buf, (tx->bytesWritten - tx->headerSize)); break; case 'h': /* Remote host */ mprPutStringToBuf(buf, conn->ip); break; case 'n': /* Local host */ mprPutStringToBuf(buf, rx->parsedUri->host); break; case 'O': /* Bytes written (including headers) */ mprPutIntToBuf(buf, tx->bytesWritten); break; case 'r': /* First line of request */ mprPutToBuf(buf, "%s %s %s", rx->method, rx->uri, conn->protocol); break; case 's': /* Response code */ mprPutIntToBuf(buf, tx->status); break; case 't': /* Time */ mprPutCharToBuf(buf, '['); timeText = mprFormatLocalTime(MPR_DEFAULT_DATE, mprGetTime()); mprPutStringToBuf(buf, timeText); mprPutCharToBuf(buf, ']'); break; case 'u': /* Remote username */ mprPutStringToBuf(buf, conn->username ? conn->username : "******"); break; case '{': /* Header line */ qualifier = fmt; if ((cp = strchr(qualifier, '}')) != 0) { fmt = &cp[1]; *cp = '\0'; c = *fmt++; scopy(keyBuf, sizeof(keyBuf), "HTTP_"); scopy(&keyBuf[5], sizeof(keyBuf) - 5, qualifier); switch (c) { case 'i': value = (char*) mprLookupKey(rx->headers, supper(keyBuf)); mprPutStringToBuf(buf, value ? value : "-"); break; default: mprPutStringToBuf(buf, qualifier); } *cp = '}'; } else { mprPutCharToBuf(buf, c); } break; case '>': if (*fmt == 's') { fmt++; mprPutIntToBuf(buf, tx->status); } break; default: mprPutCharToBuf(buf, c); break; } } mprPutCharToBuf(buf, '\n'); mprAddNullToBuf(buf); mprWriteFile(route->log, mprGetBufStart(buf), mprGetBufLength(buf)); }
static void startCgi(MaQueue *q) { MaRequest *req; MaResponse *resp; MaConn *conn; MprCmd *cmd; MprHash *hp; cchar *baseName; char **argv, **envv, *fileName; int index, argc, varCount; argv = 0; argc = 0; conn = q->conn; req = conn->request; resp = conn->response; if ((req->form || req->flags & MA_REQ_UPLOADING) && conn->state <= MPR_HTTP_STATE_CONTENT) { /* Delay starting the CGI process if uploading files or a form request. This enables env vars to be defined with file upload and form data before starting the CGI gateway. */ return; } cmd = q->queueData = mprCreateCmd(req); if (conn->http->forkCallback) { cmd->forkCallback = conn->http->forkCallback; cmd->forkData = conn->http->forkData; } /* Build the commmand line arguments */ argc = 1; /* argv[0] == programName */ buildArgs(conn, cmd, &argc, &argv); fileName = argv[0]; baseName = mprGetPathBase(q, fileName); if (strncmp(baseName, "nph-", 4) == 0 || (strlen(baseName) > 4 && strcmp(&baseName[strlen(baseName) - 4], "-nph") == 0)) { /* Pretend we've seen the header for Non-parsed Header CGI programs */ cmd->userFlags |= MA_CGI_SEEN_HEADER; } /* Build environment variables */ varCount = mprGetHashCount(req->headers) + mprGetHashCount(req->formVars); envv = (char**) mprAlloc(cmd, (varCount + 1) * (int) sizeof(char*)); index = 0; hp = mprGetFirstHash(req->headers); while (hp) { if (hp->data) { envv[index] = mprStrcat(cmd, -1, hp->key, "=", (char*) hp->data, NULL); index++; } hp = mprGetNextHash(req->headers, hp); } hp = mprGetFirstHash(req->formVars); while (hp) { if (hp->data) { envv[index] = mprStrcat(cmd, -1, hp->key, "=", (char*) hp->data, NULL); index++; } hp = mprGetNextHash(req->formVars, hp); } envv[index] = 0; mprAssert(index <= varCount); cmd->stdoutBuf = mprCreateBuf(cmd, MA_BUFSIZE, -1); cmd->stderrBuf = mprCreateBuf(cmd, MA_BUFSIZE, -1); cmd->lastActivity = mprGetTime(cmd); mprSetCmdDir(cmd, mprGetPathDir(q, fileName)); mprSetCmdCallback(cmd, cgiCallback, conn); maSetHeader(conn, 0, "Last-Modified", req->host->currentDate); maDontCacheResponse(conn); maPutForService(q, maCreateHeaderPacket(q), 0); if (mprStartCmd(cmd, argc, argv, envv, MPR_CMD_IN | MPR_CMD_OUT | MPR_CMD_ERR) < 0) { maFailRequest(conn, MPR_HTTP_CODE_SERVICE_UNAVAILABLE, "Can't run CGI process: %s, URI %s", fileName, req->url); return; } /* This will dedicate this thread to the connection. It will also put the socket into blocking mode. */ maDedicateThreadToConn(conn); }
static void cgiEvent(MaQueue *q, MprCmd *cmd, int channel) { MaConn *conn; MaResponse *resp; MprBuf *buf; int space, nbytes, err; mprLog(cmd, 6, "CGI callback channel %d", channel); buf = 0; conn = q->conn; resp = conn->response; mprAssert(resp); cmd->lastActivity = mprGetTime(cmd); switch (channel) { case MPR_CMD_STDIN: writeToCGI(q->pair); return; case MPR_CMD_STDOUT: buf = cmd->stdoutBuf; break; case MPR_CMD_STDERR: buf = cmd->stderrBuf; break; } mprAssert(buf); mprResetBufIfEmpty(buf); /* Come here for CGI stdout, stderr events. ie. reading data from the CGI program. */ while (mprGetCmdFd(cmd, channel) >= 0) { /* Read as much data from the CGI as possible */ do { if ((space = mprGetBufSpace(buf)) == 0) { mprGrowBuf(buf, MA_BUFSIZE); if ((space = mprGetBufSpace(buf)) == 0) { break; } } nbytes = mprReadCmdPipe(cmd, channel, mprGetBufEnd(buf), space); mprLog(q, 5, "CGI: read from gateway %d on channel %d. errno %d", nbytes, channel, nbytes >= 0 ? 0 : mprGetOsError()); if (nbytes < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprLog(cmd, 5, "CGI read error %d for %", mprGetError(), (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprCloseCmdFd(cmd, channel); } else if (nbytes == 0) { /* This may reap the terminated child and thus clear cmd->process if both stderr and stdout are closed. */ mprLog(cmd, 5, "CGI EOF for %s", (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprCloseCmdFd(cmd, channel); break; } else { mprLog(cmd, 5, "CGI read %d bytes from %s", nbytes, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr"); mprAdjustBufEnd(buf, nbytes); traceData(cmd, mprGetBufStart(buf), nbytes); } } while ((space = mprGetBufSpace(buf)) > 0); if (mprGetBufLength(buf) == 0) { return; } if (channel == MPR_CMD_STDERR) { /* If we have an error message, send that to the client */ if (mprGetBufLength(buf) > 0) { mprAddNullToBuf(buf); mprLog(conn, 4, mprGetBufStart(buf)); if (writeToClient(q, cmd, buf, channel) < 0) { return; } maSetResponseCode(conn, MPR_HTTP_CODE_SERVICE_UNAVAILABLE); cmd->userFlags |= MA_CGI_SEEN_HEADER; cmd->status = 0; } } else { if (!(cmd->userFlags & MA_CGI_SEEN_HEADER) && !parseHeader(conn, cmd)) { return; } if (cmd->userFlags & MA_CGI_SEEN_HEADER) { if (writeToClient(q, cmd, buf, channel) < 0) { return; } } } } }
static void outputLine(HttpQueue *q, MprDirEntry *ep, cchar *path, int nameSize) { MprPath info; MprTime when; Dir *dir; char *newPath, sizeBuf[16], timeBuf[48], *icon; struct tm tm; bool isDir; int len; cchar *ext, *mimeType; char *dirSuffix; char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; dir = q->conn->data; if (ep->size >= (1024 * 1024 * 1024)) { fmtNum(sizeBuf, sizeof(sizeBuf), (int) ep->size, 1024 * 1024 * 1024, "G"); } else if (ep->size >= (1024 * 1024)) { fmtNum(sizeBuf, sizeof(sizeBuf), (int) ep->size, 1024 * 1024, "M"); } else if (ep->size >= 1024) { fmtNum(sizeBuf, sizeof(sizeBuf), (int) ep->size, 1024, "K"); } else { mprSprintf(sizeBuf, sizeof(sizeBuf), "%6d", (int) ep->size); } newPath = mprJoinPath(path, ep->name); if (mprGetPathInfo(newPath, &info) < 0) { when = mprGetTime(); isDir = 0; } else { isDir = info.isDir ? 1 : 0; when = (MprTime) info.mtime * MPR_TICKS_PER_SEC; } if (isDir) { icon = "folder"; dirSuffix = "/"; } else { ext = mprGetPathExt(ep->name); if (ext && (mimeType = mprLookupMime(q->conn->rx->route->mimeTypes, ext)) != 0) { if (strcmp(ext, "es") == 0 || strcmp(ext, "ejs") == 0 || strcmp(ext, "php") == 0) { icon = "text"; } else if (strstr(mimeType, "text") != 0) { icon = "text"; } else { icon = "compressed"; } } else { icon = "compressed"; } dirSuffix = ""; } mprDecodeLocalTime(&tm, when); mprSprintf(timeBuf, sizeof(timeBuf), "%02d-%3s-%4d %02d:%02d", tm.tm_mday, months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour, tm.tm_min); len = (int) strlen(ep->name) + (int) strlen(dirSuffix); if (dir->fancyIndexing == 2) { httpWrite(q, "<tr><td valign=\"top\">"); httpWrite(q, "<img src=\"/icons/%s.gif\" alt=\"[ ]\", /></td>", icon); httpWrite(q, "<td><a href=\"%s%s\">%s%s</a></td>", ep->name, dirSuffix, ep->name, dirSuffix); httpWrite(q, "<td>%s</td><td>%s</td></tr>\r\n", timeBuf, sizeBuf); } else if (dir->fancyIndexing == 1) { httpWrite(q, "<img src=\"/icons/%s.gif\" alt=\"[ ]\", /> ", icon); httpWrite(q, "<a href=\"%s%s\">%s%s</a>%-*s %17s %4s\r\n", ep->name, dirSuffix, ep->name, dirSuffix, nameSize - len, "", timeBuf, sizeBuf); } else { httpWrite(q, "<li><a href=\"%s%s\"> %s%s</a></li>\r\n", ep->name, dirSuffix, ep->name, dirSuffix); } }