2 +----------------------------------------------------------------------+
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"
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
)
42 bool SocketData::closeImpl() {
44 if (valid() && !isClosed()) {
45 *s_pcloseRet
= ::close(getFd());
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
)
64 m_data(static_cast<SocketData
*>(getFileData()))
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
;
80 tv
.tv_sec
= RequestInfo::s_requestInfo
.getNoCheck()->
81 m_reqInjectionData
.getSocketDefaultTimeout();
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
));
89 setsockopt(getFd(), SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof(tv
));
91 internalSetTimeout(tv
);
92 setIsLocal(type
== AF_UNIX
);
94 // Attempt to infer stream type only if it was not explicitly specified.
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 */)
103 std::make_shared
<SocketData
>(port
, type
, nonblocking
),
104 sockfd
, type
, address
, port
, timeout
, streamType
)
107 Socket::~Socket() { }
109 void Socket::sweep() {
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() {
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
;
133 m_data
->m_timeout
= 0;
137 bool Socket::checkLiveness() {
138 if (getFd() == -1 || m_data
->m_timedOut
) {
144 p
.events
= POLLIN
| POLLERR
| POLLHUP
| POLLPRI
;
146 if (poll(&p
, 1, 0) > 0 && p
.revents
> 0) {
148 int64_t ret
= recv(getFd(), &buf
, sizeof(buf
), MSG_PEEK
);
149 if (ret
== 0 || (ret
== -1 && errno
!= EWOULDBLOCK
&& errno
!= EAGAIN
&& errno
!= EINTR
)) {
157 bool Socket::waitForData() {
158 m_data
->m_timedOut
= false;
160 struct pollfd fds
[1];
162 fds
[0].events
= POLLIN
|POLLERR
|POLLHUP
;
163 if (poll(fds
, 1, m_data
->m_timeout
/ 1000)) {
164 socklen_t lon
= sizeof(int);
166 getsockopt(getFd(), SOL_SOCKET
, SO_ERROR
, (void*)(&valopt
), &lon
);
167 if (valopt
== EINTR
) continue;
170 m_data
->m_timedOut
= true;
177 int64_t Socket::readImpl(char *buffer
, int64_t length
) {
181 IOStatusHelper
io("socket::recv", m_data
->m_address
.c_str(), m_data
->m_port
);
184 if (m_data
->m_timeout
> 0) {
185 int flags
= fcntl(getFd(), F_GETFL
, 0);
186 if ((flags
& O_NONBLOCK
) == 0) {
187 if (!waitForData()) {
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
)) {
199 return (ret
< 0) ? 0 : ret
;
202 int64_t Socket::writeImpl(const char *buffer
, int64_t length
) {
206 IOStatusHelper
io("socket::send", m_data
->m_address
.c_str(), m_data
->m_port
);
207 int64_t ret
= send(getFd(), buffer
, length
, 0);
209 m_data
->m_bytesSent
+= ret
;
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.
221 int64_t ret
= recv(getFd(), &ch
, 1, MSG_PEEK
| MSG_DONTWAIT
);
222 if (ret
== 0 || (ret
== -1 && errno
!= EWOULDBLOCK
&& errno
!= EAGAIN
&& errno
!= EINTR
)) {
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);
240 int64_t Socket::tell() {
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()) {
257 socklen_t len
= sizeof(type
);
258 result
= getsockopt(getFd(), SOL_SOCKET
, SO_TYPE
, &type
, &len
);
261 // Nothing to do here because the default stream type is empty_string_ref.
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
284 ///////////////////////////////////////////////////////////////////////////////