1/*
2 * Copyright (C) 2010 Nokia Inc. All rights reserved.
3 * Copyright (C) 2009 Google Inc.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "SocketStreamHandle.h"
34
35#include "KURL.h"
36#include "Logging.h"
37#include "NotImplemented.h"
38#include "SocketStreamHandleClient.h"
39#include "SocketStreamHandlePrivate.h"
40
41namespace WebCore {
42
43SocketStreamHandlePrivate::SocketStreamHandlePrivate(SocketStreamHandle* streamHandle, const KURL& url) : QObject()
44{
45    m_streamHandle = streamHandle;
46    m_socket = 0;
47    bool isSecure = url.protocolIs("wss");
48
49    if (isSecure) {
50#ifndef QT_NO_OPENSSL
51        m_socket = new QSslSocket(this);
52#endif
53    } else
54        m_socket = new QTcpSocket(this);
55
56    if (!m_socket)
57        return;
58
59    connect(m_socket, SIGNAL(connected()), this, SLOT(socketConnected()));
60    connect(m_socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead()));
61    connect(m_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
62    connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
63    if (isSecure)
64        connect(m_socket, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(socketSslErrors(const QList<QSslError>&)));
65
66    unsigned int port = url.hasPort() ? url.port() : (isSecure ? 443 : 80);
67
68    QString host = url.host();
69    if (isSecure) {
70#ifndef QT_NO_OPENSSL
71        static_cast<QSslSocket*>(m_socket)->connectToHostEncrypted(host, port);
72#endif
73    } else
74        m_socket->connectToHost(host, port);
75}
76
77SocketStreamHandlePrivate::~SocketStreamHandlePrivate()
78{
79    Q_ASSERT(!(m_socket && m_socket->state() == QAbstractSocket::ConnectedState));
80}
81
82void SocketStreamHandlePrivate::socketConnected()
83{
84    if (m_streamHandle && m_streamHandle->client()) {
85        m_streamHandle->m_state = SocketStreamHandleBase::Open;
86        m_streamHandle->client()->didOpen(m_streamHandle);
87    }
88}
89
90void SocketStreamHandlePrivate::socketReadyRead()
91{
92    if (m_streamHandle && m_streamHandle->client()) {
93        QByteArray data = m_socket->read(m_socket->bytesAvailable());
94        m_streamHandle->client()->didReceiveData(m_streamHandle, data.constData(), data.size());
95    }
96}
97
98int SocketStreamHandlePrivate::send(const char* data, int len)
99{
100    if (!m_socket || m_socket->state() != QAbstractSocket::ConnectedState)
101        return 0;
102    quint64 sentSize = m_socket->write(data, len);
103    QMetaObject::invokeMethod(this, "socketSentData", Qt::QueuedConnection);
104    return sentSize;
105}
106
107void SocketStreamHandlePrivate::close()
108{
109    if (m_socket && m_socket->state() == QAbstractSocket::ConnectedState)
110        m_socket->close();
111}
112
113void SocketStreamHandlePrivate::socketSentdata()
114{
115    if (m_streamHandle)
116        m_streamHandle->sendPendingData();
117}
118
119void SocketStreamHandlePrivate::socketClosed()
120{
121    QMetaObject::invokeMethod(this, "socketClosedCallback", Qt::QueuedConnection);
122}
123
124void SocketStreamHandlePrivate::socketError(QAbstractSocket::SocketError error)
125{
126    QMetaObject::invokeMethod(this, "socketErrorCallback", Qt::QueuedConnection, Q_ARG(int, error));
127}
128
129void SocketStreamHandlePrivate::socketClosedCallback()
130{
131    if (m_streamHandle && m_streamHandle->client()) {
132        SocketStreamHandle* streamHandle = m_streamHandle;
133        m_streamHandle = 0;
134        // This following call deletes _this_. Nothing should be after it.
135        streamHandle->client()->didClose(streamHandle);
136    }
137}
138
139void SocketStreamHandlePrivate::socketErrorCallback(int error)
140{
141    // FIXME - in the future, we might not want to treat all errors as fatal.
142    if (m_streamHandle && m_streamHandle->client()) {
143        SocketStreamHandle* streamHandle = m_streamHandle;
144        m_streamHandle = 0;
145        // This following call deletes _this_. Nothing should be after it.
146        streamHandle->client()->didClose(streamHandle);
147    }
148}
149
150#ifndef QT_NO_OPENSSL
151void SocketStreamHandlePrivate::socketSslErrors(const QList<QSslError>&)
152{
153    // FIXME: based on http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-68#page-15
154    // we should abort on certificate errors.
155    // We don't abort while this is still work in progress.
156    static_cast<QSslSocket*>(m_socket)->ignoreSslErrors();
157}
158#endif
159
160SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client)
161    : SocketStreamHandleBase(url, client)
162{
163    LOG(Network, "SocketStreamHandle %p new client %p", this, m_client);
164    m_p = new SocketStreamHandlePrivate(this, url);
165}
166
167SocketStreamHandle::~SocketStreamHandle()
168{
169    LOG(Network, "SocketStreamHandle %p delete", this);
170    setClient(0);
171    delete m_p;
172}
173
174int SocketStreamHandle::platformSend(const char* data, int len)
175{
176    LOG(Network, "SocketStreamHandle %p platformSend", this);
177    return m_p->send(data, len);
178}
179
180void SocketStreamHandle::platformClose()
181{
182    LOG(Network, "SocketStreamHandle %p platformClose", this);
183    m_p->close();
184}
185
186void SocketStreamHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&)
187{
188    notImplemented();
189}
190
191void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&)
192{
193    notImplemented();
194}
195
196void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&)
197{
198    notImplemented();
199}
200
201void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&)
202{
203    notImplemented();
204}
205
206} // namespace WebCore
207
208#include "moc_SocketStreamHandlePrivate.cpp"
209