root/native_client_sdk/src/tests/nacl_io_socket_test/socket_test.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. IP4ToSockAddr
  2. ki_fcntl_wrapper
  3. SetNonBlocking
  4. sock2_
  5. TearDown
  6. Bind
  7. SetUp
  8. SetUp
  9. ServerThreadMain
  10. ServerThreadMainStatic
  11. SetUp
  12. TearDown
  13. ServerLog
  14. TEST
  15. TEST_F
  16. TEST_F
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F
  22. TEST_F
  23. TEST_F
  24. TEST_F
  25. TEST_F
  26. TEST_F
  27. TEST_F
  28. TEST_F
  29. TEST_F
  30. TEST_F
  31. TEST_F
  32. TEST_F
  33. TEST_F

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>

#include <map>
#include <string>

#include "echo_server.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "nacl_io/kernel_intercept.h"
#include "nacl_io/kernel_proxy.h"
#include "nacl_io/ossocket.h"
#include "nacl_io/ostypes.h"
#include "ppapi/cpp/message_loop.h"
#include "ppapi_simple/ps.h"

#ifdef PROVIDES_SOCKET_API

using namespace nacl_io;
using namespace sdk_util;

#define LOCAL_HOST 0x7F000001
#define PORT1 4006
#define PORT2 4007
#define ANY_PORT 0

namespace {

void IP4ToSockAddr(uint32_t ip, uint16_t port, struct sockaddr_in* addr) {
  memset(addr, 0, sizeof(*addr));

  addr->sin_family = AF_INET;
  addr->sin_port = htons(port);
  addr->sin_addr.s_addr = htonl(ip);
}

static int ki_fcntl_wrapper(int fd, int request, ...) {
  va_list ap;
  va_start(ap, request);
  int rtn = ki_fcntl(fd, request, ap);
  va_end(ap);
  return rtn;
}

static void SetNonBlocking(int sock) {
  int flags = ki_fcntl_wrapper(sock, F_GETFL);
  ASSERT_NE(-1, flags);
  flags |= O_NONBLOCK;
  ASSERT_EQ(0, ki_fcntl_wrapper(sock, F_SETFL, flags));
  ASSERT_EQ(flags, ki_fcntl_wrapper(sock, F_GETFL));
}

class SocketTest : public ::testing::Test {
 public:
  SocketTest() : sock1_(-1), sock2_(-1) {}

  void TearDown() {
    if (sock1_ != -1)
      EXPECT_EQ(0, ki_close(sock1_));
    if (sock2_ != -1)
      EXPECT_EQ(0, ki_close(sock2_));
  }

  int Bind(int fd, uint32_t ip, uint16_t port) {
    sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);

    IP4ToSockAddr(ip, port, &addr);
    int err = ki_bind(fd, (sockaddr*)&addr, addrlen);

    if (err == -1)
      return errno;
    return 0;
  }

 protected:
  int sock1_;
  int sock2_;
};

class SocketTestUDP : public SocketTest {
 public:
  SocketTestUDP() {}

  void SetUp() {
    sock1_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
    sock2_ = ki_socket(AF_INET, SOCK_DGRAM, 0);

    EXPECT_GT(sock1_, -1);
    EXPECT_GT(sock2_, -1);
  }
};

class SocketTestTCP : public SocketTest {
 public:
  SocketTestTCP() {}

  void SetUp() {
    sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
    sock2_ = ki_socket(AF_INET, SOCK_STREAM, 0);

    EXPECT_GT(sock1_, -1);
    EXPECT_GT(sock2_, -1);
  }
};

class SocketTestWithServer : public ::testing::Test {
 public:
  SocketTestWithServer() : instance_(PSGetInstanceId()) {
    pthread_mutex_init(&ready_lock_, NULL);
    pthread_cond_init(&ready_cond_, NULL);
  }

  void ServerThreadMain() {
    loop_.AttachToCurrentThread();
    pp::Instance instance(PSGetInstanceId());
    EchoServer server(&instance, PORT1, ServerLog, &ready_cond_, &ready_lock_);
    loop_.Run();
  }

