<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <cstring>

#include "qhttpsocket.h"
#include "qhttpsocket_p.h"

QHttpSocketPrivate::QHttpSocketPrivate(QHttpSocket *httpSocket, QTcpSocket *tcpSocket)
    : QObject(httpSocket),
      q(httpSocket),
      socket(tcpSocket),
      readState(ReadHeaders),
      requestDataRead(0),
      writeState(WriteNone),
      responseStatusCode("200 OK")
{
    connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64)));
    connect(socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));

    // Process anything already received by the socket
    onReadyRead();
}

void QHttpSocketPrivate::onReadyRead()
{
    // Append all of the new data to the read buffer
    readBuffer.append(socket->readAll());

    if(readState == ReadHeaders) {
        if(!readHeaders()) {
            return;
        }
    }

    if(readState == ReadData) {
        readData();
    }

    if(readState == ReadFinished) {
        readBuffer.clear();
    }
}

void QHttpSocketPrivate::onBytesWritten(qint64 bytes)
{
    // Check to see if all of the response header was written
    if(writeState == WriteHeaders) {
        if(responseHeaderRemaining - bytes > 0) {
            responseHeaderRemaining -= bytes;
        } else {
            writeState = WriteData;
            bytes -= responseHeaderRemaining;
        }
    }

    if(writeState == WriteData) {
        Q_EMIT q->bytesWritten(bytes);
    }
}

void QHttpSocketPrivate::onDisconnected()
{
    // The socket should not be disconnected until both the reading and
    // writing have completed - if so, an error has occurred
    if(readState != ReadFinished || writeState != WriteFinished) {
        Q_EMIT q->error();
        q->setOpenMode(QIODevice::NotOpen);
    }

    Q_EMIT q->disconnected();
}

bool QHttpSocketPrivate::readHeaders()
{
    // Check for the double CRLF that signals the end of the headers and
    // if it is not found, wait until the next time readyRead is emitted
    int index = readBuffer.indexOf("\r\n\r\n");
    if(index == -1) {
        return false;
    }

    // Attempt to parse the headers and if a problem is encountered, abort
    // the connection (so that no more data is read or written) and return
    if(!QHttpParser::parseRequestHeaders(readBuffer.left(index), requestMethod, requestPath, requestHeaders)) {
        socket->abort();
        return false;
    }

    // Remove the headers from the buffer
    readBuffer.remove(0, index + 4);

    // Check for the content-length header - if it is present, then
    // prepare to read the specified amount of data, otherwise, no data
    // should be read from the socket and the read channel is finished
    if(requestHeaders.contains("Content-Length")) {
        readState = ReadData;
        requestDataTotal = requestHeaders.value("Content-Length").toLongLong();
    } else {
        readState = ReadFinished;
        Q_EMIT q->readChannelFinished();
    }

    // Indicate that the headers have been parsed
    Q_EMIT q->headersParsed();

    return true;
}

void QHttpSocketPrivate::readData()
{
    Q_EMIT q->readyRead();

    // Check to see if the specified amount of data has been read from the
    // socket, if so, emit the readChannelFinished() signal
    if(requestDataRead + readBuffer.size() >= requestDataTotal) {
        readState = ReadFinished;
        Q_EMIT q->readChannelFinished();
    }
}

QHttpSocket::QHttpSocket(QTcpSocket *socket, QObject *parent)
    : QIODevice(parent),
      d(new QHttpSocketPrivate(this, socket))
{
    // The device is initially open for both reading and writing
    setOpenMode(QIODevice::ReadWrite);
}

qint64 QHttpSocket::bytesAvailable() const
{
    return d->readBuffer.size() + QIODevice::bytesAvailable();
}
<commit_msg>Fixed a couple of bugs in QHttpSocket relating to reading data.<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <cstring>

#include "qhttpsocket.h"
#include "qhttpsocket_p.h"

