Reviewed by Alexey Proskuryakov.
[WebKit-https.git] / WebCore / websockets / WebSocket.cpp
1 /*
2  * Copyright (C) 2009 Google Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #if ENABLE(WEB_SOCKETS)
34
35 #include "WebSocket.h"
36
37 #include "CString.h"
38 #include "DOMWindow.h"
39 #include "Event.h"
40 #include "EventException.h"
41 #include "EventListener.h"
42 #include "EventNames.h"
43 #include "Logging.h"
44 #include "MessageEvent.h"
45 #include "ScriptExecutionContext.h"
46 #include "SecurityOrigin.h"
47 #include "WebSocketChannel.h"
48 #include <wtf/StdLibExtras.h>
49
50 namespace WebCore {
51
52 class ProcessWebSocketEventTask : public ScriptExecutionContext::Task {
53 public:
54     typedef void (WebSocket::*Method)(Event*);
55     static PassRefPtr<ProcessWebSocketEventTask> create(PassRefPtr<WebSocket> webSocket, Method method, PassRefPtr<Event> event)
56     {
57         return adoptRef(new ProcessWebSocketEventTask(webSocket, method, event));
58     }
59     virtual void performTask(ScriptExecutionContext*)
60     {
61         (m_webSocket.get()->*m_method)(m_event.get());
62     }
63
64   private:
65     ProcessWebSocketEventTask(PassRefPtr<WebSocket> webSocket, Method method, PassRefPtr<Event> event)
66         : m_webSocket(webSocket)
67         , m_method(method)
68         , m_event(event) { }
69
70     RefPtr<WebSocket> m_webSocket;
71     Method m_method;
72     RefPtr<Event> m_event;
73 };
74
75 static bool isValidProtocolString(const WebCore::String& protocol)
76 {
77     if (protocol.isNull())
78         return true;
79     if (protocol.isEmpty())
80         return false;
81     const UChar* characters = protocol.characters();
82     for (size_t i = 0; i < protocol.length(); i++) {
83         if (characters[i] < 0x21 || characters[i] > 0x7E)
84             return false;
85     }
86     return true;
87 }
88
89 WebSocket::WebSocket(ScriptExecutionContext* context)
90     : ActiveDOMObject(context, this)
91     , m_state(CONNECTING)
92 {
93 }
94
95 WebSocket::~WebSocket()
96 {
97     close();
98 }
99
100 void WebSocket::connect(const KURL& url, ExceptionCode& ec)
101 {
102     connect(url, String(), ec);
103 }
104
105 void WebSocket::connect(const KURL& url, const String& protocol, ExceptionCode& ec)
106 {
107     LOG(Network, "WebSocket %p connect to %s protocol=%s", this, url.string().utf8().data(), protocol.utf8().data());
108     m_url = url;
109     m_protocol = protocol;
110
111     if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) {
112         LOG_ERROR("Error: wrong url for WebSocket %s", url.string().utf8().data());
113         m_state = CLOSED;
114         ec = SYNTAX_ERR;
115         return;
116     }
117     if (!isValidProtocolString(m_protocol)) {
118         LOG_ERROR("Error: wrong protocol for WebSocket %s", m_protocol.utf8().data());
119         m_state = CLOSED;
120         ec = SYNTAX_ERR;
121         return;
122     }
123     // FIXME: if m_url.port() is blocking port, raise SECURITY_ERR.
124
125     m_channel = WebSocketChannel::create(scriptExecutionContext(), this, m_url, m_protocol);
126     m_channel->connect();
127 }
128
129 bool WebSocket::send(const String& message, ExceptionCode& ec)
130 {
131     LOG(Network, "WebSocket %p send %s", this, message.utf8().data());
132     if (m_state == CONNECTING) {
133         ec = INVALID_STATE_ERR;
134         return false;
135     }
136     // No exception is raised if the connection was once established but has subsequently been closed.
137     if (m_state == CLOSED)
138         return false;
139     // FIXME: check message is valid utf8.
140     return m_channel->send(message);
141 }
142
143 void WebSocket::close()
144 {
145     LOG(Network, "WebSocket %p close", this);
146     if (m_state == CLOSED)
147         return;
148     m_state = CLOSED;
149     m_channel->close();
150 }
151
152 const KURL& WebSocket::url() const
153 {
154     return m_url;
155 }
156
157 WebSocket::State WebSocket::readyState() const
158 {
159     return m_state;
160 }
161
162 unsigned long WebSocket::bufferedAmount() const
163 {
164     if (m_state == OPEN)
165         return m_channel->bufferedAmount();
166     return 0;
167 }
168
169 ScriptExecutionContext* WebSocket::scriptExecutionContext() const
170 {
171     return ActiveDOMObject::scriptExecutionContext();
172 }
173
174 void WebSocket::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
175 {
176     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
177     if (iter == m_eventListeners.end()) {
178         ListenerVector listeners;
179         listeners.append(eventListener);
180         m_eventListeners.add(eventType, listeners);
181     } else {
182         ListenerVector& listeners = iter->second;
183         for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
184             if (*listenerIter == eventListener)
185                 return;
186
187         listeners.append(eventListener);
188         m_eventListeners.add(eventType, listeners);
189     }
190 }
191
192 void WebSocket::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
193 {
194     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
195     if (iter == m_eventListeners.end())
196         return;
197
198     ListenerVector& listeners = iter->second;
199     for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
200         if (*listenerIter == eventListener) {
201             listeners.remove(listenerIter - listeners.begin());
202             return;
203         }
204 }
205
206 bool WebSocket::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec)
207 {
208     if (!evt || evt->type().isEmpty()) {
209         ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
210         return true;
211     }
212
213     ListenerVector listenersCopy = m_eventListeners.get(evt->type());
214     for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
215         evt->setTarget(this);
216         evt->setCurrentTarget(this);
217         listenerIter->get()->handleEvent(evt.get(), false);
218     }
219     return !evt->defaultPrevented();
220 }
221
222 void WebSocket::didConnect()
223 {
224     LOG(Network, "WebSocket %p didConnect", this);
225     if (m_state != CONNECTING) {
226         didClose();
227         return;
228     }
229     m_state = OPEN;
230     scriptExecutionContext()->postTask(ProcessWebSocketEventTask::create(this, &WebSocket::dispatchOpenEvent, Event::create(eventNames().openEvent, false, false)));
231 }
232
233 void WebSocket::didReceiveMessage(const String& msg)
234 {
235     LOG(Network, "WebSocket %p didReceiveMessage %s", this, msg.utf8().data());
236     if (m_state != OPEN)
237         return;
238     RefPtr<MessageEvent> evt = MessageEvent::create();
239     // FIXME: origin, lastEventId, source, messagePort.
240     evt->initMessageEvent(eventNames().messageEvent, false, false, msg, "", "", 0, 0);
241     scriptExecutionContext()->postTask(ProcessWebSocketEventTask::create(this, &WebSocket::dispatchMessageEvent, evt));
242 }
243
244 void WebSocket::didClose()
245 {
246     LOG(Network, "WebSocket %p didClose", this);
247     m_state = CLOSED;
248     scriptExecutionContext()->postTask(ProcessWebSocketEventTask::create(this, &WebSocket::dispatchCloseEvent, Event::create(eventNames().closeEvent, false, false)));
249 }
250
251 void WebSocket::dispatchOpenEvent(Event* evt)
252 {
253     if (m_onopen) {
254         evt->setTarget(this);
255         evt->setCurrentTarget(this);
256         m_onopen->handleEvent(evt, false);
257     }
258
259     ExceptionCode ec = 0;
260     dispatchEvent(evt, ec);
261     ASSERT(!ec);
262 }
263
264 void WebSocket::dispatchMessageEvent(Event* evt)
265 {
266     if (m_onmessage) {
267         evt->setTarget(this);
268         evt->setCurrentTarget(this);
269         m_onmessage->handleEvent(evt, false);
270     }
271
272     ExceptionCode ec = 0;
273     dispatchEvent(evt, ec);
274     ASSERT(!ec);
275 }
276
277 void WebSocket::dispatchCloseEvent(Event* evt)
278 {
279     if (m_onclose) {
280         evt->setTarget(this);
281         evt->setCurrentTarget(this);
282         m_onclose->handleEvent(evt, false);
283     }
284
285     ExceptionCode ec = 0;
286     dispatchEvent(evt, ec);
287     ASSERT(!ec);
288 }
289
290 }  // namespace WebCore
291
292 #endif