  static void* ServerThreadMainStatic(void* arg) {
    SocketTestWithServer* test = (SocketTestWithServer*)arg;
    test->ServerThreadMain();
    return NULL;
  }

  void SetUp() {
    loop_ = pp::MessageLoop(&instance_);
    pthread_mutex_lock(&ready_lock_);

    // Start an echo server on a background thread.
    pthread_create(&server_thread_, NULL, ServerThreadMainStatic, this);

    // Wait for thread to signal that it is ready to accept connections.
    pthread_cond_wait(&ready_cond_, &ready_lock_);
    pthread_mutex_unlock(&ready_lock_);

    sock_ = ki_socket(AF_INET, SOCK_STREAM, 0);
    EXPECT_GT(sock_, -1);
  }

  void TearDown() {
    // Stop the echo server and the background thread it runs on
    loop_.PostQuit(true);
    pthread_join(server_thread_, NULL);
    ASSERT_EQ(0, ki_close(sock_));
  }

  static void ServerLog(const char* msg) {
    // Uncomment to see logs of echo server on stdout
    //printf("server: %s\n", msg);
  }

 protected:
  int sock_;
  pp::MessageLoop loop_;
  pp::Instance instance_;
  pthread_cond_t ready_cond_;
  pthread_mutex_t ready_lock_;
  pthread_t server_thread_;
};

}  // namespace

TEST(SocketTestSimple, Socket) {
  EXPECT_EQ(-1, ki_socket(AF_UNIX, SOCK_STREAM, 0));
  EXPECT_EQ(errno, EAFNOSUPPORT);

  // We don't support RAW sockets
  EXPECT_EQ(-1, ki_socket(AF_INET, SOCK_RAW, IPPROTO_TCP));
  EXPECT_EQ(EPROTONOSUPPORT, errno);

  // Invalid type
  EXPECT_EQ(-1, ki_socket(AF_INET, -1, 0));
  EXPECT_EQ(EINVAL, errno);

  int sock1_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
  EXPECT_NE(-1, sock1_);

  int sock2_ = ki_socket(AF_INET6, SOCK_DGRAM, 0);
  EXPECT_NE(-1, sock2_);

  int sock3 = ki_socket(AF_INET, SOCK_STREAM, 0);
  EXPECT_NE(-1, sock3);

  int sock4 = ki_socket(AF_INET6, SOCK_STREAM, 0);
  EXPECT_NE(-1, sock4);

  ki_close(sock1_);
  ki_close(sock2_);
  ki_close(sock3);
  ki_close(sock4);
}

TEST_F(SocketTestUDP, Bind) {
  // Bind away.
  EXPECT_EQ(0, Bind(sock1_, LOCAL_HOST, PORT1));

  // Invalid to rebind a socket.
  EXPECT_EQ(EINVAL, Bind(sock1_, LOCAL_HOST, PORT1));

  // Addr in use.
  EXPECT_EQ(EADDRINUSE, Bind(sock2_, LOCAL_HOST, PORT1));

  // Bind with a wildcard.
  EXPECT_EQ(0, Bind(sock2_, LOCAL_HOST, ANY_PORT));

  // Invalid to rebind after wildcard
  EXPECT_EQ(EINVAL, Bind(sock2_, LOCAL_HOST, PORT1));
}