QHttpSocketPrivate::QHttpSocketPrivate(QHttpSocket *httpSocket, QTcpSocket *tcpSocket)
    : QObject(httpSocket),
      q(httpSocket),
      socket(tcpSocket),
      readState(ReadHeaders),
      requestDataRead(0),
      writeState(WriteNone),
      responseStatusCode("200 OK")
{
    connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64)));
    connect(socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));

    // Process anything already received by the socket
    onReadyRead();
}

void QHttpSocketPrivate::onReadyRead()
{
    // Append all of the new data to the read buffer
    readBuffer.append(socket->readAll());

    if(readState == ReadHeaders) {
        if(!readHeaders()) {
            return;
        }
    }

    if(readState == ReadData) {
        readData();
    } else if(readState == ReadFinished) {
        readBuffer.clear();
    }
}

void QHttpSocketPrivate::onBytesWritten(qint64 bytes)
{
    // Check to see if all of the response header was written
    if(writeState == WriteHeaders) {
        if(responseHeaderRemaining - bytes > 0) {
            responseHeaderRemaining -= bytes;
        } else {
            writeState = WriteData;
            bytes -= responseHeaderRemaining;
        }
    }

    if(writeState == WriteData) {
        Q_EMIT q->bytesWritten(bytes);
    }
}

void QHttpSocketPrivate::onDisconnected()
{
    // The socket should not be disconnected until both the reading and
    // writing have completed - if so, an error has occurred
    if(readState != ReadFinished || writeState != WriteFinished) {
        Q_EMIT q->error();
        q->setOpenMode(QIODevice::NotOpen);
    }

    Q_EMIT q->disconnected();
}

bool QHttpSocketPrivate::readHeaders()
{
    // Check for the double CRLF that signals the end of the headers and
    // if it is not found, wait until the next time readyRead is emitted
    int index = readBuffer.indexOf("\r\n\r\n");
    if(index == -1) {
        return false;
    }

    // Attempt to parse the headers and if a problem is encountered, abort
    // the connection (so that no more data is read or written) and return
    if(!QHttpParser::parseRequestHeaders(readBuffer.left(index), requestMethod, requestPath, requestHeaders)) {
        socket->abort();
        return false;
    }

    // Remove the headers from the buffer
    readBuffer.remove(0, index + 4);

    // Check for the content-length header - if it is present, then
    // prepare to read the specified amount of data, otherwise, no data
    // should be read from the socket and the read channel is finished
    if(requestHeaders.contains("Content-Length")) {
        readState = ReadData;
        requestDataTotal = requestHeaders.value("Content-Length").toLongLong();
    } else {
        readState = ReadFinished;
        Q_EMIT q->readChannelFinished();
    }

    // Indicate that the headers have been parsed
    Q_EMIT q->headersParsed();

    return true;
}

void QHttpSocketPrivate::readData()
{
    // Emit the readyRead() signal if any data is available in the buffer
    if(readBuffer.size()) {
        Q_EMIT q->readyRead();
    }

    // Check to see if the specified amount of data has been read from the
    // socket, if so, emit the readChannelFinished() signal
    if(requestDataRead + readBuffer.size() >= requestDataTotal) {
        readState = ReadFinished;
        Q_EMIT q->readChannelFinished();
    }
}

QHttpSocket::QHttpSocket(QTcpSocket *socket, QObject *parent)
    : QIODevice(parent),
      d(new QHttpSocketPrivate(this, socket))
{
    // The device is initially open for both reading and writing
    setOpenMode(QIODevice::ReadWrite);
}

qint64 QHttpSocket::bytesAvailable() const
{
    return d->readBuffer.size() + QIODevice::bytesAvailable();
}

{'fraction_non_alphanumeric': 0.0967869892899643, 'fraction_numerical': 0.003966679888932963, 'mean_word_length': 3.253479544495993, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '780174', 'n_tokens_mistral': 2870, 'n_tokens_neox': 2766, 'n_words': 984}