cchar *ediFormatField(cchar *fmt, EdiField field) { MprTime when; if (fmt == 0) { return field.value; } switch (field.type) { case EDI_TYPE_BINARY: case EDI_TYPE_BOOL: return field.value; case EDI_TYPE_DATE: if (mprParseTime(&when, field.value, MPR_LOCAL_TIMEZONE, 0) == 0) { return mprFormatLocalTime(fmt, when); } return field.value; case EDI_TYPE_FLOAT: return sfmt(fmt, atof(field.value)); case EDI_TYPE_INT: return sfmt("%Ld", stoi(field.value)); case EDI_TYPE_STRING: case EDI_TYPE_TEXT: return sfmt(fmt, field.value); default: mprError("Unknown field type %d", field.type); } return 0; }
static cchar *checkDate(EdiValidation *vp, EdiRec *rec, cchar *fieldName, cchar *value) { MprTime time; if (value && *value) { if (mprParseTime(&time, value, MPR_LOCAL_TIMEZONE, NULL) < 0) { return 0; } } return "is not a date or time"; }
/* See if there is acceptable cached content for this request. If so, return true. Will setup tx->cacheBuffer as a side-effect if the output should be captured and cached. */ static bool fetchCachedResponse(HttpConn *conn) { HttpTx *tx; MprTime modified, when; cchar *value, *key, *tag; int status, cacheOk, canUseClientCache; tx = conn->tx; /* Transparent caching. Manual caching must manually call httpWriteCached() */ key = makeCacheKey(conn); if ((value = httpGetHeader(conn, "Cache-Control")) != 0 && (scontains(value, "max-age=0") == 0 || scontains(value, "no-cache") == 0)) { mprLog(3, "Client reload. Cache-control header '%s' rejects use of cached content.", value); } else if ((tx->cachedContent = mprReadCache(conn->host->responseCache, key, &modified, 0)) != 0) { /* See if a NotModified response can be served. This is much faster than sending the response. Observe headers: If-None-Match: "ec18d-54-4d706a63" If-Modified-Since: Fri, 04 Mar 2012 04:28:19 GMT Set status to OK when content must be transmitted. */ cacheOk = 1; canUseClientCache = 0; tag = mprGetMD5(key); if ((value = httpGetHeader(conn, "If-None-Match")) != 0) { canUseClientCache = 1; if (scmp(value, tag) != 0) { cacheOk = 0; } } if (cacheOk && (value = httpGetHeader(conn, "If-Modified-Since")) != 0) { canUseClientCache = 1; mprParseTime(&when, value, 0, 0); if (modified > when) { cacheOk = 0; } } status = (canUseClientCache && cacheOk) ? HTTP_CODE_NOT_MODIFIED : HTTP_CODE_OK; mprLog(3, "cacheHandler: Use cached content for %s, status %d", key, status); httpSetStatus(conn, status); httpSetHeader(conn, "Etag", mprGetMD5(key)); httpSetHeader(conn, "Last-Modified", mprFormatUniversalTime(MPR_HTTP_DATE, modified)); return 1; } mprLog(3, "cacheHandler: No cached content for %s", key); return 0; }
static void testParseTime(MprTestGroup *gp) { MprTime when; when = 0; assert(mprParseTime(gp, &when, "today", MPR_LOCAL_TIMEZONE, NULL) == 0); assert(mprParseTime(gp, &when, "tomorrow", MPR_LOCAL_TIMEZONE, NULL) == 0); assert(mprParseTime(gp, &when, "12:00", MPR_LOCAL_TIMEZONE, NULL) == 0); assert(mprParseTime(gp, &when, "12:30 pm", MPR_LOCAL_TIMEZONE, NULL) == 0); assert(mprParseTime(gp, &when, "1/31/99", MPR_LOCAL_TIMEZONE, NULL) == 0); assert(mprParseTime(gp, &when, "Jan 17 2011", MPR_LOCAL_TIMEZONE, NULL) == 0); assert(mprParseTime(gp, &when, "March 17 2011", MPR_LOCAL_TIMEZONE, NULL) == 0); assert(when != 0); }
static EjsDate *getDateHeader(Ejs *ejs, EjsHttp *hp, cchar *key) { MprTime when; cchar *value; if (!waitForResponseHeaders(hp)) { return 0; } value = httpGetHeader(hp->conn, key); if (value == 0) { return ESV(null); } if (mprParseTime(&when, value, MPR_UTC_TIMEZONE, NULL) < 0) { value = 0; } return ejsCreateDate(ejs, when); }
/* * 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; }