- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List

# EventedClient.h

    00001 /*
    00002  *  Phusion Passenger - http://www.modrails.com/
    00003  *  Copyright (c) 2010 Phusion
    00004  *
    00005  *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
    00006  *
    00007  *  Permission is hereby granted, free of charge, to any person obtaining a copy
    00008  *  of this software and associated documentation files (the "Software"), to deal
    00009  *  in the Software without restriction, including without limitation the rights
    00010  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    00011  *  copies of the Software, and to permit persons to whom the Software is
    00012  *  furnished to do so, subject to the following conditions:
    00013  *
    00014  *  The above copyright notice and this permission notice shall be included in
    00015  *  all copies or substantial portions of the Software.
    00016  *
    00017  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    00018  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    00019  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    00020  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    00021  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    00022  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    00023  *  THE SOFTWARE.
    00024  */
    00025 #ifndef _PASSENGER_EVENTED_CLIENT_H_
    00026 #define _PASSENGER_EVENTED_CLIENT_H_
    00027
    00028 #include <ev++.h>
    00029 #include <string>
    00030 #include <sys/types.h>
    00031 #include <cstdlib>
    00032 #include <cerrno>
    00033 #include <cassert>
    00034
    00035 #include <boost/function.hpp>
    00036 #include <oxt/system_calls.hpp>
    00037 #include <oxt/thread.hpp>
    00038
    00039 #include "FileDescriptor.h"
    00040 #include "Utils/IOUtils.h"
    00041
    00042 namespace Passenger {
    00043
    00044 using namespace std;
    00045 using namespace boost;
    00046 using namespace oxt;
    00047
    00048
    00049 /**
    00050  * A utility class for making I/O handling in non-blocking libev evented servers
    00051  * much easier.
    00052  * - An EventedClient is associated with a reference counted file descriptor.
    00053  * - It contains connection state information (i.e. whether the connection is
    00054  *   established or closed). Callbacks are provided for watching connection
    00055  *   state changes (e.g. <tt>onDisconnect</tt>).
    00056  * - It provides reference counting features for simpler memory management
    00057  *   (<tt>ref()</tt> and <tt>unref()</tt>).
    00058  * - It installs input and output readiness watchers that are unregistered
    00059  *   when the EventedClient is destroyed. One can hook into input readiness
    00060  *   watcher with the <tt>onReadable</tt> callback.
    00061  * - Makes zero-copy writes easy. The <tt>write()</tt> method accepts an array
    00062  *   of buffers. Whenever possible, all of these buffers are written out in
    00063  *   the given order, using a single system call, without copying them into a
    00064  *   single temporary buffer.
    00065  * - Makes non-blocking writes easy. Normally a write() system call on a
    00066  *   non-blocking socket can fail with EAGAIN if the socket send buffer is
    00067  *   full. EventedClient schedules the data to be sent later when the socket is
    00068  *   writable again. It automatically integrates into the main loop in order
    00069  *   to do this. This allows one to have write operations occur concurrently
    00070  *   with read operations.
    00071  *   In case too many scheduled writes are being piled up, EventedClient
    00072  *   is smart enough to temporarily disable read notifications and wait until
    00073  *   everything is written out before enabling read notifications again.
    00074  *   The definition of "too many" is customizable (<tt>setOutboxLimit()</tt>).
    00075  * - EventedClient's <tt>disconnect</tt> method respects pending writes. It
    00076  *   will disconnect after all pending outgoing data have been written out.
    00077  *
    00078  * <h2>Basic usage</h2>
    00079  * Construct an EventedClient with a libev loop and a file descriptor:
    00080  *
    00081  * @code
    00082  * EventedClient *client = new EventedClient(loop, fd);
    00083  * @endcode
    00084  *
    00085  * You are probably interested in read readiness notifications on <tt>fd</tt>.
    00086  * However these notifications are disabled by default. You need to set the
    00087  * <tt>onReadable</tt> callback (which is called every time the fd is
    00088  * readable) and enable read notifications.
    00089  *
    00090  * @code
    00091  * void onReadable(EventedClient *client) {
    00092  *     // do whatever you want
    00093  * }
    00094  *
    00095  * ...
    00096  * client->onReadable = onReadable;
    00097  * client->notifyReads(true);
    00098  * @endcode
    00099  *
    00100  * <h2>Error handling</h2>
    00101  * EventedClient never raises exceptions, except when your callbacks do.
    00102  * It reports errors with the <tt>onSystemError</tt> callback. That said,
    00103  * EventedClient is exception-aware and will ensure that its internal
    00104  * state stays consistent even when your callbacks throw exceptions.
    00105  */
    00106 class EventedClient {
    00107 public:
    00108         typedef void (*Callback)(EventedClient *client);
    00109         typedef void (*SystemErrorCallback)(EventedClient *client, const string &message, int code);
    00110
    00111 private:
    00112         enum {
    00113                 /**
    00114                  * This is the initial state for a client. It means we're
    00115                  * connected to the client, ready to receive data and
    00116                  * there's no pending outgoing data. In this state we will
    00117                  * only be watching for read events.
    00118                  */
    00119                 EC_CONNECTED,
    00120
    00121                 /**
    00122                  * This state is entered from EC_CONNECTED when the write()
    00123                  * method fails to send all data immediately and EventedClient
    00124                  * schedules some data to be sent later, when the socket becomes
    00125                  * readable again. In here we will be watching for read
    00126                  * and write events. Once all data has been sent out the system
    00127                  * will transition back to EC_CONNECTED.
    00128                  */
    00129                 EC_WRITES_PENDING,
    00130
    00131                 /**
    00132                  * This state is entered from EC_WRITES_PENDING or from EC_CONNECTED
    00133                  * when the write() method fails to send all data immediately, and
    00134                  * the amount of data to be scheduled to be sent later is larger
    00135                  * than the specified outbox limit. In this state, EventedClient
    00136                  * will not watch for read events and will instead concentrate on
    00137                  * sending out all pending data before watching read events again.
    00138                  * When all pending data has been sent out the system will transition
    00139                  * to EC_CONNECTED.
    00140                  */
    00141                 EC_TOO_MANY_WRITES_PENDING,
    00142
    00143                 /**
    00144                  * This state is like EC_CONNECTED, but indicates that the write
    00145                  * side of the connection has been closed. In this state write()
    00146                  * calls won't have any effect.
    00147                  */
    00148                 EC_RO_CONNECTED,
    00149
    00150                 /**
    00151                  * This state is entered from EC_WRITES_PENDING when
    00152                  * closeWrite() has been called. The system will continue
    00153                  * to send out pending data but write() calls won't append more
    00154                  * data to the outbox. After pending data has been sent out,
    00155                  * the system will transition to EC_RO_CONNECTED.
    00156                  */
    00157                 EC_RO_CONNECTED_WITH_WRITES_PENDING,
    00158
    00159                 /**
    00160                  * This state is entered from the EC_WRITES_PENDING,
    00161                  * EC_TOO_MANY_WRITES_PENDING, EC_RO_CONNECTED_WITH_WIRTES_PENDING
    00162                  * or EC_RO_CONNECTED_WITH_TOO_MANY_WRITES_PENDING state when
    00163                  * disconnect() is called.
    00164                  * It means that we want to close the connection as soon as all
    00165                  * pending outgoing data has been sent. As soon as that happens
    00166                  * it'll transition to EC_DISCONNECTED. In this state no further
    00167                  * I/O should be allowed.
    00168                  */
    00169                 EC_DISCONNECTING_WITH_WRITES_PENDING,
    00170
    00171                 /**
    00172                  * Final state. Client connection has been closed. No
    00173                  * I/O with the client is possible.
    00174                  */
    00175                 EC_DISCONNECTED
    00176         } state;
    00177
    00178         /** A libev watcher on for watching read events on <tt>fd</tt>. */
    00179         ev::io readWatcher;
    00180         /** A libev watcher on for watching write events on <tt>fd</tt>. */
    00181         ev::io writeWatcher;
    00182         /** Storage for data that could not be sent out immediately. */
    00183         string outbox;
    00184         int refcount;
    00185         unsigned int outboxLimit;
    00186         bool m_notifyReads;
    00187
    00188         void _onReadable(ev::io &w, int revents) {
    00189                 emitEvent(onReadable);
    00190         }
    00191
    00192         void onWritable(ev::io &w, int revents) {
    00193                 assert(state != EC_CONNECTED);
    00194                 assert(state != EC_RO_CONNECTED);
    00195                 assert(state != EC_DISCONNECTED);
    00196
    00197                 this_thread::disable_interruption di;
    00198                 this_thread::disable_syscall_interruption dsi;
    00199                 size_t sent = 0;
    00200                 bool done = outbox.empty();
    00201
    00202                 while (!done) {
    00203                         ssize_t ret = syscalls::write(fd,
    00204                                 outbox.data() + sent,
    00205                                 outbox.size() - sent);
    00206                         if (ret == -1) {
    00207                                 if (errno != EAGAIN) {
    00208                                         int e = errno;
    00209                                         if (writeErrorAction == DISCONNECT_FULL) {
    00210                                                 disconnect(true);
    00211                                         } else {
    00212                                                 closeWrite();
    00213                                         }
    00214                                         emitSystemErrorEvent("Cannot write data to client", e);
    00215                                         return;
    00216                                 }
    00217                                 done = true;
    00218                         } else {
    00219                                 sent += ret;
    00220                                 done = sent == outbox.size();
    00221                         }
    00222                 }
    00223                 if (sent > 0) {
    00224                         outbox.erase(0, sent);
    00225                 }
    00226
    00227                 updateWatcherStates();
    00228                 if (outbox.empty()) {
    00229                         emitEvent(onPendingDataFlushed);
    00230                 }
    00231         }
    00232
    00233         bool outboxTooLarge() {
    00234                 return outbox.size() > 0 && outbox.size() >= outboxLimit;
    00235         }
    00236
    00237         void updateWatcherStates() {
    00238                 if (outbox.empty()) {
    00239                         switch (state) {
    00240                         case EC_CONNECTED:
    00241                         case EC_RO_CONNECTED:
    00242                                 watchReadEvents(m_notifyReads);
    00243                                 watchWriteEvents(false);
    00244                                 break;
    00245                         case EC_WRITES_PENDING:
    00246                         case EC_TOO_MANY_WRITES_PENDING:
    00247                                 state = EC_CONNECTED;
    00248                                 watchReadEvents(m_notifyReads);
    00249                                 watchWriteEvents(false);
    00250                                 break;
    00251                         case EC_RO_CONNECTED_WITH_WRITES_PENDING:
    00252                                 state = EC_RO_CONNECTED;
    00253                                 watchReadEvents(m_notifyReads);
    00254                                 watchWriteEvents(false);
    00255                                 break;
    00256                         case EC_DISCONNECTING_WITH_WRITES_PENDING:
    00257                                 state = EC_DISCONNECTED;
    00258                                 watchReadEvents(false);
    00259                                 watchWriteEvents(false);
    00260                                 try {
    00261                                         fd.close();
    00262                                 } catch (const SystemException &e) {
    00263                                         emitSystemErrorEvent(e.brief(), e.code());
    00264                                 }
    00265                                 emitEvent(onDisconnect);
    00266                                 break;
    00267                         default:
    00268                                 // Should never be reached.
    00269                                 abort();
    00270                         }
    00271                 } else {
    00272                         switch (state) {
    00273                         case EC_CONNECTED:
    00274                                 if (outboxTooLarge()) {
    00275                                         // If we have way too much stuff in the outbox then
    00276                                         // suspend reading until we've sent out the entire outbox.
    00277                                         state = EC_TOO_MANY_WRITES_PENDING;
    00278                                         watchReadEvents(false);
    00279                                         watchWriteEvents(true);
    00280                                 } else {
    00281                                         state = EC_WRITES_PENDING;
    00282                                         watchReadEvents(m_notifyReads);
    00283                                         watchWriteEvents(true);
    00284                                 }
    00285                                 break;
    00286                         case EC_RO_CONNECTED:
    00287                                 fprintf(stderr, "BUG: when outbox is non-empty the state should never be EC_RO_CONNECTED!\n");
    00288                                 abort();
    00289                                 break;
    00290                         case EC_WRITES_PENDING:
    00291                         case EC_RO_CONNECTED_WITH_WRITES_PENDING:
    00292                                 watchReadEvents(m_notifyReads);
    00293                                 watchWriteEvents(true);
    00294                                 break;
    00295                         case EC_TOO_MANY_WRITES_PENDING:
    00296                         case EC_DISCONNECTING_WITH_WRITES_PENDING:
    00297                                 watchReadEvents(false);
    00298                                 watchWriteEvents(true);
    00299                                 break;
    00300                         default:
    00301                                 // Should never be reached.
    00302                                 abort();
    00303                         }
    00304                 }
    00305         }
    00306
    00307         void watchReadEvents(bool enable = true) {
    00308                 if (readWatcher.is_active() && !enable) {
    00309                         readWatcher.stop();
    00310                 } else if (!readWatcher.is_active() && enable) {
    00311                         readWatcher.start();
    00312                 }
    00313         }
    00314
    00315         void watchWriteEvents(bool enable = true) {
    00316                 if (writeWatcher.is_active() && !enable) {
    00317                         writeWatcher.stop();
    00318                 } else if (!writeWatcher.is_active() && enable) {
    00319                         writeWatcher.start();
    00320                 }
    00321         }
    00322
    00323         void emitEvent(Callback callback) {
    00324                 if (callback != NULL) {
    00325                         callback(this);
    00326                 }
    00327         }
    00328
    00329         void emitSystemErrorEvent(const string &message, int code) {
    00330                 if (onSystemError != NULL) {
    00331                         onSystemError(this, message, code);
    00332                 }
    00333         }
    00334
    00335 public:
    00336         /** The client's file descriptor. Could be -1: see <tt>ioAllowed()</tt>. */
    00337         FileDescriptor fd;
    00338
    00339         /** Controls what to do when a write error is encountered. */
    00340         enum {
    00341                 /** Forcefully disconnect the client. */
    00342                 DISCONNECT_FULL,
    00343                 /** Close the writer side of the connection, but continue allowing reading. */
    00344                 DISCONNECT_WRITE
    00345         } writeErrorAction;
    00346
    00347         /**
    00348          * Called when the file descriptor becomes readable and read notifications
    00349          * are enabled (see <tt>notifyRead()</tt>). When there's too much pending
    00350          * outgoing data, readability notifications are temporarily disabled; see
    00351          * <tt>write()</tt> for details.
    00352          */
    00353         Callback onReadable;
    00354
    00355         /**
    00356          * Called when the client is disconnected. This happens either immediately
    00357          * when <tt>disconnect()</tt> is called, or a short amount of time later.
    00358          * See the documentation for that function for details.
    00359          *
    00360          * Please note that destroying an EventedClient object does *not* cause
    00361          * this callback to be called.
    00362          */
    00363         Callback onDisconnect;
    00364
    00365         /**
    00366          * Called when <tt>detach()</tt> is called for the first time.
    00367          */
    00368         Callback onDetach;
    00369
    00370         /**
    00371          * Called after all pending outgoing data have been written out.
    00372          * If <tt>write()</tt> can be completed immediately without scheduling
    00373          * data for later, then <tt>write()</tt> will call this callback
    00374          * immediately after writing.
    00375          */
    00376         Callback onPendingDataFlushed;
    00377
    00378         /**
    00379          * System call errors are reported with this callback.
    00380          */
    00381         SystemErrorCallback onSystemError;
    00382
    00383         /**
    00384          * EventedClient doesn't do anything with this. Set it to whatever you want.
    00385          */
    00386         void *userData;
    00387
    00388         /**
    00389          * Creates a new EventedClient with the given libev loop and file descriptor.
    00390          * The initial reference count is 1.
    00391          */
    00392         EventedClient(struct ev_loop *loop, const FileDescriptor &_fd)
    00393                 : readWatcher(loop),
    00394                   writeWatcher(loop),
    00395                   fd(_fd)
    00396         {
    00397                 state              = EC_CONNECTED;
    00398                 refcount           = 1;
    00399                 m_notifyReads      = false;
    00400                 outboxLimit        = 1024 * 32;
    00401                 writeErrorAction   = DISCONNECT_FULL;
    00402                 onReadable         = NULL;
    00403                 onDisconnect       = NULL;
    00404                 onDetach           = NULL;
    00405                 onPendingDataFlushed = NULL;
    00406                 onSystemError      = NULL;
    00407                 userData           = NULL;
    00408                 readWatcher.set(fd, ev::READ);
    00409                 readWatcher.set<EventedClient, &EventedClient::_onReadable>(this);
    00410                 writeWatcher.set<EventedClient, &EventedClient::onWritable>(this);
    00411                 writeWatcher.set(fd, ev::WRITE);
    00412         }
    00413
    00414         virtual ~EventedClient() {
    00415                 // Unregister file descriptor from the event loop poller before
    00416                 // closing the file descriptor.
    00417                 watchReadEvents(false);
    00418                 watchWriteEvents(false);
    00419         }
    00420
    00421         /**
    00422          * Increase reference count.
    00423          */
    00424         void ref() {
    00425                 refcount++;
    00426         }
    00427
    00428         /**
    00429          * Decrease reference count. Upon reaching 0, this EventedClient object
    00430          * will be destroyed.
    00431          */
    00432         void unref() {
    00433                 refcount--;
    00434                 assert(refcount >= 0);
    00435                 if (refcount == 0) {
    00436                         delete this;
    00437                 }
    00438         }
    00439
    00440         /**
    00441          * Returns whether it is allowed to perform some kind of I/O with
    00442          * this client, either reading or writing.
    00443          * Usually true, and false when the client is either being disconnected
    00444          * or has been disconnected. A return value of false indicates that
    00445          * <tt>fd</tt> might be -1, but even when it isn't -1 you shouldn't
    00446          * access <tt>fd</tt> anymore.
    00447          * When the connection is half-closed (e.g. after closeWrite() has
    00448          * been called) the return value is still be true. Only when I/O of any
    00449          * kind is disallowed will this function return false.
    00450          */
    00451         bool ioAllowed() const {
    00452                 return state != EC_DISCONNECTING_WITH_WRITES_PENDING
    00453                         && state != EC_DISCONNECTED;
    00454         }
    00455
    00456         /**
    00457          * Returns whether it is allowed to write data to the client.
    00458          * Usually true, and false when the client is either being disconnected
    00459          * or has been disconnected or when the writer side of the client
    00460          * connection has been closed. write() will do nothing if this function
    00461          * returns false.
    00462          */
    00463         bool writeAllowed() const {
    00464                 return state == EC_CONNECTED
    00465                         || state == EC_WRITES_PENDING
    00466                         || state == EC_TOO_MANY_WRITES_PENDING
    00467                         || state == EC_RO_CONNECTED_WITH_WRITES_PENDING;
    00468         }
    00469
    00470         /** Used by unit tests. */
    00471         bool readWatcherActive() const {
    00472                 return readWatcher.is_active();
    00473         }
    00474
    00475         /**
    00476          * Returns the number of bytes that are scheduled to be sent to the
    00477          * client at a later time.
    00478          *
    00479          * @see write()
    00480          */
    00481         size_t pendingWrites() const {
    00482                 return outbox.size();
    00483         }
    00484
    00485         /**
    00486          * Sets whether you're interested in read events. This will start or
    00487          * stop the input readiness watcher appropriately according to the
    00488          * current state.
    00489          *
    00490          * If the client connection is already being closed or has already
    00491          * been closed then this method does nothing.
    00492          */
    00493         void notifyReads(bool enable) {
    00494                 if (!ioAllowed()) {
    00495                         return;
    00496                 }
    00497
    00498                 this_thread::disable_interruption di;
    00499                 this_thread::disable_syscall_interruption dsi;
    00500                 m_notifyReads = enable;
    00501                 updateWatcherStates();
    00502         }
    00503
    00504         /**
    00505          * Sets a limit on the client outbox. The outbox is where data is stored
    00506          * that could not be immediately sent to the client, e.g. because of
    00507          * network congestion. Whenver the outbox's size grows past this limit,
    00508          * EventedClient will enter a state in which it will stop listening for
    00509          * read events and instead concentrate on sending out all pending data.
    00510          *
    00511          * Setting this to 0 means that the outbox has an unlimited size. Please
    00512          * note however that this also means that the outbox's memory could grow
    00513          * unbounded if the client is too slow at receiving data.
    00514          *
    00515          * The default value is some non-zero value.
    00516          *
    00517          * If the client connection is already being closed or has already
    00518          * been closed then this method does nothing.
    00519          */
    00520         void setOutboxLimit(unsigned int size) {
    00521                 if (!ioAllowed()) {
    00522                         return;
    00523                 }
    00524
    00525                 this_thread::disable_interruption di;
    00526                 this_thread::disable_syscall_interruption dsi;
    00527                 outboxLimit = size;
    00528                 updateWatcherStates();
    00529         }
    00530
    00531         void write(const StaticString &data) {
    00532                 write(&data, 1);
    00533         }
    00534
    00535         /**
    00536          * Sends data to this client. This method will try to send the data
    00537          * immediately (in which no intermediate copies of the data will be made),
    00538          * but if the client is not yet ready to receive data (e.g. because of
    00539          * network congestion) then the data will be buffered and scheduled for
    00540          * sending later.
    00541          *
    00542          * If an I/O error was encountered then the action taken depends on the
    00543          * value of <em>writeActionError</em>. By default it is DISCONNECT_FULL,
    00544          * meaning the client connection will be closed by calling
    00545          * <tt>disconnect(true)</tt>. This means this method could potentially
    00546          * call the <tt>onDisconnect</tt> callback.
    00547          *
    00548          * If the client connection is already being closed, has already
    00549          * been closed or if the writer side is closed, then this method does
    00550          * nothing.
    00551          *
    00552          * The <tt>onPendingDataFlushed</tt> callback will be called after
    00553          * this data and whatever existing pending data have been written
    00554          * out. That may either be immediately or after a short period of
    00555          * of time.
    00556          */
    00557         void write(const StaticString data[], unsigned int count) {
    00558                 if (!writeAllowed()) {
    00559                         return;
    00560                 }
    00561
    00562                 ssize_t ret;
    00563                 this_thread::disable_interruption di;
    00564                 this_thread::disable_syscall_interruption dsi;
    00565
    00566                 ret = gatheredWrite(fd, data, count, outbox);
    00567                 if (ret == -1) {
    00568                         int e = errno;
    00569                         if (writeErrorAction == DISCONNECT_FULL) {
    00570                                 disconnect(true);
    00571                         } else {
    00572                                 closeWrite();
    00573                         }
    00574                         emitSystemErrorEvent("Cannot write data to client", e);
    00575                 } else {
    00576                         updateWatcherStates();
    00577                         if (outbox.empty()) {
    00578                                 emitEvent(onPendingDataFlushed);
    00579                         }
    00580                 }
    00581         }
    00582
    00583         /**
    00584          * Close only the writer side of the client connection.
    00585          * After calling this method, subsequent write() calls won't do anything
    00586          * anymore. Any pending outgoing data will be sent out whenever the
    00587          * opportunity arises.
    00588          *
    00589          * This function does nothing if the client is being disconnected,
    00590          * already disconnected or if only the writer side is closed.
    00591          */
    00592         void closeWrite() {
    00593                 this_thread::disable_syscall_interruption dsi;
    00594
    00595                 switch (state) {
    00596                 case EC_CONNECTED:
    00597                         assert(outbox.empty());
    00598                         state = EC_RO_CONNECTED;
    00599                         if (syscalls::shutdown(fd, SHUT_WR) == -1) {
    00600                                 int e = errno;
    00601                                 emitSystemErrorEvent(
    00602                                         "Cannot shutdown writer half of the client socket",
    00603                                         e);
    00604                         }
    00605                         break;
    00606                 case EC_WRITES_PENDING:
    00607                 case EC_TOO_MANY_WRITES_PENDING:
    00608                         state = EC_RO_CONNECTED_WITH_WRITES_PENDING;
    00609                         break;
    00610                 default:
    00611                         break;
    00612                 }
    00613                 updateWatcherStates();
    00614         }
    00615
    00616         /**
    00617          * Disconnects the client. This actually closes the underlying file
    00618          * descriptor, even if the FileDescriptor object still has references.
    00619          *
    00620          * If <em>force</em> is true then the client will be disconnected
    00621          * immediately, and any pending outgoing data will be discarded.
    00622          * Otherwise the client will be disconnected after all pending
    00623          * outgoing data have been sent; in the mean time no new data can be
    00624          * received from or sent to the client.
    00625          *
    00626          * After the client has actually been disconnected (which may be either
    00627          * immediately or after a short period of time), a disconnect event will
    00628          * be emitted.
    00629          *
    00630          * If the client connection has already been closed then this method
    00631          * does nothing. If the client connection is being closed (because
    00632          * there's pending outgoing data) then the behavior depends on the
    00633          * <tt>force</tt> argument: if true then the connection is closed
    00634          * immediately and the pending data is discarded, otherwise this
    00635          * method does nothing.
    00636          *
    00637          * The <tt>onDisconnect</tt> callback will be called after the file
    00638          * descriptor is closed, which is either immediately or after all
    00639          * pending data has been sent out.
    00640          */
    00641         void disconnect(bool force = false) {
    00642                 if (!ioAllowed() && !(state == EC_DISCONNECTING_WITH_WRITES_PENDING && force)) {
    00643                         return;
    00644                 }
    00645
    00646                 this_thread::disable_interruption di;
    00647                 this_thread::disable_syscall_interruption dsi;
    00648
    00649                 if (state == EC_CONNECTED || state == EC_RO_CONNECTED || force) {
    00650                         state = EC_DISCONNECTED;
    00651                         watchReadEvents(false);
    00652                         watchWriteEvents(false);
    00653                         try {
    00654                                 fd.close();
    00655                         } catch (const SystemException &e) {
    00656                                 emitSystemErrorEvent(e.brief(), e.code());
    00657                         }
    00658                         emitEvent(onDisconnect);
    00659                 } else {
    00660                         state = EC_DISCONNECTING_WITH_WRITES_PENDING;
    00661                         watchReadEvents(false);
    00662                         watchWriteEvents(true);
    00663                         if (syscalls::shutdown(fd, SHUT_RD) == -1) {
    00664                                 int e = errno;
    00665                                 emitSystemErrorEvent(
    00666                                         "Cannot shutdown reader half of the client socket",
    00667                                         e);
    00668                         }
    00669                 }
    00670         }
    00671
    00672         /**
    00673          * Detaches the client file descriptor so that this EventedClient no longer
    00674          * has any control over it. Any EventedClient I/O watchers on the client file
    00675          * descriptor will be stopped and further I/O on the file descriptor via
    00676          * EventedClient will become impossible. Any pending outgoing data will be
    00677          * discarded. The original client file descriptor is returned and
    00678          * <tt>onDetach</tt> is called. Subsequent calls to this function will
    00679          * return -1 and will no longer call <tt>onDetach</tt>.
    00680          *
    00681          * @post !ioAllowed()
    00682          * @post fd == -1
    00683          */
    00684         FileDescriptor detach() {
    00685                 if (state == EC_DISCONNECTED) {
    00686                         return fd;
    00687                 } else {
    00688                         FileDescriptor oldFd = fd;
    00689                         state = EC_DISCONNECTED;
    00690                         watchReadEvents(false);
    00691                         watchWriteEvents(false);
    00692                         fd = -1;
    00693                         emitEvent(onDetach);
    00694                         return oldFd;
    00695                 }
    00696         }
    00697 };
    00698
    00699
    00700 } // namespace Passenger
    00701
    00702 #endif /* _PASSENGER_EVENTED_CLIENT_H_ */