blob: b50b17cdbcac2cc77c120bac3c8fd7a8786ddf10 [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 "WebSocketServerConnection.h"
#include "WebSocketServer.h"
#include "WebSocketServerClient.h"
#include <WebCore/HTTPRequest.h>
#include <WebCore/NotImplemented.h>
#include <WebCore/SocketStreamError.h>
#include <WebCore/SocketStreamHandle.h>
#include <WebCore/WebSocketChannel.h>
#include <WebCore/WebSocketHandshake.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>
using namespace WebCore;
namespace WebKit {
WebSocketServerConnection::WebSocketServerConnection(WebSocketServerClient* client, WebSocketServer* server)
: m_identifier(0)
, m_mode(HTTP)
, m_server(server)
, m_client(client)
, m_shutdownAfterSend(false)
{
}
WebSocketServerConnection::~WebSocketServerConnection()
{
shutdownNow();
}
void WebSocketServerConnection::setSocketHandle(PassRefPtr<WebCore::SocketStreamHandle> socket)
{
ASSERT(!m_socket);
m_socket = socket;
}
void WebSocketServerConnection::shutdownNow()
{
if (!m_socket)
return;
RefPtr<SocketStreamHandle> socket = m_socket.release();
socket->close();
m_shutdownAfterSend = false;
}
void WebSocketServerConnection::shutdownAfterSendOrNow()
{
if (m_socket->bufferedAmount()) {
m_shutdownAfterSend = true;
return;
}
shutdownNow();
}
void WebSocketServerConnection::sendWebSocketMessage(const String& message)
{
CString payload = message.utf8();
const bool final = true, compress = false, masked = false;
WebSocketFrame frame(WebSocketFrame::OpCodeText, final, compress, masked, payload.data(), payload.length());
Vector<char> frameData;
frame.makeFrameData(frameData);
m_socket->send(frameData.data(), frameData.size());
}
void WebSocketServerConnection::sendHTTPResponseHeader(int statusCode, const String& statusText, const HTTPHeaderMap& headerFields)
{
StringBuilder builder;
builder.appendLiteral("HTTP/1.1 ");
builder.appendNumber(statusCode);
builder.append(' ');
builder.append(statusText);
builder.appendLiteral("\r\n");
HTTPHeaderMap::const_iterator end = headerFields.end();
for (HTTPHeaderMap::const_iterator it = headerFields.begin(); it != end; ++it) {
builder.append(it->key);
builder.appendLiteral(": ");
builder.append(it->value);
builder.appendLiteral("\r\n");
}
builder.appendLiteral("\r\n");
CString header = builder.toString().latin1();
m_socket->send(header.data(), header.length());
}
void WebSocketServerConnection::sendRawData(const char* data, size_t length)
{
m_socket->send(data, length);
}
void WebSocketServerConnection::didCloseSocketStream(SocketStreamHandle*)
{
// Destroy the SocketStreamHandle now to prevent closing an already closed socket later.
m_socket.clear();
// Web Socket Mode.
if (m_mode == WebSocket)
m_client->didCloseWebSocketConnection(this);
// Tell the server to get rid of this.
m_server->didCloseWebSocketServerConnection(this);
}
void WebSocketServerConnection::didReceiveSocketStreamData(SocketStreamHandle*, const char* data, int length)
{
// Each didReceiveData call adds more data to our buffer.
// We clear the buffer when we have handled data from it.
m_bufferedData.append(data, length);
switch (m_mode) {
case HTTP:
readHTTPMessage();
break;
case WebSocket:
readWebSocketFrames();
break;
default:
// For any new modes added in the future.
ASSERT_NOT_REACHED();
}
}
void WebSocketServerConnection::didUpdateBufferedAmount(WebCore::SocketStreamHandle*, size_t)
{
if (m_shutdownAfterSend && !m_socket->bufferedAmount())
shutdownNow();
}
void WebSocketServerConnection::didFailSocketStream(SocketStreamHandle*, const SocketStreamError&)
{
// Possible read or write error.
}
void WebSocketServerConnection::readHTTPMessage()
{
String failureReason;
RefPtr<HTTPRequest> request = HTTPRequest::parseHTTPRequestFromBuffer(m_bufferedData.data(), m_bufferedData.size(), failureReason);
if (!request)
return;
// Assume all the input has been read if we are reading an HTTP Request.
m_bufferedData.clear();
// If this is a WebSocket request, perform the WebSocket Handshake.
const HTTPHeaderMap& headers = request->headerFields();
String upgradeHeaderValue = headers.get("Upgrade");
if (upgradeHeaderValue == "websocket") {
upgradeToWebSocketServerConnection(request);
return;
}
if (upgradeHeaderValue == "WebSocket") {
LOG_ERROR("WebSocket protocol version < Hybi-10 not supported. Upgrade your client.");
return;
}
// Otherwise, this is an HTTP Request we don't know how to deal with.
m_client->didReceiveUnrecognizedHTTPRequest(this, request);
}
void WebSocketServerConnection::upgradeToWebSocketServerConnection(PassRefPtr<HTTPRequest> request)
{
ASSERT(request);
ASSERT(m_mode == HTTP);
m_mode = WebSocket;
RefPtr<HTTPRequest> protectedRequest(request);
// Ask the client if we should upgrade for this or not.
if (!m_client->didReceiveWebSocketUpgradeHTTPRequest(this, protectedRequest)) {
shutdownNow();
return;
}
// Build and send the WebSocket handshake response.
const HTTPHeaderMap& requestHeaders = protectedRequest->headerFields();
String accept = WebSocketHandshake::getExpectedWebSocketAccept(requestHeaders.get("Sec-WebSocket-Key"));
HTTPHeaderMap responseHeaders;
responseHeaders.add("Upgrade", requestHeaders.get("Upgrade"));
responseHeaders.add("Connection", requestHeaders.get("Connection"));
responseHeaders.add("Sec-WebSocket-Accept", accept);
sendHTTPResponseHeader(101, "WebSocket Protocol Handshake", responseHeaders);
m_client->didEstablishWebSocketConnection(this, protectedRequest);
}
void WebSocketServerConnection::readWebSocketFrames()
{
while (true) {
bool didReadOneFrame = readWebSocketFrame();
if (!didReadOneFrame)
break;
if (m_bufferedData.isEmpty())
break;
}
}
bool WebSocketServerConnection::readWebSocketFrame()
{
WebSocketFrame frame;
const char* frameEnd;
String errorString;
WebSocketFrame::ParseFrameResult result = WebSocketFrame::parseFrame(m_bufferedData.data(), m_bufferedData.size(), frame, frameEnd, errorString);
// Incomplete frame. Wait to receive more data.
if (result == WebSocketFrame::FrameIncomplete)
return false;
if (result == WebSocketFrame::FrameError) {
shutdownNow();
} else if (frame.opCode == WebSocketFrame::OpCodeText) {
// Delegate Text frames to our client.
String msg = String::fromUTF8(frame.payload, frame.payloadLength);
m_client->didReceiveWebSocketMessage(this, msg);
} else
notImplemented();
// Remove the frame from our buffer.
m_bufferedData.remove(0, frameEnd - m_bufferedData.data());
return true;
}
}
#endif // ENABLE(INSPECTOR_SERVER)