sync the repo
[hiphop-php.git] / hphp / runtime / server / fastcgi / fastcgi-server.cpp
blobdb4b1f4911c77d584adcf7f67d43d5d363b692a0
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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/server/fastcgi/fastcgi-server.h"
19 #include "hphp/runtime/server/http-server.h"
20 #include "hphp/util/configs/server.h"
22 namespace HPHP {
24 ////////////////////////////////////////////////////////////////////////////////
26 bool FastCGIAcceptor::canAccept(const folly::SocketAddress& /*address*/) {
27 // TODO: Support server IP whitelist.
28 auto const cons = m_server->getLibEventConnectionCount();
29 return (Cfg::Server::ConnectionLimit == 0 ||
30 cons < Cfg::Server::ConnectionLimit);
33 void FastCGIAcceptor::onNewConnection(
34 folly::AsyncTransportWrapper::UniquePtr sock,
35 const folly::SocketAddress* peerAddress,
36 const std::string& /*nextProtocolName*/,
37 SecureTransportType /*secureProtocolType*/,
38 const ::wangle::TransportInfo& /*tinfo*/) {
39 folly::SocketAddress localAddress;
40 try {
41 sock->getLocalAddress(&localAddress);
42 } catch (std::system_error&) {
43 // If getSockName fails it's bad news; abort the connection
44 return;
47 // Will delete itself when it gets a closing callback
48 auto session = new FastCGISession(
49 m_server->getEventBaseManager()->getExistingEventBase(),
50 m_server->getDispatcher(),
51 std::move(sock),
52 localAddress,
53 *peerAddress
56 // NB: ~ManagedConnection will call removeConnection() before the session
57 // destroys itself.
58 Acceptor::addConnection(session);
61 void FastCGIAcceptor::onConnectionsDrained() {
62 m_server->onConnectionsDrained();
65 ////////////////////////////////////////////////////////////////////////////////
67 FastCGIServer::FastCGIServer(const std::string &address,
68 int port,
69 int workers,
70 bool useFileSocket)
71 : Server(address, port),
72 m_worker(&m_eventBaseManager),
73 m_dispatcher(workers, workers,
74 Cfg::Server::ThreadDropCacheTimeoutSeconds,
75 Cfg::Server::ThreadDropStack,
76 this,
77 Cfg::Server::ThreadJobLIFOSwitchThreshold,
78 Cfg::Server::ThreadJobMaxQueuingMilliSeconds,
79 RequestPriority::k_numPriorities) {
80 folly::SocketAddress sock_addr;
81 if (useFileSocket) {
82 sock_addr.setFromPath(address);
83 } else if (address.empty()) {
84 sock_addr.setFromHostPort("localhost", port);
85 assert(sock_addr.isLoopbackAddress());
86 } else {
87 sock_addr.setFromHostPort(address, port);
89 auto accConfig = std::make_shared<wangle::ServerSocketConfig>();
90 accConfig->bindAddress = sock_addr;
91 accConfig->acceptBacklog = Cfg::Server::Backlog;
92 std::chrono::seconds timeout;
93 if (Cfg::Server::ConnectionTimeoutSeconds >= 0) {
94 timeout = std::chrono::seconds(Cfg::Server::ConnectionTimeoutSeconds);
95 } else {
96 // default to 2 minutes
97 timeout = std::chrono::seconds(120);
99 accConfig->connectionIdleTimeout = timeout;
100 m_socketConfig = std::move(accConfig);
103 void FastCGIServer::start() {
104 // It's not safe to call this function more than once
105 m_socket.reset(new folly::AsyncServerSocket(m_worker.getEventBase()));
106 try {
107 m_socket->bind(m_socketConfig->bindAddress);
108 } catch (const std::system_error& ex) {
109 Logger::Error(std::string(ex.what()));
110 if (m_socketConfig->bindAddress.getFamily() == AF_UNIX) {
111 throw FailedToListenException(m_socketConfig->bindAddress.getPath());
113 throw FailedToListenException(m_socketConfig->bindAddress.getAddressStr(),
114 m_socketConfig->bindAddress.getPort());
116 if (m_socketConfig->bindAddress.getFamily() == AF_UNIX) {
117 auto path = m_socketConfig->bindAddress.getPath();
118 chmod(path.c_str(), 0760);
120 m_acceptor.reset(new FastCGIAcceptor(m_socketConfig, this));
121 m_acceptor->init(m_socket.get(), m_worker.getEventBase());
122 m_worker.getEventBase()->runInEventBaseThread([&] {
123 if (!m_socket) {
124 // Someone called stop before we got here. With the exception of a
125 // second call to start being made this should be safe as any place
126 // we mutate m_socket is done within the event base.
127 return;
129 m_socket->listen(m_socketConfig->acceptBacklog);
130 m_socket->startAccepting();
132 setStatus(RunStatus::RUNNING);
133 folly::AsyncTimeout::attachEventBase(m_worker.getEventBase());
134 m_worker.start();
135 m_dispatcher.start();
138 void FastCGIServer::waitForEnd() {
139 // When m_worker stops the server has stopped accepting new requests, there
140 // may be pedning vm jobs. wait() is always safe to call regardless of thread
141 m_worker.wait();
144 void FastCGIServer::stop() {
145 if (getStatus() != RunStatus::RUNNING) return; // nothing to do
147 setStatus(RunStatus::STOPPING);
148 HttpServer::MarkShutdownStat(ShutdownEvent::SHUTDOWN_DRAIN_READS);
150 m_worker.getEventBase()->runInEventBaseThread([&] {
151 // Shutdown the server socket. Unfortunately, we will drop all unaccepted
152 // connections; there is no way to do a partial shutdown of a server socket
153 m_socket->stopAccepting();
155 if (Cfg::Server::GracefulShutdownWait > 0) {
156 // Gracefully drain any incomplete requests. We cannot go offline until
157 // they are finished as we own their dispatcher and event base.
158 if (m_acceptor) {
159 m_acceptor->startDrainingAllConnections();
162 std::chrono::seconds s(Cfg::Server::GracefulShutdownWait);
163 std::chrono::milliseconds m(s);
164 scheduleTimeout(m);
165 } else {
166 // Drop all connections. We cannot shutdown until they stop because we
167 // own their dispatcher and event base.
168 if (m_acceptor) {
169 m_acceptor->forceStop();
172 terminateServer();
177 void FastCGIServer::onConnectionsDrained() {
178 // NOTE: called from FastCGIAcceptor::onConnectionsDrained()
179 cancelTimeout();
180 terminateServer();
183 void FastCGIServer::timeoutExpired() noexcept {
184 // Acceptor failed to drain connections on time; drop them so that we can
185 // shutdown.
186 if (m_acceptor) {
187 m_acceptor->forceStop();
190 terminateServer();
193 void FastCGIServer::terminateServer() {
194 if (getStatus() != RunStatus::STOPPING) {
195 setStatus(RunStatus::STOPPING);
197 // Wait for the server socket thread to stop running
198 m_worker.stopWhenIdle();
200 HttpServer::MarkShutdownStat(ShutdownEvent::SHUTDOWN_DRAIN_DISPATCHER);
201 // Wait for VMs to shutdown
202 m_dispatcher.stop();
204 setStatus(RunStatus::STOPPED);
205 HttpServer::MarkShutdownStat(ShutdownEvent::SHUTDOWN_DONE);
207 // Notify HttpServer that we've shutdown
208 for (auto listener: m_listeners) {
209 listener->serverStopped(this);
213 ////////////////////////////////////////////////////////////////////////////////