OSDN Git Service

Merge WebKit at r78450: Initial merge by git.
[android-x86/external-webkit.git] / Source / WebKit / qt / WebCoreSupport / InspectorServerQt.cpp
1 /*
2   Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
3
4   This library is free software; you can redistribute it and/or
5   modify it under the terms of the GNU Library General Public
6   License as published by the Free Software Foundation; either
7   version 2 of the License, or (at your option) any later version.
8
9   This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Library General Public License for more details.
13
14   You should have received a copy of the GNU Library General Public License
15   along with this library; see the file COPYING.LIB.  If not, write to
16   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17   Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include "InspectorServerQt.h"
22
23 #include "InspectorClientQt.h"
24 #include "InspectorController.h"
25 #include "MD5.h"
26 #include "Page.h"
27 #include "qwebpage.h"
28 #include "qwebpage_p.h"
29 #include <QFile>
30 #include <QHttpHeader>
31 #include <QHttpRequestHeader>
32 #include <QHttpResponseHeader>
33 #include <QString>
34 #include <QStringList>
35 #include <QTcpServer>
36 #include <QTcpSocket>
37 #include <QUrl>
38 #include <QWidget>
39 #include <qendian.h>
40 #include <wtf/text/CString.h>
41
42 namespace WebCore {
43
44 /*!
45     Computes the WebSocket handshake response given the two challenge numbers and key3.
46  */
47 static void generateWebSocketChallengeResponse(uint32_t number1, uint32_t number2, const unsigned char key3[8], unsigned char response[16])
48 {
49     uint8_t challenge[16];
50     qToBigEndian<qint32>(number1, &challenge[0]);
51     qToBigEndian<qint32>(number2, &challenge[4]);
52     memcpy(&challenge[8], key3, 8);
53     MD5 md5;
54     md5.addBytes(challenge, sizeof(challenge));
55     Vector<uint8_t, 16> digest;
56     md5.checksum(digest);
57     memcpy(response, digest.data(), 16);
58 }
59
60 /*!
61     Parses and returns a WebSocket challenge number according to the
62     method specified in the WebSocket protocol.
63
64     The field contains numeric digits interspersed with spaces and
65     non-numeric digits. The protocol ignores the characters that are
66     neither digits nor spaces. The digits are concatenated and
67     interpreted as a long int. The result is this number divided by
68     the number of spaces.
69  */
70 static quint32 parseWebSocketChallengeNumber(QString field)
71 {
72     QString nString;
73     int numSpaces = 0;
74     for (int i = 0; i < field.size(); i++) {
75         QChar c = field[i];
76         if (c == (QChar)' ') {
77             numSpaces++;
78         } else if ((c >= (QChar)'0') && (c <= (QChar)'9')) {
79             nString.append((QChar)c);
80         }
81     }
82     quint32 num = nString.toLong();
83     quint32 result = (numSpaces ? (num / numSpaces) : num);
84     return result;
85 }
86
87 static InspectorServerQt* s_inspectorServer;
88
89 InspectorServerQt* InspectorServerQt::server()
90 {
91     // s_inspectorServer is deleted in unregisterClient() when the last client is unregistered.
92     if (!s_inspectorServer)
93         s_inspectorServer = new InspectorServerQt();
94
95     return s_inspectorServer;
96 }
97
98 InspectorServerQt::InspectorServerQt()
99     : QObject()
100     , m_tcpServer(0)
101     , m_pageNumber(1)
102 {
103 }
104
105 InspectorServerQt::~InspectorServerQt()
106 {
107     close();
108 }
109
110 void InspectorServerQt::listen(quint16 port)
111 {
112     if (m_tcpServer)
113         return;
114
115     m_tcpServer = new QTcpServer();
116     m_tcpServer->listen(QHostAddress::Any, port);
117     connect(m_tcpServer, SIGNAL(newConnection()), SLOT(newConnection()));
118 }
119
120 void InspectorServerQt::close()
121 {
122     if (m_tcpServer) {
123         m_tcpServer->close();
124         delete m_tcpServer;
125     }
126     m_tcpServer = 0;
127 }
128
129 InspectorClientQt* InspectorServerQt::inspectorClientForPage(int pageNum)
130 {
131     InspectorClientQt* client = m_inspectorClients.value(pageNum);
132     return client;
133 }
134
135 void InspectorServerQt::registerClient(InspectorClientQt* client)
136 {
137     if (!m_inspectorClients.key(client))
138         m_inspectorClients.insert(m_pageNumber++, client);
139 }
140
141 void InspectorServerQt::unregisterClient(InspectorClientQt* client)
142 {
143     int pageNum = m_inspectorClients.key(client, -1);
144     if (pageNum >= 0)
145         m_inspectorClients.remove(pageNum);
146     if (!m_inspectorClients.size()) {
147         // s_inspectorServer points to this.
148         s_inspectorServer = 0;
149         close();
150         deleteLater();
151     }
152 }
153
154 void InspectorServerQt::newConnection()
155 {
156     QTcpSocket* tcpConnection = m_tcpServer->nextPendingConnection();
157     InspectorServerRequestHandlerQt* handler = new InspectorServerRequestHandlerQt(tcpConnection, this);
158     handler->setParent(this);
159 }
160
161 InspectorServerRequestHandlerQt::InspectorServerRequestHandlerQt(QTcpSocket* tcpConnection, InspectorServerQt* server)
162     : QObject(server)
163     , m_tcpConnection(tcpConnection)
164     , m_server(server)
165     , m_inspectorClient(0)
166 {
167     m_endOfHeaders = false;    
168     m_contentLength = 0;
169
170     connect(m_tcpConnection, SIGNAL(readyRead()), SLOT(tcpReadyRead()));
171     connect(m_tcpConnection, SIGNAL(disconnected()), SLOT(tcpConnectionDisconnected()));
172 }
173
174 InspectorServerRequestHandlerQt::~InspectorServerRequestHandlerQt()
175 {
176 }
177
178 void InspectorServerRequestHandlerQt::tcpReadyRead()
179 {
180     QHttpRequestHeader header;
181     bool isWebSocket = false;
182     if (!m_tcpConnection)
183         return;
184
185     if (!m_endOfHeaders) {
186         while (m_tcpConnection->bytesAvailable() && !m_endOfHeaders) {
187             QByteArray line = m_tcpConnection->readLine();
188             m_data.append(line);
189             if (line == "\r\n")
190                 m_endOfHeaders = true;
191         }
192         if (m_endOfHeaders) {
193             header = QHttpRequestHeader(QString::fromLatin1(m_data));
194             if (header.isValid()) {
195                 m_path = header.path();
196                 m_contentType = header.contentType().toLatin1();
197                 m_contentLength = header.contentLength();
198                 if (header.hasKey("Upgrade") && (header.value("Upgrade") == QLatin1String("WebSocket")))
199                     isWebSocket = true;
200
201                 m_data.clear();
202             }
203         }
204     }
205
206     if (m_endOfHeaders) {
207         QStringList pathAndQuery = m_path.split("?");
208         m_path = pathAndQuery[0];
209         QStringList words = m_path.split(QString::fromLatin1("/"));
210
211         if (isWebSocket) {
212             // switch to websocket-style WebSocketService messaging
213             if (m_tcpConnection) {
214                 m_tcpConnection->disconnect(SIGNAL(readyRead()));
215                 connect(m_tcpConnection, SIGNAL(readyRead()), SLOT(webSocketReadyRead()));
216
217                 QByteArray key3 = m_tcpConnection->read(8);
218
219                 quint32 number1 = parseWebSocketChallengeNumber(header.value("Sec-WebSocket-Key1"));
220                 quint32 number2 = parseWebSocketChallengeNumber(header.value("Sec-WebSocket-Key2"));
221
222                 char responseData[16];
223                 generateWebSocketChallengeResponse(number1, number2, (unsigned char*)key3.data(), (unsigned char*)responseData);
224                 QByteArray response(responseData, sizeof(responseData));
225
226                 QHttpResponseHeader responseHeader(101, "WebSocket Protocol Handshake", 1, 1);
227                 responseHeader.setValue("Upgrade", header.value("Upgrade"));
228                 responseHeader.setValue("Connection", header.value("Connection"));
229                 responseHeader.setValue("Sec-WebSocket-Origin", header.value("Origin"));
230                 responseHeader.setValue("Sec-WebSocket-Location", ("ws://" + header.value("Host") + m_path));
231                 responseHeader.setContentLength(response.size());
232                 m_tcpConnection->write(responseHeader.toString().toLatin1());
233                 m_tcpConnection->write(response);
234                 m_tcpConnection->flush();
235
236                 if ((words.size() == 4)
237                     && (words[1] == QString::fromLatin1("devtools"))
238                     && (words[2] == QString::fromLatin1("page"))) {
239                     int pageNum = words[3].toInt();
240
241                     m_inspectorClient = m_server->inspectorClientForPage(pageNum);
242                     // Attach remoteFrontendChannel to inspector, also transferring ownership.
243                     if (m_inspectorClient)
244                         m_inspectorClient->attachAndReplaceRemoteFrontend(new RemoteFrontendChannel(this));
245                 }
246
247             }
248
249             return;
250         }
251         if (m_contentLength && (m_tcpConnection->bytesAvailable() < m_contentLength))
252             return;
253
254         QByteArray content = m_tcpConnection->read(m_contentLength);
255         m_endOfHeaders = false;
256
257         QByteArray response;
258         int code = 200;
259         QString text = QString::fromLatin1("OK");
260
261         // If no path is specified, generate an index page.
262         if ((m_path == "") || (m_path == "/")) {
263             QString indexHtml = "<html><head><title>Remote Web Inspector</title></head><body><ul>\n";
264             for (QMap<int, InspectorClientQt* >::const_iterator it = m_server->m_inspectorClients.begin();
265                  it != m_server->m_inspectorClients.end(); 
266                  ++it) {
267                 indexHtml.append(QString("<li><a href=\"/webkit/inspector/inspector.html?page=%1\">%2</li>\n")
268                                  .arg(it.key())
269                                  .arg(it.value()->m_inspectedWebPage->mainFrame()->url().toString()));
270             }
271             indexHtml.append("</ul></body></html>");
272             response = indexHtml.toLatin1();
273         } else {
274             QString path = QString(":%1").arg(m_path);
275             QFile file(path);
276             // It seems that there should be an enum or define for these status codes somewhere in Qt or WebKit,
277             // but grep fails to turn one up.
278             // QNetwork uses the numeric values directly.
279             if (file.exists()) {
280                 file.open(QIODevice::ReadOnly);
281                 response = file.readAll();
282             } else {
283                 code = 404;
284                 text = QString::fromLatin1("Not OK");
285             }
286         }
287
288         QHttpResponseHeader responseHeader(code, text, 1, 0);
289         responseHeader.setContentLength(response.size());
290         if (!m_contentType.isEmpty())
291             responseHeader.setContentType(QString::fromLatin1(m_contentType));
292
293         QByteArray asciiHeader = responseHeader.toString().toAscii();
294         m_tcpConnection->write(asciiHeader);
295
296         m_tcpConnection->write(response);
297         m_tcpConnection->flush();
298         m_tcpConnection->close();
299
300         return;
301     }
302 }
303
304 void InspectorServerRequestHandlerQt::tcpConnectionDisconnected()
305 {
306     if (m_inspectorClient)
307         m_inspectorClient->detachRemoteFrontend();
308     m_tcpConnection->deleteLater();
309     m_tcpConnection = 0;
310 }
311
312 int InspectorServerRequestHandlerQt::webSocketSend(QByteArray payload)
313 {
314     Q_ASSERT(m_tcpConnection);
315     m_tcpConnection->putChar(0x00);
316     int nBytes = m_tcpConnection->write(payload);
317     m_tcpConnection->putChar(0xFF);
318     m_tcpConnection->flush();
319     return nBytes;
320 }
321
322 int InspectorServerRequestHandlerQt::webSocketSend(const char* data, size_t length)
323 {
324     Q_ASSERT(m_tcpConnection);
325     m_tcpConnection->putChar(0x00);
326     int nBytes = m_tcpConnection->write(data, length);
327     m_tcpConnection->putChar(0xFF);
328     m_tcpConnection->flush();
329     return nBytes;
330 }
331
332 void InspectorServerRequestHandlerQt::webSocketReadyRead()
333 {
334     Q_ASSERT(m_tcpConnection);
335     if (!m_tcpConnection->bytesAvailable())
336         return;
337     QByteArray content = m_tcpConnection->read(m_tcpConnection->bytesAvailable());
338     m_data.append(content);
339     while (m_data.size() > 0) {
340         // first byte in websocket frame should be 0
341         Q_ASSERT(!m_data[0]);
342
343         // Start of WebSocket frame is indicated by 0
344         if (m_data[0]) {
345             qCritical() <<  "webSocketReadyRead: unknown frame type" << m_data[0];
346             m_data.clear();
347             m_tcpConnection->close();
348             return;
349         }
350
351         // End of WebSocket frame indicated by 0xff.
352         int pos = m_data.indexOf(0xff, 1);
353         if (pos < 1)
354             return;
355
356         // After above checks, length will be >= 0.
357         size_t length = pos - 1;
358         if (length <= 0)
359             return;
360
361         QByteArray payload = m_data.mid(1, length);
362
363 #if ENABLE(INSPECTOR)
364         if (m_inspectorClient) {
365           InspectorController* inspectorController = m_inspectorClient->m_inspectedWebPage->d->page->inspectorController();
366           inspectorController->dispatchMessageFromFrontend(QString::fromUtf8(payload));
367         }
368 #endif
369
370         // Remove this WebSocket message from m_data (payload, start-of-frame byte, end-of-frame byte).
371         m_data = m_data.mid(length + 2);
372     }
373 }
374
375 RemoteFrontendChannel::RemoteFrontendChannel(InspectorServerRequestHandlerQt* requestHandler)
376     : QObject(requestHandler)
377     , m_requestHandler(requestHandler)
378 {
379 }
380
381 bool RemoteFrontendChannel::sendMessageToFrontend(const String& message)
382 {
383     if (!m_requestHandler)
384         return false;
385     CString cstr = message.utf8();
386     return m_requestHandler->webSocketSend(cstr.data(), cstr.length());
387 }
388
389 }