TEST_F(SocketTestUDP, SendRcv) {
  char outbuf[256];
  char inbuf[512];

  memset(outbuf, 1, sizeof(outbuf));
  memset(inbuf, 0, sizeof(inbuf));

  EXPECT_EQ(0, Bind(sock1_, LOCAL_HOST, PORT1));
  EXPECT_EQ(0, Bind(sock2_, LOCAL_HOST, PORT2));

  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);
  IP4ToSockAddr(LOCAL_HOST, PORT2, &addr);

  int len1 =
     ki_sendto(sock1_, outbuf, sizeof(outbuf), 0, (sockaddr*) &addr, addrlen);
  EXPECT_EQ(sizeof(outbuf), len1);

  // Ensure the buffers are different
  EXPECT_NE(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
  memset(&addr, 0, sizeof(addr));

  // Try to receive the previously sent packet
  int len2 =
    ki_recvfrom(sock2_, inbuf, sizeof(inbuf), 0, (sockaddr*) &addr, &addrlen);
  EXPECT_EQ(sizeof(outbuf), len2);
  EXPECT_EQ(sizeof(sockaddr_in), addrlen);
  EXPECT_EQ(PORT1, htons(addr.sin_port));

  // Now they should be the same
  EXPECT_EQ(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
}

TEST_F(SocketTestUDP, SendRcvUnbound) {
  char outbuf[256];
  char inbuf[512];

  memset(outbuf, 1, sizeof(outbuf));
  memset(inbuf, 0, sizeof(inbuf));

  // Don't bind sock1_, this will automatically bind sock1_ to a random port
  // at the time of the first send.
  EXPECT_EQ(0, Bind(sock2_, LOCAL_HOST, PORT2));

  sockaddr_in addr;
  sockaddr_in addr2;
  socklen_t addrlen = sizeof(addr2);
  IP4ToSockAddr(LOCAL_HOST, PORT2, &addr2);

  // The first send hasn't occurred, so the socket is not yet bound.
  socklen_t out_addrlen = sizeof(addr);
  ASSERT_EQ(0, getsockname(sock1_, (sockaddr*)&addr, &out_addrlen));
  EXPECT_EQ(addrlen, out_addrlen);
  EXPECT_EQ(0, htonl(addr.sin_addr.s_addr));
  EXPECT_EQ(0, htons(addr.sin_port));

  int len1 =
     sendto(sock1_, outbuf, sizeof(outbuf), 0, (sockaddr*) &addr2, addrlen);
  EXPECT_EQ(sizeof(outbuf), len1);

  // After the first send, the socket should be bound; the port is set, but
  // the address is still 0.
  ASSERT_EQ(0, getsockname(sock1_, (sockaddr*)&addr, &out_addrlen));
  EXPECT_EQ(addrlen, out_addrlen);
  EXPECT_EQ(0, htonl(addr.sin_addr.s_addr));
  EXPECT_NE(0, htons(addr.sin_port));

  // Ensure the buffers are different
  EXPECT_NE(0, memcmp(outbuf, inbuf, sizeof(outbuf)));

  // Try to receive the previously sent packet
  int len2 =
    recvfrom(sock2_, inbuf, sizeof(inbuf), 0, (sockaddr*) &addr, &addrlen);
  EXPECT_EQ(sizeof(outbuf), len2);
  EXPECT_EQ(sizeof(sockaddr_in), addrlen);
  EXPECT_EQ(LOCAL_HOST, htonl(addr.sin_addr.s_addr));
  EXPECT_NE(0, htons(addr.sin_port));

  // Now they should be the same
  EXPECT_EQ(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
}

const size_t kQueueSize = 65536 * 8;
TEST_F(SocketTestUDP, FullFifo) {
  char outbuf[16 * 1024];

  ASSERT_EQ(0, Bind(sock1_, LOCAL_HOST, PORT1));
  ASSERT_EQ(0, Bind(sock2_, LOCAL_HOST, PORT2));

  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);
  IP4ToSockAddr(LOCAL_HOST, PORT2, &addr);

  size_t total = 0;
  while (total < kQueueSize * 8) {
    int len = ki_sendto(sock1_, outbuf, sizeof(outbuf), MSG_DONTWAIT,
                     (sockaddr*) &addr, addrlen);

    if (len <= 0) {
      EXPECT_EQ(-1, len);
      EXPECT_EQ(EWOULDBLOCK, errno);
      break;
    }

    if (len >= 0) {
      EXPECT_EQ(sizeof(outbuf), len);
      total += len;
    }
  }
  EXPECT_GT(total, kQueueSize - 1);
  EXPECT_LT(total, kQueueSize * 8);
}

TEST_F(SocketTestWithServer, TCPConnect) {
  char outbuf[256];
  char inbuf[512];

  memset(outbuf, 1, sizeof(outbuf));

  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);

  IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);

  ASSERT_EQ(0, ki_connect(sock_, (sockaddr*) &addr, addrlen))
      << "Failed with " << errno << ": " << strerror(errno);

  // Send two different messages to the echo server and verify the
  // response matches.
  strcpy(outbuf, "hello");
  memset(inbuf, 0, sizeof(inbuf));
  ASSERT_EQ(sizeof(outbuf), ki_write(sock_, outbuf, sizeof(outbuf)))
      << "socket write failed with: " << strerror(errno);
  ASSERT_EQ(sizeof(outbuf), ki_read(sock_, inbuf, sizeof(inbuf)));
  EXPECT_EQ(0, memcmp(outbuf, inbuf, sizeof(outbuf)));

  strcpy(outbuf, "world");
  memset(inbuf, 0, sizeof(inbuf));
  ASSERT_EQ(sizeof(outbuf), ki_write(sock_, outbuf, sizeof(outbuf)));
  ASSERT_EQ(sizeof(outbuf), ki_read(sock_, inbuf, sizeof(inbuf)));
  EXPECT_EQ(0, memcmp(outbuf, inbuf, sizeof(outbuf)));
}

