1/* 2 * Copyright (C) 2011 Apple Inc. All Rights Reserved. 3 * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28 29#if ENABLE(INSPECTOR_SERVER) 30 31#include "WebInspectorServer.h" 32 33#include "HTTPHeaderNames.h" 34#include "HTTPRequest.h" 35#include "WebInspectorProxy.h" 36#include "WebSocketServerConnection.h" 37 38using namespace WebCore; 39 40namespace WebKit { 41 42static unsigned pageIdFromRequestPath(const String& path) 43{ 44 size_t start = path.reverseFind('/'); 45 String numberString = path.substring(start + 1, path.length() - start - 1); 46 47 bool ok = false; 48 unsigned number = numberString.toUIntStrict(&ok); 49 if (!ok) 50 return 0; 51 return number; 52} 53 54WebInspectorServer& WebInspectorServer::shared() 55{ 56 static WebInspectorServer& server = *new WebInspectorServer; 57 return server; 58} 59 60WebInspectorServer::WebInspectorServer() 61 : WebSocketServer(this) 62 , m_nextAvailablePageId(1) 63{ 64} 65 66WebInspectorServer::~WebInspectorServer() 67{ 68 // Close any remaining open connections. 69 HashMap<unsigned, WebSocketServerConnection*>::iterator end = m_connectionMap.end(); 70 for (HashMap<unsigned, WebSocketServerConnection*>::iterator it = m_connectionMap.begin(); it != end; ++it) { 71 WebSocketServerConnection* connection = it->value; 72 WebInspectorProxy* client = m_clientMap.get(connection->identifier()); 73 closeConnection(client, connection); 74 } 75} 76 77int WebInspectorServer::registerPage(WebInspectorProxy* client) 78{ 79#ifndef ASSERT_DISABLED 80 ClientMap::iterator end = m_clientMap.end(); 81 for (ClientMap::iterator it = m_clientMap.begin(); it != end; ++it) 82 ASSERT(it->value != client); 83#endif 84 85 int pageId = m_nextAvailablePageId++; 86 m_clientMap.set(pageId, client); 87 return pageId; 88} 89 90void WebInspectorServer::unregisterPage(int pageId) 91{ 92 m_clientMap.remove(pageId); 93 WebSocketServerConnection* connection = m_connectionMap.get(pageId); 94 if (connection) 95 closeConnection(0, connection); 96} 97 98String WebInspectorServer::inspectorUrlForPageID(int) 99{ 100 return String(); 101} 102 103void WebInspectorServer::sendMessageOverConnection(unsigned pageIdForConnection, const String& message) 104{ 105 WebSocketServerConnection* connection = m_connectionMap.get(pageIdForConnection); 106 if (connection) 107 connection->sendWebSocketMessage(message); 108} 109 110void WebInspectorServer::didReceiveUnrecognizedHTTPRequest(WebSocketServerConnection* connection, PassRefPtr<HTTPRequest> request) 111{ 112 // request->url() contains only the path extracted from the HTTP request line 113 // and URL is poor at parsing incomplete URLs, so extract the interesting parts manually. 114 String path = request->url(); 115 size_t pathEnd = path.find('?'); 116 if (pathEnd == notFound) 117 pathEnd = path.find('#'); 118 if (pathEnd != notFound) 119 path.truncate(pathEnd); 120 121 // Ask for the complete payload in memory for the sake of simplicity. A more efficient way would be 122 // to ask for header data and then let the platform abstraction write the payload straight on the connection. 123 Vector<char> body; 124 String contentType; 125 bool found = platformResourceForPath(path, body, contentType); 126 127 HTTPHeaderMap headerFields; 128 headerFields.set(HTTPHeaderName::Connection, "close"); 129 headerFields.set(HTTPHeaderName::ContentLength, String::number(body.size())); 130 if (found) 131 headerFields.set(HTTPHeaderName::ContentType, contentType); 132 133 // Send when ready and close immediately afterwards. 134 connection->sendHTTPResponseHeader(found ? 200 : 404, found ? "OK" : "Not Found", headerFields); 135 connection->sendRawData(body.data(), body.size()); 136 connection->shutdownAfterSendOrNow(); 137} 138 139bool WebInspectorServer::didReceiveWebSocketUpgradeHTTPRequest(WebSocketServerConnection*, PassRefPtr<HTTPRequest> request) 140{ 141 String path = request->url(); 142 143 // NOTE: Keep this in sync with WebCore/inspector/front-end/inspector.js. 144 DEPRECATED_DEFINE_STATIC_LOCAL(const String, inspectorWebSocketConnectionPathPrefix, (ASCIILiteral("/devtools/page/"))); 145 146 // Unknown path requested. 147 if (!path.startsWith(inspectorWebSocketConnectionPathPrefix)) 148 return false; 149 150 int pageId = pageIdFromRequestPath(path); 151 // Invalid page id. 152 if (!pageId) 153 return false; 154 155 // There is no client for that page id. 156 WebInspectorProxy* client = m_clientMap.get(pageId); 157 if (!client) 158 return false; 159 160 return true; 161} 162 163void WebInspectorServer::didEstablishWebSocketConnection(WebSocketServerConnection* connection, PassRefPtr<HTTPRequest> request) 164{ 165 String path = request->url(); 166 unsigned pageId = pageIdFromRequestPath(path); 167 ASSERT(pageId); 168 169 // Ignore connections to a page that already have a remote inspector connected. 170 if (m_connectionMap.contains(pageId)) { 171 LOG_ERROR("A remote inspector connection already exist for page ID %d. Ignoring.", pageId); 172 connection->shutdownNow(); 173 return; 174 } 175 176 // Map the pageId to the connection in case we need to close the connection locally. 177 connection->setIdentifier(pageId); 178 m_connectionMap.set(pageId, connection); 179 180 WebInspectorProxy* client = m_clientMap.get(pageId); 181 client->remoteFrontendConnected(); 182} 183 184void WebInspectorServer::didReceiveWebSocketMessage(WebSocketServerConnection* connection, const String& message) 185{ 186 // Dispatch incoming remote message locally. 187 unsigned pageId = connection->identifier(); 188 ASSERT(pageId); 189 WebInspectorProxy* client = m_clientMap.get(pageId); 190 client->dispatchMessageFromRemoteFrontend(message); 191} 192 193void WebInspectorServer::didCloseWebSocketConnection(WebSocketServerConnection* connection) 194{ 195 // Connection has already shut down. 196 unsigned pageId = connection->identifier(); 197 if (!pageId) 198 return; 199 200 // The socket closing means the remote side has caused the close. 201 WebInspectorProxy* client = m_clientMap.get(pageId); 202 closeConnection(client, connection); 203} 204 205void WebInspectorServer::closeConnection(WebInspectorProxy* client, WebSocketServerConnection* connection) 206{ 207 // Local side cleanup. 208 if (client) 209 client->remoteFrontendDisconnected(); 210 211 // Remote side cleanup. 212 m_connectionMap.remove(connection->identifier()); 213 connection->setIdentifier(0); 214 connection->shutdownNow(); 215} 216 217} 218 219#endif // ENABLE(INSPECTOR_SERVER) 220