/*!
    \internal
    Called with an incoming connection
*/
void QJsonServer::handleLocalConnection()
{
    QLocalServer *server = qobject_cast<QLocalServer *>(sender());
    if (!server)
        return;

    if (QLocalSocket *socket = server->nextPendingConnection()) {
        socket->setReadBufferSize(64*1024);
        Q_D(QJsonServer);
        QJsonAuthority *authority = d->m_localServers.value(server);
        QJsonServerClient *client = new QJsonServerClient(this);
        client->setAuthority(authority);
        client->setSocket(socket);
        connect(client, SIGNAL(authorized(const QString&)),
                this, SLOT(handleClientAuthorized(const QString&)));
        connect(client, SIGNAL(disconnected(const QString&)),
                this, SLOT(clientDisconnected(const QString&)));
        connect(client, SIGNAL(messageReceived(const QString&, const QJsonObject&)),
                this, SLOT(receiveMessage(const QString&, const QJsonObject&)));
        connect(client, SIGNAL(authorizationFailed()),
                this, SLOT(handleAuthorizationFailed()));
        client->start();
    }
/**
 * handles http header reading for WebSocket upgrade
 * @param client WSclient_t * ///< pointer to the client struct
 * @param headerLine String ///< the header being read / processed
 */
void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {

    headerLine->trim(); // remove \r

    if(headerLine->length() > 0) {
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str());

        // websocket requests always start with GET see rfc6455
        if(headerLine->startsWith("GET ")) {

            // cut URL out
            client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4));

            //reset non-websocket http header validation state for this client
			client->cHttpHeadersValid = true;
			client->cMandatoryHeadersCount = 0;

        } else if(headerLine->indexOf(':')) {
            String headerName = headerLine->substring(0, headerLine->indexOf(':'));
            String headerValue = headerLine->substring(headerLine->indexOf(':') + 2);

            if(headerName.equalsIgnoreCase("Connection")) {
                headerValue.toLowerCase();
            	if(headerValue.indexOf("upgrade") >= 0) {
                    client->cIsUpgrade = true;
                }
            } else if(headerName.equalsIgnoreCase("Upgrade")) {
                if(headerValue.equalsIgnoreCase("websocket")) {
                    client->cIsWebsocket = true;
                }
            } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) {
                client->cVersion = headerValue.toInt();
            } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Key")) {
                client->cKey = headerValue;
                client->cKey.trim(); // see rfc6455
            } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) {
                client->cProtocol = headerValue;
            } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) {
                client->cExtensions = headerValue;
            } else if(headerName.equalsIgnoreCase("Authorization")) {
                client->base64Authorization = headerValue;
            } else {
            	client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue);
            	if (_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) {
            		client->cMandatoryHeadersCount++;
            	}
            }

        } else {
            DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
        }

        (*headerLine) = "";
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
        client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine)));
#endif
    } else {

        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num);
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cURL: %s\n", client->num, client->cUrl.c_str());
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cIsUpgrade: %d\n", client->num, client->cIsUpgrade);
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cIsWebsocket: %d\n", client->num, client->cIsWebsocket);
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cKey: %s\n", client->num, client->cKey.c_str());
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cProtocol: %s\n", client->num, client->cProtocol.c_str());
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cExtensions: %s\n", client->num, client->cExtensions.c_str());
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cVersion: %d\n", client->num, client->cVersion);
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - base64Authorization: %s\n", client->num, client->base64Authorization.c_str());
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cHttpHeadersValid: %d\n", client->num, client->cHttpHeadersValid);
        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cMandatoryHeadersCount: %d\n", client->num, client->cMandatoryHeadersCount);

        bool ok = (client->cIsUpgrade && client->cIsWebsocket);

        if(ok) {
            if(client->cUrl.length() == 0) {
                ok = false;
            }
            if(client->cKey.length() == 0) {
                ok = false;
            }
            if(client->cVersion != 13) {
                ok = false;
            }
            if(!client->cHttpHeadersValid) {
            	ok = false;
            }
            if (client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) {
            	ok = false;
            }
        }

        if(_base64Authorization.length() > 0) {
            if(client->base64Authorization.length() > 0) {
                String auth = "Basic ";
                auth += _base64Authorization;
                if(auth != client->base64Authorization) {
                    DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num);
                    handleAuthorizationFailed(client);
                    return;
                }
            } else {
                ok = false;
            }
        }

        if(ok) {

            DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num);

            // generate Sec-WebSocket-Accept key
            String sKey = acceptKey(client->cKey);

            DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - sKey: %s\n", client->num, sKey.c_str());

            client->status = WSC_CONNECTED;

            client->tcp->write("HTTP/1.1 101 Switching Protocols\r\n"
                    "Server: arduino-WebSocketsServer\r\n"
                    "Upgrade: websocket\r\n"
                    "Connection: Upgrade\r\n"
                    "Sec-WebSocket-Version: 13\r\n"
                    "Sec-WebSocket-Accept: ");
            client->tcp->write(sKey.c_str(), sKey.length());

            if(_origin.length() > 0) {
                String origin = "\r\nAccess-Control-Allow-Origin: ";
                origin += _origin;
                origin += "\r\n";
                client->tcp->write(origin.c_str(), origin.length());
            }

            if(client->cProtocol.length() > 0) {
                String protocol = "\r\nSec-WebSocket-Protocol: ";
                protocol += _protocol;
                protocol += "\r\n";
                client->tcp->write(protocol.c_str(), protocol.length());
            } else {
                client->tcp->write("\r\n");
            }

            // header end
            client->tcp->write("\r\n");

            headerDone(client);

            // send ping
            WebSockets::sendFrame(client, WSop_ping);

            runCbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length());

        } else {
            handleNonWebsocketConnection(client);
        }
    }
}