Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / socket.cpp
blob198a05e75c8d5feaa17fae90e07a551c0c1cd299
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-event-handler.h"
22 #include "hphp/runtime/base/thread-info.h"
23 #include "hphp/runtime/base/exceptions.h"
24 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/server/server-stats.h"
26 #include "hphp/runtime/base/request-local.h"
27 #include "hphp/util/logger.h"
29 namespace HPHP {
30 ///////////////////////////////////////////////////////////////////////////////
32 __thread int Socket::s_lastErrno;
34 ///////////////////////////////////////////////////////////////////////////////
35 // constructors and destructor
37 SocketData::SocketData(int port, int type, bool nonblocking)
38 : FileData(nonblocking)
39 , m_port(port)
40 , m_type(type)
43 bool SocketData::closeImpl() {
44 s_pcloseRet = 0;
45 if (valid() && !isClosed()) {
46 s_pcloseRet = ::close(getFd());
47 setIsClosed(true);
48 setFd(-1);
50 return FileData::closeImpl();
53 SocketData::~SocketData() {
54 SocketData::closeImpl();
57 Socket::Socket(bool nonblocking /* = true*/)
58 : File(std::make_shared<SocketData>(0, -1, nonblocking)),
59 m_data(static_cast<SocketData*>(getFileData()))
63 Socket::Socket(std::shared_ptr<SocketData> data)
64 : File(data),
65 m_data(static_cast<SocketData*>(getFileData()))
67 assert(data);
68 inferStreamType();
71 Socket::Socket(std::shared_ptr<SocketData> data, int sockfd, int type,
72 const char* address /* = NULL */, int /*port*/ /* = 0 */,
73 double timeout /* = 0 */,
74 const StaticString& streamType /* = empty_string_ref */)
75 : File(data, null_string, streamType), m_data(data.get()) {
76 if (address) m_data->m_address = address;
77 setFd(sockfd);
79 struct timeval tv;
80 if (timeout <= 0) {
81 tv.tv_sec = ThreadInfo::s_threadInfo.getNoCheck()->
82 m_reqInjectionData.getSocketDefaultTimeout();
83 tv.tv_usec = 0;
84 } else {
85 tv.tv_sec = (int)timeout;
86 tv.tv_usec = (timeout - tv.tv_sec) * 1e6;
88 setsockopt(getFd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
89 s_lastErrno = errno;
90 setsockopt(getFd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
91 s_lastErrno = errno;
92 internalSetTimeout(tv);
93 setIsLocal(type == AF_UNIX);
95 // Attempt to infer stream type only if it was not explicitly specified.
96 inferStreamType();
99 Socket::Socket(int sockfd, int type, const char *address /* = NULL */,
100 int port /* = 0 */, double timeout /* = 0 */,
101 const StaticString& streamType /* = empty_string_ref */,
102 bool nonblocking /* = true */)
103 : Socket(
104 std::make_shared<SocketData>(port, type, nonblocking),
105 sockfd, type, address, port, timeout, streamType)
108 Socket::~Socket() { }
110 void Socket::sweep() {
111 Socket::closeImpl();
112 File::sweep();
113 m_data = nullptr;
116 ///////////////////////////////////////////////////////////////////////////////
118 void Socket::setError(int err) {
119 s_lastErrno = m_data->m_error = err;
122 bool Socket::open(const String& /*filename*/, const String& /*mode*/) {
123 throw_not_supported(__func__, "cannot open socket this way");
126 bool Socket::close() {
127 invokeFiltersOnClose();
128 return closeImpl();
131 void Socket::internalSetTimeout(struct timeval &tv) {
132 if (tv.tv_sec >= 0 && tv.tv_usec >= 0) {
133 m_data->m_timeout = tv.tv_sec * 1000000 + tv.tv_usec;
134 } else {
135 m_data->m_timeout = 0;
139 bool Socket::checkLiveness() {
140 if (getFd() == -1 || m_data->m_timedOut) {
141 return false;
144 pollfd p;
145 p.fd = getFd();
146 p.events = POLLIN | POLLERR | POLLHUP | POLLPRI;
147 p.revents = 0;
148 if (poll(&p, 1, 0) > 0 && p.revents > 0) {
149 char buf;
150 int64_t ret = recv(getFd(), &buf, sizeof(buf), MSG_PEEK);
151 if (ret == 0 || (ret == -1 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)) {
152 return false;
156 return true;
159 bool Socket::waitForData() {
160 m_data->m_timedOut = false;
161 while (true) {
162 struct pollfd fds[1];
163 fds[0].fd = getFd();
164 fds[0].events = POLLIN|POLLERR|POLLHUP;
165 if (poll(fds, 1, m_data->m_timeout / 1000)) {
166 socklen_t lon = sizeof(int);
167 int valopt;
168 getsockopt(getFd(), SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
169 if (valopt == EINTR) continue;
170 return valopt == 0;
171 } else {
172 m_data->m_timedOut = true;
173 return true;
176 return false;
179 int64_t Socket::readImpl(char *buffer, int64_t length) {
180 assert(getFd());
181 assert(length > 0);
183 IOStatusHelper io("socket::recv", m_data->m_address.c_str(), m_data->m_port);
185 int recvFlags = 0;
186 if (m_data->m_timeout > 0) {
187 int flags = fcntl(getFd(), F_GETFL, 0);
188 if ((flags & O_NONBLOCK) == 0) {
189 if (!waitForData()) {
190 setEof(true);
191 return 0;
193 recvFlags = MSG_DONTWAIT; // polled, so no need to wait any more
197 int64_t ret = recv(getFd(), buffer, length, recvFlags);
198 if (ret == 0 || (ret == -1 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)) {
199 setEof(true);
201 return (ret < 0) ? 0 : ret;
204 int64_t Socket::writeImpl(const char *buffer, int64_t length) {
205 assert(getFd());
206 assert(length > 0);
207 setEof(false);
208 IOStatusHelper io("socket::send", m_data->m_address.c_str(), m_data->m_port);
209 int64_t ret = send(getFd(), buffer, length, 0);
210 if (ret >= 0) {
211 m_data->m_bytesSent += ret;
213 return ret;
216 bool Socket::eof() {
217 if (!getEof() && valid() && bufferedLen() == 0) {
218 // Test if stream is EOF if the flag is not already set.
219 // Attempt to peek at one byte from the stream, checking for:
220 // i) recv() closing gracefully, or
221 // ii) recv() failed due to no waiting data on non-blocking socket.
222 char ch;
223 int64_t ret = recv(getFd(), &ch, 1, MSG_PEEK | MSG_DONTWAIT);
224 if (ret == 0 || (ret == -1 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)) {
225 setEof(true);
228 return getEof();
231 const StaticString
232 s_timed_out("timed_out"),
233 s_blocked("blocked");
235 Array Socket::getMetaData() {
236 Array ret = File::getMetaData();
237 ret.set(s_timed_out, m_data->m_timedOut);
238 ret.set(s_blocked, (fcntl(getFd(), F_GETFL, 0) & O_NONBLOCK) == 0);
239 return ret;
242 int64_t Socket::tell() {
243 return 0;
246 static const StaticString
247 s_tcp_socket("tcp_socket"),
248 s_tcp_socket_ssl("tcp_socket/ssl"),
249 s_udp_socket("udp_socket"),
250 s_unix_socket("unix_socket"),
251 s_udg_socket("udg_socket");
253 // Tries to infer stream type based on getsockopt() values.
254 // If no conclusive match can be found, leaves m_streamType
255 // as its default value of empty_string_ref.
256 void Socket::inferStreamType() {
257 if (empty_string() == getStreamType()) {
258 int result, type;
259 socklen_t len = sizeof(type);
260 result = getsockopt(getFd(), SOL_SOCKET, SO_TYPE, &type, &len);
261 if (result != 0) {
262 // getsockopt error.
263 // Nothing to do here because the default stream type is empty_string_ref.
264 return;
267 if (m_data->m_type == AF_INET || m_data->m_type == AF_INET6) {
268 if (type == SOCK_STREAM) {
269 if (RuntimeOption::EnableHipHopSyntax) {
270 setStreamType(s_tcp_socket);
271 } else {
272 // Note: PHP returns "tcp_socket/ssl" for this query,
273 // even though the socket is clearly not an SSL socket.
274 setStreamType(s_tcp_socket_ssl);
276 } else if (type == SOCK_DGRAM) {
277 setStreamType(s_udp_socket);
279 } else if (m_data->m_type == AF_UNIX) {
280 if (type == SOCK_STREAM) {
281 setStreamType(s_unix_socket);
282 } else if (type == SOCK_DGRAM) {
283 setStreamType(s_udg_socket);
287 // Nothing to do in default case because the default stream type is
288 // empty_string_ref.
292 ///////////////////////////////////////////////////////////////////////////////