| /* |
| * 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) |