TEST_F(SocketTestWithServer, TCPConnectNonBlock) {
  char outbuf[256];
  //char inbuf[512];

  memset(outbuf, 1, sizeof(outbuf));

  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);

  IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);

  SetNonBlocking(sock_);
  ASSERT_EQ(-1, ki_connect(sock_, (sockaddr*) &addr, addrlen));
  ASSERT_EQ(EINPROGRESS, errno)
     << "expected EINPROGRESS but got: " << strerror(errno);
  ASSERT_EQ(-1, ki_connect(sock_, (sockaddr*) &addr, addrlen));
  ASSERT_EQ(EALREADY, errno);

  // Wait for the socket connection to complete using poll()
  struct pollfd pollfd = { sock_, POLLIN|POLLOUT, 0 };
  ASSERT_EQ(1, ki_poll(&pollfd, 1, -1));
  ASSERT_EQ(POLLOUT, pollfd.revents);

  // Attempts to connect again should yield EISCONN
  ASSERT_EQ(-1, ki_connect(sock_, (sockaddr*) &addr, addrlen));
  ASSERT_EQ(EISCONN, errno);

  // And SO_ERROR should be 0.
}

TEST_F(SocketTest, Getsockopt) {
  sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
  EXPECT_GT(sock1_, -1);
  int socket_error = 99;
  socklen_t len = sizeof(socket_error);

  // Test for valid option (SO_ERROR) which should be 0 when a socket
  // is first created.
  ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_ERROR,
                             &socket_error, &len));
  ASSERT_EQ(0, socket_error);
  ASSERT_EQ(sizeof(socket_error), len);

  // Test for an invalid option (-1)
  ASSERT_EQ(-1, ki_getsockopt(sock1_, SOL_SOCKET, -1, &socket_error, &len));
  ASSERT_EQ(ENOPROTOOPT, errno);
}

TEST_F(SocketTest, Setsockopt) {
  sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
  EXPECT_GT(sock1_, -1);

  // It should not be possible to set SO_ERROR using setsockopt.
  int socket_error = 10;
  socklen_t len = sizeof(socket_error);
  ASSERT_EQ(-1, ki_setsockopt(sock1_, SOL_SOCKET, SO_ERROR,
                              &socket_error, len));
  ASSERT_EQ(ENOPROTOOPT, errno);
}

