Adds a modified copy of talk/base to webrtc/base. It is the first step in
migrating talk/base to webrtc/base.

BUG=N/A
R=niklas.enbom@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/17479005

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@6129 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/base/macasyncsocket.cc b/base/macasyncsocket.cc
new file mode 100644
index 0000000..ee982ff
--- /dev/null
+++ b/base/macasyncsocket.cc
@@ -0,0 +1,477 @@
+/*
+ *  Copyright 2010 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+//
+// MacAsyncSocket is a kind of AsyncSocket. It does not support the SOCK_DGRAM
+// type (yet). It works asynchronously, which means that users of this socket
+// should connect to the various events declared in asyncsocket.h to receive
+// notifications about this socket.  It uses CFSockets for signals, but prefers
+// the basic bsd socket operations rather than their CFSocket wrappers when
+// possible.
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <fcntl.h>
+
+#include "webrtc/base/macasyncsocket.h"
+
+#include "webrtc/base/logging.h"
+#include "webrtc/base/macsocketserver.h"
+
+namespace rtc {
+
+static const int kCallbackFlags = kCFSocketReadCallBack |
+                                  kCFSocketConnectCallBack |
+                                  kCFSocketWriteCallBack;
+
+MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family)
+    : ss_(ss),
+      socket_(NULL),
+      native_socket_(INVALID_SOCKET),
+      source_(NULL),
+      current_callbacks_(0),
+      disabled_(false),
+      error_(0),
+      state_(CS_CLOSED),
+      resolver_(NULL) {
+  Initialize(family);
+}
+
+MacAsyncSocket::~MacAsyncSocket() {
+  Close();
+}
+
+// Returns the address to which the socket is bound.  If the socket is not
+// bound, then the any-address is returned.
+SocketAddress MacAsyncSocket::GetLocalAddress() const {
+  SocketAddress address;
+
+  // The CFSocket doesn't pick up on implicit binds from the connect call.
+  // Calling bind in before connect explicitly causes errors, so just query
+  // the underlying bsd socket.
+  sockaddr_storage addr;
+  socklen_t addrlen = sizeof(addr);
+  int result = ::getsockname(native_socket_,
+                             reinterpret_cast<sockaddr*>(&addr), &addrlen);
+  if (result >= 0) {
+    SocketAddressFromSockAddrStorage(addr, &address);
+  }
+  return address;
+}
+
+// Returns the address to which the socket is connected.  If the socket is not
+// connected, then the any-address is returned.
+SocketAddress MacAsyncSocket::GetRemoteAddress() const {
+  SocketAddress address;
+
+  // Use native_socket for consistency with GetLocalAddress.
+  sockaddr_storage addr;
+  socklen_t addrlen = sizeof(addr);
+  int result = ::getpeername(native_socket_,
+                             reinterpret_cast<sockaddr*>(&addr), &addrlen);
+  if (result >= 0) {
+    SocketAddressFromSockAddrStorage(addr, &address);
+  }
+  return address;
+}
+
+// Bind the socket to a local address.
+int MacAsyncSocket::Bind(const SocketAddress& address) {
+  sockaddr_storage saddr = {0};
+  size_t len = address.ToSockAddrStorage(&saddr);
+  int err = ::bind(native_socket_, reinterpret_cast<sockaddr*>(&saddr), len);
+  if (err == SOCKET_ERROR) error_ = errno;
+  return err;
+}
+
+void MacAsyncSocket::OnResolveResult(SignalThread* thread) {
+  if (thread != resolver_) {
+    return;
+  }
+  int error = resolver_->GetError();
+  if (error == 0) {
+    error = DoConnect(resolver_->address());
+  } else {
+    Close();
+  }
+  if (error) {
+    error_ = error;
+    SignalCloseEvent(this, error_);
+  }
+}
+
+// Connect to a remote address.
+int MacAsyncSocket::Connect(const SocketAddress& addr) {
+  // TODO(djw): Consolidate all the connect->resolve->doconnect implementations.
+  if (state_ != CS_CLOSED) {
+    SetError(EALREADY);
+    return SOCKET_ERROR;
+  }
+  if (addr.IsUnresolved()) {
+    LOG(LS_VERBOSE) << "Resolving addr in MacAsyncSocket::Connect";
+    resolver_ = new AsyncResolver();
+    resolver_->SignalWorkDone.connect(this,
+                                      &MacAsyncSocket::OnResolveResult);
+    resolver_->Start(addr);
+    state_ = CS_CONNECTING;
+    return 0;
+  }
+  return DoConnect(addr);
+}
+
+int MacAsyncSocket::DoConnect(const SocketAddress& addr) {
+  if (!valid()) {
+    Initialize(addr.family());
+    if (!valid())
+      return SOCKET_ERROR;
+  }
+
+  sockaddr_storage saddr;
+  size_t len = addr.ToSockAddrStorage(&saddr);
+  int result = ::connect(native_socket_, reinterpret_cast<sockaddr*>(&saddr),
+                         len);
+
+  if (result != SOCKET_ERROR) {
+    state_ = CS_CONNECTED;
+  } else {
+    error_ = errno;
+    if (error_ == EINPROGRESS) {
+      state_ = CS_CONNECTING;
+      result = 0;
+    }
+  }
+  return result;
+}
+
+// Send to the remote end we're connected to.
+int MacAsyncSocket::Send(const void* buffer, size_t length) {
+  if (!valid()) {
+    return SOCKET_ERROR;
+  }
+
+  int sent = ::send(native_socket_, buffer, length, 0);
+
+  if (sent == SOCKET_ERROR) {
+    error_ = errno;
+
+    if (IsBlocking()) {
+      // Reenable the writable callback (once), since we are flow controlled.
+      CFSocketEnableCallBacks(socket_, kCallbackFlags);
+      current_callbacks_ = kCallbackFlags;
+    }
+  }
+  return sent;
+}
+
+// Send to the given address. We may or may not be connected to anyone.
+int MacAsyncSocket::SendTo(const void* buffer, size_t length,
+                           const SocketAddress& address) {
+  if (!valid()) {
+    return SOCKET_ERROR;
+  }
+
+  sockaddr_storage saddr;
+  size_t len = address.ToSockAddrStorage(&saddr);
+  int sent = ::sendto(native_socket_, buffer, length, 0,
+                      reinterpret_cast<sockaddr*>(&saddr), len);
+
+  if (sent == SOCKET_ERROR) {
+    error_ = errno;
+  }
+
+  return sent;
+}
+
+// Read data received from the remote end we're connected to.
+int MacAsyncSocket::Recv(void* buffer, size_t length) {
+  int received = ::recv(native_socket_, reinterpret_cast<char*>(buffer),
+                        length, 0);
+  if (received == SOCKET_ERROR) error_ = errno;
+
+  // Recv should only be called when there is data to read
+  ASSERT((received != 0) || (length == 0));
+  return received;
+}
+
+// Read data received from any remote party
+int MacAsyncSocket::RecvFrom(void* buffer, size_t length,
+                             SocketAddress* out_addr) {
+  sockaddr_storage saddr;
+  socklen_t addr_len = sizeof(saddr);
+  int received = ::recvfrom(native_socket_, reinterpret_cast<char*>(buffer),
+                            length, 0, reinterpret_cast<sockaddr*>(&saddr),
+                            &addr_len);
+  if (received >= 0 && out_addr != NULL) {
+    SocketAddressFromSockAddrStorage(saddr, out_addr);
+  } else if (received == SOCKET_ERROR) {
+    error_ = errno;
+  }
+  return received;
+}
+
+int MacAsyncSocket::Listen(int backlog) {
+  if (!valid()) {
+    return SOCKET_ERROR;
+  }
+
+  int res = ::listen(native_socket_, backlog);
+  if (res != SOCKET_ERROR)
+    state_ = CS_CONNECTING;
+  else
+    error_ = errno;
+
+  return res;
+}
+
+MacAsyncSocket* MacAsyncSocket::Accept(SocketAddress* out_addr) {
+  sockaddr_storage saddr;
+  socklen_t addr_len = sizeof(saddr);
+
+  int socket_fd = ::accept(native_socket_, reinterpret_cast<sockaddr*>(&saddr),
+                           &addr_len);
+  if (socket_fd == INVALID_SOCKET) {
+    error_ = errno;
+    return NULL;
+  }
+
+  MacAsyncSocket* s = new MacAsyncSocket(ss_, saddr.ss_family, socket_fd);
+  if (s && s->valid()) {
+    s->state_ = CS_CONNECTED;
+    if (out_addr)
+      SocketAddressFromSockAddrStorage(saddr, out_addr);
+  } else {
+    delete s;
+    s = NULL;
+  }
+  return s;
+}
+
+int MacAsyncSocket::Close() {
+  if (source_ != NULL) {
+    CFRunLoopSourceInvalidate(source_);
+    CFRelease(source_);
+    if (ss_) ss_->UnregisterSocket(this);
+    source_ = NULL;
+  }
+
+  if (socket_ != NULL) {
+    CFSocketInvalidate(socket_);
+    CFRelease(socket_);
+    socket_ = NULL;
+  }
+
+  if (resolver_) {
+    resolver_->Destroy(false);
+    resolver_ = NULL;
+  }
+
+  native_socket_ = INVALID_SOCKET;  // invalidates the socket
+  error_ = 0;
+  state_ = CS_CLOSED;
+  return 0;
+}
+
+int MacAsyncSocket::EstimateMTU(uint16* mtu) {
+  ASSERT(false && "NYI");
+  return -1;
+}
+
+int MacAsyncSocket::GetError() const {
+  return error_;
+}
+
+void MacAsyncSocket::SetError(int error) {
+  error_ = error;
+}
+
+Socket::ConnState MacAsyncSocket::GetState() const {
+  return state_;
+}
+
+int MacAsyncSocket::GetOption(Option opt, int* value) {
+  ASSERT(false && "NYI");
+  return -1;
+}
+
+int MacAsyncSocket::SetOption(Option opt, int value) {
+  ASSERT(false && "NYI");
+  return -1;
+}
+
+void MacAsyncSocket::EnableCallbacks() {
+  if (valid()) {
+    disabled_ = false;
+    CFSocketEnableCallBacks(socket_, current_callbacks_);
+  }
+}
+
+void MacAsyncSocket::DisableCallbacks() {
+  if (valid()) {
+    disabled_ = true;
+    CFSocketDisableCallBacks(socket_, kCallbackFlags);
+  }
+}
+
+MacAsyncSocket::MacAsyncSocket(MacBaseSocketServer* ss, int family,
+                               int native_socket)
+    : ss_(ss),
+      socket_(NULL),
+      native_socket_(native_socket),
+      source_(NULL),
+      current_callbacks_(0),
+      disabled_(false),
+      error_(0),
+      state_(CS_CLOSED),
+      resolver_(NULL) {
+  Initialize(family);
+}
+
+// Create a new socket, wrapping the native socket if provided or creating one
+// otherwise. In case of any failure, consume the native socket.  We assume the
+// wrapped socket is in the closed state.  If this is not the case you must
+// update the state_ field for this socket yourself.
+void MacAsyncSocket::Initialize(int family) {
+  CFSocketContext ctx = { 0 };
+  ctx.info = this;
+
+  // First create the CFSocket
+  CFSocketRef cf_socket = NULL;
+  bool res = false;
+  if (native_socket_ == INVALID_SOCKET) {
+    cf_socket = CFSocketCreate(kCFAllocatorDefault,
+                               family, SOCK_STREAM, IPPROTO_TCP,
+                               kCallbackFlags, MacAsyncSocketCallBack, &ctx);
+  } else {
+    cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault,
+                                         native_socket_, kCallbackFlags,
+                                         MacAsyncSocketCallBack, &ctx);
+  }
+
+  if (cf_socket) {
+    res = true;
+    socket_ = cf_socket;
+    native_socket_ = CFSocketGetNative(cf_socket);
+    current_callbacks_ = kCallbackFlags;
+  }
+
+  if (res) {
+    // Make the underlying socket asynchronous
+    res = (-1 != ::fcntl(native_socket_, F_SETFL,
+                         ::fcntl(native_socket_, F_GETFL, 0) | O_NONBLOCK));
+  }
+
+  if (res) {
+    // Add this socket to the run loop, at priority 1 so that it will be
+    // queued behind any pending signals.
+    source_ = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 1);
+    res = (source_ != NULL);
+    if (!res) errno = EINVAL;
+  }
+
+  if (res) {
+    if (ss_) ss_->RegisterSocket(this);
+    CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopCommonModes);
+  }
+
+  if (!res) {
+    int error = errno;
+    Close();  //  Clears error_.
+    error_ = error;
+  }
+}
+
+// Call CFRelease on the result when done using it
+CFDataRef MacAsyncSocket::CopyCFAddress(const SocketAddress& address) {
+  sockaddr_storage saddr;
+  size_t len = address.ToSockAddrStorage(&saddr);
+
+  const UInt8* bytes = reinterpret_cast<UInt8*>(&saddr);
+
+  CFDataRef cf_address = CFDataCreate(kCFAllocatorDefault,
+                                      bytes, len);
+
+  ASSERT(cf_address != NULL);
+  return cf_address;
+}
+
+void MacAsyncSocket::MacAsyncSocketCallBack(CFSocketRef s,
+                                            CFSocketCallBackType callbackType,
+                                            CFDataRef address,
+                                            const void* data,
+                                            void* info) {
+  MacAsyncSocket* this_socket =
+      reinterpret_cast<MacAsyncSocket*>(info);
+  ASSERT(this_socket != NULL && this_socket->socket_ == s);
+
+  // Don't signal any socket messages if the socketserver is not listening on
+  // them.  When we are reenabled they will be requeued and will fire again.
+  if (this_socket->disabled_)
+    return;
+
+  switch (callbackType) {
+    case kCFSocketReadCallBack:
+      // This callback is invoked in one of 3 situations:
+      // 1. A new connection is waiting to be accepted.
+      // 2. The remote end closed the connection (a recv will return 0).
+      // 3. Data is available to read.
+      // 4. The connection closed unhappily (recv will return -1).
+      if (this_socket->state_ == CS_CONNECTING) {
+        // Case 1.
+        this_socket->SignalReadEvent(this_socket);
+      } else {
+        char ch, amt;
+        amt = ::recv(this_socket->native_socket_, &ch, 1, MSG_PEEK);
+        if (amt == 0) {
+          // Case 2.
+          this_socket->state_ = CS_CLOSED;
+
+          // Disable additional callbacks or we will signal close twice.
+          CFSocketDisableCallBacks(this_socket->socket_, kCFSocketReadCallBack);
+          this_socket->current_callbacks_ &= ~kCFSocketReadCallBack;
+          this_socket->SignalCloseEvent(this_socket, 0);
+        } else if (amt > 0) {
+          // Case 3.
+          this_socket->SignalReadEvent(this_socket);
+        } else {
+          // Case 4.
+          int error = errno;
+          if (error == EAGAIN) {
+            // Observed in practice.  Let's hope it's a spurious or out of date
+            // signal, since we just eat it.
+          } else {
+            this_socket->error_ = error;
+            this_socket->SignalCloseEvent(this_socket, error);
+          }
+        }
+      }
+      break;
+
+    case kCFSocketConnectCallBack:
+      if (data != NULL) {
+        // An error occured in the background while connecting
+        this_socket->error_ = errno;
+        this_socket->state_ = CS_CLOSED;
+        this_socket->SignalCloseEvent(this_socket, this_socket->error_);
+      } else {
+        this_socket->state_ = CS_CONNECTED;
+        this_socket->SignalConnectEvent(this_socket);
+      }
+      break;
+
+    case kCFSocketWriteCallBack:
+      // Update our callback tracking.  Write doesn't reenable, so it's off now.
+      this_socket->current_callbacks_ &= ~kCFSocketWriteCallBack;
+      this_socket->SignalWriteEvent(this_socket);
+      break;
+
+    default:
+      ASSERT(false && "Invalid callback type for socket");
+  }
+}
+
+}  // namespace rtc