static FskErr KprWebSocketServerRequestDoRead(FskThreadDataHandler handler UNUSED, FskThreadDataSource source UNUSED, void *refCon) { FskErr err = kFskErrNone; KprWebSocketServerRequest self = refCon; int size = -1; char buffer[kHeaderBufferSize]; while (err == kFskErrNone) { err = FskNetSocketRecvTCP(self->skt, buffer, kHeaderBufferSize, &size); if (err == kFskErrNoData) return kFskErrNone; FskHeadersParseChunk(buffer, size, kFskHeaderTypeRequest, self->requestHeaders); if (self->requestHeaders->headersParsed) { KprWebSocketServerHandleRequest(self); KprWebSocketServerRequestDispose(self); break; } } return err; }
static void httpServerEngineCycle(void *param) { FskErr err = kFskErrNone, retVal = kFskErrNeedMoreTime; FskHTTPServerRequest request = (FskHTTPServerRequest)param; int amt, ret, chunkSize = 0; UInt32 chunkSizeL; char *s, *p; Boolean readSomeMore, needsDispose = false; FskThreadRemoveDataHandler(&request->dataHandler); switch (request->state) { case kHTTPNewSession: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); FskTimeGetNow(&request->stats.requestStarted); request->state = kHTTPReadRequestHeaders; case kHTTPReadRequestHeaders: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); shoveBuffer(request->in); amt = request->in.bufferSize - request->in.max; if (amt) { err = FskNetSocketRecvTCP(request->skt, &request->in.buf[request->in.max], amt, &ret); switch (err) { case kFskErrNone: #if SUPPORT_INSTRUMENTATION if (FskInstrumentedItemHasListeners(request)) { FskHTTPInstrMsgDataRecord msg; msg.buffer = &request->in.buf[request->in.max]; msg.amt = ret; FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestRecvData, &msg); } #endif request->http->stats.requestsStarted += 1; request->in.max += ret; request->state = kHTTPProcessRequestHeaders; request->stats.bytesReceived += ret; request->http->stats.bytesReceived += ret; break; case kFskErrNoData: retVal = kFskErrNoData; break; case kFskErrConnectionClosed: if (request->stats.bytesReceived) { request->state = kHTTPSocketError; } else { request->state = kHTTPDone; } break; default: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgErrString, "kHTTPReadRequestHeaders: RecvTCP - error"); request->state = kHTTPSocketError; break; } } else request->state = kHTTPProcessRequestHeaders; if (request->state != kHTTPProcessRequestHeaders) break; case kHTTPProcessRequestHeaders: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); amt = request->in.max - request->in.pos; if (amt) { ret = FskHeadersParseChunk(&request->in.buf[request->in.pos], amt, kFskHeaderTypeRequest, request->requestHeaders); if (ret < 0) { err = kFskErrBadData; request->state = kHTTPSocketError; break; } request->in.pos += ret; if (request->requestHeaders->headersParsed) { httpProcessRequestHeaders(request); } else if (ret != amt) { // odd case - we didn't consume all the data, but // the header parsing isn't complete. request->state = kHTTPServerError; } } else request->state = kHTTPReadRequestHeaders; if (request->state != kHTTPReadRequestBody) break; case kHTTPReadRequestBody: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); readSomeMore = false; amt = request->in.max - request->in.pos; if (request->requestBodyContentLength > 0) { if (amt) { request->state = kHTTPProcessRequestBody; } else { request->in.max = 0; request->in.pos = 0; readSomeMore = true; } } else if (request->requestBodyChunked) { // chunked if (amt == 0) { readSomeMore = true; } else { p = &request->in.buf[request->in.pos]; while ((amt > 1) && lineEnd(p)) { // consume line-ends amt -= 2; request->in.pos += 2; p += 2; } while ((amt > 1) && !lineEnd(p)) { // scan for chunk size amt--; p++; } if ((amt > 1) && lineEnd(p)) { // convert the chunksize s = &request->in.buf[request->in.pos]; chunkSize = FskStrHexToNum(s, p-s); p += 2; //lineend request->requestBodyContentLength = chunkSize; request->in.pos += (p-s); request->state = kHTTPReadRequestBody; if (0 == chunkSize) { // we've read the end indicator (0) if ((amt > 1) && lineEnd(p)) request->in.pos += 2; // consume last cr/lf } request->requestBodyChunked = false; } } else { readSomeMore = true; } } } else { // we're done reading chunks // we're done reading the request request->state = kHTTPPrepareResponse; doCallCondition(request->http->callbacks->requestCondition, request, kFskHTTPConditionRequestRequestFinished, request->refCon); } if (readSomeMore) { shoveBuffer(request->in); err = FskNetSocketRecvTCP(request->skt, &request->in.buf[request->in.max], request->in.bufferSize - request->in.max, &ret); switch (err) { case kFskErrNone: #if SUPPORT_INSTRUMENTATION if (FskInstrumentedItemHasListeners(request)) { FskHTTPInstrMsgDataRecord msg; msg.buffer = &request->in.buf[request->in.max]; msg.amt = ret; FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestRecvData, &msg); } #endif request->in.max += ret; if (request->requestBodyChunked) // chunked? request->state = kHTTPReadRequestBody; else request->state = kHTTPProcessRequestBody; request->stats.bytesReceived += ret; request->http->stats.bytesReceived += ret; break; case kFskErrNoData: retVal = kFskErrNoData; break; default: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgErrString, "kHTTPReadRequestBody: RecvTCP - error"); request->http->stats.requestsFailed += 1; request->state = kHTTPSocketError; break; } } if (request->state != kHTTPProcessRequestBody) break; case kHTTPProcessRequestBody: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); amt = request->in.max - request->in.pos; if (amt > request->requestBodyContentLength) { if (false == request->requestBodyChunked) request->requestBodyContentLength = amt; else amt = request->requestBodyContentLength; } chunkSizeL = (UInt32)chunkSize; err = doDataCallback(request->http->callbacks->requestReceiveRequest, request, &request->in.buf[request->in.pos], amt, &chunkSizeL, request->refCon); chunkSize = (int)chunkSizeL; if (kFskErrNone == err) { if (chunkSize) { request->in.pos += chunkSize; request->requestBodyContentLength -= chunkSize; request->stats.requestBodyReceived += chunkSize; if (false == request->requestBodyChunked) { if (0 == request->requestBodyContentLength) request->state = kHTTPPrepareResponse; else request->state = kHTTPReadRequestBody; } else request->state = kHTTPReadRequestBody; } else { // data callback wants to suspend the session and not // consume the chunk, it can do so if (request->state != kHTTPSessionSuspend) request->state = kHTTPServerError; } } else { // the data callback returned an error. if (request->state != kHTTPSessionSuspend) request->state = kHTTPServerError; } if (request->state != kHTTPPrepareResponse) break; doCallCondition(request->http->callbacks->requestCondition, request, kFskHTTPConditionRequestRequestFinished, request->refCon); if (request->state != kHTTPPrepareResponse) break; case kHTTPPrepareResponse: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); request->state = kHTTPProcessResponse; err = doCallCondition(request->http->callbacks->requestCondition, request, kFskHTTPConditionRequestGenerateResponseHeaders, request->refCon); if (err == kFskErrNeedMoreTime) request->state = kHTTPSessionSuspend; else if (err) request->responseHeaders->responseCode = 500; if (request->state != kHTTPProcessResponse) break; case kHTTPProcessResponse: request->state = kHTTPGetDataChunk; httpPrepareResponseHeaders(request); if (request->state != kHTTPGetDataChunk) break; case kHTTPGetDataChunk: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); if (0 == FskStrCompare(kFskStrHEAD, FskHeaderMethod(request->requestHeaders))) { request->state = kHTTPSendDataChunk; request->nextState = kHTTPSetupNextRequest; break; } p = &request->out.buf[request->out.max]; if (request->transferEncoding == kFskTransferEncodingChunked) { request->out.max += 6; } chunkSize = 0; amt = (request->out.bufferSize - request->out.max) - 2; // fetch response data from callback chunkSizeL = (UInt32)chunkSize; err = doDataCallback(request->http->callbacks->requestGenerateResponseBody, request, &request->out.buf[request->out.max], amt, &chunkSizeL, request->refCon); chunkSize = (int)chunkSizeL; FskInstrumentedTypePrintfDebug(&gFskHTTPServerTypeInstrumentation, "doDataCallback returns err: %d, chunkSize: %d\n", err, chunkSize); if ((kFskErrNone != err) && (kFskErrEndOfFile != err)) { FskInstrumentedTypePrintfDebug(&gFskHTTPServerTypeInstrumentation, "about to doCallCondition -requestResponseFinished - FAIL\n"); doCallCondition(request->http->callbacks->requestCondition, request, kFskHTTPConditionRequestResponseFinished, request->refCon); request->http->stats.requestsFailed += 1; request->state = kHTTPServerError; break; } request->out.max += chunkSize; if ((0 == chunkSize) && (request->state == kHTTPSessionSuspend)) { if (kFskTransferEncodingChunked == request->transferEncoding) request->out.max -= 6; break; } request->state = kHTTPSendDataChunk; if ((chunkSize == 0) || (kFskErrEndOfFile == err)) { request->nextState = kHTTPSetupNextRequest; } else request->nextState = kHTTPGetDataChunk; if (request->transferEncoding == kFskTransferEncodingChunked) { FskStrNumToHex(chunkSize, p, 4); p += 4; *p++ = kFskCR; *p++ = kFskLF; if (chunkSize) p += chunkSize; *p++ = kFskCR; *p++ = kFskLF; request->out.max += 2; } request->stats.bodyBytesSent += chunkSize; if (request->state != kHTTPSendDataChunk) break; case kHTTPSendDataChunk: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); FskInstrumentedTypePrintfDebug(&gFskHTTPServerTypeInstrumentation, " - request->out.max %d - request->out.pos %d\n", request->out.max, request->out.pos); amt = request->out.max - request->out.pos; if (0 == amt) { FskInstrumentedTypePrintfDebug(&gFskHTTPServerTypeInstrumentation, "amt is zero now - transition to next state.\n"); request->state = request->nextState; break; } err = FskNetSocketSendTCP(request->skt, &request->out.buf[request->out.pos], amt, &ret); switch (err) { case kFskErrNone: #if SUPPORT_INSTRUMENTATION if (FskInstrumentedItemHasListeners(request)) { FskHTTPInstrMsgDataRecord msg; msg.buffer = &request->out.buf[request->out.pos]; msg.amt = ret; FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestSendData, &msg); } #endif request->out.pos += ret; request->stats.bytesSent += ret; request->http->stats.bytesSent += ret; if (request->transferEncoding == kFskTransferEncodingChunked) { request->stats.bytesSent -= 8; } if (request->out.pos == request->out.max) { request->out.pos = 0; request->out.max = 0; request->state = request->nextState; } break; case kFskErrNoData: retVal = kFskErrSocketFull; break; default: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgErrString, "kHTTPSendDataChunk: SendTCP - error"); request->state = kHTTPSocketError; request->http->stats.requestsFailed += 1; break; } if (request->state != kHTTPSetupNextRequest) break; case kHTTPSetupNextRequest: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); FskTimeGetNow(&request->stats.requestStopped); request->http->stats.requestsFinished += 1; doCallCondition(request->http->callbacks->requestCondition, request, kFskHTTPConditionRequestResponseFinished, request->refCon); request->state = kHTTPDone; if (request->keepAlive && !request->http->stopped) { if ((request->in.max - request->in.pos) > 0) { FskHeaderStructDispose(request->requestHeaders); FskHeaderStructDispose(request->responseHeaders); FskHeaderStructNew(&request->requestHeaders); FskHeaderStructNew(&request->responseHeaders); request->keepAlive = false; request->out.max = 0; request->out.pos = 0; FskMemSet(&request->stats, 0, sizeof(request->stats)); FskTimeGetNow(&request->stats.requestStarted); request->state = kHTTPProcessRequestHeaders; } } if (request->state != kHTTPDone) break; case kHTTPDone: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); if (request->keepAlive && !request->http->stopped) { FskHeaderStructDispose(request->requestHeaders); FskHeaderStructDispose(request->responseHeaders); FskHeaderStructNew(&request->requestHeaders); FskHeaderStructNew(&request->responseHeaders); request->keepAlive = false; request->in.max = 0; request->in.pos = 0; request->out.max = 0; request->out.pos = 0; FskMemSet(&request->stats, 0, sizeof(request->stats)); retVal = kFskErrNoData; // will cause data handler to be installed request->state = kHTTPNewSession; } else { request->state = kHTTPClose; retVal = kFskErrNeedMoreTime; } break; case kHTTPFulfillExpectation: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); request->state = kHTTPSendDataChunk; request->nextState = kHTTPReadRequestBody; request->out.max += snprintf(request->out.buf, request->out.bufferSize, "%s %d %s\r\n\r\n", httpProtocolVersionString(request), 100, FskFindResponse(100)); break; case kHTTPDenyExpectation: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); request->state = kHTTPSendDataChunk; request->nextState = kHTTPDone; request->out.max += snprintf(request->out.buf, request->out.bufferSize, "%s %d %s\r\n\r\n", httpProtocolVersionString(request), 417, FskFindResponse(417)); break; case kHTTPServerError: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); request->state = kHTTPSocketError; // fall through case kHTTPSocketError: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); request->http->stats.connectionsAborted++; doCallCondition(request->http->callbacks->requestCondition, request, kFskHTTPConditionRequestErrorAbort, request->refCon); request->state = kHTTPClose; // fall through case kHTTPClose: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); request->state = kHTTPRequestComplete; needsDispose = true; break; case kHTTPRequestComplete: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); retVal = kFskErrNone; // request is finished, don't call back break; case kHTTPSessionSuspend: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); retVal = kFskErrNone; // do nothing in suspend state break; default: FskInstrumentedItemSendMessage(request, kFskHTTPInstrMsgRequestState, request); request->state = kHTTPClose; // unknown state break; ; } if ((request->state == kHTTPServerError) || (request->state == kHTTPSocketError) || (request->state == kHTTPDone) || (request->state == kHTTPClose)) retVal = kFskErrNeedMoreTime; if (retVal == kFskErrNoData) { FskThreadAddDataHandler(&request->dataHandler, (FskThreadDataSource)request->skt, httpServerDataHandler, true, false, request); } else if (retVal == kFskErrSocketFull) { FskThreadAddDataHandler(&request->dataHandler, (FskThreadDataSource)request->skt, httpServerDataHandler, false, true, request); } else if (retVal == kFskErrNeedMoreTime) { FskTimeCallbackScheduleNextRun(request->cycleCallback, httpServerTimeCycle, request); } else if (retVal == kFskErrNone) { // nothing doin } else { FskInstrumentedTypePrintfDebug(&gFskHTTPServerTypeInstrumentation, "httpCycle - weird retVal %d\n", retVal); } if (needsDispose) { FskListRemove((FskList*)&request->http->activeRequests, request); FskTimeCallbackDispose(request->keepAliveKillCallback); request->keepAliveKillCallback = NULL; if (kFskErrNone != doCallCondition(request->http->callbacks->requestCondition, request, kFskHTTPConditionConnectionTerminating, request->refCon)) FskHTTPServerRequestDispose(request); } else { if ((retVal == kFskErrNeedMoreTime) || ((retVal != kFskErrNone) && request->keepAlive)) { FskTimeCallbackScheduleFuture(request->keepAliveKillCallback, FskHTTPServerRequestGetKeepAliveTimeout(request), 0, httpKillKeepAlive, request); } } }