TEST_F(SocketTest, Sockopt_TCP_NODELAY) {
  int option = 0;
  socklen_t len = sizeof(option);
  // Getting and setting TCP_NODELAY on UDP socket should fail
  sock1_ = socket(AF_INET, SOCK_DGRAM, 0);
  ASSERT_EQ(-1, ki_setsockopt(sock1_, IPPROTO_TCP, TCP_NODELAY, &option, len));
  ASSERT_EQ(ENOPROTOOPT, errno);
  ASSERT_EQ(-1, ki_getsockopt(sock1_, IPPROTO_TCP, TCP_NODELAY, &option, &len));
  ASSERT_EQ(ENOPROTOOPT, errno);

  // Getting and setting TCP_NODELAY on TCP socket should preserve value
  sock2_ = socket(AF_INET, SOCK_STREAM, 0);
  ASSERT_EQ(0, ki_getsockopt(sock2_, IPPROTO_TCP, TCP_NODELAY, &option, &len));
  ASSERT_EQ(0, option);
  ASSERT_EQ(sizeof(option), len);

  option = 1;
  len = sizeof(option);
  ASSERT_EQ(0, ki_setsockopt(sock2_, IPPROTO_TCP, TCP_NODELAY, &option, len))
      << "Failed with " << errno << ": " << strerror(errno);
  ASSERT_EQ(1, option);
}

TEST_F(SocketTest, Sockopt_KEEPALIVE) {
  sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
  ASSERT_GT(sock1_, -1);
  sock2_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
  ASSERT_GT(sock2_, -1);

  int value = 0;
  socklen_t len = sizeof(value);
  ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_KEEPALIVE, &value, &len));
  ASSERT_EQ(0, value);
  ASSERT_EQ(sizeof(int), len);
}

// Disabled until we support SO_LINGER (i.e. syncronouse close()/shutdown())
// TODO(sbc): re-enable once we fix http://crbug.com/312401
TEST_F(SocketTest, DISABLED_Sockopt_LINGER) {
  sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);
  ASSERT_GT(sock1_, -1);
  sock2_ = ki_socket(AF_INET, SOCK_DGRAM, 0);
  ASSERT_GT(sock2_, -1);

  struct linger linger = { 7, 8 };
  socklen_t len = sizeof(linger);
  ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_LINGER, &linger, &len));
  ASSERT_EQ(0, linger.l_onoff);
  ASSERT_EQ(0, linger.l_linger);
  ASSERT_EQ(sizeof(struct linger), len);
  ASSERT_EQ(0, ki_getsockopt(sock2_, SOL_SOCKET, SO_LINGER, &linger, &len));
  ASSERT_EQ(0, linger.l_onoff);
  ASSERT_EQ(0, linger.l_linger);
  ASSERT_EQ(sizeof(struct linger), len);

  linger.l_onoff = 1;
  linger.l_linger = 77;
  len = sizeof(linger);
  ASSERT_EQ(0, ki_setsockopt(sock1_, SOL_SOCKET, SO_LINGER, &linger, len));
  linger.l_onoff = 1;
  linger.l_linger = 88;
  ASSERT_EQ(0, ki_setsockopt(sock2_, SOL_SOCKET, SO_LINGER, &linger, len));

  len = sizeof(linger);
  ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_LINGER, &linger, &len));
  ASSERT_EQ(1, linger.l_onoff);
  ASSERT_EQ(77, linger.l_linger);
  ASSERT_EQ(sizeof(struct linger), len);
  ASSERT_EQ(0, ki_getsockopt(sock2_, SOL_SOCKET, SO_LINGER, &linger, &len));
  ASSERT_EQ(1, linger.l_onoff);
  ASSERT_EQ(88, linger.l_linger);
  ASSERT_EQ(sizeof(struct linger), len);
}

TEST_F(SocketTest, Sockopt_REUSEADDR) {
  int value = 1;
  socklen_t len = sizeof(value);
  sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0);

  ASSERT_GT(sock1_, -1);
  ASSERT_EQ(0, ki_setsockopt(sock1_, SOL_SOCKET, SO_REUSEADDR, &value, len));

  value = 0;
  len = sizeof(value);
  ASSERT_EQ(0, ki_getsockopt(sock1_, SOL_SOCKET, SO_REUSEADDR, &value, &len));
  ASSERT_EQ(1, value);
  ASSERT_EQ(sizeof(int), len);
}

