Boolean mp3ReaderCanHandle(const char *mimeType) { const char *semi = FskStrChr(mimeType, ';'); UInt32 length = semi ? (UInt32)(semi - mimeType) : FskStrLen(mimeType); if (0 == FskStrCompareCaseInsensitiveWithLength("audio/mpeg", mimeType, length)) return true; if (0 == FskStrCompareCaseInsensitiveWithLength("audio/aac", mimeType, length)) return true; if (0 == FskStrCompareCaseInsensitiveWithLength("audio/vnd.dlna.adts", mimeType, length)) return true; return false; }
Boolean KprHTTPConnectionCandidate(KprHTTPConnection self, KprMessage message) { Boolean result = false; if (!self->client) result = true; else if (!self->client->host) result = true; else { UInt32 hostLength = FskStrLen(self->client->host); result = (FskStrCompareCaseInsensitiveWithLength(self->client->host, message->parts.host, (hostLength > message->parts.hostLength) ? hostLength : message->parts.hostLength) == 0) && (self->client->hostPort == (int)message->parts.port) && (self->client->priority == abs(message->priority)); if (result && self->target) // only serialize if the message target is the same result = self->target->message->request.target == message->request.target; } return result; }
void KprURLSplit(char* url, KprURLParts parts) { char c, *p, *q; FskMemSet(parts, 0, sizeof(KprURLPartsRecord)); q = p = url; while ((c = *p)) { if ((c == ':') || (c == '/') || (c == '?') || (c == '#')) break; p++; } if (c == ':') { parts->scheme = q; parts->schemeLength = p - q; p++; q = p; c = *p; } if (c == '/') { p++; c = *p; if (c == '/') { char *semi = NULL; p++; parts->authority = q = p; while ((c = *p)) { if (c == '/') break; if (c == ':') semi = p; if (c == '@') { parts->host = p + 1; parts->user = q; if (semi) { parts->userLength = semi - q; parts->password = semi + 1; parts->passwordLength = p - semi - 1; semi = NULL; } else parts->userLength = p - q; } p++; } parts->authorityLength = p - q; if (parts->authorityLength) { if (!parts->host) parts->host = q; if (semi) { parts->hostLength = semi - parts->host; parts->port = FskStrToNum(semi + 1); } else { if (parts->schemeLength) { if (!FskStrCompareCaseInsensitiveWithLength(parts->scheme, "https", 5)) parts->port = 443; else if (!FskStrCompareCaseInsensitiveWithLength(parts->scheme, "http", 4)) parts->port = 80; } parts->hostLength = p - parts->host; } } q = p; } else if (c) parts->name = p; } while ((c = *p)) { if (c == '/') parts->name = p + 1; if ((c == '?') || (c == '#')) break; p++; } parts->path = q; parts->pathLength = p - q; if (parts->name) parts->nameLength = p - parts->name; if (c == '?') { p++; parts->query = q = p; while ((c = *p)) { if (c == '#') break; p++; } parts->queryLength = p - q; } if (c == '#') { p++; parts->fragment = q = p; while ((c = *p)) { p++; } parts->fragmentLength = p - q; } }
FskErr mp3RefillReadBuffer(mp3Reader state, UInt32 minimumBytesNeeded) { FskErr err = kFskErrNone; UInt32 bytesInBuffer = (UInt32)(state->readBufferEnd - state->readBufferPtr); UInt32 bytesRead; void *buffer; Boolean firstTime = true; FskMemMove(state->readBuffer, state->readBufferPtr, bytesInBuffer); state->readBufferPtr = state->readBuffer; state->readBufferEnd = state->readBufferPtr + bytesInBuffer; while (true) { UInt32 bytesToRead = kMP3ReadBufferSize - bytesInBuffer; if (state->spoolerSize && ((state->position + bytesToRead) > state->spoolerSize)) { bytesToRead = (UInt32)(state->spoolerSize - state->position); if (0 == bytesToRead) { err = kFskErrEndOfFile; goto bail; } } if (0 != state->icy.metaInt) { if (0 != state->icy.metaBytesToCollect) { err = FskMediaSpoolerRead(state->spooler, state->position, state->icy.metaBytesToCollect, &buffer, &bytesRead); if (kFskErrNone != err) goto readErr; state->position += bytesRead; err = FskMemPtrRealloc(state->icy.metaBytesCollected + bytesRead + 1, &state->icy.metaBytes); if (err) return err; FskMemMove(state->icy.metaBytes + state->icy.metaBytesCollected, buffer, bytesRead); state->icy.metaBytes[state->icy.metaBytesCollected + bytesRead] = 0; state->icy.metaBytesCollected += bytesRead; state->icy.metaBytesToCollect -= bytesRead; if (0 == state->icy.metaBytesToCollect) { if (0 == FskStrCompareCaseInsensitiveWithLength((char *)state->icy.metaBytes, "StreamTitle=", 12)) { char *start = (char *)state->icy.metaBytes + 13; char *end = start; char *dash; FskMediaPropertyValueRecord prop; while (true) { end = FskStrChr(end, start[-1]); if (NULL == end) break; if ((0 != end[1]) && (';' != end[1])) { end += 1; continue; } break; } if (end) *end = 0; while (true) { if (kFskErrNone != FskMediaMetaDataRemove(state->mi.meta, "FullName", 0)) break; } dash = FskStrStr(start, " - "); if (NULL != dash) { while (true) { if (kFskErrNone != FskMediaMetaDataRemove(state->mi.meta, "Artist", 0)) break; } *dash = 0; prop.type = kFskMediaPropertyTypeString; prop.value.str = icyString(start); FskMediaMetaDataAdd(state->mi.meta, "Artist", NULL, &prop, kFskMediaMetaDataFlagOwnIt); prop.type = kFskMediaPropertyTypeString; prop.value.str = icyString(dash + 3); FskMediaMetaDataAdd(state->mi.meta, "FullName", NULL, &prop, kFskMediaMetaDataFlagOwnIt); } else { prop.type = kFskMediaPropertyTypeString; prop.value.str = icyString(start); FskMediaMetaDataAdd(state->mi.meta, "FullName", NULL, &prop, kFskMediaMetaDataFlagOwnIt); } FskMediaReaderSendEvent(state->reader, kFskEventMediaPlayerMetaDataChanged); } FskMemPtrDisposeAt((void**)(void*)(&state->icy.metaBytes)); } continue; } else if (state->position == state->icy.nextMetaPosition) { err = FskMediaSpoolerRead(state->spooler, state->position, 1, &buffer, &bytesRead); if (kFskErrNone != err) goto readErr; state->position += 1; state->icy.metaBytesToCollect = ((unsigned char *)buffer)[0] * 16; state->icy.metaBytesCollected = 0; state->icy.nextMetaPosition += 1 + state->icy.metaBytesToCollect + state->icy.metaInt; continue; } else if ((state->position <= state->icy.nextMetaPosition) && (state->icy.nextMetaPosition < (state->position + bytesToRead))) bytesToRead = (UInt32)(state->icy.nextMetaPosition - state->position); } err = FskMediaSpoolerRead(state->spooler, state->position, bytesToRead, &buffer, &bytesRead); readErr: if (err) { if (false == firstTime) { err = kFskErrNone; break; } goto bail; } FskMemMove(state->readBufferEnd, buffer, bytesRead); state->position += bytesRead; state->readBufferEnd += bytesRead; bytesInBuffer = (UInt32)(state->readBufferEnd - state->readBufferPtr); if ((kMP3ReadBufferSize == bytesInBuffer) || (bytesInBuffer >= minimumBytesNeeded)) break; firstTime = false; } if (bytesInBuffer < minimumBytesNeeded) { err = kFskErrNeedMoreTime; goto bail; } bail: return err; }
static void httpPrepareResponseHeaders(FskHTTPServerRequest request) { char *str; int requestProtocolVersion; requestProtocolVersion = FskHeaderHTTPVersion(request->requestHeaders); if (NULL == FskHeaderFind(kFskStrServer, request->responseHeaders)) FskHeaderAddString(kFskStrServer, kHTTPServerIdentifier, request->responseHeaders); str = FskHeaderFind(kFskStrConnection, request->responseHeaders); if (str && FskStrCompareCaseInsensitiveWithLength(str, kFskStrClose, 5) == 0) request->keepAlive = false; str = FskHeaderFind(kFskStrTransferEncoding, request->responseHeaders); if (str && (FskStrCompareCaseInsensitiveWithLength(str, kFskStrChunked, FskStrLen(kFskStrChunked)) == 0)) { request->transferEncoding = kFskTransferEncodingChunked; } else { request->transferEncoding = kFskTransferEncodingNone; str = FskHeaderFind(kFskStrContentLength, request->responseHeaders); if (str) { request->stats.expectedBodyToSend = FskStrToNum(str); } else if (requestProtocolVersion >= kFskHTTPVersion1dot1) { // DHWG 7.8.1 - if http server is responding to 1.1 requests, // it must use connection:close if there's no content length // or chunked encoding FskHeaderRemove(kFskStrConnection, request->responseHeaders); FskHeaderAddString(kFskStrConnection, kFskStrClose, request->responseHeaders); } } if (requestProtocolVersion <= kFskHTTPVersion1dot0) { request->keepAlive = false; // DHWG 7.8.21 (must ignore keepalive) FskHeaderRemove(kFskStrConnection, request->responseHeaders); FskHeaderAddString(kFskStrConnection, kFskStrClose, request->responseHeaders); FskMemPtrDispose(request->requestHeaders->protocol); request->requestHeaders->protocol = FskStrDoCopy("HTTP/1.0"); if (0 == FskStrCompareCaseInsensitiveWithLength(FskHeaderFind(kFskStrTransferEncoding, request->responseHeaders), kFskStrChunked, FskStrLen(kFskStrChunked))) { FskHeaderRemove(kFskStrTransferEncoding, request->responseHeaders); request->transferEncoding = kFskTransferEncodingNone; } if (request->stats.expectedBodyToSend == 0) { FskHeaderRemove(kFskStrConnection, request->responseHeaders); FskHeaderAddString(kFskStrConnection, kFskStrClose, request->responseHeaders); } } if (request->requestHeaders->responseCode >= 400) { request->out.max = snprintf(request->out.buf, request->out.bufferSize, "%s %d %s\r\n", httpProtocolVersionString(request), request->requestHeaders->responseCode, FskFindResponse(request->requestHeaders->responseCode)); } else if (request->responseHeaders->responseCode > 0) { str = FskFindResponse(request->responseHeaders->responseCode); request->out.max = snprintf(request->out.buf, request->out.bufferSize, "%s %d %s\r\n", httpProtocolVersionString(request), request->responseHeaders->responseCode, str ? str : ""); if (request->keepAlive) { FskHeaderRemove(kFskStrConnection, request->responseHeaders); FskHeaderAddString(kFskStrConnection, kFskStrKeepAlive, request->responseHeaders); } } else { request->responseHeaders->responseCode = 500; request->out.max = snprintf(request->out.buf, request->out.bufferSize, "%s 500 Internal Sever Error\r\n", httpProtocolVersionString(request)); } if (NULL == FskHeaderFind(kFskStrDate, request->responseHeaders)) { char dateString[32]; FskTimeMakeDate(dateString, 31); FskHeaderAddString(kFskStrDate, dateString, request->responseHeaders); } // *** generate the response request->out.max += FskHeaderGenerateOutputBlob(&request->out.buf[request->out.max], request->out.bufferSize - request->out.max, true, request->responseHeaders); if ((!request->keepAlive) || (request->responseHeaders->responseCode >= 400)) request->nextState = kHTTPDone; }
static void httpProcessRequestHeaders(FskHTTPServerRequest request) { char *str; FskHeaders* headers = request->requestHeaders; UInt32 version = FskHeaderHTTPVersion(headers); char* host = FskHeaderFind(kFskStrHost, headers); char* uri = FskHeaderURI(headers); char* filename = FskHeaderFilename(headers); request->state = kHTTPReadRequestBody; if (FskStrCompareWithLength(uri, "http://", 7) == 0) { // remove host from filename char* p = FskStrStr(filename, "://") + 3; p = FskStrChr(p, '/') + 1; FskMemMove(filename, p, FskStrLen(p) + 1); } else { if (host) { if (FskMemPtrNewClear(FskStrLen(host) + FskStrLen(uri) + 9, &str) != kFskErrNone) headers->responseCode = 500; else { FskStrCat(str, "http://"); FskStrCat(str, host); FskStrCat(str, "/"); FskStrCat(str, headers->URI); FskMemPtrDispose(headers->URI); headers->URI = str; } } else if (version >= kFskHTTPVersion1dot1) headers->responseCode = 400; else if (version == kFskHTTPVersion1dot0) { if (FskMemPtrNewClear(FskStrLen(uri) + 9, &str) != kFskErrNone) headers->responseCode = 500; else { FskStrCat(str, "http:///"); FskStrCat(str, headers->URI); FskMemPtrDispose(headers->URI); headers->URI = str; } } } str = FskHeaderFind(kFskStrConnection, request->requestHeaders); if (str && FskStrCompareCaseInsensitiveWithLength(str, kFskStrClose, 5) == 0) request->keepAlive = false; else request->keepAlive = true; str = FskHeaderFind(kFskStrContentLength, request->requestHeaders); if (str) { request->requestBodyContentLength = FskStrToNum(str); request->stats.expectedBytesToReceive = FskStrToNum(str); } else request->stats.expectedBytesToReceive = 0; str = FskHeaderFind(kFskStrTransferEncoding, request->requestHeaders); if (str && (FskStrCompareCaseInsensitiveWithLength(str, kFskStrChunked, FskStrLen(kFskStrChunked)) == 0)) request->requestBodyChunked = true; else request->requestBodyChunked = false; doCallCondition(request->http->callbacks->requestCondition, request, kFskHTTPConditionRequestReceivedRequestHeaders, request->refCon); if (NULL != (str = FskHeaderFind(kFskStrExpect, request->requestHeaders))) { if (0 == FskStrCompareCaseInsensitive(kFskStr100Continue, str)) request->state = kHTTPFulfillExpectation; else request->state = kHTTPDenyExpectation; } }
// ------------------------------------------------------------------------ static int sParseStartLine(char *startLine, UInt16 headerType, FskHeaders *headers) { FskErr err; int l; const char *p; char *c = startLine; char *firstPart; // Get method or protocol p = c; c = FskStrChr(c, ' '); if (!c) return -1; l = (c++) - p; err = FskMemPtrNew(l+1, &firstPart); if (err != kFskErrNone) return -1; FskStrNCopy(firstPart, p, l); firstPart[l] = '\0'; if (kFskHeaderTypeResponse == headerType) headers->protocol = firstPart; else headers->method = firstPart; c = FskStrStripHeadSpace(c); // skip over space headers->headerType = headerType; if (kFskHeaderTypeResponse == headerType) { // Get response code and message (if message not in HTTP_Responses) headers->responseCode = FskStrToNum(c); if (headers->flags & kFskHeadersNonStandardResponseReasonPhrase) { c = FskStrChr(c, ' '); if (c) { char *r, *s; s = FskStrStripHeadSpace(c); r = FskFindResponse(headers->responseCode); if (!r || (0 != FskStrCompareCaseInsensitiveWithLength(s, r, FskStrLen(r)))) { headers->responseReasonPhrase = FskStrDoCopy(s); if (NULL != headers->responseReasonPhrase) FskStrStripTailSpace(headers->responseReasonPhrase); } } } } else { char *s, *t = NULL; char *uri = NULL; // Get URI if ((*c == '/') && !(headers->flags & kFskHeadersDoNotStripURILeadingSlash)) c++; s = FskStrChr(c, ' '); if (!s) { headers->responseCode = 400; return -1; } headers->protocol = FskStrDoCopy(s + 1); if (NULL != headers->protocol) FskStrStripTailSpace(headers->protocol); BAIL_IF_ERR(FskMemPtrNew((s-c)+1, &uri)); BAIL_IF_ERR(FskMemPtrNew((s-c)+1, &t)); FskMemCopy(uri, c, s-c); uri[s-c] = '\0'; s = FskStrChr(uri, '?'); if (s) *s = 0; FskStrDecodeEscapedChars(uri, t); if (s) { *s = '?'; FskStrCat(t, s); } headers->URI = FskStrDoCopy(t); // Break URI into filename and parameters s = FskStrChr(t, '?'); if (!s) { headers->filename = FskStrDoCopy(t); } else { // URI has parameters *s++ = '\0'; // cap off the filename headers->filename = FskStrDoCopy(t); headers->parameters = FskAssociativeArrayNew(); while (s) { char *name = s; char *value = FskStrChr(name, '='); if (!value) break; s = FskStrChr(value, '&'); *value++ = '\0'; // cap off the name if (s) *s++ = '\0'; // cap off the value FskAssociativeArrayElementSetString(headers->parameters, name, value); } } bail: FskMemPtrDispose(uri); FskMemPtrDispose(t); } return headers->headerType; }
Boolean KprHTTPClientPutTargetCache(KprHTTPTarget target) { FskErr err = kFskErrNone; char* control = NULL; KprHTTPClient self = gKprHTTPClient; KprMessage message = target->message; UInt32 date = 0; UInt32 maxAge = 0; UInt32 lifetime = 0; char* start; char* next; char* end; Boolean foundAge = false; Boolean revalidate = false; KprHTTPCacheValue cached = NULL; bailIfError(message->error || !FskStrCompareCaseInsensitiveWithLength(message->url, "https://", 8) || ((message->status != 200) && (message->status != 203)) || (message->method && FskStrCompare(message->method, "GET")) // only caching GET method || (message->user != NULL) // do not cache request with credentials || (message->password != NULL) // do not cache request with credentials || (message->response.size == 0) // only caching something || (message->response.size > 384000)); // not caching big files if ((control = KprMessageGetResponseHeader(message, kFskStrCacheControl))) { start = control; end = control + FskStrLen(control); // split while (start < end) { if (*start == ',') *start = 0; start++; } start = control; while (start <= end) { next = start + FskStrLen(start) + 1; start = FskStrStripHeadSpace(start); if (!FskStrCompareCaseInsensitiveWithLength(start, "max-age", 7)) { bailIfError(!(start = FskStrStripHeadSpace(start + 7))); bailIfError(*start != '='); bailIfError(!(start = FskStrStripHeadSpace(start + 1))); maxAge = FskStrToNum(start); foundAge = true; } else if (!FskStrCompareCaseInsensitiveWithLength(start, "no-cache", 8)) { revalidate = true; } else if (!FskStrCompareCaseInsensitiveWithLength(start, "no-store", 8)) { BAIL(kFskErrInvalidParameter); } else if (!FskStrCompareCaseInsensitiveWithLength(start, "must-revalidate", 15)) { revalidate = true; } start = next; } } KprDateFromHTTP(KprMessageGetResponseHeader(message, kprHTTPHeaderDate), &date); if (foundAge) lifetime = maxAge; else { UInt32 expire = 0; KprDateFromHTTP(KprMessageGetResponseHeader(message, kFskStrExpires), &expire); if (date && expire) lifetime = (expire > date) ? expire - date : 0; else if (!KprMessageGetResponseHeader(message, kprHTTPHeaderETag) && !KprMessageGetResponseHeader(message, kprHTTPHeaderLastModified)) BAIL(kFskErrInvalidParameter); // not cached } if (KprMessageGetResponseHeader(message, kprHTTPHeaderVary)) revalidate = true; if (!revalidate) lifetime += 180; // guess 3 minutes // put in cache // fprintf(stderr, "%p: CACHE %s for %d (%d)\n", message, message->url, lifetime, revalidate); bailIfError(KprHTTPCacheValueNew(&cached)); KprDateFromHTTP(KprMessageGetResponseHeader(message, kprHTTPHeaderAge), &cached->age); cached->date = date; cached->lifetime = lifetime; cached->requestDate = target->requestDate; cached->responseDate = target->responseDate; cached->status = message->status; cached->headers = FskAssociativeArrayNew(); { FskAssociativeArrayIterator iterate = FskAssociativeArrayIteratorNew(message->response.headers); while (iterate) { FskAssociativeArrayElementSet(cached->headers, iterate->name, iterate->value, 0, kFskStringType); iterate = FskAssociativeArrayIteratorNext(iterate); } FskAssociativeArrayIteratorDispose(iterate); } KprHTTPCacheValueCleanupHeaders(cached); cached->size = message->response.size; cached->body = message->response.body; target->cached = cached; bailIfError(KprHTTPCachePut(self->cache, message->url, cached)); bail: if (control) { // unsplit start = control; while (start < end) { if (*start == 0) *start = ','; start++; } } if (err == kFskErrNone) return true; target->cached = NULL; if (cached) KprMemPtrDispose(cached); return false; }