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 |