// The size of the data to send is deliberately chosen to be
// larger than the TCP buffer in nacl_io.
// TODO(sbc): use ioctl to discover the actual buffer size at
// runtime.
#define LARGE_SEND_BYTES (800 * 1024)
TEST_F(SocketTestWithServer, LargeSend) {
  char* outbuf = (char*)malloc(LARGE_SEND_BYTES);
  char* inbuf = (char*)malloc(LARGE_SEND_BYTES);
  int bytes_sent = 0;
  int bytes_received = 0;

  // Fill output buffer with ascending integers
  int* outbuf_int = (int*)outbuf;
  int* inbuf_int = (int*)inbuf;
  for (int i = 0; i < LARGE_SEND_BYTES/sizeof(int); i++) {
    outbuf_int[i] = i;
  }

  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);

  IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
  ASSERT_EQ(0, ki_connect(sock_, (sockaddr*) &addr, addrlen))
      << "Failed with " << errno << ": " << strerror(errno);

  // Call send an recv until all bytes have been transfered.
  while (bytes_received < LARGE_SEND_BYTES) {
    if (bytes_sent < LARGE_SEND_BYTES) {
      int sent = ki_send(sock_, outbuf + bytes_sent,
                      LARGE_SEND_BYTES - bytes_sent, MSG_DONTWAIT);
      if (sent < 0)
        ASSERT_EQ(EWOULDBLOCK, errno) << "send failed: " << strerror(errno);
      else
        bytes_sent += sent;
    }

    int received = ki_recv(sock_, inbuf + bytes_received,
                           LARGE_SEND_BYTES - bytes_received, MSG_DONTWAIT);
    if (received < 0)
      ASSERT_EQ(EWOULDBLOCK, errno) << "recv failed: " << strerror(errno);
    else
      bytes_received += received;
  }

  // Make sure there is nothing else to recv at this point
  char dummy[10];
  ASSERT_EQ(-1, ki_recv(sock_, dummy, 10, MSG_DONTWAIT));
  ASSERT_EQ(EWOULDBLOCK, errno);

  int errors = 0;
  for (int i = 0; i < LARGE_SEND_BYTES/4; i++) {
    if (inbuf_int[i] != outbuf_int[i]) {
      printf("%d: in=%d out=%d\n", i, inbuf_int[i], outbuf_int[i]);
      if (errors++ > 50)
        break;
    }
  }

  for (int i = 0; i < LARGE_SEND_BYTES; i++) {
    ASSERT_EQ(outbuf[i], inbuf[i]) << "cmp failed at " << i;
  }

  ASSERT_EQ(0, memcmp(inbuf, outbuf, LARGE_SEND_BYTES));

  free(inbuf);
  free(outbuf);
}

TEST_F(SocketTestUDP, Listen) {
  EXPECT_EQ(-1, ki_listen(sock1_, 10));
  EXPECT_EQ(errno, ENOTSUP);
}

