void maClientUnlock(MaClient *cp) { mprAssert(cp); cp->unlock(); }
/* * Parse the request headers. Return true if the header parsed. */ static bool parseHeaders(MaConn *conn, MaPacket *packet) { MaHostAddress *address; MaRequest *req; MaHost *host, *hp; MaLimits *limits; MprBuf *content; char keyBuf[MPR_MAX_STRING]; char *key, *value, *cp, *tok; int count, keepAlive; req = conn->request; host = req->host; content = packet->content; conn->request->headerPacket = packet; limits = &conn->http->limits; keepAlive = 0; strcpy(keyBuf, "HTTP_"); mprAssert(strstr((char*) content->start, "\r\n")); for (count = 0; content->start[0] != '\r' && !conn->connectionFailed; count++) { if (count >= limits->maxNumHeaders) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Too many headers"); return 0; } if ((key = getToken(conn, ":")) == 0 || *key == '\0') { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad header format"); return 0; } value = getToken(conn, "\r\n"); while (isspace((int) *value)) { value++; } if (conn->requestFailed) { continue; } mprStrUpper(key); for (cp = key; *cp; cp++) { if (*cp == '-') { *cp = '_'; } } mprLog(req, 8, "Key %s, value %s", key, value); if (strspn(key, "%<>/\\") > 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad header key value"); continue; } /* * Define the header with a "HTTP_" prefix */ mprStrcpy(&keyBuf[5], sizeof(keyBuf) - 5, key); mprAddDuplicateHash(req->headers, keyBuf, value); switch (key[0]) { case 'A': if (strcmp(key, "AUTHORIZATION") == 0) { value = mprStrdup(req, value); req->authType = mprStrTok(value, " \t", &tok); req->authDetails = tok; } else if (strcmp(key, "ACCEPT_CHARSET") == 0) { req->acceptCharset = value; } else if (strcmp(key, "ACCEPT") == 0) { req->accept = value; } else if (strcmp(key, "ACCEPT_ENCODING") == 0) { req->acceptEncoding = value; } break; case 'C': if (strcmp(key, "CONTENT_LENGTH") == 0) { if (req->length >= 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Mulitple content length headers"); continue; } req->length = mprAtoi(value, 10); if (req->length < 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad content length"); continue; } if (req->length >= host->limits->maxBody) { maFailConnection(conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, "Request content length %Ld is too big. Limit %Ld", req->length, host->limits->maxBody); continue; } mprAssert(req->length >= 0); req->remainingContent = req->length; req->contentLengthStr = value; } else if (strcmp(key, "CONTENT_RANGE") == 0) { /* * This headers specifies the range of any posted body data * Format is: Content-Range: bytes n1-n2/length * Where n1 is first byte pos and n2 is last byte pos */ char *sp; int start, end, size; start = end = size = -1; sp = value; while (*sp && !isdigit((int) *sp)) { sp++; } if (*sp) { start = (int) mprAtoi(sp, 10); if ((sp = strchr(sp, '-')) != 0) { end = (int) mprAtoi(++sp, 10); } if ((sp = strchr(sp, '/')) != 0) { /* * Note this is not the content length transmitted, but the original size of the input of which * the client is transmitting only a portion. */ size = (int) mprAtoi(++sp, 10); } } if (start < 0 || end < 0 || size < 0 || end <= start) { maFailRequest(conn, MPR_HTTP_CODE_RANGE_NOT_SATISFIABLE, "Bad content range"); continue; } req->inputRange = maCreateRange(conn, start, end); } else if (strcmp(key, "CONTENT_TYPE") == 0) { req->mimeType = value; req->form = strstr(value, "application/x-www-form-urlencoded") != 0; } else if (strcmp(key, "COOKIE") == 0) { if (req->cookie && *req->cookie) { req->cookie = mprStrcat(req, -1, req->cookie, "; ", value, NULL); } else { req->cookie = value; } } else if (strcmp(key, "CONNECTION") == 0) { req->connection = value; if (mprStrcmpAnyCase(value, "KEEP-ALIVE") == 0) { keepAlive++; } else if (mprStrcmpAnyCase(value, "CLOSE") == 0) { conn->keepAliveCount = 0; } if (!host->keepAlive) { conn->keepAliveCount = 0; } } break; case 'F': req->forwarded = value; break; case 'H': if (strcmp(key, "HOST") == 0) { req->hostName = value; address = conn->address; if (maIsNamedVirtualHostAddress(address)) { hp = maLookupVirtualHost(address, value); if (hp == 0) { maFailRequest(conn, 404, "No host to serve request. Searching for %s", value); mprLog(conn, 1, "Can't find virtual host %s", value); continue; } req->host = hp; /* * Reassign this request to a new host */ maRemoveConn(host, conn); host = hp; conn->host = hp; maAddConn(hp, conn); } } break; case 'I': if ((strcmp(key, "IF_MODIFIED_SINCE") == 0) || (strcmp(key, "IF_UNMODIFIED_SINCE") == 0)) { MprTime newDate = 0; char *cp; bool ifModified = (key[3] == 'M'); if ((cp = strchr(value, ';')) != 0) { *cp = '\0'; } if (mprParseTime(conn, &newDate, value, MPR_UTC_TIMEZONE, NULL) < 0) { mprAssert(0); break; } if (newDate) { setIfModifiedDate(conn, newDate, ifModified); req->flags |= MA_REQ_IF_MODIFIED; } } else if ((strcmp(key, "IF_MATCH") == 0) || (strcmp(key, "IF_NONE_MATCH") == 0)) { char *word, *tok; bool ifMatch = key[3] == 'M'; if ((tok = strchr(value, ';')) != 0) { *tok = '\0'; } req->ifMatch = ifMatch; req->flags |= MA_REQ_IF_MODIFIED; value = mprStrdup(conn, value); word = mprStrTok(value, " ,", &tok); while (word) { addMatchEtag(conn, word); word = mprStrTok(0, " ,", &tok); } } else if (strcmp(key, "IF_RANGE") == 0) { char *word, *tok; if ((tok = strchr(value, ';')) != 0) { *tok = '\0'; } req->ifMatch = 1; req->flags |= MA_REQ_IF_MODIFIED; value = mprStrdup(conn, value); word = mprStrTok(value, " ,", &tok); while (word) { addMatchEtag(conn, word); word = mprStrTok(0, " ,", &tok); } } break; case 'P': if (strcmp(key, "PRAGMA") == 0) { req->pragma = value; } break; case 'R': if (strcmp(key, "RANGE") == 0) { if (!parseRange(conn, value)) { maFailRequest(conn, MPR_HTTP_CODE_RANGE_NOT_SATISFIABLE, "Bad range"); } } else if (strcmp(key, "REFERER") == 0) { /* NOTE: yes the header is misspelt in the spec */ req->referer = value; } break; case 'T': if (strcmp(key, "TRANSFER_ENCODING") == 0) { mprStrLower(value); if (strcmp(value, "chunked") == 0) { req->flags |= MA_REQ_CHUNKED; /* * This will be revised by the chunk filter as chunks are processed and will be set to zero when the * last chunk has been received. */ req->remainingContent = MAXINT; } } break; #if BLD_DEBUG case 'X': if (strcmp(key, "X_APPWEB_CHUNK_SIZE") == 0) { mprStrUpper(value); conn->response->chunkSize = atoi(value); if (conn->response->chunkSize <= 0) { conn->response->chunkSize = 0; } else if (conn->response->chunkSize > conn->http->limits.maxChunkSize) { conn->response->chunkSize = conn->http->limits.maxChunkSize; } } break; #endif case 'U': if (strcmp(key, "USER_AGENT") == 0) { req->userAgent = value; } break; } } if (conn->protocol == 0 && !keepAlive) { conn->keepAliveCount = 0; } if (!(req->flags & MA_REQ_CHUNKED)) { /* * Step over "\r\n" after headers. As an optimization, don't do this if chunked so chunking can parse a single * chunk delimiter of "\r\nSIZE ...\r\n" */ mprAdjustBufStart(content, 2); } mprLog(conn, 3, "Select host \"%s\"", conn->host->name); if (maSetRequestUri(conn, req->url, "") < 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad URI format"); return 0; } if (conn->host->secure) { req->parsedUri->scheme = mprStrdup(req, "https"); } req->parsedUri->port = conn->sock->port; req->parsedUri->host = req->hostName ? req->hostName : conn->host->name; return 1; }
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); }
/* Set the parse arg */ void mprXmlSetParseArg(MprXml *xp, void *parseArg) { mprAssert(xp); xp->parseArg = parseArg; }
/* Parse an XML file. Return 0 for success, -1 for error. */ int mprXmlParse(MprXml *xp) { mprAssert(xp); return parseNext(xp, MPR_XML_BEGIN); }
void MprLogService::error(char *file, int line, int flags, char *fmt, va_list args) { static int recurseGate = 0; Mpr *mpr; char msg[MPR_MAX_LOG_STRING], buf[MPR_MAX_LOG_STRING]; mpr = mprGetMpr(); // // Errors post close // #if BLD_FEATURE_MULTITHREAD if (mutex == 0) { return; } #endif lock(); if (recurseGate > 0) { unlock(); return; } recurseGate++; mprVsprintf(msg, sizeof(msg), fmt, args); if (flags & MPR_TRAP) { mprSprintf(buf, sizeof(buf), "Assertion %s, failed (%s:%d)\n", msg, file, line); output(defaultModule, flags, MPR_ERROR, buf); // // Break to the debugger // breakpoint(file, line); } else if (flags & MPR_LOG) { // // Write to both the O/S log and to the trace log // mprSprintf(buf, sizeof(buf), "Error: %s\n", msg); output(defaultModule, flags, MPR_ERROR, buf); if (mpr) { mpr->writeToOsLog(buf, flags | MPR_INFO); } } else if (flags & MPR_USER) { mprSprintf(buf, sizeof(buf), "Error: %s\n", msg); output(defaultModule, flags, MPR_ERROR, buf); if (mpr) { mpr->writeToOsLog(buf, flags | MPR_WARN); } } else if (flags & MPR_ALERT) { // FUTURE -- TBD } else { mprAssert(0); } recurseGate--; unlock(); }
/* Lexical analyser for XML. Return the next token reading input as required. It uses a one token look ahead and push back mechanism (LAR1 parser). Text token identifiers are left in the tokBuf parser buffer on exit. This Lex has special cases for the states MPR_XML_ELT_DATA where we have an optimized read of element data, and MPR_XML_AFTER_LS where we distinguish between element names, processing instructions and comments. */ static MprXmlToken getXmlToken(MprXml *xp, int state) { MprBuf *tokBuf; char *cp; int c, rc; mprAssert(state >= 0); tokBuf = xp->tokBuf; if ((c = getNextChar(xp)) < 0) { return MPR_XMLTOK_EOF; } mprFlushBuf(tokBuf); /* Special case parsing for names and for element data. We do this for performance so we can return to the caller the largest token possible. */ if (state == MPR_XML_ELT_DATA) { /* Read all the data up to the start of the closing element "<" or the start of a sub-element. */ if (c == '<') { if ((c = getNextChar(xp)) < 0) { return MPR_XMLTOK_EOF; } if (c == '/') { return MPR_XMLTOK_LS_SLASH; } putLastChar(xp, c); return MPR_XMLTOK_LS; } do { if (mprPutCharToBuf(tokBuf, c) < 0) { return MPR_XMLTOK_TOO_BIG; } if ((c = getNextChar(xp)) < 0) { return MPR_XMLTOK_EOF; } } while (c != '<'); /* Put back the last look-ahead character */ putLastChar(xp, c); /* If all white space, then zero the token buffer */ for (cp = tokBuf->start; *cp; cp++) { if (!isspace((uchar) *cp & 0x7f)) { return MPR_XMLTOK_TEXT; } } mprFlushBuf(tokBuf); return MPR_XMLTOK_TEXT; } while (1) { switch (c) { case ' ': case '\n': case '\t': case '\r': break; case '<': if ((c = getNextChar(xp)) < 0) { return MPR_XMLTOK_EOF; } if (c == '/') { return MPR_XMLTOK_LS_SLASH; } putLastChar(xp, c); return MPR_XMLTOK_LS; case '=': return MPR_XMLTOK_EQ; case '>': return MPR_XMLTOK_GR; case '/': if ((c = getNextChar(xp)) < 0) { return MPR_XMLTOK_EOF; } if (c == '>') { return MPR_XMLTOK_SLASH_GR; } return MPR_XMLTOK_ERR; case '\"': case '\'': xp->quoteChar = c; /* Fall through */ default: /* We handle element names, attribute names and attribute values here. We do NOT handle data between elements here. Read the token. Stop on white space or a closing element ">" */ if (xp->quoteChar) { if ((c = getNextChar(xp)) < 0) { return MPR_XMLTOK_EOF; } while (c != xp->quoteChar) { if (mprPutCharToBuf(tokBuf, c) < 0) { return MPR_XMLTOK_TOO_BIG; } if ((c = getNextChar(xp)) < 0) { return MPR_XMLTOK_EOF; } } xp->quoteChar = 0; } else { while (!isspace((uchar) c) && c != '>' && c != '/' && c != '=') { if (mprPutCharToBuf(tokBuf, c) < 0) { return MPR_XMLTOK_TOO_BIG; } if ((c = getNextChar(xp)) < 0) { return MPR_XMLTOK_EOF; } } putLastChar(xp, c); } if (mprGetBufLength(tokBuf) < 0) { return MPR_XMLTOK_ERR; } mprAddNullToBuf(tokBuf); if (state == MPR_XML_AFTER_LS) { /* If we are just inside an element "<", then analyze what we have to see if we have an element name, instruction or comment. Tokbuf will hold "?" for instructions or "!--" for comments. */ if (mprLookAtNextCharInBuf(tokBuf) == '?') { /* Just ignore processing instructions */ rc = scanFor(xp, "?>"); if (rc < 0) { return MPR_XMLTOK_TOO_BIG; } else if (rc == 0) { return MPR_XMLTOK_ERR; } return MPR_XMLTOK_INSTRUCTIONS; } else if (mprLookAtNextCharInBuf(tokBuf) == '!') { if (strncmp((char*) tokBuf->start, "![CDATA[", 8) == 0) { mprAdjustBufStart(tokBuf, 8); rc = scanFor(xp, "]]>"); if (rc < 0) { return MPR_XMLTOK_TOO_BIG; } else if (rc == 0) { return MPR_XMLTOK_ERR; } return MPR_XMLTOK_CDATA; } else { mprFlushBuf(tokBuf); rc = scanFor(xp, "-->"); if (rc < 0) { return MPR_XMLTOK_TOO_BIG; } else if (rc == 0) { return MPR_XMLTOK_ERR; } return MPR_XMLTOK_COMMENT; } } } trimToken(xp); return MPR_XMLTOK_TEXT; } if ((c = getNextChar(xp)) < 0) { return MPR_XMLTOK_EOF; } } /* Should never get here */ mprAssert(0); return MPR_XMLTOK_ERR; }
int MprSocket::read(char *buf, int bufsize) { #if BLD_FEATURE_IPV6 struct sockaddr_storage server6; #endif struct sockaddr_in server; struct sockaddr *sa; MprSocklen addrlen; int bytes, errCode; mprAssert(buf); mprAssert(bufsize > 0); mprAssert(~(flags & MPR_SOCKET_CLOSED)); lock(); if (flags & MPR_SOCKET_EOF) { unlock(); return 0; } again: if (flags & MPR_SOCKET_DATAGRAM) { #if BLD_FEATURE_IPV6 if (ipv6) { sa = (struct sockaddr*) &server6; addrlen = sizeof(server6); } else #endif { sa = (struct sockaddr*) &server; addrlen = sizeof(server); } bytes = recvfrom(sock, buf, bufsize, MSG_NOSIGNAL, sa, (SocketLenPtr) &addrlen); } else { bytes = recv(sock, buf, bufsize, MSG_NOSIGNAL); } if (bytes < 0) { errCode = getError(); if (errCode == EINTR) { goto again; } else if (errCode == EAGAIN || errCode == EWOULDBLOCK) { bytes = 0; // No data available } else if (errCode == ECONNRESET) { flags |= MPR_SOCKET_EOF; // Disorderly disconnect bytes = 0; } else { flags |= MPR_SOCKET_EOF; // Some other error bytes = -errCode; } } else if (bytes == 0) { // EOF flags |= MPR_SOCKET_EOF; mprLog(8, log, "%d: read: %d bytes, EOF\n", sock, bytes); } else { mprLog(8, log, "%d: read: %d bytes\n", sock, bytes); } unlock(); return bytes; }
struct hostent *mprGetHostByName(char *name) { Mpr *mpr; struct hostent *hp; mpr = mprGetMpr(); hp = new hostent; memset(hp, 0, sizeof(struct hostent)); mpr->lock(); #if VXWORKS struct in_addr inaddr; inaddr.s_addr = (ulong) hostGetByName(name); if (inaddr.s_addr < 0) { mpr->unlock(); mprAssert(0); return 0; } hp->h_addrtype = AF_INET; hp->h_length = sizeof(int); hp->h_name = mprStrdup(name); hp->h_addr_list = 0; hp->h_aliases = 0; hp->h_addr_list = new char*[2]; hp->h_addr_list[0] = (char *) mprMalloc(sizeof(struct in_addr)); memcpy(&hp->h_addr_list[0], &inaddr, hp->h_length); hp->h_addr_list[1] = 0; #else struct hostent *ip; int count, i; #undef gethostbyname ip = gethostbyname(name); if (ip == 0) { mpr->unlock(); return 0; } hp->h_addrtype = ip->h_addrtype; hp->h_length = ip->h_length; hp->h_name = mprStrdup(ip->h_name); hp->h_addr_list = 0; hp->h_aliases = 0; for (count = 0; ip->h_addr_list[count] != 0; ) { count++; } if (count > 0) { count++; hp->h_addr_list = new char*[count]; for (i = 0; ip->h_addr_list[i] != 0; i++) { memcpy(&hp->h_addr_list[i], &ip->h_addr_list[i], ip->h_length); } hp->h_addr_list[i] = 0; } for (count = 0; ip->h_aliases[count] != 0; ) { count++; } if (count > 0) { count++; hp->h_aliases = new char*[count]; for (i = 0; ip->h_aliases[i] != 0; i++) { hp->h_aliases[i] = mprStrdup(ip->h_aliases[i]); } hp->h_aliases[i] = 0; } #endif mpr->unlock(); return hp; }
int ejsSetPropertyTrait(Ejs *ejs, EjsVar *vp, int slot, EjsType *propType, int attributes) { mprAssert(vp->type->helpers->setPropertyTrait); return (vp->type->helpers->setPropertyTrait)(ejs, vp, slot, propType, attributes); }
/* Convert the string buffer to a Number. */ static MprNumber parseNumber(Ejs *ejs, cchar *str) { int64 num; int radix, c, negative; mprAssert(str); num = 0; negative = 0; if (*str == '-') { str++; negative = 1; } else if (*str == '+') { str++; } #if BLD_FEATURE_FLOATING_POINT if (*str != '.' && !isdigit((int) *str)) { return ejs->nanValue->value; } /* Floating format: [DIGITS].[DIGITS][(e|E)[+|-]DIGITS] */ if (!(*str == '0' && tolower((int) str[1]) == 'x')) { MprNumber n; cchar *cp; for (cp = str; *cp; cp++) { if (*cp == '.' || tolower((int) *cp) == 'e') { n = atof(str); if (negative) { n = 0.0 - n; } return n; } } } #else if (*str != '.' && !isdigit(*str)) { return 0; } #endif /* Parse an integer. Observe hex and octal prefixes (0x, 0). */ if (*str != '0') { /* * Normal numbers (Radix 10) */ while (isdigit((int) *str)) { num = (*str - '0') + (num * 10); str++; } } else { str++; if (tolower((int) *str) == 'x') { str++; radix = 16; while (*str) { c = tolower((int) *str); if (isdigit(c)) { num = (c - '0') + (num * radix); } else if (c >= 'a' && c <= 'f') { num = (c - 'a' + 10) + (num * radix); } else { break; } str++; } } else{ radix = 8; while (*str) { c = tolower((int) *str); if (isdigit(c) && c < '8') { num = (c - '0') + (num * radix); } else { break; } str++; } } } if (negative) { return (MprNumber) (0 - num); } return (MprNumber) num; }
/* Set the property name and return the slot number. Slot may be -1 to allocate a new slot. */ int ejsSetPropertyName(Ejs *ejs, EjsVar *vp, int slot, EjsName *qname) { mprAssert(vp->type->helpers->setPropertyName); return (vp->type->helpers->setPropertyName)(ejs, vp, slot, qname); }
/** Return the number of properties in the variable. @return Returns the number of properties. */ int ejsGetPropertyCount(Ejs *ejs, EjsVar *vp) { mprAssert(vp->type->helpers->getPropertyCount); return (vp->type->helpers->getPropertyCount)(ejs, vp); }
static int compileInner(EcCompiler *cp, int argc, char **argv) { Ejs *ejs; EjsModule *mp; MprList *nodes; EjsBlock *block; EcLocation loc; cchar *ext; char *msg; int next, i, j, nextModule, lflags, rc, paused; ejs = cp->ejs; if ((nodes = mprCreateList(-1, 0)) == 0) { return EJS_ERR; } cp->nodes = nodes; /* Warn about source files mentioned multiple times. TODO OPT. This is slow. */ for (i = 0; i < argc; i++) { for (j = 0; j < argc; j++) { if (i == j) { continue; } if (mprSamePath(argv[i], argv[j])) { compileError(cp, "Loading source %s multiple times. Ignoring extra copies.", argv[i]); return EJS_ERR; } } if (cp->outputFile && mprSamePath(cp->outputFile, argv[i])) { compileError(cp, "Output file is the same as input file: %s", argv[i]); return EJS_ERR; } } /* Compile source files and load any module files */ for (i = 0; i < argc && !cp->fatalError; i++) { ext = mprGetPathExt(argv[i]); if (scasecmp(ext, "mod") == 0 || scasecmp(ext, BIT_SHOBJ) == 0) { nextModule = mprGetListLength(ejs->modules); lflags = cp->strict ? EJS_LOADER_STRICT : 0; if ((rc = ejsLoadModule(cp->ejs, ejsCreateStringFromAsc(ejs, argv[i]), -1, -1, lflags)) < 0) { msg = sfmt("Error initializing module %s\n%s", argv[i], ejsGetErrorMsg(cp->ejs, 1)); memset(&loc, 0, sizeof(EcLocation)); loc.filename = sclone(argv[i]); if (rc == MPR_ERR_CANT_INITIALIZE) { ecError(cp, "Error", &loc, msg); } else { ecError(cp, "Error", &loc, msg); } cp->nodes = NULL; return EJS_ERR; } if (cp->merge) { /* If merging, we must emit the loaded module into the output. So add to the compiled modules list. */ for (next = nextModule; (mp = mprGetNextItem(ejs->modules, &next)) != 0; ) { if (mprLookupItem(cp->modules, mp) < 0 && mprAddItem(cp->modules, mp) < 0) { compileError(cp, "Can't add module %s", mp->name); } } } mprAddItem(nodes, 0); } else { mprAssert(!MPR->marking); paused = ejsBlockGC(ejs); mprAddItem(nodes, ecParseFile(cp, argv[i])); ejsUnblockGC(ejs, paused); } mprAssert(!MPR->marking); } mprAssert(ejs->result == 0 || (MPR_GET_GEN(MPR_GET_MEM(ejs->result)) != MPR->heap->dead)); /* Allocate the eval frame stack. This is used for property lookups. We have one dummy block at the top always. */ block = ejsCreateBlock(ejs, 0); mprSetName(block, "Compiler"); ejsPushBlock(ejs, block); /* Process the internal representation and generate code */ paused = ejsBlockGC(ejs); if (!cp->parseOnly && cp->errorCount == 0) { ecResetParser(cp); if (ecAstProcess(cp) < 0) { ejsPopBlock(ejs); cp->nodes = NULL; ejsUnblockGC(ejs, paused); return EJS_ERR; } if (cp->errorCount == 0) { ecResetParser(cp); if (ecCodeGen(cp) < 0) { ejsPopBlock(ejs); cp->nodes = NULL; ejsUnblockGC(ejs, paused); return EJS_ERR; } } } ejsPopBlock(ejs); mprAssert(ejs->result == 0 || (MPR_GET_GEN(MPR_GET_MEM(ejs->result)) != MPR->heap->dead)); /* Add compiled modules to the interpreter */ for (next = 0; ((mp = (EjsModule*) mprGetNextItem(cp->modules, &next)) != 0); ) { ejsAddModule(cp->ejs, mp); } cp->nodes = NULL; ejsUnblockGC(ejs, paused); if (!paused) { mprYield(0); } mprAssert(ejs->result == 0 || (MPR_GET_GEN(MPR_GET_MEM(ejs->result)) != MPR->heap->dead)); return (cp->errorCount > 0) ? EJS_ERR: 0; }
/* Configure the server. If the configFile is defined, use it. If not, then consider home, documents, ip and port. */ int maConfigureServer(MaServer *server, cchar *configFile, cchar *home, cchar *documents, cchar *ip, int port) { MaAppweb *appweb; Http *http; HttpEndpoint *endpoint; HttpHost *host; HttpRoute *route; char *path; appweb = server->appweb; http = appweb->http; if (configFile) { path = mprGetAbsPath(configFile); if (maParseConfig(server, path, 0) < 0) { /* mprUserError("Can't configure server using %s", path); */ return MPR_ERR_CANT_INITIALIZE; } return 0; } else { mprLog(2, "DocumentRoot %s", documents); if ((endpoint = httpCreateConfiguredEndpoint(home, documents, ip, port)) == 0) { return MPR_ERR_CANT_OPEN; } maAddEndpoint(server, endpoint); host = mprGetFirstItem(endpoint->hosts); mprAssert(host); route = mprGetFirstItem(host->routes); mprAssert(route); #if UNUSED searchPath = getSearchPath(dir); mprSetModuleSearchPath(searchPath); #endif #if BLD_FEATURE_CGI maLoadModule(appweb, "cgiHandler", "mod_cgi"); if (httpLookupStage(http, "cgiHandler")) { httpAddRouteHandler(route, "cgiHandler", "cgi cgi-nph bat cmd pl py"); /* Add cgi-bin with a route for the /cgi-bin URL prefix. */ path = "cgi-bin"; if (mprPathExists(path, X_OK)) { HttpRoute *cgiRoute; cgiRoute = httpCreateAliasRoute(route, "/cgi-bin/", path, 0); mprLog(4, "ScriptAlias \"/cgi-bin/\":\"%s\"", path); httpSetRouteHandler(cgiRoute, "cgiHandler"); httpFinalizeRoute(cgiRoute); } } #endif #if BLD_FEATURE_ESP maLoadModule(appweb, "espHandler", "mod_esp"); if (httpLookupStage(http, "espHandler")) { httpAddRouteHandler(route, "espHandler", "esp"); } #endif #if BLD_FEATURE_EJSCRIPT maLoadModule(appweb, "ejsHandler", "mod_ejs"); if (httpLookupStage(http, "ejsHandler")) { httpAddRouteHandler(route, "ejsHandler", "ejs"); } #endif #if BLD_FEATURE_PHP maLoadModule(appweb, "phpHandler", "mod_php"); if (httpLookupStage(http, "phpHandler")) { httpAddRouteHandler(route, "phpHandler", "php"); } #endif httpAddRouteHandler(route, "fileHandler", ""); httpFinalizeRoute(route); } if (home) { maSetServerHome(server, home); } if (ip || port > 0) { maSetServerAddress(server, ip, port); } return 0; }
int MprSocket::openServer(char *addr, int portNum, MprSocketAcceptProc acceptFn, void *data, int initialFlags) { #if BLD_FEATURE_IPV6 struct addrinfo hints, *res; struct sockaddr_storage sockAddr6; char portNumString[MPR_MAX_IP_PORT]; char addrBuf[MPR_MAX_IP_ADDR]; char *bindName; #endif struct sockaddr_in sockAddr; struct sockaddr *sa; struct hostent *hostent; MprSocklen addrlen; int datagram, rc; mprAssert(addr); if (addr == 0 || *addr == '\0') { mprLog(6, log, "openServer: *:%d, flags %x\n", portNum, initialFlags); } else { mprLog(6, log, "openServer: %s:%d, flags %x\n", addr, portNum, initialFlags); } #if BLD_FEATURE_IPV6 if (addr[0] == '[') { ipv6 = 1; mprStrcpy(addrBuf, sizeof(addrBuf), &addr[1]); mprAssert(addrBuf[strlen(addrBuf) - 1] == ']'); addrBuf[strlen(addrBuf) - 1] = '\0'; addr = addrBuf; } if (ipv6) { memset((char *) &hints, '\0', sizeof(hints)); memset((char *) &sockAddr6, '\0', sizeof(struct sockaddr_storage)); mprSprintf(portNumString, sizeof(portNumString), "%d", portNum); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_INET6; if (strcmp(addr, "") != 0) { bindName = addr; } else { bindName = NULL; hints.ai_flags |= AI_PASSIVE; /* Bind to 0.0.0.0 and :: */ /* Sets to IN6ADDR_ANY_INIT */ } rc = getaddrinfo(bindName, portNumString, &hints, &res); if (rc) { return MPR_ERR_CANT_OPEN; } sa = (struct sockaddr*) &sockAddr6; memcpy(sa, res->ai_addr, res->ai_addrlen); addrlen = res->ai_addrlen; freeaddrinfo(res); } else #endif { /* * TODO could we use getaddrinfo in all cases. ie. merge with IPV6 code */ memset((char *) &sockAddr, '\0', sizeof(struct sockaddr_in)); addrlen = sizeof(struct sockaddr_in); sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons((short) (portNum & 0xFFFF)); if (strcmp(addr, "") != 0) { sockAddr.sin_addr.s_addr = inet_addr(addr); if (sockAddr.sin_addr.s_addr == INADDR_NONE) { hostent = mprGetHostByName(addr); if (hostent != 0) { memcpy((char*) &sockAddr.sin_addr, (char*) hostent->h_addr_list[0], (size_t) hostent->h_length); mprFreeGetHostByName(hostent); } else { return MPR_ERR_NOT_FOUND; } } } else { sockAddr.sin_addr.s_addr = INADDR_ANY; } sa = (struct sockaddr*) &sockAddr; } lock(); port = portNum; acceptCallback = acceptFn; acceptData = data; flags = (initialFlags & (MPR_SOCKET_BROADCAST | MPR_SOCKET_DATAGRAM | MPR_SOCKET_BLOCK | MPR_SOCKET_LISTENER | MPR_SOCKET_NOREUSE | MPR_SOCKET_NODELAY)); ipAddr = mprStrdup(addr); datagram = flags & MPR_SOCKET_DATAGRAM; // // Create the O/S socket // sock = socket(sa->sa_family, datagram ? SOCK_DGRAM: SOCK_STREAM, 0); if (sock < 0) { unlock(); return MPR_ERR_CANT_OPEN; } #if !WIN && !WINCE && !VXWORKS fcntl(sock, F_SETFD, FD_CLOEXEC); // Children won't inherit this fd #endif #if CYGWIN || LINUX || MACOSX || VXWORKS || FREEBSD if (!(flags & MPR_SOCKET_NOREUSE)) { rc = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &rc, sizeof(rc)); } #endif rc = bind(sock, sa, addrlen); // rc = bind(sock, res->ai_addr, res->ai_addrlen); if (rc < 0) { int err = errno; err = err; ::closesocket(sock); sock = -1; unlock(); return MPR_ERR_CANT_OPEN; } if (! datagram) { flags |= MPR_SOCKET_LISTENER; if (listen(sock, SOMAXCONN) < 0) { ::closesocket(sock); sock = -1; unlock(); return MPR_ERR_CANT_OPEN; } handler = new MprSelectHandler(sock, MPR_SOCKET_READABLE, (MprSelectProc) acceptProcWrapper, (void*) this, handlerPriority); } handlerMask |= MPR_SOCKET_READABLE; //TODO - what about WINCE? #if WIN // // Delay setting reuse until now so that we can be assured that we // have exclusive use of the port. // if (!(flags & MPR_SOCKET_NOREUSE)) { rc = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &rc, sizeof(rc)); } #endif setBlockingMode((bool) (flags & MPR_SOCKET_BLOCK)); // // TCP/IP stacks have the No delay option (nagle algorithm) on by default. // if (flags & MPR_SOCKET_NODELAY) { setNoDelay(1); } unlock(); return sock; }
int MprLogService::setLogSpec(char *fileSpec) { MprLogModule *mp; MprLogListener *lp; char namBuf[MPR_MAX_FNAME]; char *spec, *cp, *sizeStr; int maxSize; mprAssert(!logging); #if BLD_FEATURE_MULTITHREAD if (mutex == 0) { mutex = new MprMutex(); } #endif lock(); logging = 1; if (fileSpec == NULL || *fileSpec == '\0') { fileSpec = "trace.txt"; } logSpec = mprStrdup(fileSpec); spec = mprStrdup(fileSpec); for (cp = spec; *cp; cp++) { if (*cp == ':' && isdigit(cp[1])) { break; } } maxSize = MPR_MAX_LOG_SIZE; if (*cp) { *cp++ = '\0'; moduleSpecs = strchr(cp, ','); sizeStr = strchr(cp, '.'); if (sizeStr != 0) { *sizeStr++ = '\0'; maxSize = atoi(sizeStr); // Size in MB } // // Set all modules to the default trace level and then examine // the modules spec to override specified modules // defaultLevel = atoi(cp); if (defaultLevel < 0) { defaultLevel = 0; } mp = (MprLogModule*) moduleList.getFirst(); while (mp) { mp->setLevel(defaultLevel); mp = (MprLogModule*) moduleList.getNext(mp); } if (moduleSpecs) { moduleSpecs++; mp = (MprLogModule*) moduleList.getFirst(); while (mp) { mprSprintf(namBuf, sizeof(namBuf), "%s:", mp->getName()); if ((cp = strstr(moduleSpecs, namBuf)) != 0) { if ((cp = strchr(cp, ':')) != 0) { mp->setLevel(atoi(++cp)); } } mp = (MprLogModule*) moduleList.getNext(mp); } } } if (spec != 0 && *spec != '\0') { lp = (MprLogListener*) listeners.getFirst(); while (lp) { if (lp->setLogSpec(spec, maxSize) < 0) { mprFree(spec); unlock(); return MPR_ERR_CANT_OPEN; } lp = (MprLogListener*) listeners.getNext(lp); } } mprFree(spec); unlock(); return 0; }
int MprSocket::openClient(char *addr, int portNum, int initialFlags) { #if BLD_FEATURE_IPV6 struct addrinfo hints, *res; struct sockaddr_storage remoteAddr6; char portNum_string[MPR_MAX_IP_PORT]; char addrBuf[MPR_MAX_IP_ADDR]; #endif struct sockaddr_in remoteAddr; struct hostent *hostent; struct sockaddr *sa; MprSocklen addrlen; int broadcast, datagram, rc, err; mprLog(6, log, "openClient: %s:%d, flags %x\n", addr, portNum, initialFlags); #if BLD_FEATURE_IPV6 if (addr[0] == '[') { ipv6 = 1; mprStrcpy(addrBuf, sizeof(addr), &addr[1]); mprAssert(addrBuf[strlen(addrBuf) - 2] == ']'); addrBuf[strlen(addrBuf) - 2] = '\0'; addr = addrBuf; } else { ipv6 = 0; } if (ipv6) { memset((char*) &hints, '\0', sizeof(hints)); memset((char*) &remoteAddr6, '\0', sizeof(struct sockaddr_storage)); mprSprintf(portNum_string, sizeof(portNum_string), "%d", portNum); hints.ai_socktype = SOCK_STREAM; rc = getaddrinfo(addr, portNum_string, &hints, &res); if (rc) { /* no need to unlock yet */ return MPR_ERR_CANT_OPEN; } sa = (struct sockaddr*) &remoteAddr6; memcpy(sa, res->ai_addr, res->ai_addrlen); addrlen = res->ai_addrlen; freeaddrinfo(res); } else #endif { memset((char *) &remoteAddr, '\0', sizeof(struct sockaddr_in)); remoteAddr.sin_family = AF_INET; remoteAddr.sin_port = htons((short) (portNum & 0xFFFF)); sa = (struct sockaddr*) &remoteAddr; addrlen = sizeof(remoteAddr); } lock(); port = portNum; flags = (initialFlags & (MPR_SOCKET_BROADCAST | MPR_SOCKET_DATAGRAM | MPR_SOCKET_BLOCK | MPR_SOCKET_LISTENER | MPR_SOCKET_NOREUSE | MPR_SOCKET_NODELAY)); // Save copy of the address ipAddr = mprStrdup(addr); #if BLD_FEATURE_IPV6 if (!ipv6) { // Nothing here } else #endif { remoteAddr.sin_addr.s_addr = inet_addr(ipAddr); if (remoteAddr.sin_addr.s_addr == INADDR_NONE) { hostent = mprGetHostByName(ipAddr); if (hostent != 0) { memcpy((char*) &remoteAddr.sin_addr, (char*) hostent->h_addr_list[0], (size_t) hostent->h_length); mprFreeGetHostByName(hostent); } else { unlock(); return MPR_ERR_NOT_FOUND; } } } broadcast = flags & MPR_SOCKET_BROADCAST; if (broadcast) { flags |= MPR_SOCKET_DATAGRAM; } datagram = flags & MPR_SOCKET_DATAGRAM; // // Create the O/S socket // sock = socket(sa->sa_family, datagram ? SOCK_DGRAM: SOCK_STREAM, 0); if (sock < 0) { err = getError(); unlock(); return -err; } #if !WIN && !WINCE && !VXWORKS fcntl(sock, F_SETFD, FD_CLOEXEC); // Children won't inherit this fd #endif if (broadcast) { int flag = 1; if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *) &flag, sizeof(flag)) < 0) { err = getError(); ::closesocket(sock); sock = -1; unlock(); return -err; } } if (!datagram) { flags |= MPR_SOCKET_CONNECTING; rc = connect(sock, sa, addrlen); if (rc < 0) { err = getError(); ::closesocket(sock); sock = -1; unlock(); #if UNUSED // // If the listen backlog is too high, ECONNREFUSED is returned // if (err == EADDRINUSE || err == ECONNREFUSED) { return MPR_ERR_BUSY; } #endif return -err; } } setBlockingMode((bool) (flags & MPR_SOCKET_BLOCK)); // // TCP/IP stacks have the No delay option (nagle algorithm) on by default. // if (flags & MPR_SOCKET_NODELAY) { setNoDelay(1); } unlock(); return sock; }
/* XML recursive descent parser. Return -1 for errors, 0 for EOF and 1 if there is still more data to parse. */ static int parseNext(MprXml *xp, int state) { MprXmlHandler handler; MprXmlToken token; MprBuf *tokBuf; char *tname, *aname; int rc; mprAssert(state >= 0); tokBuf = xp->tokBuf; handler = xp->handler; tname = aname = 0; rc = 0; /* In this parse loop, the state is never assigned EOF or ERR. In such cases we always return EOF or ERR. */ while (1) { token = getXmlToken(xp, state); if (token == MPR_XMLTOK_TOO_BIG) { xmlError(xp, "XML token is too big"); return MPR_ERR_WONT_FIT; } switch (state) { case MPR_XML_BEGIN: /* ------------------------------------------ */ /* Expect to get an element, comment or processing instruction */ switch (token) { case MPR_XMLTOK_EOF: return 0; case MPR_XMLTOK_LS: /* Recurse to handle the new element, comment etc. */ rc = parseNext(xp, MPR_XML_AFTER_LS); if (rc < 0) { return rc; } break; default: xmlError(xp, "Syntax error"); return MPR_ERR_BAD_SYNTAX; } break; case MPR_XML_AFTER_LS: /* ------------------------------------------ */ switch (token) { case MPR_XMLTOK_COMMENT: state = MPR_XML_COMMENT; rc = (*handler)(xp, state, "!--", 0, mprGetBufStart(tokBuf)); if (rc < 0) { return rc; } return 1; case MPR_XMLTOK_CDATA: state = MPR_XML_CDATA; rc = (*handler)(xp, state, "!--", 0, mprGetBufStart(tokBuf)); if (rc < 0) { return rc; } return 1; case MPR_XMLTOK_INSTRUCTIONS: /* Just ignore processing instructions */ return 1; case MPR_XMLTOK_TEXT: state = MPR_XML_NEW_ELT; tname = sclone(mprGetBufStart(tokBuf)); if (tname == 0) { mprAssert(!MPR_ERR_MEMORY); return MPR_ERR_MEMORY; } rc = (*handler)(xp, state, tname, 0, 0); if (rc < 0) { return rc; } break; default: xmlError(xp, "Syntax error"); return MPR_ERR_BAD_SYNTAX; } break; case MPR_XML_NEW_ELT: /* ------------------------------------------ */ /* We have seen the opening "<element" for a new element and have not yet seen the terminating ">" of the opening element. */ switch (token) { case MPR_XMLTOK_TEXT: /* Must be an attribute name */ aname = sclone(mprGetBufStart(tokBuf)); token = getXmlToken(xp, state); if (token != MPR_XMLTOK_EQ) { xmlError(xp, "Missing assignment for attribute \"%s\"", aname); return MPR_ERR_BAD_SYNTAX; } token = getXmlToken(xp, state); if (token != MPR_XMLTOK_TEXT) { xmlError(xp, "Missing value for attribute \"%s\"", aname); return MPR_ERR_BAD_SYNTAX; } state = MPR_XML_NEW_ATT; rc = (*handler)(xp, state, tname, aname, mprGetBufStart(tokBuf)); if (rc < 0) { return rc; } state = MPR_XML_NEW_ELT; break; case MPR_XMLTOK_GR: /* This is ">" the termination of the opening element */ if (*tname == '\0') { xmlError(xp, "Missing element name"); return MPR_ERR_BAD_SYNTAX; } /* Tell the user that the opening element is now complete */ state = MPR_XML_ELT_DEFINED; rc = (*handler)(xp, state, tname, 0, 0); if (rc < 0) { return rc; } state = MPR_XML_ELT_DATA; break; case MPR_XMLTOK_SLASH_GR: /* If we see a "/>" then this is a solo element */ if (*tname == '\0') { xmlError(xp, "Missing element name"); return MPR_ERR_BAD_SYNTAX; } state = MPR_XML_SOLO_ELT_DEFINED; rc = (*handler)(xp, state, tname, 0, 0); if (rc < 0) { return rc; } return 1; default: xmlError(xp, "Syntax error"); return MPR_ERR_BAD_SYNTAX; } break; case MPR_XML_ELT_DATA: /* -------------------------------------- */ /* We have seen the full opening element "<name ...>" and now await data or another element. */ if (token == MPR_XMLTOK_LS) { /* Recurse to handle the new element, comment etc. */ rc = parseNext(xp, MPR_XML_AFTER_LS); if (rc < 0) { return rc; } break; } else if (token == MPR_XMLTOK_LS_SLASH) { state = MPR_XML_END_ELT; break; } else if (token != MPR_XMLTOK_TEXT) { return rc; } if (mprGetBufLength(tokBuf) > 0) { /* Pass the data between the element to the user */ rc = (*handler)(xp, state, tname, 0, mprGetBufStart(tokBuf)); if (rc < 0) { return rc; } } break; case MPR_XML_END_ELT: /* -------------------------------------- */ if (token != MPR_XMLTOK_TEXT) { xmlError(xp, "Missing closing element name for \"%s\"", tname); return MPR_ERR_BAD_SYNTAX; } /* The closing element name must match the opening element name */ if (strcmp(tname, mprGetBufStart(tokBuf)) != 0) { xmlError(xp, "Closing element name \"%s\" does not match on line %d. Opening name \"%s\"", mprGetBufStart(tokBuf), xp->lineNumber, tname); return MPR_ERR_BAD_SYNTAX; } rc = (*handler)(xp, state, tname, 0, 0); if (rc < 0) { return rc; } if (getXmlToken(xp, state) != MPR_XMLTOK_GR) { xmlError(xp, "Syntax error"); return MPR_ERR_BAD_SYNTAX; } return 1; case MPR_XML_EOF: /* ---------------------------------------------- */ return 0; case MPR_XML_ERR: /* ---------------------------------------------- */ default: return MPR_ERR; } } mprAssert(0); }
void MprSocket::close(int timeout) { MprSelectService *ss; Mpr *mpr; char buf[1024]; int handlerFlags, timesUp; mpr = mprGetMpr(); mprLog(7, log, "%d: close\n", sock); ss = mpr->selectService; lock(); mprAssert(!(flags & MPR_SOCKET_CLOSED)); if (flags & MPR_SOCKET_CLOSED) { unlock(); return; } flags |= MPR_SOCKET_CLOSED; handlerFlags = (handler) ? handler->getFlags() : 0; if (handler) { handler->dispose(); handler = 0; } if (sock >= 0) { // // Do a graceful shutdown. Read any outstanding read data to prevent // resets. Then do a shutdown to send a FIN and read outstanding // data. All non-blocking. // //TODO - what about WINCE? #if WIN if (ss->getFlags() & MPR_ASYNC_SELECT) { if (handlerFlags & MPR_SELECT_CLIENT_CLOSED) { // // Client initiated close. We have already received an FD_CLOSE // closesocket(sock); sock = -1; } else { if (shutdown(sock, SHUT_WR) == 0) { // // Do a graceful shutdown. Read any outstanding read data to // prevent resets. Then do a shutdown to send a FIN and lastly // read data when the FD_CLOSE is received (see select.cpp). // All done non-blocking. // timesUp = mprGetTime(0) + timeout; do { if (recv(sock, buf, sizeof(buf), 0) <= 0) { break; } } while (mprGetTime(0) < timesUp); } // // Delayed close call must be first so we are ready when the // FD_CLOSE arrives. Other way round and there is a race if // multi-threaded. // ss->delayedClose(sock); // // We need to ensure we receive an FD_CLOSE to complete the // delayed close. Despite disposing the hander above, socket // messages will still be sent from windows and so select can // cleanup the delayed close socket. // WSAAsyncSelect(sock, ss->getHwnd(), ss->getMessage(), FD_CLOSE); } } else { #endif if (shutdown(sock, SHUT_WR) < 0) { ss->delayedClose(sock); } else { setBlockingMode(0); timesUp = mprGetTime(0) + timeout; do { if (recv(sock, buf, sizeof(buf), 0) <= 0) { break; } } while (mprGetTime(0) < timesUp); } // // Use delayed close to prevent anyone else reusing the socket // while select has not fully cleaned it out of its masks. // ss->delayedClose(sock); ss->awaken(0); } #if WIN } #endif // // Re-initialize all socket variables so the Socket can be reused. // acceptCallback = 0; acceptData = 0; selectEvents = 0; currentEvents = 0; error = 0; flags = MPR_SOCKET_CLOSED; ioCallback = 0; ioData = 0; ioData2 = 0; handlerMask = 0; handlerPriority = MPR_NORMAL_PRIORITY; interestEvents = 0; port = -1; sock = -1; if (ipAddr) { mprFree(ipAddr); ipAddr = 0; } unlock(); }
void mprXmlSetParserHandler(MprXml *xp, MprXmlHandler h) { mprAssert(xp); xp->handler = h; }
int MprSocket::write(char *buf, int bufsize) { #if BLD_FEATURE_IPV6 struct addrinfo hints, *res; struct sockaddr_storage server6; char port_string[MPR_MAX_IP_PORT]; int rc; #endif struct sockaddr_in server; struct sockaddr *sa; MprSocklen addrlen; int sofar, errCode, len, written; mprAssert(buf); mprAssert(bufsize >= 0); mprAssert((flags & MPR_SOCKET_CLOSED) == 0); addrlen = 0; sa = 0; lock(); if (flags & (MPR_SOCKET_BROADCAST | MPR_SOCKET_DATAGRAM)) { #if BLD_FEATURE_IPV6 if (ipv6) { memset((char *) &hints, '\0', sizeof(hints)); memset((char *) &server, '\0', sizeof(struct sockaddr_storage)); mprSprintf(port_string, sizeof(port_string), "%d", port); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; if (strcmp(ipAddr, "") == 0) { // Note that IPv6 does not support broadcast, there is no // 255.255.255.255 equiv. Multicast can be used over a specific // link, but the user must provide that address plus %scope_id. unlock(); return -1; } rc = getaddrinfo(ipAddr, port_string, &hints, &res); if (rc) { unlock(); return -1; } else { memcpy(&server, res->ai_addr, res->ai_addrlen); addrlen = res->ai_addrlen; freeaddrinfo(res); } sa = (struct sockaddr*) &server6; } else #endif { memset((char*) &server, '\0', sizeof(struct sockaddr_in)); addrlen = sizeof(struct sockaddr_in); server.sin_family = AF_INET; server.sin_port = htons((short) (port & 0xFFFF)); if (strcmp(ipAddr, "") != 0) { server.sin_addr.s_addr = inet_addr(ipAddr); } else { server.sin_addr.s_addr = INADDR_ANY; } sa = (struct sockaddr*) &server; } } if (flags & MPR_SOCKET_EOF) { sofar = bufsize; } else { errCode = 0; len = bufsize; sofar = 0; while (len > 0) { if ((flags & MPR_SOCKET_BROADCAST) || (flags & MPR_SOCKET_DATAGRAM)) { written = sendto(sock, &buf[sofar], len, MSG_NOSIGNAL, sa, addrlen); } else { written = send(sock, &buf[sofar], len, MSG_NOSIGNAL); } if (written < 0) { errCode = getError(); if (errCode == EINTR) { mprLog(8, log, "%d: write: EINTR\n", sock); continue; } else if (errCode == EAGAIN || errCode == EWOULDBLOCK) { mprLog(8, log, "%d: write: EAGAIN returning %d\n", sock, sofar); unlock(); return sofar; } mprLog(8, log, "%d: write: error %d\n", sock, -errCode); unlock(); return -errCode; } len -= written; sofar += written; } } mprLog(8, log, "%d: write: %d bytes, ask %d, flags %x\n", sock, sofar, bufsize, flags); unlock(); return sofar; }
/* Set the parse arg */ void *mprXmlGetParseArg(MprXml *xp) { mprAssert(xp); return xp->parseArg; }
/* * Add a shell parameter then do the regular init */ Mpr *mprCreateEx(int argc, char **argv, MprAllocNotifier cback, void *shell) { MprFileSystem *fs; Mpr *mpr; char *cp; if (cback == 0) { cback = memoryFailure; } mpr = (Mpr*) mprCreateAllocService(cback, (MprDestructor) mprDestructor); if (mpr == 0) { mprAssert(mpr); return 0; } /* * Wince and Vxworks passes an arg via argc, and the program name in argv. NOTE: this will only work on 32-bit systems. */ #if WINCE mprMakeArgv(mpr, (char*) argv, mprToAsc(mpr, (uni*) argc), &argc, &argv); #elif VXWORKS mprMakeArgv(mpr, NULL, (char*) argc, &argc, &argv); #endif mpr->argc = argc; mpr->argv = argv; mpr->name = mprStrdup(mpr, BLD_PRODUCT); mpr->title = mprStrdup(mpr, BLD_NAME); mpr->version = mprStrdup(mpr, BLD_VERSION); mpr->idleCallback = mprServicesAreIdle; if (mprCreateTimeService(mpr) < 0) { goto error; } if ((mpr->osService = mprCreateOsService(mpr)) < 0) { goto error; } /* * See if any of the preceeding allocations failed and mark all blocks allocated so far as required. * They will then be omitted from leak reports. */ if (mprHasAllocError(mpr)) { goto error; } #if BREW mprSetShell(mpr, shell); #endif #if BLD_FEATURE_MULTITHREAD mpr->multiThread = 1; if ((mpr->threadService = mprCreateThreadService(mpr)) == 0) { goto error; } mpr->mutex = mprCreateLock(mpr); mpr->spin = mprCreateSpinLock(mpr); #endif if ((fs = mprCreateFileSystem(mpr, "/")) == 0) { goto error; } mprAddFileSystem(mpr, fs); if ((mpr->moduleService = mprCreateModuleService(mpr)) == 0) { goto error; } if ((mpr->dispatcher = mprCreateDispatcher(mpr)) == 0) { goto error; } #if BLD_FEATURE_CMD if ((mpr->cmdService = mprCreateCmdService(mpr)) == 0) { goto error; } #endif #if BLD_FEATURE_MULTITHREAD if ((mpr->workerService = mprCreateWorkerService(mpr)) == 0) { goto error; } #endif if ((mpr->waitService = mprCreateWaitService(mpr)) == 0) { goto error; } if ((mpr->socketService = mprCreateSocketService(mpr)) == 0) { goto error; } #if BLD_FEATURE_HTTP if ((mpr->httpService = mprCreateHttpService(mpr)) == 0) { goto error; } #endif if (mpr->argv && mpr->argv[0] && *mpr->argv[0]) { mprFree(mpr->name); mpr->name = mprGetPathBase(mpr, mpr->argv[0]); if ((cp = strchr(mpr->name, '.')) != 0) { *cp = '\0'; } } /* * Now catch all memory allocation errors up to this point. Should be none. */ if (mprHasAllocError(mpr)) { goto error; } return mpr; /* * Error return */ error: mprFree(mpr); return 0; }
/* * Create the host addresses for a host. Called for hosts or for NameVirtualHost directives (host == 0). */ int maCreateHostAddresses(MaServer *server, MaHost *host, cchar *configValue) { MaListen *listen; MaHostAddress *address; char *ipAddrPort, *ipAddr, *value, *tok; char addrBuf[MPR_MAX_IP_ADDR_PORT]; int next, port; address = 0; value = mprStrdup(server, configValue); ipAddrPort = mprStrTok(value, " \t", &tok); while (ipAddrPort) { if (mprStrcmpAnyCase(ipAddrPort, "_default_") == 0) { mprAssert(0); ipAddrPort = "*:*"; } if (mprParseIp(server, ipAddrPort, &ipAddr, &port, -1) < 0) { mprError(server, "Can't parse ipAddr %s", ipAddrPort); continue; } mprAssert(ipAddr && *ipAddr); if (ipAddr[0] == '*') { ipAddr = mprStrdup(server, ""); } /* * For each listening endpiont, */ for (next = 0; (listen = mprGetNextItem(server->listens, &next)) != 0; ) { if (port > 0 && port != listen->port) { continue; } if (listen->ipAddr[0] != '\0' && ipAddr[0] != '\0' && strcmp(ipAddr, listen->ipAddr) != 0) { continue; } /* * Find the matching host address or create a new one */ if ((address = maLookupHostAddress(server, listen->ipAddr, listen->port)) == 0) { address = maCreateHostAddress(server, listen->ipAddr, listen->port); mprAddItem(server->hostAddresses, address); } /* * If a host is specified */ if (host == 0) { maSetNamedVirtualHostAddress(address); } else { maInsertVirtualHost(address, host); if (listen->ipAddr[0] != '\0') { mprSprintf(addrBuf, sizeof(addrBuf), "%s:%d", listen->ipAddr, listen->port); } else { mprSprintf(addrBuf, sizeof(addrBuf), "%s:%d", ipAddr, listen->port); } maSetHostName(host, addrBuf); } } mprFree(ipAddr); ipAddrPort = mprStrTok(0, " \t", &tok); } if (host) { if (address == 0) { mprError(server, "No valid IP address for host %s", host->name); mprFree(value); return MPR_ERR_CANT_INITIALIZE; } if (maIsNamedVirtualHostAddress(address)) { maSetNamedVirtualHost(host); } } mprFree(value); return 0; }
static void netOutgoingService(HttpQueue *q) { HttpConn *conn; HttpTx *tx; ssize written; int errCode; conn = q->conn; tx = conn->tx; conn->lastActivity = conn->http->now; mprAssert(conn->sock); if (!conn->sock || conn->connectorComplete) { return; } if (tx->flags & HTTP_TX_NO_BODY) { httpDiscardQueueData(q, 1); } if ((tx->bytesWritten + q->count) > conn->limits->transmissionBodySize) { httpError(conn, HTTP_CODE_REQUEST_TOO_LARGE | ((tx->bytesWritten) ? HTTP_ABORT : 0), "Http transmission aborted. Exceeded transmission max body of %,Ld bytes", conn->limits->transmissionBodySize); if (tx->bytesWritten) { httpConnectorComplete(conn); return; } } if (tx->flags & HTTP_TX_SENDFILE) { /* Relay via the send connector */ if (tx->file == 0) { if (tx->flags & HTTP_TX_HEADERS_CREATED) { tx->flags &= ~HTTP_TX_SENDFILE; } else { tx->connector = conn->http->sendConnector; httpSendOpen(q); } } if (tx->file) { httpSendOutgoingService(q); return; } } while (q->first || q->ioIndex) { if (q->ioIndex == 0 && buildNetVec(q) <= 0) { break; } /* Issue a single I/O request to write all the blocks in the I/O vector */ mprAssert(q->ioIndex > 0); written = mprWriteSocketVector(conn->sock, q->iovec, q->ioIndex); LOG(5, "Net connector wrote %d, written so far %Ld, q->count %d/%d", written, tx->bytesWritten, q->count, q->max); if (written < 0) { errCode = mprGetError(q); if (errCode == EAGAIN || errCode == EWOULDBLOCK) { /* Socket full, wait for an I/O event */ httpSocketBlocked(conn); break; } if (errCode != EPIPE && errCode != ECONNRESET) { LOG(5, "netOutgoingService write failed, error %d", errCode); } httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Write error %d", errCode); httpConnectorComplete(conn); break; } else if (written == 0) { /* Socket full, wait for an I/O event */ httpSocketBlocked(conn); break; } else if (written > 0) { tx->bytesWritten += written; freeNetPackets(q, written); adjustNetVec(q, written); } } if (q->ioCount == 0) { if ((q->flags & HTTP_QUEUE_EOF)) { httpConnectorComplete(conn); } else { httpNotifyWritable(conn); } } }
/* * 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; }
/* * Get the next chunk size. Chunked data format is: * Chunk spec <CRLF> * Data <CRLF> * Chunk spec (size == 0) <CRLF> * <CRLF> * Chunk spec is: "HEX_COUNT; chunk length DECIMAL_COUNT\r\n". The "; chunk length DECIMAL_COUNT is optional. * As an optimization, use "\r\nSIZE ...\r\n" as the delimiter so that the CRLF after data does not special consideration. * Achive this by parseHeaders reversing the input start by 2. */ static void incomingChunkData(MaQueue *q, MaPacket *packet) { MaConn *conn; MaRequest *req; MprBuf *buf; char *start, *cp; int bad; conn = q->conn; req = conn->request; buf = packet->content; mprAssert(req->flags & MA_REQ_CHUNKED); if (packet->content == 0) { if (req->chunkState == MA_CHUNK_DATA) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad chunk state"); return; } req->chunkState = MA_CHUNK_EOF; } /* * NOTE: the request head ensures that packets are correctly sized by packet inspection. The packet will never * have more data than the chunk state expects. */ switch (req->chunkState) { case MA_CHUNK_START: /* * Validate: "\r\nSIZE.*\r\n" */ if (mprGetBufLength(buf) < 5) { break; } start = mprGetBufStart(buf); bad = (start[0] != '\r' || start[1] != '\n'); for (cp = &start[2]; cp < buf->end && *cp != '\n'; cp++) {} if (*cp != '\n' && (cp - start) < 80) { break; } bad += (cp[-1] != '\r' || cp[0] != '\n'); if (bad) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad chunk specification"); return; } req->chunkSize = (int) mprAtoi(&start[2], 16); if (!isxdigit((int) start[2]) || req->chunkSize < 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad chunk specification"); return; } mprAdjustBufStart(buf, (int) (cp - start + 1)); req->remainingContent = req->chunkSize; if (req->chunkSize == 0) { req->chunkState = MA_CHUNK_EOF; /* * We are lenient if the request does not have a trailing "\r\n" after the last chunk */ cp = mprGetBufStart(buf); if (mprGetBufLength(buf) == 2 && *cp == '\r' && cp[1] == '\n') { mprAdjustBufStart(buf, 2); } } else { req->chunkState = MA_CHUNK_DATA; } mprAssert(mprGetBufLength(buf) == 0); maFreePacket(q, packet); mprLog(q, 5, "chunkFilter: start incoming chunk of %d bytes", req->chunkSize); break; case MA_CHUNK_DATA: mprAssert(maGetPacketLength(packet) <= req->chunkSize); mprLog(q, 5, "chunkFilter: data %d bytes, req->remainingContent %d", maGetPacketLength(packet), req->remainingContent); maPutNext(q, packet); if (req->remainingContent == 0) { req->chunkState = MA_CHUNK_START; req->remainingContent = MA_BUFSIZE; } break; case MA_CHUNK_EOF: mprAssert(maGetPacketLength(packet) == 0); maPutNext(q, packet); mprLog(q, 5, "chunkFilter: last chunk"); break; default: mprAssert(0); } }
// // Algorithm originally in the GoAhead WebServer. // int MaUrl::parse(char *url) { char *tok, *cp, *portStr, *last_delim, *hostbuf, *portbuf; char *htmlExt = "html"; int c, len, ulen; mprAssert(url && *url); if (parsedUrlBuf) { mprFree(parsedUrlBuf); } ulen = strlen(url); // // Allocate a single buffer to hold all the cracked fields. // Store host, port and url strings (3 nulls). // len = ulen * 2 + MAX_PORT_LEN + 3; parsedUrlBuf = (char*) mprMalloc(len * sizeof(char)); portbuf = &parsedUrlBuf[len - MAX_PORT_LEN - 1]; hostbuf = &parsedUrlBuf[ulen+1]; strcpy(parsedUrlBuf, url); url = parsedUrlBuf; // // Defaults for missing ULR fields // strcpy(portbuf, "80"); portStr = portbuf; uri = "/"; proto = "http"; host = "localhost"; query = ""; ext = htmlExt; if (strncmp(url, "http://", 7) == 0) { tok = &url[7]; tok[-3] = '\0'; proto = url; host = tok; for (cp = tok; *cp; cp++) { if (*cp == '/') { break; } if (*cp == ':') { *cp++ = '\0'; portStr = cp; tok = cp; } } if ((cp = strchr(tok, '/')) != NULL) { c = *cp; *cp = '\0'; mprStrcpy(hostbuf, ulen + 1, host); mprStrcpy(portbuf, MAX_PORT_LEN, portStr); *cp = c; host = hostbuf; portStr = portbuf; uri = cp; tok = cp; } } else { uri = url; tok = url; } // // Split off the query string. // if ((cp = strchr(tok, '?')) != NULL) { *cp++ = '\0'; query = cp; uri = tok; tok = query; } // // Split off fragment identifier. // if ((cp = strchr(tok, '#')) != NULL) { *cp++ = '\0'; if (*query == 0) { uri = tok; } } // // FUTURE -- this logic could be improved // if ((cp = strrchr(uri, '.')) != NULL) { if ((last_delim = strrchr(uri, '/')) != NULL) { if (last_delim > cp) { ext = htmlExt; } else { ext = cp + 1; #if WIN mprStrLower(ext); #endif } } else { ext = cp + 1; #if WIN mprStrLower(ext); #endif } } else { if (uri[strlen(uri) - 1] == '/') { ext = htmlExt; } } port = atoi(portStr); return 0; }
void maClientLock(MaClient *cp) { mprAssert(cp); cp->lock(); }