ConnectionProvider.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /***************************************************************************
  2. *
  3. * Project _____ __ ____ _ _
  4. * ( _ ) /__\ (_ _)_| |_ _| |_
  5. * )(_)( /(__)\ )( (_ _)(_ _)
  6. * (_____)(__)(__)(__) |_| |_|
  7. *
  8. *
  9. * Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
  10. *
  11. * Licensed under the Apache License, Version 2.0 (the "License");
  12. * you may not use this file except in compliance with the License.
  13. * You may obtain a copy of the License at
  14. *
  15. * http://www.apache.org/licenses/LICENSE-2.0
  16. *
  17. * Unless required by applicable law or agreed to in writing, software
  18. * distributed under the License is distributed on an "AS IS" BASIS,
  19. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  20. * See the License for the specific language governing permissions and
  21. * limitations under the License.
  22. *
  23. ***************************************************************************/
  24. #include "./ConnectionProvider.hpp"
  25. #include "oatpp/core/utils/ConversionUtils.hpp"
  26. #include <fcntl.h>
  27. #if defined(WIN32) || defined(_WIN32)
  28. #include <io.h>
  29. #include <winsock2.h>
  30. #include <ws2tcpip.h>
  31. #else
  32. #include <netdb.h>
  33. #include <arpa/inet.h>
  34. #include <sys/socket.h>
  35. #include <netinet/tcp.h>
  36. #include <unistd.h>
  37. #if defined(__FreeBSD__)
  38. #include <netinet/in.h>
  39. #endif
  40. #endif
  41. // Workaround for MinGW from: https://www.mail-archive.com/users@ipv6.org/msg02107.html
  42. #if defined(__MINGW32__) && _WIN32_WINNT < 0x0600
  43. const char * inet_ntop (int af, const void *src, char *dst, socklen_t cnt) {
  44. if (af == AF_INET) {
  45. struct sockaddr_in in;
  46. memset (&in, 0, sizeof(in));
  47. in.sin_family = AF_INET;
  48. memcpy (&in.sin_addr, src, sizeof(struct in_addr));
  49. getnameinfo ((struct sockaddr *)&in, sizeof (struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
  50. return dst;
  51. } else if (af == AF_INET6) {
  52. struct sockaddr_in6 in;
  53. memset (&in, 0, sizeof(in));
  54. in.sin6_family = AF_INET6;
  55. memcpy (&in.sin6_addr, src, sizeof(struct in_addr6));
  56. getnameinfo ((struct sockaddr *)&in, sizeof (struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
  57. return dst;
  58. }
  59. return NULL;
  60. }
  61. #endif
  62. namespace oatpp { namespace network { namespace tcp { namespace server {
  63. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  64. // ExtendedConnection
  65. const char* const ConnectionProvider::ExtendedConnection::PROPERTY_PEER_ADDRESS = "peer_address";
  66. const char* const ConnectionProvider::ExtendedConnection::PROPERTY_PEER_ADDRESS_FORMAT = "peer_address_format";
  67. const char* const ConnectionProvider::ExtendedConnection::PROPERTY_PEER_PORT = "peer_port";
  68. ConnectionProvider::ExtendedConnection::ExtendedConnection(v_io_handle handle, data::stream::Context::Properties&& properties)
  69. : Connection(handle)
  70. , m_context(data::stream::StreamType::STREAM_INFINITE, std::forward<data::stream::Context::Properties>(properties))
  71. {}
  72. oatpp::data::stream::Context& ConnectionProvider::ExtendedConnection::getOutputStreamContext() {
  73. return m_context;
  74. }
  75. oatpp::data::stream::Context& ConnectionProvider::ExtendedConnection::getInputStreamContext() {
  76. return m_context;
  77. }
  78. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  79. // ConnectionProvider::ConnectionInvalidator
  80. void ConnectionProvider::ConnectionInvalidator::invalidate(const std::shared_ptr<data::stream::IOStream>& connection) {
  81. /************************************************
  82. * WARNING!!!
  83. *
  84. * shutdown(handle, SHUT_RDWR) <--- DO!
  85. * close(handle); <--- DO NOT!
  86. *
  87. * DO NOT CLOSE file handle here -
  88. * USE shutdown instead.
  89. * Using close prevent FDs popping out of epoll,
  90. * and they'll be stuck there forever.
  91. ************************************************/
  92. auto c = std::static_pointer_cast<network::tcp::Connection>(connection);
  93. v_io_handle handle = c->getHandle();
  94. #if defined(WIN32) || defined(_WIN32)
  95. shutdown(handle, SD_BOTH);
  96. #else
  97. shutdown(handle, SHUT_RDWR);
  98. #endif
  99. }
  100. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  101. // ConnectionProvider
  102. ConnectionProvider::ConnectionProvider(const network::Address& address, bool useExtendedConnections)
  103. : m_invalidator(std::make_shared<ConnectionInvalidator>())
  104. , m_address(address)
  105. , m_closed(false)
  106. , m_useExtendedConnections(useExtendedConnections)
  107. {
  108. setProperty(PROPERTY_HOST, m_address.host);
  109. setProperty(PROPERTY_PORT, oatpp::utils::conversion::int32ToStr(m_address.port));
  110. m_serverHandle = instantiateServer();
  111. }
  112. ConnectionProvider::~ConnectionProvider() {
  113. stop();
  114. }
  115. void ConnectionProvider::stop() {
  116. if(!m_closed) {
  117. m_closed = true;
  118. #if defined(WIN32) || defined(_WIN32)
  119. ::closesocket(m_serverHandle);
  120. #else
  121. ::close(m_serverHandle);
  122. #endif
  123. }
  124. }
  125. #if defined(WIN32) || defined(_WIN32)
  126. oatpp::v_io_handle ConnectionProvider::instantiateServer(){
  127. SOCKET serverHandle = INVALID_SOCKET;
  128. struct addrinfo *result = nullptr;
  129. struct addrinfo hints;
  130. ZeroMemory(&hints, sizeof(hints));
  131. hints.ai_socktype = SOCK_STREAM;
  132. hints.ai_protocol = 0;
  133. hints.ai_flags = AI_PASSIVE;
  134. switch(m_address.family) {
  135. case Address::IP_4: hints.ai_family = AF_INET; break;
  136. case Address::IP_6: hints.ai_family = AF_INET6; break;
  137. default:
  138. hints.ai_family = AF_UNSPEC;
  139. }
  140. auto portStr = oatpp::utils::conversion::int32ToStr(m_address.port);
  141. const int iResult = getaddrinfo(m_address.host->c_str(), portStr->c_str(), &hints, &result);
  142. if (iResult != 0) {
  143. OATPP_LOGE("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]", "Error. Call to getaddrinfo() failed with result=%d", iResult);
  144. throw std::runtime_error("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]: Error. Call to getaddrinfo() failed.");
  145. }
  146. struct addrinfo* currResult = result;
  147. while(currResult != nullptr) {
  148. serverHandle = socket(currResult->ai_family, currResult->ai_socktype, currResult->ai_protocol);
  149. if (serverHandle != INVALID_SOCKET) {
  150. int no = 0;
  151. if (hints.ai_family == AF_UNSPEC || hints.ai_family == Address::IP_6) {
  152. if (setsockopt(serverHandle, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof( int ) ) != 0 ) {
  153. OATPP_LOGW("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]",
  154. "Warning. Failed to set %s for accepting socket: %s", "IPV6_V6ONLY",
  155. strerror(errno));
  156. }
  157. }
  158. if (bind(serverHandle, currResult->ai_addr, (int) currResult->ai_addrlen) != SOCKET_ERROR &&
  159. listen(serverHandle, SOMAXCONN) != SOCKET_ERROR)
  160. {
  161. break;
  162. }
  163. closesocket(serverHandle);
  164. }
  165. currResult = currResult->ai_next;
  166. }
  167. freeaddrinfo(result);
  168. if (currResult == nullptr) {
  169. OATPP_LOGE("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]",
  170. "Error. Couldn't bind. WSAGetLastError=%ld", WSAGetLastError());
  171. throw std::runtime_error("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]: "
  172. "Error. Couldn't bind ");
  173. }
  174. u_long flags = 1;
  175. if(NO_ERROR != ioctlsocket(serverHandle, FIONBIO, &flags)) {
  176. throw std::runtime_error("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]: Error. Call to ioctlsocket failed.");
  177. }
  178. // Update port after binding (typicaly in case of port = 0)
  179. struct ::sockaddr_in s_in;
  180. ::memset(&s_in, 0, sizeof(s_in));
  181. ::socklen_t s_in_len = sizeof(s_in);
  182. ::getsockname(serverHandle, (struct sockaddr *)&s_in, &s_in_len);
  183. setProperty(PROPERTY_PORT, oatpp::utils::conversion::int32ToStr(ntohs(s_in.sin_port)));
  184. return serverHandle;
  185. }
  186. #else
  187. oatpp::v_io_handle ConnectionProvider::instantiateServer(){
  188. oatpp::v_io_handle serverHandle;
  189. v_int32 ret;
  190. int yes = 1;
  191. struct addrinfo *result = NULL;
  192. struct addrinfo hints;
  193. memset(&hints, 0, sizeof(hints));
  194. hints.ai_socktype = SOCK_STREAM;
  195. hints.ai_protocol = 0;
  196. hints.ai_flags = AI_PASSIVE;
  197. switch(m_address.family) {
  198. case Address::IP_4: hints.ai_family = AF_INET; break;
  199. case Address::IP_6: hints.ai_family = AF_INET6; break;
  200. default:
  201. hints.ai_family = AF_UNSPEC;
  202. }
  203. auto portStr = oatpp::utils::conversion::int32ToStr(m_address.port);
  204. ret = getaddrinfo(m_address.host->c_str(), portStr->c_str(), &hints, &result);
  205. if (ret != 0) {
  206. OATPP_LOGE("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]", "Error. Call to getaddrinfo() failed with result=%d: %s", ret, strerror(errno));
  207. throw std::runtime_error("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]: Error. Call to getaddrinfo() failed.");
  208. }
  209. struct addrinfo* currResult = result;
  210. while(currResult != nullptr) {
  211. serverHandle = socket(currResult->ai_family, currResult->ai_socktype, currResult->ai_protocol);
  212. if (serverHandle >= 0) {
  213. if (setsockopt(serverHandle, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0) {
  214. OATPP_LOGW("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]",
  215. "Warning. Failed to set %s for accepting socket: %s", "SO_REUSEADDR", strerror(errno));
  216. }
  217. if (bind(serverHandle, currResult->ai_addr, (int) currResult->ai_addrlen) == 0 &&
  218. listen(serverHandle, 10000) == 0)
  219. {
  220. break;
  221. }
  222. ::close(serverHandle);
  223. }
  224. currResult = currResult->ai_next;
  225. }
  226. freeaddrinfo(result);
  227. if (currResult == nullptr) {
  228. std::string err = strerror(errno);
  229. OATPP_LOGE("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]",
  230. "Error. Couldn't bind. %s", err.c_str());
  231. throw std::runtime_error("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]: "
  232. "Error. Couldn't bind " + err);
  233. }
  234. fcntl(serverHandle, F_SETFL, O_NONBLOCK);
  235. // Update port after binding (typicaly in case of port = 0)
  236. struct ::sockaddr_in s_in;
  237. ::memset(&s_in, 0, sizeof(s_in));
  238. ::socklen_t s_in_len = sizeof(s_in);
  239. ::getsockname(serverHandle, (struct sockaddr *)&s_in, &s_in_len);
  240. setProperty(PROPERTY_PORT, oatpp::utils::conversion::int32ToStr(ntohs(s_in.sin_port)));
  241. return serverHandle;
  242. }
  243. #endif
  244. bool ConnectionProvider::prepareConnectionHandle(oatpp::v_io_handle handle) {
  245. if (handle < 0) {
  246. v_int32 error = errno;
  247. if(error == EAGAIN || error == EWOULDBLOCK){
  248. return false;
  249. } else {
  250. if(!m_closed) { // m_serverHandle==0 if ConnectionProvider was closed. Not an error.
  251. OATPP_LOGD("[oatpp::network::tcp::server::ConnectionProvider::prepareConnectionHandle()]", "Error. %d", error);
  252. }
  253. return false;
  254. }
  255. }
  256. #ifdef SO_NOSIGPIPE
  257. int yes = 1;
  258. v_int32 ret = setsockopt(handle, SOL_SOCKET, SO_NOSIGPIPE, &yes, sizeof(int));
  259. if(ret < 0) {
  260. OATPP_LOGD("[oatpp::network::tcp::server::ConnectionProvider::prepareConnectionHandle()]", "Warning. Failed to set %s for socket", "SO_NOSIGPIPE");
  261. }
  262. #endif
  263. return true;
  264. }
  265. provider::ResourceHandle<data::stream::IOStream> ConnectionProvider::getDefaultConnection() {
  266. oatpp::v_io_handle handle = accept(m_serverHandle, nullptr, nullptr);
  267. if(!oatpp::isValidIOHandle(handle)) {
  268. return nullptr;
  269. }
  270. if(prepareConnectionHandle(handle)) {
  271. return provider::ResourceHandle<data::stream::IOStream>(
  272. std::make_shared<Connection>(handle),
  273. m_invalidator
  274. );
  275. }
  276. return nullptr;
  277. }
  278. provider::ResourceHandle<data::stream::IOStream> ConnectionProvider::getExtendedConnection() {
  279. struct sockaddr_storage clientAddress;
  280. socklen_t clientAddressSize = sizeof(clientAddress);
  281. data::stream::Context::Properties properties;
  282. oatpp::v_io_handle handle = accept(m_serverHandle, (struct sockaddr*) &clientAddress, &clientAddressSize);
  283. if(!oatpp::isValidIOHandle(handle)) {
  284. return nullptr;
  285. }
  286. if (clientAddress.ss_family == AF_INET) {
  287. char strIp[INET_ADDRSTRLEN];
  288. struct sockaddr_in* sockAddress = (struct sockaddr_in*) &clientAddress;
  289. inet_ntop(AF_INET, &sockAddress->sin_addr, strIp, INET_ADDRSTRLEN);
  290. properties.put_LockFree(ExtendedConnection::PROPERTY_PEER_ADDRESS, oatpp::String((const char*) strIp));
  291. properties.put_LockFree(ExtendedConnection::PROPERTY_PEER_ADDRESS_FORMAT, "ipv4");
  292. properties.put_LockFree(ExtendedConnection::PROPERTY_PEER_PORT, oatpp::utils::conversion::int32ToStr(sockAddress->sin_port));
  293. } else if (clientAddress.ss_family == AF_INET6) {
  294. char strIp[INET6_ADDRSTRLEN];
  295. struct sockaddr_in6* sockAddress = (struct sockaddr_in6*) &clientAddress;
  296. inet_ntop(AF_INET6, &sockAddress->sin6_addr, strIp, INET6_ADDRSTRLEN);
  297. properties.put_LockFree(ExtendedConnection::PROPERTY_PEER_ADDRESS, oatpp::String((const char*) strIp));
  298. properties.put_LockFree(ExtendedConnection::PROPERTY_PEER_ADDRESS_FORMAT, "ipv6");
  299. properties.put_LockFree(ExtendedConnection::PROPERTY_PEER_PORT, oatpp::utils::conversion::int32ToStr(sockAddress->sin6_port));
  300. } else {
  301. #if defined(WIN32) || defined(_WIN32)
  302. ::closesocket(handle);
  303. #else
  304. ::close(handle);
  305. #endif
  306. OATPP_LOGE("[oatpp::network::tcp::server::ConnectionProvider::getExtendedConnection()]", "Error. Unknown address family.");
  307. return nullptr;
  308. }
  309. if(prepareConnectionHandle(handle)) {
  310. return provider::ResourceHandle<data::stream::IOStream>(
  311. std::make_shared<ExtendedConnection>(handle, std::move(properties)),
  312. m_invalidator
  313. );
  314. }
  315. return nullptr;
  316. }
  317. provider::ResourceHandle<oatpp::data::stream::IOStream> ConnectionProvider::get() {
  318. fd_set set;
  319. struct timeval timeout;
  320. FD_ZERO(&set);
  321. FD_SET(m_serverHandle, &set);
  322. timeout.tv_sec = 1;
  323. timeout.tv_usec = 0;
  324. while(!m_closed) {
  325. auto res = select(int(m_serverHandle + 1), &set, nullptr, nullptr, &timeout);
  326. if (res >= 0) {
  327. break;
  328. }
  329. }
  330. if(m_useExtendedConnections) {
  331. return getExtendedConnection();
  332. }
  333. return getDefaultConnection();
  334. }
  335. }}}}