TEST_F(SocketTestTCP, Listen) {
  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);
  const char* client_greeting = "hello";
  const char* server_reply = "reply";
  const int greeting_len = strlen(client_greeting);
  const int reply_len = strlen(server_reply);

  int server_sock = sock1_;

  // Accept before listen should fail
  ASSERT_EQ(-1, accept(server_sock, (sockaddr*)&addr, &addrlen));

  // Listen should fail on unbound socket
  ASSERT_EQ(-1,  ki_listen(server_sock, 10));

  // Bind and Listen
  ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
  ASSERT_EQ(0,  ki_listen(server_sock, 10))
    << "listen failed with: " << strerror(errno);

  // Connect to listening socket, and send greeting
  int client_sock = sock2_;
  IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
  addrlen = sizeof(addr);
  ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
    << "Failed with " << errno << ": " << strerror(errno);

  ASSERT_EQ(greeting_len, ki_send(client_sock, client_greeting,
                                  greeting_len, 0));

  // Pass in addrlen that is larger than our actual address to make
  // sure that it is correctly set back to sizeof(sockaddr_in)
  addrlen = sizeof(addr) + 10;
  int new_socket = accept(server_sock, (sockaddr*)&addr, &addrlen);
  ASSERT_GT(new_socket, -1)
    << "accept failed with " << errno << ": " << strerror(errno);

  // Verify addr and addrlen were set correctly
  ASSERT_EQ(addrlen, sizeof(sockaddr_in));
  sockaddr_in client_addr;
  ASSERT_EQ(0, ki_getsockname(client_sock, (sockaddr*)&client_addr, &addrlen));
  ASSERT_EQ(client_addr.sin_family, addr.sin_family);
  ASSERT_EQ(client_addr.sin_port, addr.sin_port);
  ASSERT_EQ(client_addr.sin_addr.s_addr, addr.sin_addr.s_addr);

  // Recv greeting from client and send reply
  char inbuf[512];
  ASSERT_EQ(greeting_len, ki_recv(new_socket, inbuf, sizeof(inbuf), 0));
  inbuf[greeting_len] = 0;
  ASSERT_STREQ(inbuf, client_greeting);
  ASSERT_EQ(reply_len, ki_send(new_socket, server_reply, reply_len, 0));

  // Recv reply on client socket
  ASSERT_EQ(reply_len, ki_recv(client_sock, inbuf, sizeof(inbuf), 0));
  inbuf[reply_len] = 0;
  ASSERT_STREQ(inbuf, server_reply);

  ASSERT_EQ(0, ki_close(new_socket));
}

TEST_F(SocketTestTCP, ListenNonBlocking) {
  int server_sock = sock1_;

  // Set non-blocking
  SetNonBlocking(server_sock);

  // bind and listen
  ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
  ASSERT_EQ(0, ki_listen(server_sock, 10))
    << "listen failed with: " << strerror(errno);

  // Accept should fail with EAGAIN since there is no incomming
  // connection.
  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);
  ASSERT_EQ(-1, accept(server_sock, (sockaddr*)&addr, &addrlen));
  ASSERT_EQ(EAGAIN, errno);

  // If we poll the listening socket it should also return
  // not readable to indicate that no connections are available
  // to accept.
  struct pollfd pollfd = { server_sock, POLLIN|POLLOUT, 0 };
  ASSERT_EQ(0, ki_poll(&pollfd, 1, 0));

  // Connect to listening socket
  int client_sock = sock2_;
  IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
  addrlen = sizeof(addr);
  ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
    << "Failed with " << errno << ": " << strerror(errno);

  // Not poll again but with an infintie timeout.
  pollfd.fd = server_sock;
  pollfd.events = POLLIN | POLLOUT;
  ASSERT_EQ(1, ki_poll(&pollfd, 1, -1));

  // Now non-blocking accept should return the new socket
  int new_socket = accept(server_sock, (sockaddr*)&addr, &addrlen);
  ASSERT_NE(-1, new_socket)
    << "accept failed with: " << strerror(errno);
  ASSERT_EQ(0, ki_close(new_socket));

  // Accept calls should once again fail with EAGAIN
  ASSERT_EQ(-1, accept(server_sock, (sockaddr*)&addr, &addrlen));
  ASSERT_EQ(EAGAIN, errno);

  // As should polling the listening socket
  pollfd.fd = server_sock;
  pollfd.events = POLLIN | POLLOUT;
  ASSERT_EQ(0, ki_poll(&pollfd, 1, 0));
}

