#include <common.h>
#include "RecordReader.h"
#include "fastcgi.h"

enum {

    // Limit the number of back-to-back receives done on one socket.
    MAX_READS = 10,

    // Largest possible record size (including overhead).
    MAX_RECORD_SIZE = 0x10108

};

FCGI::RecordReader::RecordReader() :
    pBuffer(0), iBuffer(0), nPayload(0), iRecord(0), nPadding(0), nNeeded(8), nRecord(0) {
    pBuffer = new char[MAX_RECORD_SIZE];
}

FCGI::RecordReader::~RecordReader() {
    delete pBuffer;
}

bool FCGI::RecordReader::onRead(SOCKET socket) {

    // Perform back-to-back reads sufficient to flush out most messages.
    for (int i = 0; i < MAX_READS; ++i) {

        // Read as much as possible, into the remaining buffer space.
        int nSpace = MAX_RECORD_SIZE - nPayload;
        int nRead = ::recv(socket,pBuffer + nPayload,nSpace,0);
        if (nRead < 0) {
            return (EWOULDBLOCK == errno) || (EAGAIN == errno);
        }
        nPayload += nRead;

        // Parse out records from the buffer.
        int nPresent = nPayload - iRecord;
        while (nNeeded <= nPresent) {
            // Are we at the beginning of the record?
            if (iRecord == iBuffer) {
                char* p = pBuffer + iRecord;
                int v;
                v = 255 & *p++;
                if (FCGI_VERSION_1 != v) {
                    return false; // We cannot handle this conversation.
                }
                v = 255 & *p++;
                recordDispatch.iType = v;
                v = 255 & *p++;
                v = (v << 8) | (255 & *p++);
                recordDispatch.idRequest = v;
                v = 255 & *p++;
                v = (v << 8) | (255 & *p++);
                recordDispatch.nLength = v;
                v = 255 & *p++;
                nPadding = v;
                ++p; // reserved
                iBuffer = (int) (p - pBuffer);
                nRecord = 8 + recordDispatch.nLength + nPadding;
                nNeeded = nRecord;
                continue;
            }
            // We now have the entire FCGI record.
            if (!recordDispatch.onRecord(pBuffer + iRecord + 8)) {
                return false;
            }
            nNeeded = 8;
            iRecord += nRecord;
            nPresent = nPayload - iRecord;
            assert(iRecord <= nPayload);
            // Is there anything left over after the record?
            if (0 == nPresent) {
                iBuffer = iRecord = nPayload = 0;
                break;
            }
            iBuffer = iRecord;
        }

        // Make room in buffer by moving down any record fragment.
        if (0 < iRecord) {
            if (0 < nPresent) {
                memcpy(pBuffer,pBuffer + iRecord,nPresent);
            }
            iRecord = iBuffer = 0;
            nPayload = nPresent;
        }
    }
    return true;
}