blob: bc585e94dc84371e956ed0cf5e69eafb026b1276 [file] [log] [blame]
/*
* 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 "WebInspectorProxy.h"
#include "WebSocketServerConnection.h"
#include <WebCore/HTTPRequest.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::shared()
{
static WebInspectorServer& server = *new WebInspectorServer;
return server;
}
WebInspectorServer::WebInspectorServer()
: WebSocketServer(this)
, m_nextAvailablePageId(1)
{
}
WebInspectorServer::~WebInspectorServer()
{
// Close any remaining open connections.
HashMap<unsigned, WebSocketServerConnection*>::iterator end = m_connectionMap.end();
for (HashMap<unsigned, WebSocketServerConnection*>::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<HTTPRequest> request)
{
// request->url() contains only the path extracted from the HTTP request line
// and KURL 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<char> body;
String contentType;
bool found = platformResourceForPath(path, body, contentType);
HTTPHeaderMap headerFields;
headerFields.set("Connection", "close");
headerFields.set("Content-Length", String::number(body.size()));
if (found)
headerFields.set("Content-Type", 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<HTTPRequest> request)
{
String path = request->url();
// NOTE: Keep this in sync with WebCore/inspector/front-end/inspector.js.
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<HTTPRequest> 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)