TEST_F(SocketTestTCP, SendRecvAfterRemoteShutdown) {
  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);

  int server_sock = sock1_;
  int client_sock = sock2_;

  // bind and listen
  ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
  ASSERT_EQ(0, ki_listen(server_sock, 10))
    << "listen failed with: " << strerror(errno);

  // connect to listening socket
  IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
  ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
    << "Failed with " << errno << ": " << strerror(errno);

  addrlen = sizeof(addr);
  int new_sock = accept(server_sock, (sockaddr*)&addr, &addrlen);
  ASSERT_NE(-1, new_sock);

  const char* send_buf = "hello world";
  ASSERT_EQ(strlen(send_buf), ki_send(new_sock, send_buf, strlen(send_buf), 0));

  // Recv first 10 bytes
  char buf[256];
  ASSERT_EQ(10, ki_recv(client_sock, buf, 10, 0));

  // Close the new socket
  ASSERT_EQ(0, ki_close(new_sock));

  // Recv remainder
  int bytes_remaining = strlen(send_buf) - 10;
  ASSERT_EQ(bytes_remaining, ki_recv(client_sock, buf, 256, 0));

  // Attempt to read/write after remote shutdown, with no bytes remainging
  ASSERT_EQ(0, ki_recv(client_sock, buf, 10, 0));
  ASSERT_EQ(0, ki_recv(client_sock, buf, 10, 0));
  ASSERT_EQ(-1, ki_send(client_sock, buf, 10, 0));
  ASSERT_EQ(errno, EPIPE);
}

TEST_F(SocketTestTCP, SendRecvAfterLocalShutdown) {
  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);

  int server_sock = sock1_;
  int client_sock = sock2_;

  // bind and listen
  ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
  ASSERT_EQ(0, ki_listen(server_sock, 10))
    << "listen failed with: " << strerror(errno);

  // connect to listening socket
  IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
  ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
    << "Failed with " << errno << ": " << strerror(errno);

  addrlen = sizeof(addr);
  int new_sock = accept(server_sock, (sockaddr*)&addr, &addrlen);
  ASSERT_NE(-1, new_sock);

  // Close the new socket
  ASSERT_EQ(0, ki_shutdown(client_sock, SHUT_RDWR));

  // Attempt to read/write after shutdown
  char buffer[10];
  ASSERT_EQ(0, ki_recv(client_sock, buffer, sizeof(buffer), 0));
  ASSERT_EQ(-1, ki_send(client_sock, buffer, sizeof(buffer), 0));
  ASSERT_EQ(errno, EPIPE);
}

#define SEND_BYTES (1024)
TEST_F(SocketTestTCP, SendBufferedDataAfterShutdown) {
  sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);

  int server_sock = sock1_;
  int client_sock = sock2_;

  // bind and listen
  ASSERT_EQ(0, Bind(server_sock, LOCAL_HOST, PORT1));
  ASSERT_EQ(0, ki_listen(server_sock, 10))
    << "listen failed with: " << strerror(errno);

  // connect to listening socket
  IP4ToSockAddr(LOCAL_HOST, PORT1, &addr);
  ASSERT_EQ(0, ki_connect(client_sock, (sockaddr*)&addr, addrlen))
    << "Failed with " << errno << ": " << strerror(errno);

  addrlen = sizeof(addr);
  int new_sock = accept(server_sock, (sockaddr*)&addr, &addrlen);
  ASSERT_NE(-1, new_sock);

  // send a fairly large amount of data and immediately close
  // the socket.
  void* buffer = alloca(SEND_BYTES);
  ASSERT_EQ(SEND_BYTES, ki_send(client_sock, buffer, SEND_BYTES, 0));
  ASSERT_EQ(0, ki_close(client_sock));

  // avoid double close of sock2_
  sock2_ = -1;

  // Attempt to recv() all the sent data.  None should be lost.
  int remainder = SEND_BYTES;
  while (remainder > 0) {
    int rtn = ki_recv(new_sock, buffer, remainder, 0);
    ASSERT_GT(rtn, 0);
    remainder -= rtn;
  }

  ASSERT_EQ(0, ki_close(new_sock));
}

#endif  // PROVIDES_SOCKET_API

/* [<][>][^][v][top][bottom][index][help] */