/* * Copyright (C) 2011 Apple Inc. All Rights Reserved. * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #if ENABLE(INSPECTOR_SERVER) #include "WebInspectorServer.h" #include "HTTPHeaderNames.h" #include "HTTPRequest.h" #include "WebInspectorProxy.h" #include "WebSocketServerConnection.h" using namespace WebCore; namespace WebKit { static unsigned pageIdFromRequestPath(const String& path) { size_t start = path.reverseFind('/'); String numberString = path.substring(start + 1, path.length() - start - 1); bool ok = false; unsigned number = numberString.toUIntStrict(&ok); if (!ok) return 0; return number; } WebInspectorServer& WebInspectorServer::singleton() { static WebInspectorServer& server = *new WebInspectorServer; return server; } WebInspectorServer::WebInspectorServer() : WebSocketServer(this) , m_nextAvailablePageId(1) { } WebInspectorServer::~WebInspectorServer() { // Close any remaining open connections. HashMap::iterator end = m_connectionMap.end(); for (HashMap::iterator it = m_connectionMap.begin(); it != end; ++it) { WebSocketServerConnection* connection = it->value; WebInspectorProxy* client = m_clientMap.get(connection->identifier()); closeConnection(client, connection); } } int WebInspectorServer::registerPage(WebInspectorProxy* client) { #ifndef ASSERT_DISABLED ClientMap::iterator end = m_clientMap.end(); for (ClientMap::iterator it = m_clientMap.begin(); it != end; ++it) ASSERT(it->value != client); #endif int pageId = m_nextAvailablePageId++; m_clientMap.set(pageId, client); return pageId; } void WebInspectorServer::unregisterPage(int pageId) { m_clientMap.remove(pageId); WebSocketServerConnection* connection = m_connectionMap.get(pageId); if (connection) closeConnection(0, connection); } #if !PLATFORM(QT) String WebInspectorServer::inspectorUrlForPageID(int) { return String(); } #endif void WebInspectorServer::sendMessageOverConnection(unsigned pageIdForConnection, const String& message) { WebSocketServerConnection* connection = m_connectionMap.get(pageIdForConnection); if (connection) connection->sendWebSocketMessage(message); } void WebInspectorServer::didReceiveUnrecognizedHTTPRequest(WebSocketServerConnection* connection, PassRefPtr request) { // request->url() contains only the path extracted from the HTTP request line // and URL is poor at parsing incomplete URLs, so extract the interesting parts manually. String path = request->url(); size_t pathEnd = path.find('?'); if (pathEnd == notFound) pathEnd = path.find('#'); if (pathEnd != notFound) path.truncate(pathEnd); // Ask for the complete payload in memory for the sake of simplicity. A more efficient way would be // to ask for header data and then let the platform abstraction write the payload straight on the connection. Vector body; String contentType; bool found = platformResourceForPath(path, body, contentType); HTTPHeaderMap headerFields; headerFields.set(HTTPHeaderName::Connection, "close"); headerFields.set(HTTPHeaderName::ContentLength, String::number(body.size())); if (found) headerFields.set(HTTPHeaderName::ContentType, contentType); // Send when ready and close immediately afterwards. connection->sendHTTPResponseHeader(found ? 200 : 404, found ? "OK" : "Not Found", headerFields); connection->sendRawData(body.data(), body.size()); connection->shutdownAfterSendOrNow(); } bool WebInspectorServer::didReceiveWebSocketUpgradeHTTPRequest(WebSocketServerConnection*, PassRefPtr request) { String path = request->url(); // NOTE: Keep this in sync with WebCore/inspector/front-end/inspector.js. DEPRECATED_DEFINE_STATIC_LOCAL(const String, inspectorWebSocketConnectionPathPrefix, (ASCIILiteral("/devtools/page/"))); // Unknown path requested. if (!path.startsWith(inspectorWebSocketConnectionPathPrefix)) return false; int pageId = pageIdFromRequestPath(path); // Invalid page id. if (!pageId) return false; // There is no client for that page id. WebInspectorProxy* client = m_clientMap.get(pageId); if (!client) return false; return true; } void WebInspectorServer::didEstablishWebSocketConnection(WebSocketServerConnection* connection, PassRefPtr request) { String path = request->url(); unsigned pageId = pageIdFromRequestPath(path); ASSERT(pageId); // Ignore connections to a page that already have a remote inspector connected. if (m_connectionMap.contains(pageId)) { LOG_ERROR("A remote inspector connection already exist for page ID %d. Ignoring.", pageId); connection->shutdownNow(); return; } // Map the pageId to the connection in case we need to close the connection locally. connection->setIdentifier(pageId); m_connectionMap.set(pageId, connection); WebInspectorProxy* client = m_clientMap.get(pageId); client->remoteFrontendConnected(); } void WebInspectorServer::didReceiveWebSocketMessage(WebSocketServerConnection* connection, const String& message) { // Dispatch incoming remote message locally. unsigned pageId = connection->identifier(); ASSERT(pageId); WebInspectorProxy* client = m_clientMap.get(pageId); client->dispatchMessageFromRemoteFrontend(message); } void WebInspectorServer::didCloseWebSocketConnection(WebSocketServerConnection* connection) { // Connection has already shut down. unsigned pageId = connection->identifier(); if (!pageId) return; // The socket closing means the remote side has caused the close. WebInspectorProxy* client = m_clientMap.get(pageId); closeConnection(client, connection); } void WebInspectorServer::closeConnection(WebInspectorProxy* client, WebSocketServerConnection* connection) { // Local side cleanup. if (client) client->remoteFrontendDisconnected(); // Remote side cleanup. m_connectionMap.remove(connection->identifier()); connection->setIdentifier(0); connection->shutdownNow(); } } #endif // ENABLE(INSPECTOR_SERVER)