Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(181)

Unified Diff: WebCore/platform/network/soup/SocketStreamHandleSoup.cpp

Issue 155079: WebSocket implementation in WebKit (Closed)
Patch Set: Rewrite to use SocketStreamHandle Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « WebCore/platform/network/soup/SocketStreamError.h ('k') | WebCore/websockets/WebSocket.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: WebCore/platform/network/soup/SocketStreamHandleSoup.cpp
diff --git a/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp b/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ece62f67be10a369a5da97a44d5392977ba3a5c2
--- /dev/null
+++ b/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * 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 COMPUTER, 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 COMPUTER, 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"
+#include "SocketStreamHandle.h"
+
+#include <errno.h>
+#include <glib.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <wtf/PassOwnPtr.h>
+
+#include "CString.h"
+#include "Logging.h"
+#include "KURL.h"
+#include "NotImplemented.h"
+#include "SharedBuffer.h"
+#include "SocketStreamError.h"
+#include "SocketStreamHandleClient.h"
+
+#undef LOG
+#define LOG(channel, ...) do { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } while (0);
+
+namespace WebCore {
+
+const int kSocketStreamHandleBufSize = 1024;
+
+class SocketStreamHandleInternal {
+public:
+ static PassOwnPtr<SocketStreamHandleInternal> create(SocketStreamHandle* handle) { return new SocketStreamHandleInternal(handle); }
+ ~SocketStreamHandleInternal();
+
+ void connect(const KURL&);
+ int send(const char*, int);
+ void close();
+
+ static gboolean handleRead(GIOChannel* source, GIOCondition condition, gpointer data);
+ static gboolean handleWrite(GIOChannel* source, GIOCondition condition, gpointer data);
+ static gboolean handleError(GIOChannel* source, GIOCondition condition, gpointer data);
+ static gboolean handleClose(gpointer data);
+
+private:
+ explicit SocketStreamHandleInternal(SocketStreamHandle*);
+
+ gboolean canReceive();
+ gboolean canSend();
+ gboolean didError(GIOCondition condition);
+ gboolean didClose();
+
+ SocketStreamHandle* m_handle;
+ struct addrinfo* m_addrinfo;
+ int m_sock;
+ GIOChannel* m_g_io;
+ guint m_in_watch;
+ guint m_out_watch;
+ guint m_err_watch;
+ Vector<char> m_send_buffer;
+ gchar m_read_buf[kSocketStreamHandleBufSize];
+};
+
+/* static */
+gboolean SocketStreamHandleInternal::handleRead(GIOChannel* source, GIOCondition cdondition, gpointer data)
+{
+ LOG(Network, "SocketStreamHandle handleRead");
+ SocketStreamHandleInternal* internal = reinterpret_cast<SocketStreamHandleInternal*>(data);
+ return internal->canReceive();
+}
+
+/* static */
+gboolean SocketStreamHandleInternal::handleWrite(GIOChannel* source, GIOCondition condition, gpointer data)
+{
+ LOG(Network, "SocketStreamHandle handleWrite");
+ SocketStreamHandleInternal* internal = reinterpret_cast<SocketStreamHandleInternal*>(data);
+ return internal->canSend();
+}
+
+/* static */
+gboolean SocketStreamHandleInternal::handleError(GIOChannel* source, GIOCondition condition, gpointer data)
+{
+ LOG(Network, "SocketStreamHandle handleError");
+ SocketStreamHandleInternal* internal = reinterpret_cast<SocketStreamHandleInternal*>(data);
+ return internal->didError(condition);
+}
+
+/* static */
+gboolean SocketStreamHandleInternal::handleClose(gpointer data)
+{
+ LOG(Network, "SocketStreamHandle handleClose");
+ SocketStreamHandleInternal* internal = reinterpret_cast<SocketStreamHandleInternal*>(data);
+ internal->didClose();
+ return FALSE;
+}
+
+SocketStreamHandleInternal::SocketStreamHandleInternal(SocketStreamHandle* handle)
+ : m_handle(handle)
+ , m_addrinfo(NULL)
+ , m_sock(-1)
+ , m_g_io(NULL)
+ , m_in_watch(0)
+ , m_out_watch(0)
+ , m_err_watch(0)
+{
+ LOG(Network, "new SocketStreamHandleInternal %p", this);
+}
+
+SocketStreamHandleInternal::~SocketStreamHandleInternal()
+{
+ LOG(Network, "delete SocketStreamHandleInternal %p", this);
+ m_handle = NULL;
+ if (m_g_io) {
+ GError* gerr = NULL;
+ g_io_channel_shutdown(m_g_io, false, &gerr);
+ if (gerr) {
+ LOG(Network, "shutdown failed: %s", gerr->message);
+ g_error_free(gerr);
+ }
+ g_io_channel_unref(m_g_io);
+ m_g_io = NULL;
+ }
+ if (m_sock >= 0)
+ ::close(m_sock);
+ if (m_addrinfo)
+ freeaddrinfo(m_addrinfo);
+}
+
+void SocketStreamHandleInternal::connect(const KURL& url)
+{
+ LOG(Network, "SocketStreamHandleInternal %p connect %s", this, url.prettyURL().utf8().data());
+
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_STREAM;
+
+ LOG(Network, "SocketStreamHandleInternal %p connect %s:%d", this, url.host().utf8().data(), url.port());
+ int err = getaddrinfo(url.host().utf8().data(), String::number(url.port()).utf8().data(), &hints, &m_addrinfo);
+ if (err) {
+ LOG(Network, "SocketStreamHandleInternal %p resolve error %s:%d", this, url.host().utf8().data(), url.port());
+ close();
+ return;
+ }
+ ASSERT(m_addrinfo);
+
+ if (m_handle->m_client)
+ m_handle->m_client->willOpenStream(m_handle, url);
+
+ struct addrinfo* rp;
+ for (rp = m_addrinfo; rp != NULL; rp = rp->ai_next) {
+ m_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (m_sock < 0) {
+ LOG(Network, "SocketStreamHandleInternal %p socket error %d", this, errno);
+ continue;
+ }
+ m_g_io = g_io_channel_unix_new(m_sock);
+ ASSERT(m_g_io);
+
+ GError* gerr = NULL;
+ GIOStatus status = g_io_channel_set_encoding(m_g_io, NULL, &gerr);
+ ASSERT(status == G_IO_STATUS_NORMAL);
+ ASSERT(!gerr);
+
+ g_io_channel_set_buffered(m_g_io, FALSE);
+
+ m_out_watch = g_io_add_watch(m_g_io, G_IO_OUT, &SocketStreamHandleInternal::handleWrite, this);
+ m_err_watch = g_io_add_watch(m_g_io, static_cast<GIOCondition>(G_IO_ERR|G_IO_HUP), &SocketStreamHandleInternal::handleError, this);
+
+ status = g_io_channel_set_flags(m_g_io, G_IO_FLAG_NONBLOCK, &gerr);
+ ASSERT(status == G_IO_STATUS_NORMAL);
+ ASSERT(!gerr);
+
+ LOG(Network, "SocketStreamHandleInternal %p CONNECTING", this);
+ int err = ::connect(m_sock, rp->ai_addr, rp->ai_addrlen);
+ if (err == 0) {
+ LOG(Network, "SocketStreamHandleInternal %p connect ok", this);
+ m_handle->m_client->didOpen(m_handle);
+ return;
+ } else {
+ if (errno == EINPROGRESS)
+ return;
+ LOG(Network, "SocketStreamHandleInternal %p connect failed %d", this, errno);
+ m_out_watch = m_err_watch = 0;
+ g_io_channel_unref(m_g_io);
+ m_g_io = NULL;
+ ::close(m_sock);
+ m_sock = -1;
+ }
+ }
+ LOG(Network, "SocketStreamHandleInternal %p connect all failed", this);
+ close();
+}
+
+int SocketStreamHandleInternal::send(const char* buf, int size)
+{
+ LOG(Network, "SocketStreamHandleInternal %p send %d bytes", this, size);
+ if (!m_g_io) {
+ LOG_ERROR("SocketStreamHandleInternal %p already closed", this);
+ return -1;
+ }
+ if (!m_send_buffer.isEmpty()) {
+ LOG(Network, "SocketStreamHandleInternal %p send has buffer %d", this, m_send_buffer.size());
+ return 0;
+ }
+
+ m_send_buffer.append(buf, size);
+ if (m_out_watch == 0)
+ m_out_watch = g_io_add_watch(m_g_io, G_IO_OUT, &SocketStreamHandleInternal::handleWrite, this);
+ return size;
+}
+
+void SocketStreamHandleInternal::close()
+{
+ LOG(Network, "SocketStreamHandleInternal %p close", this);
+ if (m_handle->m_state != Closed)
+ g_idle_add(&SocketStreamHandleInternal::handleClose, this);
+}
+
+gboolean SocketStreamHandleInternal::canReceive()
+{
+ LOG(Network, "SocketStreamHandleInternal %p canReceive", this);
+ if (!m_g_io) {
+ LOG_ERROR("SocketStreamHandleInternal %p already closed", this);
+ return FALSE;
+ }
+ ASSERT(m_g_io);
+ gsize num_read;
+ GError* gerr = NULL;
+ GIOStatus status = g_io_channel_read_chars(m_g_io, m_read_buf, sizeof(m_read_buf), &num_read, &gerr);
+ if (status == G_IO_STATUS_AGAIN) {
+ LOG(Network, "SocketStreamHandleInternal %p read resource temporarily unavailable", this);
+ return TRUE;
+ }
+ if (status != G_IO_STATUS_NORMAL) {
+ LOG(Network, "SocketStreamHandleInternal %p read error %d %s", this, status, gerr ? gerr->message : "");
+ if (gerr)
+ g_error_free(gerr);
+ return didError(G_IO_ERR);
+ }
+ ASSERT(!gerr);
+ ASSERT(num_read < kSocketStreamHandleBufSize);
+
+ gchar tmp_buf[kSocketStreamHandleBufSize + 1];
+ memcpy(tmp_buf, m_read_buf, num_read);
+ tmp_buf[num_read] = '\0';
+
+ LOG(Network, "SocketStreamHandleInternal %p read %s (%lu)", this, tmp_buf, num_read);
+ if (m_handle && m_handle->m_client)
+ m_handle->m_client->didReceivedData(m_handle, m_read_buf, num_read);
+ return TRUE;
+}
+
+gboolean SocketStreamHandleInternal::canSend()
+{
+ LOG(Network, "SocketStreamHandleInternal %p canSend", this);
+ if (!m_g_io) {
+ LOG_ERROR("SocketStreamHandleInternal %p already closed", this);
+ return FALSE;
+ }
+ ASSERT(m_g_io);
+ if (m_handle->m_state == Connecting) {
+ int optval;
+ socklen_t optlen = sizeof(optval);
+ int err = getsockopt(m_sock, SOL_SOCKET, SO_ERROR, (void*)(&optval), &optlen);
+ if (err < 0 || optval != 0) {
+ // TODO(ukai): errno = EINPROGRESS or EALREADY?
+ LOG(Network, "SocketStreamHandleInternal %p async connect failed %d so_error=%d", this, errno, optval);
+ return didError(G_IO_ERR);
+ }
+ LOG(Network, "SocketStreamHandleInternal %p CONNECTED", this);
+ m_in_watch = g_io_add_watch(m_g_io, G_IO_IN, &SocketStreamHandleInternal::handleRead, this);
+ m_handle->m_state = Open;
+ // TODO(ukai): proxy, ssl.
+ LOG(Network, "SocketStreamHandleInternal %p didOpen", m_handle->m_client);
+ if (m_handle->m_client)
+ m_handle->m_client->didOpen(m_handle);
+ }
+
+ if (m_handle->m_client)
+ m_handle->m_client->willSendData(m_handle, m_send_buffer.data(), m_send_buffer.size());
+ gsize bytes_written;
+ GError* gerr = NULL;
+ GIOStatus status = g_io_channel_write_chars(m_g_io, m_send_buffer.data(), m_send_buffer.size(), &bytes_written, &gerr);
+ if (status == G_IO_STATUS_AGAIN) {
+ LOG(Network, "SocketStreamHandleInternal %p write resource temporarily unavailable", this);
+ return TRUE;
+ }
+ if (status != G_IO_STATUS_NORMAL) {
+ LOG(Network, "SocketStreamHandleInternal %p write error %d %s", this, status, gerr ? gerr->message : "");
+ if (gerr)
+ g_error_free(gerr);
+ return didError(G_IO_ERR);
+ }
+ LOG(Network, "SocketStreamHandleInternal %p write %lu bytes", this, bytes_written);
+ ASSERT(!gerr);
+
+ if (m_send_buffer.size() > bytes_written) {
+ LOG(Network, "SocketStreamHandleInternal %p send buf size %lu", this, m_send_buffer.size() - bytes_written);
+ Vector<char> buf;
+ buf.append(m_send_buffer.data() + bytes_written,
+ m_send_buffer.size() - bytes_written);
+ m_send_buffer.swap(buf);
+ return TRUE;
+ }
+ // All bytes in m_send_buffer were sent.
+ LOG(Network, "SocketStreamHandleInternal %p no send buf", this);
+ m_send_buffer.clear();
+ m_out_watch = 0;
+ return FALSE;
+}
+
+gboolean SocketStreamHandleInternal::didError(GIOCondition condition)
+{
+ LOG(Network, "SocketStreamHandleInternal %p error", this);
+ m_handle->m_state = Closed;
+ SocketStreamError err(condition);
+ if (m_handle->m_client)
+ m_handle->m_client->didFail(m_handle, err);
+ m_handle->m_client = NULL;
+ return FALSE;
+}
+
+gboolean SocketStreamHandleInternal::didClose()
+{
+ LOG(Network, "SocketStreamHandleInternal %p close", this);
+ m_handle->m_state = Closed;
+ if (m_handle->m_client)
+ m_handle->m_client->didClose(m_handle);
+ m_handle->m_client = NULL;
+ return FALSE;
+}
+
+SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client)
+ : m_url(url)
+{
+ setClient(client);
+ LOG(Network, "SocketStreamHandle %p new client %p", this, m_client);
+ internal = SocketStreamHandleInternal::create(this);
+ internal->connect(m_url);
+}
+
+SocketStreamHandle::~SocketStreamHandle()
+{
+ LOG(Network, "SocketStreamHandle %p delete", this);
+ setClient(NULL);
+ internal.clear();
+}
+
+int SocketStreamHandle::platformSend(const char *buf, int len)
+{
+ LOG(Network, "SocketStreamHandle %p platformSend %d", this, len);
+ if (internal.get())
+ return internal->send(buf, len);
+ return 0;
+}
+
+void SocketStreamHandle::platformClose()
+{
+ LOG(Network, "SocketStreamHandle %p platformClose", this);
+ if (internal.get())
+ internal->close();
+}
+
+void SocketStreamHandle::didReceivedAuthenticationChallenge(const AuthenticationChallenge& challenge)
+{
+ if (m_client)
+ m_client->didReceivedAuthenticationChallenge(this, challenge);
+}
+
+void SocketStreamHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
+{
+ notImplemented();
+}
+
+void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
+{
+ notImplemented();
+}
+
+void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge& challenge)
+{
+ if (m_client)
+ m_client->receivedCancellation(this, challenge);
+}
+
+} // namespace WebCore
« no previous file with comments | « WebCore/platform/network/soup/SocketStreamError.h ('k') | WebCore/websockets/WebSocket.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698