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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/server/fastcgi/fastcgi-server.h"
19 #include "hphp/runtime/server/http-server.h"
23 ////////////////////////////////////////////////////////////////////////////////
25 bool FastCGIAcceptor::canAccept(const folly::SocketAddress
& /*address*/) {
26 // TODO: Support server IP whitelist.
27 auto const cons
= m_server
->getLibEventConnectionCount();
28 return (RuntimeOption::ServerConnectionLimit
== 0 ||
29 cons
< RuntimeOption::ServerConnectionLimit
);
32 void FastCGIAcceptor::onNewConnection(
33 folly::AsyncTransportWrapper::UniquePtr sock
,
34 const folly::SocketAddress
* peerAddress
,
35 const std::string
& /*nextProtocolName*/,
36 SecureTransportType
/*secureProtocolType*/,
37 const ::wangle::TransportInfo
& /*tinfo*/) {
38 folly::SocketAddress localAddress
;
40 sock
->getLocalAddress(&localAddress
);
41 } catch (std::system_error
& e
) {
42 // If getSockName fails it's bad news; abort the connection
46 // Will delete itself when it gets a closing callback
47 auto session
= new FastCGISession(
48 m_server
->getEventBaseManager()->getExistingEventBase(),
49 m_server
->getDispatcher(),
55 // NB: ~ManagedConnection will call removeConnection() before the session
57 Acceptor::addConnection(session
);
60 void FastCGIAcceptor::onConnectionsDrained() {
61 m_server
->onConnectionsDrained();
64 ////////////////////////////////////////////////////////////////////////////////
66 FastCGIServer::FastCGIServer(const std::string
&address
,
70 : Server(address
, port
),
71 m_worker(&m_eventBaseManager
),
73 RuntimeOption::ServerThreadDropCacheTimeoutSeconds
,
74 RuntimeOption::ServerThreadDropStack
,
76 RuntimeOption::ServerThreadJobLIFOSwitchThreshold
,
77 RuntimeOption::ServerThreadJobMaxQueuingMilliSeconds
,
78 RequestPriority::k_numPriorities
) {
79 folly::SocketAddress sock_addr
;
81 sock_addr
.setFromPath(address
);
82 } else if (address
.empty()) {
83 sock_addr
.setFromLocalPort(port
);
85 sock_addr
.setFromHostPort(address
, port
);
87 m_socketConfig
.bindAddress
= sock_addr
;
88 m_socketConfig
.acceptBacklog
= RuntimeOption::ServerBacklog
;
89 std::chrono::seconds timeout
;
90 if (RuntimeOption::ConnectionTimeoutSeconds
>= 0) {
91 timeout
= std::chrono::seconds(RuntimeOption::ConnectionTimeoutSeconds
);
93 // default to 2 minutes
94 timeout
= std::chrono::seconds(120);
96 m_socketConfig
.connectionIdleTimeout
= timeout
;
99 void FastCGIServer::start() {
100 // It's not safe to call this function more than once
101 m_socket
.reset(new folly::AsyncServerSocket(m_worker
.getEventBase()));
103 m_socket
->bind(m_socketConfig
.bindAddress
);
104 } catch (const std::system_error
& ex
) {
105 LOG(ERROR
) << ex
.what();
106 if (m_socketConfig
.bindAddress
.getFamily() == AF_UNIX
) {
107 throw FailedToListenException(m_socketConfig
.bindAddress
.getPath());
109 throw FailedToListenException(m_socketConfig
.bindAddress
.getAddressStr(),
110 m_socketConfig
.bindAddress
.getPort());
112 if (m_socketConfig
.bindAddress
.getFamily() == AF_UNIX
) {
113 auto path
= m_socketConfig
.bindAddress
.getPath();
114 chmod(path
.c_str(), 0760);
116 m_acceptor
.reset(new FastCGIAcceptor(m_socketConfig
, this));
117 m_acceptor
->init(m_socket
.get(), m_worker
.getEventBase());
118 m_worker
.getEventBase()->runInEventBaseThread([&] {
120 // Someone called stop before we got here. With the exception of a
121 // second call to start being made this should be safe as any place
122 // we mutate m_socket is done within the event base.
125 m_socket
->listen(m_socketConfig
.acceptBacklog
);
126 m_socket
->startAccepting();
128 setStatus(RunStatus::RUNNING
);
129 folly::AsyncTimeout::attachEventBase(m_worker
.getEventBase());
131 m_dispatcher
.start();
134 void FastCGIServer::waitForEnd() {
135 // When m_worker stops the server has stopped accepting new requests, there
136 // may be pedning vm jobs. wait() is always safe to call regardless of thread
140 void FastCGIServer::stop() {
141 if (getStatus() != RunStatus::RUNNING
) return; // nothing to do
143 setStatus(RunStatus::STOPPING
);
144 HttpServer::MarkShutdownStat(ShutdownEvent::SHUTDOWN_DRAIN_READS
);
146 m_worker
.getEventBase()->runInEventBaseThread([&] {
147 // Shutdown the server socket. Unfortunately, we will drop all unaccepted
148 // connections; there is no way to do a partial shutdown of a server socket
149 m_socket
->stopAccepting();
151 if (RuntimeOption::ServerGracefulShutdownWait
> 0) {
152 // Gracefully drain any incomplete requests. We cannot go offline until
153 // they are finished as we own their dispatcher and event base.
155 m_acceptor
->drainAllConnections();
158 std::chrono::seconds
s(RuntimeOption::ServerGracefulShutdownWait
);
159 std::chrono::milliseconds
m(s
);
162 // Drop all connections. We cannot shutdown until they stop because we
163 // own their dispatcher and event base.
165 m_acceptor
->forceStop();
173 void FastCGIServer::onConnectionsDrained() {
174 // NOTE: called from FastCGIAcceptor::onConnectionsDrained()
179 void FastCGIServer::timeoutExpired() noexcept
{
180 // Acceptor failed to drain connections on time; drop them so that we can
183 m_acceptor
->forceStop();
189 void FastCGIServer::terminateServer() {
190 if (getStatus() != RunStatus::STOPPING
) {
191 setStatus(RunStatus::STOPPING
);
193 // Wait for the server socket thread to stop running
194 m_worker
.stopWhenIdle();
196 HttpServer::MarkShutdownStat(ShutdownEvent::SHUTDOWN_DRAIN_DISPATCHER
);
197 // Wait for VMs to shutdown
200 setStatus(RunStatus::STOPPED
);
201 HttpServer::MarkShutdownStat(ShutdownEvent::SHUTDOWN_DONE
);
203 // Notify HttpServer that we've shutdown
204 for (auto listener
: m_listeners
) {
205 listener
->serverStopped(this);
209 ////////////////////////////////////////////////////////////////////////////////