static int KprWebSocketServerValidateRequest(FskHeaders *requestHeaders, FskHeaders *responseHeaders) { FskErr err; char *value; char *decoded = NULL; UInt32 len; int statusCode = 400; value = FskHeaderFind("Upgrade", requestHeaders); if (!value || FskStrCompareCaseInsensitive(value, "websocket") != 0) goto bail; value = FskHeaderFind("Connection", requestHeaders); if (!value || FskStrCompareCaseInsensitive(value, "Upgrade") != 0) goto bail; value = FskHeaderFind("Sec-WebSocket-Version", requestHeaders); if (!value || FskStrCompare(value, "13") != 0) { statusCode = 426; FskHeaderAddString("Sec-WebSocket-Version", "13", responseHeaders); goto bail; } value = FskHeaderFind("Sec-WebSocket-Key", requestHeaders); if (!value) goto bail; bailIfError(FskStrB64Decode(value, FskStrLen(value), &decoded, &len)); if (len != 16) goto bail; statusCode = 101; bail: FskMemPtrDispose(decoded); return statusCode; }
static void KprWebSocketServerPrepareResponseHeaders(KprWebSocketServerRequest request) { if (NULL == FskHeaderFind(kFskStrServer, request->responseHeaders)) FskHeaderAddString(kFskStrServer, kWebSocketServerIdentifier, request->responseHeaders); if (NULL == FskHeaderFind(kFskStrContentLength, request->responseHeaders)) FskHeaderAddString(kFskStrContentLength, "0", request->responseHeaders); if (NULL == FskHeaderFind(kFskStrDate, request->responseHeaders)) { char dateString[32]; FskTimeMakeDate(dateString, 31); FskHeaderAddString(kFskStrDate, dateString, request->responseHeaders); } }
static Boolean KprWebSocketEndpointValidateResponse(KprWebSocketEndpoint self, FskHeaders *response) { FskErr err; char *value; char *encoded = NULL; Boolean result = false; { FskHeaderIterator iter = FskHeaderIteratorNew(response); FskDebugStr("HANDSHAKE: response headers\n"); while (iter) { FskDebugStr("> %s: %s\n", iter->name, iter->value); iter = FskHeaderIteratorNext(iter); } FskHeaderIteratorDispose(iter); } if (response->responseCode != 101) return false; if (!KprWebSocketEndpointCheckHeaderValue(response, "Upgrade", "websocket")) return false; if (!KprWebSocketEndpointCheckHeaderValue(response, "Connection", "Upgrade")) return false; value = FskHeaderFind("Sec-WebSocket-Accept", response); if (!value) return false; bailIfError(KprWebSocketCalculateHash(self->key, &encoded)); result = (FskStrCompare(value, encoded) == 0); bail: if (encoded) FskMemPtrDispose(encoded); return result; }
static Boolean KprWebSocketEndpointCheckHeaderValue(FskHeaders *response, char *name, char *bingo) { char *value; value = FskHeaderFind(name, response); if (!value) return false; return (FskStrCompareCaseInsensitive(value, bingo) == 0); }
static void KprWebSocketServerGenerateUpgradeResponse(KprWebSocketServerRequest request) { FskHeaders *responseHeaders = request->responseHeaders; char *encoded = NULL; char *value; responseHeaders->responseCode = 101; FskHeaderAddString("Upgrade", "websocket", responseHeaders); FskHeaderAddString("Connection", "Upgrade", responseHeaders); value = FskHeaderFind("Sec-WebSocket-Key", request->requestHeaders); if (value) { KprWebSocketCalculateHash(value, &encoded); if (encoded) { FskHeaderAddString("Sec-WebSocket-Accept", encoded, responseHeaders); FskMemPtrDispose(encoded); } } }
FskErr mp3SpoolerCallback(void *clientRefCon, UInt32 operation, void *param) { mp3Reader state = clientRefCon; FskErr err = kFskErrNone; switch (operation) { case kFskMediaSpoolerOperationDataReady: state->spoolerPosition += (UInt32)param; if (state->reader->mediaState < kFskMediaPlayerStateStopped) { if (state->id3.data) { UInt32 percent, bytesRead; void *buffer; if (state->id3.size != state->id3.offset) { err = FskMediaSpoolerRead(state->spooler, state->id3.offset, (UInt32)(state->id3.size - state->id3.offset), &buffer, &bytesRead); if (kFskErrNone == err) { FskMemMove((char *)state->id3.data + state->id3.offset, buffer, bytesRead); state->id3.offset += bytesRead; } } percent = (UInt32)((((float)state->id3.offset) / ((float)state->id3.size)) * 100.0); if (percent < 100) (state->reader->doSetState)(state->reader, kFskMediaPlayerStateInstantiatingProgress + percent); else err = mp3Instantiate(state); } else err = mp3Instantiate(state); } break; case kFskMediaSpoolerOperationSetHeaders: { FskHeaders *headers = param; FskHeaderAddString("icy-metadata", "1", headers); } break; case kFskMediaSpoolerOperationGetHeaders: { FskHeaders *headers = param; char *value = FskHeaderFind("icy-metaint", headers); state->icy.metaInt = (NULL != value) ? FskStrToNum(value) : 0; state->icy.nextMetaPosition = state->icy.metaInt; if ((NULL == state->mi.meta) || (kFskErrNone != FskMediaMetaDataGet(state->mi.meta, "FullName", 0, NULL, NULL))) { value = FskHeaderFind("icy-name", headers); if (NULL == value) value = FskHeaderFind("x-audiocast-name", headers); state->icy.title = FskStrDoCopy(value); } state->icy.isProtocol = 0 == FskStrCompare(headers->protocol, "ICY"); value = FskHeaderFind("Server", headers); if (NULL != value) { if (0 == FskStrCompareWithLength("Orbiter", value, 7)) { if (!state->isOrbiter) { state->isOrbiter = true; state->reader->needsIdle = true; FskTimeGetNow(&state->orbStart); } } } if (state->icy.isNanocaster) { value = FskHeaderFind("icy-genre", headers); if ((NULL != value) && (0 == FskStrCompareCaseInsensitive(value, "error"))) (state->reader->doSetState)(state->reader, kFskMediaPlayerStateFailed); } value = FskHeaderFind("availableSeekRange.dlna.org", headers); if (value && (0 == FskStrCompareWithLength("1 npt=", value, 6))) { char *dash = FskStrChr(value + 6, '-'); double duration; if (dash && (kFskErrNone == FskMediaParseNPT(dash + 1, &duration))) state->dlnaDuration = duration; } } break; case kFskMediaSpoolerOperationGetURI: state->spoolerPosition = ((FskMediaSpoolerGetURI)param)->position; break; } 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; } }