Squashed 'third_party/ntcore_2016/' content from commit d8de5e4
Change-Id: Id4839f41b6a620d8bae58dcf1710016671cc4992
git-subtree-dir: third_party/ntcore_2016
git-subtree-split: d8de5e4f19e612e7102172c0dbf152ce82d3d63a
diff --git a/src/tcpsockets/NetworkAcceptor.h b/src/tcpsockets/NetworkAcceptor.h
new file mode 100644
index 0000000..4702b7f
--- /dev/null
+++ b/src/tcpsockets/NetworkAcceptor.h
@@ -0,0 +1,26 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2015. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef TCPSOCKETS_NETWORKACCEPTOR_H_
+#define TCPSOCKETS_NETWORKACCEPTOR_H_
+
+#include "NetworkStream.h"
+
+class NetworkAcceptor {
+ public:
+ NetworkAcceptor() = default;
+ virtual ~NetworkAcceptor() = default;
+
+ virtual int start() = 0;
+ virtual void shutdown() = 0;
+ virtual std::unique_ptr<NetworkStream> accept() = 0;
+
+ NetworkAcceptor(const NetworkAcceptor&) = delete;
+ NetworkAcceptor& operator=(const NetworkAcceptor&) = delete;
+};
+
+#endif // TCPSOCKETS_NETWORKACCEPTOR_H_
diff --git a/src/tcpsockets/NetworkStream.h b/src/tcpsockets/NetworkStream.h
new file mode 100644
index 0000000..63aedb4
--- /dev/null
+++ b/src/tcpsockets/NetworkStream.h
@@ -0,0 +1,39 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2015. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef TCPSOCKETS_NETWORKSTREAM_H_
+#define TCPSOCKETS_NETWORKSTREAM_H_
+
+#include <cstddef>
+
+#include "llvm/StringRef.h"
+
+class NetworkStream {
+ public:
+ NetworkStream() = default;
+ virtual ~NetworkStream() = default;
+
+ enum Error {
+ kConnectionClosed = 0,
+ kConnectionReset = -1,
+ kConnectionTimedOut = -2
+ };
+
+ virtual std::size_t send(const char* buffer, std::size_t len, Error* err) = 0;
+ virtual std::size_t receive(char* buffer, std::size_t len, Error* err,
+ int timeout = 0) = 0;
+ virtual void close() = 0;
+
+ virtual llvm::StringRef getPeerIP() const = 0;
+ virtual int getPeerPort() const = 0;
+ virtual void setNoDelay() = 0;
+
+ NetworkStream(const NetworkStream&) = delete;
+ NetworkStream& operator=(const NetworkStream&) = delete;
+};
+
+#endif // TCPSOCKETS_NETWORKSTREAM_H_
diff --git a/src/tcpsockets/SocketError.cpp b/src/tcpsockets/SocketError.cpp
new file mode 100644
index 0000000..9619edd
--- /dev/null
+++ b/src/tcpsockets/SocketError.cpp
@@ -0,0 +1,31 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2015. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "SocketError.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <string.h>
+#endif
+
+namespace tcpsockets {
+
+std::string SocketStrerror(int code) {
+#ifdef _WIN32
+ LPSTR errstr = nullptr;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, code, 0, (LPSTR)&errstr, 0, 0);
+ std::string rv(errstr);
+ LocalFree(errstr);
+ return rv;
+#else
+ return strerror(code);
+#endif
+}
+
+} // namespace tcpsockets
diff --git a/src/tcpsockets/SocketError.h b/src/tcpsockets/SocketError.h
new file mode 100644
index 0000000..267e8da
--- /dev/null
+++ b/src/tcpsockets/SocketError.h
@@ -0,0 +1,37 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2015. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef TCPSOCKETS_SOCKETERROR_H_
+#define TCPSOCKETS_SOCKETERROR_H_
+
+#include <string>
+
+#ifdef _WIN32
+#include <WinSock2.h>
+#else
+#include <errno.h>
+#endif
+
+namespace tcpsockets {
+
+static inline int SocketErrno() {
+#ifdef _WIN32
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+std::string SocketStrerror(int code);
+
+static inline std::string SocketStrerror() {
+ return SocketStrerror(SocketErrno());
+}
+
+} // namespace tcpsockets
+
+#endif // TCPSOCKETS_SOCKETERROR_H_
diff --git a/src/tcpsockets/TCPAcceptor.cpp b/src/tcpsockets/TCPAcceptor.cpp
new file mode 100644
index 0000000..3b7e16e
--- /dev/null
+++ b/src/tcpsockets/TCPAcceptor.cpp
@@ -0,0 +1,182 @@
+/*
+ TCPAcceptor.cpp
+
+ TCPAcceptor class definition. TCPAcceptor provides methods to passively
+ establish TCP/IP connections with clients.
+
+ ------------------------------------------
+
+ Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "TCPAcceptor.h"
+
+#include <cstdio>
+#include <cstring>
+#ifdef _WIN32
+#include <WinSock2.h>
+#pragma comment(lib, "Ws2_32.lib")
+#else
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+
+#include "llvm/SmallString.h"
+#include "../Log.h"
+#include "SocketError.h"
+
+using namespace tcpsockets;
+
+TCPAcceptor::TCPAcceptor(int port, const char* address)
+ : m_lsd(0),
+ m_port(port),
+ m_address(address),
+ m_listening(false) {
+ m_shutdown = false;
+#ifdef _WIN32
+ WSAData wsaData;
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ WSAStartup(wVersionRequested, &wsaData);
+#endif
+}
+
+TCPAcceptor::~TCPAcceptor() {
+ if (m_lsd > 0) {
+ shutdown();
+#ifdef _WIN32
+ closesocket(m_lsd);
+#else
+ close(m_lsd);
+#endif
+ }
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+int TCPAcceptor::start() {
+ if (m_listening) return 0;
+
+ m_lsd = socket(PF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in address;
+
+ std::memset(&address, 0, sizeof(address));
+ address.sin_family = PF_INET;
+ if (m_address.size() > 0) {
+#ifdef _WIN32
+ llvm::SmallString<128> addr_copy(m_address);
+ addr_copy.push_back('\0');
+ int size = sizeof(address);
+ WSAStringToAddress(addr_copy.data(), PF_INET, nullptr, (struct sockaddr*)&address, &size);
+#else
+ inet_pton(PF_INET, m_address.c_str(), &(address.sin_addr));
+#endif
+ } else {
+ address.sin_addr.s_addr = INADDR_ANY;
+ }
+ address.sin_port = htons(m_port);
+
+ int optval = 1;
+ setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof optval);
+
+ int result = bind(m_lsd, (struct sockaddr*)&address, sizeof(address));
+ if (result != 0) {
+ ERROR("bind() failed: " << SocketStrerror());
+ return result;
+ }
+
+ result = listen(m_lsd, 5);
+ if (result != 0) {
+ ERROR("listen() failed: " << SocketStrerror());
+ return result;
+ }
+ m_listening = true;
+ return result;
+}
+
+void TCPAcceptor::shutdown() {
+ m_shutdown = true;
+#ifdef _WIN32
+ ::shutdown(m_lsd, SD_BOTH);
+
+ // this is ugly, but the easiest way to do this
+ // force wakeup of accept() with a non-blocking connect to ourselves
+ struct sockaddr_in address;
+
+ std::memset(&address, 0, sizeof(address));
+ address.sin_family = PF_INET;
+ llvm::SmallString<128> addr_copy;
+ if (m_address.size() > 0)
+ addr_copy = m_address;
+ else
+ addr_copy = "127.0.0.1";
+ addr_copy.push_back('\0');
+ int size = sizeof(address);
+ if (WSAStringToAddress(addr_copy.data(), PF_INET, nullptr,
+ (struct sockaddr*)&address, &size) != 0)
+ return;
+ address.sin_port = htons(m_port);
+
+ fd_set sdset;
+ struct timeval tv;
+ int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
+
+ // Set socket to non-blocking
+ u_long mode = 1;
+ ioctlsocket(sd, FIONBIO, &mode);
+
+ // Try to connect
+ ::connect(sd, (struct sockaddr*)&address, sizeof(address));
+
+ // Close
+ ::closesocket(sd);
+
+#else
+ ::shutdown(m_lsd, SHUT_RDWR);
+ int nullfd = ::open("/dev/null", O_RDONLY);
+ if (nullfd >= 0) {
+ ::dup2(nullfd, m_lsd);
+ ::close(nullfd);
+ }
+#endif
+}
+
+std::unique_ptr<NetworkStream> TCPAcceptor::accept() {
+ if (!m_listening || m_shutdown) return nullptr;
+
+ struct sockaddr_in address;
+#ifdef _WIN32
+ int len = sizeof(address);
+#else
+ socklen_t len = sizeof(address);
+#endif
+ std::memset(&address, 0, sizeof(address));
+ int sd = ::accept(m_lsd, (struct sockaddr*)&address, &len);
+ if (sd < 0) {
+ if (!m_shutdown) ERROR("accept() failed: " << SocketStrerror());
+ return nullptr;
+ }
+ if (m_shutdown) {
+#ifdef _WIN32
+ closesocket(sd);
+#else
+ close(sd);
+#endif
+ return nullptr;
+ }
+ return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
+}
diff --git a/src/tcpsockets/TCPAcceptor.h b/src/tcpsockets/TCPAcceptor.h
new file mode 100644
index 0000000..d6a6ccc
--- /dev/null
+++ b/src/tcpsockets/TCPAcceptor.h
@@ -0,0 +1,50 @@
+/*
+ TCPAcceptor.h
+
+ TCPAcceptor class interface. TCPAcceptor provides methods to passively
+ establish TCP/IP connections with clients.
+
+ ------------------------------------------
+
+ Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef TCPSOCKETS_TCPACCEPTOR_H_
+#define TCPSOCKETS_TCPACCEPTOR_H_
+
+#include <atomic>
+#include <memory>
+#include <string>
+
+#include "NetworkAcceptor.h"
+#include "TCPStream.h"
+
+class TCPAcceptor : public NetworkAcceptor {
+ int m_lsd;
+ int m_port;
+ std::string m_address;
+ bool m_listening;
+ std::atomic_bool m_shutdown;
+
+ public:
+ TCPAcceptor(int port, const char* address);
+ ~TCPAcceptor();
+
+ int start() override;
+ void shutdown() override;
+ std::unique_ptr<NetworkStream> accept() override;
+};
+
+#endif
diff --git a/src/tcpsockets/TCPConnector.cpp b/src/tcpsockets/TCPConnector.cpp
new file mode 100644
index 0000000..665500d
--- /dev/null
+++ b/src/tcpsockets/TCPConnector.cpp
@@ -0,0 +1,167 @@
+/*
+ TCPConnector.h
+
+ TCPConnector class definition. TCPConnector provides methods to actively
+ establish TCP/IP connections with a server.
+
+ ------------------------------------------
+
+ Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+*/
+
+#include "TCPConnector.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <cstdio>
+#include <cstring>
+#ifdef _WIN32
+#include <WinSock2.h>
+#include <WS2tcpip.h>
+#else
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#endif
+
+#include "TCPStream.h"
+
+#include "llvm/SmallString.h"
+#include "../Log.h"
+#include "SocketError.h"
+
+using namespace tcpsockets;
+
+static int ResolveHostName(const char* hostname, struct in_addr* addr) {
+ struct addrinfo hints;
+ struct addrinfo* res;
+
+ hints.ai_flags = 0;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ hints.ai_addrlen = 0;
+ hints.ai_addr = nullptr;
+ hints.ai_canonname = nullptr;
+ hints.ai_next = nullptr;
+ int result = getaddrinfo(hostname, nullptr, &hints, &res);
+ if (result == 0) {
+ std::memcpy(addr, &((struct sockaddr_in*)res->ai_addr)->sin_addr,
+ sizeof(struct in_addr));
+ freeaddrinfo(res);
+ }
+ return result;
+}
+
+std::unique_ptr<NetworkStream> TCPConnector::connect(const char* server,
+ int port, int timeout) {
+#ifdef _WIN32
+ struct WSAHelper {
+ WSAHelper() {
+ WSAData wsaData;
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ WSAStartup(wVersionRequested, &wsaData);
+ }
+ ~WSAHelper() { WSACleanup(); }
+ };
+ static WSAHelper helper;
+#endif
+ struct sockaddr_in address;
+
+ std::memset(&address, 0, sizeof(address));
+ address.sin_family = AF_INET;
+ if (ResolveHostName(server, &(address.sin_addr)) != 0) {
+#ifdef _WIN32
+ llvm::SmallString<128> addr_copy(server);
+ addr_copy.push_back('\0');
+ int size = sizeof(address);
+ if (WSAStringToAddress(addr_copy.data(), PF_INET, nullptr, (struct sockaddr*)&address, &size) != 0) {
+ ERROR("could not resolve " << server << " address");
+ return nullptr;
+ }
+#else
+ inet_pton(PF_INET, server, &(address.sin_addr));
+#endif
+ }
+ address.sin_port = htons(port);
+
+ if (timeout == 0) {
+ int sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (::connect(sd, (struct sockaddr*)&address, sizeof(address)) != 0) {
+ ERROR("connect() to " << server << " port " << port << " failed: " << SocketStrerror());
+ return nullptr;
+ }
+ return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
+ }
+
+ fd_set sdset;
+ struct timeval tv;
+ socklen_t len;
+ int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
+
+ // Set socket to non-blocking
+#ifdef _WIN32
+ u_long mode = 1;
+ ioctlsocket(sd, FIONBIO, &mode);
+#else
+ long arg;
+ arg = fcntl(sd, F_GETFL, nullptr);
+ arg |= O_NONBLOCK;
+ fcntl(sd, F_SETFL, arg);
+#endif
+
+ // Connect with time limit
+ if ((result = ::connect(sd, (struct sockaddr*)&address, sizeof(address))) <
+ 0) {
+ int my_errno = SocketErrno();
+#ifdef _WIN32
+ if (my_errno == WSAEWOULDBLOCK || my_errno == WSAEINPROGRESS) {
+#else
+ if (my_errno == EWOULDBLOCK || my_errno == EINPROGRESS) {
+#endif
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ FD_ZERO(&sdset);
+ FD_SET(sd, &sdset);
+ if (select(sd + 1, nullptr, &sdset, nullptr, &tv) > 0) {
+ len = sizeof(int);
+ getsockopt(sd, SOL_SOCKET, SO_ERROR, (char*)(&valopt), &len);
+ if (valopt) {
+ ERROR("select() to " << server << " port " << port << " error " << valopt << " - " << SocketStrerror(valopt));
+ }
+ // connection established
+ else
+ result = 0;
+ } else
+ INFO("connect() to " << server << " port " << port << " timed out");
+ } else
+ ERROR("connect() to " << server << " port " << port << " error " << SocketErrno() << " - " << SocketStrerror());
+ }
+
+ // Return socket to blocking mode
+#ifdef _WIN32
+ mode = 0;
+ ioctlsocket(sd, FIONBIO, &mode);
+#else
+ arg = fcntl(sd, F_GETFL, nullptr);
+ arg &= (~O_NONBLOCK);
+ fcntl(sd, F_SETFL, arg);
+#endif
+
+ // Create stream object if connected
+ if (result == -1) return nullptr;
+ return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
+}
diff --git a/src/tcpsockets/TCPConnector.h b/src/tcpsockets/TCPConnector.h
new file mode 100644
index 0000000..ebac859
--- /dev/null
+++ b/src/tcpsockets/TCPConnector.h
@@ -0,0 +1,37 @@
+/*
+ TCPConnector.h
+
+ TCPConnector class interface. TCPConnector provides methods to actively
+ establish TCP/IP connections with a server.
+
+ ------------------------------------------
+
+ Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+*/
+
+#ifndef TCPSOCKETS_TCPCONNECTOR_H_
+#define TCPSOCKETS_TCPCONNECTOR_H_
+
+#include <memory>
+
+#include "NetworkStream.h"
+
+class TCPConnector {
+ public:
+ static std::unique_ptr<NetworkStream> connect(const char* server, int port,
+ int timeout = 0);
+};
+
+#endif
diff --git a/src/tcpsockets/TCPStream.cpp b/src/tcpsockets/TCPStream.cpp
new file mode 100644
index 0000000..3149be6
--- /dev/null
+++ b/src/tcpsockets/TCPStream.cpp
@@ -0,0 +1,157 @@
+/*
+ TCPStream.h
+
+ TCPStream class definition. TCPStream provides methods to trasnfer
+ data between peers over a TCP/IP connection.
+
+ ------------------------------------------
+
+ Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "TCPStream.h"
+
+#ifdef _WIN32
+#include <WinSock2.h>
+#else
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#endif
+
+TCPStream::TCPStream(int sd, struct sockaddr_in* address) : m_sd(sd) {
+ char ip[50];
+#ifdef _WIN32
+ unsigned long size = sizeof(ip) - 1;
+ WSAAddressToString((struct sockaddr*)address, sizeof sockaddr_in, nullptr, ip, &size);
+#else
+ inet_ntop(PF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip,
+ sizeof(ip) - 1);
+#endif
+ m_peerIP = ip;
+ m_peerPort = ntohs(address->sin_port);
+}
+
+TCPStream::~TCPStream() { close(); }
+
+std::size_t TCPStream::send(const char* buffer, std::size_t len, Error* err) {
+ if (m_sd < 0) {
+ *err = kConnectionClosed;
+ return 0;
+ }
+#ifdef _WIN32
+ WSABUF wsaBuf;
+ wsaBuf.buf = const_cast<char*>(buffer);
+ wsaBuf.len = (ULONG)len;
+ DWORD rv;
+ bool result = true;
+ while (WSASend(m_sd, &wsaBuf, 1, &rv, 0, nullptr, nullptr) == SOCKET_ERROR) {
+ if (WSAGetLastError() != WSAEWOULDBLOCK) {
+ result = false;
+ break;
+ }
+ Sleep(1);
+ }
+ if (!result) {
+ char Buffer[128];
+#ifdef _MSC_VER
+ sprintf_s(Buffer, "Send() failed: WSA error=%d\n", WSAGetLastError());
+#else
+ std::snprintf(Buffer, 128, "Send() failed: WSA error=%d\n", WSAGetLastError());
+#endif
+ OutputDebugStringA(Buffer);
+ *err = kConnectionReset;
+ return 0;
+ }
+#else
+ ssize_t rv = write(m_sd, buffer, len);
+ if (rv < 0) {
+ *err = kConnectionReset;
+ return 0;
+ }
+#endif
+ return static_cast<std::size_t>(rv);
+}
+
+std::size_t TCPStream::receive(char* buffer, std::size_t len, Error* err,
+ int timeout) {
+ if (m_sd < 0) {
+ *err = kConnectionClosed;
+ return 0;
+ }
+#ifdef _WIN32
+ int rv;
+#else
+ ssize_t rv;
+#endif
+ if (timeout <= 0) {
+#ifdef _WIN32
+ rv = recv(m_sd, buffer, len, 0);
+#else
+ rv = read(m_sd, buffer, len);
+#endif
+ }
+ else if (WaitForReadEvent(timeout)) {
+#ifdef _WIN32
+ rv = recv(m_sd, buffer, len, 0);
+#else
+ rv = read(m_sd, buffer, len);
+#endif
+ } else {
+ *err = kConnectionTimedOut;
+ return 0;
+ }
+ if (rv < 0) {
+ *err = kConnectionReset;
+ return 0;
+ }
+ return static_cast<std::size_t>(rv);
+}
+
+void TCPStream::close() {
+ if (m_sd >= 0) {
+#ifdef _WIN32
+ ::shutdown(m_sd, SD_BOTH);
+ closesocket(m_sd);
+#else
+ ::shutdown(m_sd, SHUT_RDWR);
+ ::close(m_sd);
+#endif
+ }
+ m_sd = -1;
+}
+
+llvm::StringRef TCPStream::getPeerIP() const { return m_peerIP; }
+
+int TCPStream::getPeerPort() const { return m_peerPort; }
+
+void TCPStream::setNoDelay() {
+ int optval = 1;
+ setsockopt(m_sd, IPPROTO_TCP, TCP_NODELAY, (char*)&optval, sizeof optval);
+}
+
+bool TCPStream::WaitForReadEvent(int timeout) {
+ fd_set sdset;
+ struct timeval tv;
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ FD_ZERO(&sdset);
+ FD_SET(m_sd, &sdset);
+ if (select(m_sd + 1, &sdset, NULL, NULL, &tv) > 0) {
+ return true;
+ }
+ return false;
+}
diff --git a/src/tcpsockets/TCPStream.h b/src/tcpsockets/TCPStream.h
new file mode 100644
index 0000000..21ef6fd
--- /dev/null
+++ b/src/tcpsockets/TCPStream.h
@@ -0,0 +1,67 @@
+/*
+ TCPStream.h
+
+ TCPStream class interface. TCPStream provides methods to trasnfer
+ data between peers over a TCP/IP connection.
+
+ ------------------------------------------
+
+ Copyright © 2013 [Vic Hargrave - http://vichargrave.com]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef TCPSOCKETS_TCPSTREAM_H_
+#define TCPSOCKETS_TCPSTREAM_H_
+
+#include <cstddef>
+#include <string>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#endif
+
+#include "NetworkStream.h"
+
+class TCPStream : public NetworkStream {
+ int m_sd;
+ std::string m_peerIP;
+ int m_peerPort;
+
+ public:
+ friend class TCPAcceptor;
+ friend class TCPConnector;
+
+ ~TCPStream();
+
+ std::size_t send(const char* buffer, std::size_t len, Error* err) override;
+ std::size_t receive(char* buffer, std::size_t len, Error* err,
+ int timeout = 0) override;
+ void close() override;
+
+ llvm::StringRef getPeerIP() const override;
+ int getPeerPort() const override;
+ void setNoDelay() override;
+
+ TCPStream(const TCPStream& stream) = delete;
+ TCPStream& operator=(const TCPStream&) = delete;
+ private:
+ bool WaitForReadEvent(int timeout);
+
+ TCPStream(int sd, struct sockaddr_in* address);
+ TCPStream() = delete;
+};
+
+#endif