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-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"
30 ///////////////////////////////////////////////////////////////////////////////
32 __thread
int Socket::s_lastErrno
;
34 ///////////////////////////////////////////////////////////////////////////////
35 // constructors and destructor
37 SocketData::SocketData(int port
, int type
, bool nonblocking
)
38 : FileData(nonblocking
)
43 bool SocketData::closeImpl() {
45 if (valid() && !isClosed()) {
46 s_pcloseRet
= ::close(getFd());
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
)
65 m_data(static_cast<SocketData
*>(getFileData()))
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
;
81 tv
.tv_sec
= ThreadInfo::s_threadInfo
.getNoCheck()->
82 m_reqInjectionData
.getSocketDefaultTimeout();
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
));
90 setsockopt(getFd(), SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof(tv
));
92 internalSetTimeout(tv
);
93 setIsLocal(type
== AF_UNIX
);
95 // Attempt to infer stream type only if it was not explicitly specified.
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 */)
104 std::make_shared
<SocketData
>(port
, type
, nonblocking
),
105 sockfd
, type
, address
, port
, timeout
, streamType
)
108 Socket::~Socket() { }
110 void Socket::sweep() {
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();
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
;
135 m_data
->m_timeout
= 0;
139 bool Socket::checkLiveness() {
140 if (getFd() == -1 || m_data
->m_timedOut
) {
146 p
.events
= POLLIN
| POLLERR
| POLLHUP
| POLLPRI
;
148 if (poll(&p
, 1, 0) > 0 && p
.revents
> 0) {
150 int64_t ret
= recv(getFd(), &buf
, sizeof(buf
), MSG_PEEK
);
151 if (ret
== 0 || (ret
== -1 && errno
!= EWOULDBLOCK
&& errno
!= EAGAIN
&& errno
!= EINTR
)) {
159 bool Socket::waitForData() {
160 m_data
->m_timedOut
= false;
162 struct pollfd fds
[1];
164 fds
[0].events
= POLLIN
|POLLERR
|POLLHUP
;
165 if (poll(fds
, 1, m_data
->m_timeout
/ 1000)) {
166 socklen_t lon
= sizeof(int);
168 getsockopt(getFd(), SOL_SOCKET
, SO_ERROR
, (void*)(&valopt
), &lon
);
169 if (valopt
== EINTR
) continue;
172 m_data
->m_timedOut
= true;
179 int64_t Socket::readImpl(char *buffer
, int64_t length
) {
183 IOStatusHelper
io("socket::recv", m_data
->m_address
.c_str(), m_data
->m_port
);
186 if (m_data
->m_timeout
> 0) {
187 int flags
= fcntl(getFd(), F_GETFL
, 0);
188 if ((flags
& O_NONBLOCK
) == 0) {
189 if (!waitForData()) {
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
)) {
201 return (ret
< 0) ? 0 : ret
;
204 int64_t Socket::writeImpl(const char *buffer
, int64_t length
) {
208 IOStatusHelper
io("socket::send", m_data
->m_address
.c_str(), m_data
->m_port
);
209 int64_t ret
= send(getFd(), buffer
, length
, 0);
211 m_data
->m_bytesSent
+= ret
;
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.
223 int64_t ret
= recv(getFd(), &ch
, 1, MSG_PEEK
| MSG_DONTWAIT
);
224 if (ret
== 0 || (ret
== -1 && errno
!= EWOULDBLOCK
&& errno
!= EAGAIN
&& errno
!= EINTR
)) {
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);
242 int64_t Socket::tell() {
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()) {
259 socklen_t len
= sizeof(type
);
260 result
= getsockopt(getFd(), SOL_SOCKET
, SO_TYPE
, &type
, &len
);
263 // Nothing to do here because the default stream type is empty_string_ref.
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
);
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
292 ///////////////////////////////////////////////////////////////////////////////