Make comparison IR ops layout agnostic
[hiphop-php.git] / hphp / runtime / base / socket.cpp
blob110a3a295cfb4526d61184dc6b7dbb6c56dade00
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/socket.h"
18 #include <folly/portability/Fcntl.h>
19 #include <folly/portability/Sockets.h>
21 #include "hphp/runtime/base/request-info.h"
22 #include "hphp/runtime/base/exceptions.h"
23 #include "hphp/runtime/base/runtime-option.h"
24 #include "hphp/runtime/server/server-stats.h"
25 #include "hphp/util/logger.h"
26 #include "hphp/util/rds-local.h"
28 namespace HPHP {
29 ///////////////////////////////////////////////////////////////////////////////
31 RDS_LOCAL(int, Socket::s_lastErrno);
33 ///////////////////////////////////////////////////////////////////////////////
34 // constructors and destructor
36 SocketData::SocketData(int port, int type, bool nonblocking)
37 : FileData(nonblocking)
38 , m_port(port)
39 , m_type(type)
42 bool SocketData::closeImpl() {
43 *s_pcloseRet = 0;
44 if (valid() && !isClosed()) {
45 *s_pcloseRet = ::close(getFd());
46 setIsClosed(true);
47 setFd(-1);
49 return FileData::closeImpl();
52 SocketData::~SocketData() {
53 SocketData::closeImpl();
56 Socket::Socket(bool nonblocking /* = true*/)
57 : File(std::make_shared<SocketData>(0, -1, nonblocking)),
58 m_data(static_cast<SocketData*>(getFileData()))
62 Socket::Socket(std::shared_ptr<SocketData> data)
63 : File(data),
64 m_data(static_cast<SocketData*>(getFileData()))
66 assertx(data);
67 inferStreamType();
70 Socket::Socket(std::shared_ptr<SocketData> data, int sockfd, int type,
71 const char* address /* = NULL */, int /*port*/ /* = 0 */,
72 double timeout /* = 0 */,
73 const StaticString& streamType /* = empty_string_ref */)
74 : File(data, null_string, streamType), m_data(data.get()) {
75 if (address) m_data->m_address = address;
76 setFd(sockfd);
78 struct timeval tv;
79 if (timeout <= 0) {
80 tv.tv_sec = RequestInfo::s_requestInfo.getNoCheck()->
81 m_reqInjectionData.getSocketDefaultTimeout();
82 tv.tv_usec = 0;
83 } else {
84 tv.tv_sec = (int)timeout;
85 tv.tv_usec = (timeout - tv.tv_sec) * 1e6;
87 setsockopt(getFd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
88 *s_lastErrno = errno;
89 setsockopt(getFd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
90 *s_lastErrno = errno;
91 internalSetTimeout(tv);
92 setIsLocal(type == AF_UNIX);
94 // Attempt to infer stream type only if it was not explicitly specified.
95 inferStreamType();
98 Socket::Socket(int sockfd, int type, const char *address /* = NULL */,
99 int port /* = 0 */, double timeout /* = 0 */,
100 const StaticString& streamType /* = empty_string_ref */,
101 bool nonblocking /* = true */)
102 : Socket(
103 std::make_shared<SocketData>(port, type, nonblocking),
104 sockfd, type, address, port, timeout, streamType)
107 Socket::~Socket() { }
109 void Socket::sweep() {
110 Socket::closeImpl();
111 File::sweep();
112 m_data = nullptr;
115 ///////////////////////////////////////////////////////////////////////////////
117 void Socket::setError(int err) {
118 *s_lastErrno = m_data->m_error = err;
121 bool Socket::open(const String& /*filename*/, const String& /*mode*/) {
122 throw_not_supported(__func__, "cannot open socket this way");
125 bool Socket::close() {
126 return closeImpl();
129 void Socket::internalSetTimeout(struct timeval &tv) {
130 if (tv.tv_sec >= 0 && tv.tv_usec >= 0) {
131 m_data->m_timeout = tv.tv_sec * 1000000 + tv.tv_usec;
132 } else {
133 m_data->m_timeout = 0;
137 bool Socket::checkLiveness() {
138 if (getFd() == -1 || m_data->m_timedOut) {
139 return false;
142 pollfd p;
143 p.fd = getFd();
144 p.events = POLLIN | POLLERR | POLLHUP | POLLPRI;
145 p.revents = 0;
146 if (poll(&p, 1, 0) > 0 && p.revents > 0) {
147 char buf;
148 int64_t ret = recv(getFd(), &buf, sizeof(buf), MSG_PEEK);
149 if (ret == 0 || (ret == -1 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)) {
150 return false;
154 return true;
157 bool Socket::waitForData() {
158 m_data->m_timedOut = false;
159 while (true) {
160 struct pollfd fds[1];
161 fds[0].fd = getFd();
162 fds[0].events = POLLIN|POLLERR|POLLHUP;
163 if (poll(fds, 1, m_data->m_timeout / 1000)) {
164 socklen_t lon = sizeof(int);
165 int valopt;
166 getsockopt(getFd(), SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
167 if (valopt == EINTR) continue;
168 return valopt == 0;
169 } else {
170 m_data->m_timedOut = true;
171 return true;
174 return false;
177 int64_t Socket::readImpl(char *buffer, int64_t length) {
178 assertx(getFd());
179 assertx(length > 0);
181 IOStatusHelper io("socket::recv", m_data->m_address.c_str(), m_data->m_port);
183 int recvFlags = 0;
184 if (m_data->m_timeout > 0) {
185 int flags = fcntl(getFd(), F_GETFL, 0);
186 if ((flags & O_NONBLOCK) == 0) {
187 if (!waitForData()) {
188 setEof(true);
189 return 0;
191 recvFlags = MSG_DONTWAIT; // polled, so no need to wait any more
195 int64_t ret = recv(getFd(), buffer, length, recvFlags);
196 if (ret == 0 || (ret == -1 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)) {
197 setEof(true);
199 return (ret < 0) ? 0 : ret;
202 int64_t Socket::writeImpl(const char *buffer, int64_t length) {
203 assertx(getFd());
204 assertx(length > 0);
205 setEof(false);
206 IOStatusHelper io("socket::send", m_data->m_address.c_str(), m_data->m_port);
207 int64_t ret = send(getFd(), buffer, length, 0);
208 if (ret >= 0) {
209 m_data->m_bytesSent += ret;
211 return ret;
214 bool Socket::eof() {
215 if (!getEof() && valid() && bufferedLen() == 0) {
216 // Test if stream is EOF if the flag is not already set.
217 // Attempt to peek at one byte from the stream, checking for:
218 // i) recv() closing gracefully, or
219 // ii) recv() failed due to no waiting data on non-blocking socket.
220 char ch;
221 int64_t ret = recv(getFd(), &ch, 1, MSG_PEEK | MSG_DONTWAIT);
222 if (ret == 0 || (ret == -1 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)) {
223 setEof(true);
226 return getEof();
229 const StaticString
230 s_timed_out("timed_out"),
231 s_blocked("blocked");
233 Array Socket::getMetaData() {
234 Array ret = File::getMetaData();
235 ret.set(s_timed_out, m_data->m_timedOut);
236 ret.set(s_blocked, (fcntl(getFd(), F_GETFL, 0) & O_NONBLOCK) == 0);
237 return ret;
240 int64_t Socket::tell() {
241 return 0;
244 static const StaticString
245 s_tcp_socket("tcp_socket"),
246 s_tcp_socket_ssl("tcp_socket/ssl"),
247 s_udp_socket("udp_socket"),
248 s_unix_socket("unix_socket"),
249 s_udg_socket("udg_socket");
251 // Tries to infer stream type based on getsockopt() values.
252 // If no conclusive match can be found, leaves m_streamType
253 // as its default value of empty_string_ref.
254 void Socket::inferStreamType() {
255 if (empty_string() == getStreamType()) {
256 int result, type;
257 socklen_t len = sizeof(type);
258 result = getsockopt(getFd(), SOL_SOCKET, SO_TYPE, &type, &len);
259 if (result != 0) {
260 // getsockopt error.
261 // Nothing to do here because the default stream type is empty_string_ref.
262 return;
265 if (m_data->m_type == AF_INET || m_data->m_type == AF_INET6) {
266 if (type == SOCK_STREAM) {
267 setStreamType(s_tcp_socket);
268 } else if (type == SOCK_DGRAM) {
269 setStreamType(s_udp_socket);
271 } else if (m_data->m_type == AF_UNIX) {
272 if (type == SOCK_STREAM) {
273 setStreamType(s_unix_socket);
274 } else if (type == SOCK_DGRAM) {
275 setStreamType(s_udg_socket);
279 // Nothing to do in default case because the default stream type is
280 // empty_string_ref.
284 ///////////////////////////////////////////////////////////////////////////////