~george-edison55/jethttp/jethttp-singlethread

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
//=================================================
//
//           JetHTTP - CJetHTTPSocket.cpp
//           Copyright 2011 - Nathan Osman
//     This file is released under the GPL v3
//
//=================================================

#include "CJetHTTPSocket.h"

// Qt includes
#include <QStringList>

// JetHTTP includes
#include <CFileOutput.h>
#include <CPHPOutput.h>

CJetHTTPSocket::CJetHTTPSocket(QObject * parent, QTcpSocket * socket)
    : QObject(parent), m_socket(socket), m_request_type(RT_UNKNOWN), m_content_length(0), m_output(NULL),
      m_output_size(0), m_output_amount(0), m_header_received(false)
{
    // Claim the socket
    m_socket->setParent(this);

    // Connect to the readyread signal of the socket
    connect(m_socket, SIGNAL(readyRead()), SLOT(OnReadyRead()));

    // and the byteswritten signal
    connect(m_socket, SIGNAL(bytesWritten(qint64)), SLOT(OnBytesWritten(qint64)));
}

CJetHTTPSocket::~CJetHTTPSocket()
{
    if(m_output)
        delete m_output;

    qDebug("CJetHTTPSocket::~CJetHTTPSocket");
}

void CJetHTTPSocket::OnReadyRead()
{
    // Read all of the data that is available
    m_data_read.append(m_socket->readAll());

    // If we still haven't received the end
    // of the request header...
    if(!m_header_received)
    {
        // Check for the header
        int iPos = m_data_read.indexOf("\r\n\r\n");

        if(iPos != -1)
        {
            // We have reached the end of the header in this chunk,
            // so we extract the actual header data, send it off to
            // our header splitting function, which converts it into
            // a key/value array.
            m_header_received = true;

            // First we need to strip the first line from the
            // input headers.
            int iStart = m_data_read.indexOf("\r\n");

            // Process the top line of the request
            QString top_line = m_data_read.mid(0, iStart);

            if(!ProcessTopLine(top_line))
            {
                WriteOutput();
                return;
            }

            // Add 2 for extracting the headers
            iStart += 2;

            QString raw_header = m_data_read.mid(iStart, iPos - iStart);
            ProcessHeader(raw_header);

            if(m_request_type == RT_GET)
                ProcessRequest();
            else
            {
                // Get the value of the content length header
                QString val;

                if(m_headers.GetHeader("content-length", val))
                {
                    // Store this value
                    m_content_length = val.toInt();
                }
                else
                {
                    SendErrorReply(411, "Length Required", "Content-length HTTP header expected.");
                    WriteOutput();
                    return;
                }

                // Now we only need the data in the m_data_read string
                m_data_read = m_data_read.right(m_data_read.length() - (iPos + 4));

                // Check to see if we have already reached the
                // end of the POST data.
                if(m_data_read.length() == m_content_length)
                    ProcessRequest();
            }
        }
    }
    else
    {
        // Check to see how much data we are expecting
        // to receive (via the content-length HTTP header)
        // and compare that to the data that we *have*
        // received so far.
        if(m_data_read.length() == m_content_length)
            ProcessRequest();
    }
}

void CJetHTTPSocket::OnBytesWritten(qint64 amount)
{
    m_output_amount += amount;

    // Check to see if we're done!
    if(m_output_amount == m_output_size)
        this->deleteLater();
}

void CJetHTTPSocket::ProcessRequest()
{
    // First, we split the request into its two
    // components - the resource and the query string.
    QStringList url_components = m_request_url.split("?");

    QString component_url = url_components.at(0);
    QString component_querystring;

    if(url_components.size() > 1)
        component_querystring = url_components.at(1);

    // Now we determine which output source to use
    // for processing the given request.

    // First check to see if the file extension
    // is one that's currently supported.
    QString extension = component_url.right(3);

    if(!extension.compare("php", Qt::CaseInsensitive))
        m_output = new CPHPOutput();
    else
        m_output = new CFileOutput();

    //m_output->SetHeaderFunction
    m_output->ProcessRequest(component_url, component_querystring, m_headers);

    WriteOutput();
}

bool CJetHTTPSocket::ProcessTopLine(QString & data)
{
    // Split the top line into its principle components
    QStringList parts = data.split(" ");

    if(parts.size() > 2)
    {
        // Check the method used for this page
        if(parts.at(0) == "GET" || parts.at(0) == "POST")
        {
            // Now check the HTTP version
            if(parts.at(2) != "HTTP/1.0" && parts.at(2) != "HTTP/1.1")
            {
                SendErrorReply(505, "Invalid HTTP Version", "The HTTP version specified in the header is invalid.");
                return false;
            }
            else
            {
                // Grab the method
                if(parts.at(0) == "GET")
                    m_request_type = RT_GET;
                else
                    m_request_type = RT_POST;

                // Grab the URL
                m_request_url = parts.at(1);
            }
        }
        else
        {
            SendErrorReply(405, "Invalid Method", tr("The %1 method is not supported.").arg(parts.at(0)));
            m_output->SetHeader("Allow", "GET, POST");
            return false;
        }
    }
    else
    {
        SendErrorReply(400, "Invalid Request", "Your browser sent an incomplete request.");
        return false;
    }

    return true;
}

void CJetHTTPSocket::ProcessHeader(QString & data)
{
    QStringList lines = data.split("\r\n");

    QString line;
    foreach(line, lines)
    {
        // Process this line
        QStringList tokens = line.split(":");

        if(tokens.size() > 1)
        {
            m_headers.SetHeader(tokens.at(0).trimmed(), tokens.at(1).trimmed());
        }
    }
}

void CJetHTTPSocket::SendErrorReply(int status, QString status_str, QString error_msg)
{
    // Initialize the error output class
    m_output = new COutput;

    m_output->Error(status, status_str, error_msg);
}

void CJetHTTPSocket::WriteOutput()
{
    // Grab the size and send the data
    QByteArray data = m_output->GetContentBody();
    m_output_size = data.length();

    m_socket->write(data);
}