void PipeDescriptor::Heartbeat() { // If an inactivity timeout is defined, then check for it. if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout)) ScheduleClose (false); //bCloseNow = true; }
void ConnectionDescriptor::_DispatchInboundData (const char *buffer, int size) { #ifdef WITH_SSL if (SslBox) { SslBox->PutCiphertext (buffer, size); int s; char B [2048]; while ((s = SslBox->GetPlaintext (B, sizeof(B) - 1)) > 0) { _CheckHandshakeStatus(); B [s] = 0; _GenericInboundDispatch(B, s); } // If our SSL handshake had a problem, shut down the connection. if (s == -2) { ScheduleClose(false); return; } _CheckHandshakeStatus(); _DispatchCiphertext(); } else { _GenericInboundDispatch(buffer, size); } #endif #ifdef WITHOUT_SSL _GenericInboundDispatch(buffer, size); #endif }
void PipeDescriptor::Heartbeat() { // If an inactivity timeout is defined, then check for it. if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout)) ScheduleClose (false); //bCloseNow = true; }
void PipeDescriptor::Read() { int sd = GetSocket(); if (sd == INVALID_SOCKET) { assert (!bReadAttemptedAfterClose); bReadAttemptedAfterClose = true; return; } LastIo = gCurrentLoopTime; int total_bytes_read = 0; char readbuffer [16 * 1024]; for (int i=0; i < 10; i++) { // Don't read just one buffer and then move on. This is faster // if there is a lot of incoming. // But don't read indefinitely. Give other sockets a chance to run. // NOTICE, we're reading one less than the buffer size. // That's so we can put a guard byte at the end of what we send // to user code. // Use read instead of recv, which on Linux gives a "socket operation // on nonsocket" error. int r = read (sd, readbuffer, sizeof(readbuffer) - 1); //cerr << "<R:" << r << ">"; if (r > 0) { total_bytes_read += r; // Add a null-terminator at the the end of the buffer // that we will send to the callback. // DO NOT EVER CHANGE THIS. We want to explicitly allow users // to be able to depend on this behavior, so they will have // the option to do some things faster. Additionally it's // a security guard against buffer overflows. readbuffer [r] = 0; _GenericInboundDispatch(readbuffer, r); } else if (r == 0) { break; } else { // Basically a would-block, meaning we've read everything there is to read. break; } } if (total_bytes_read == 0) { // If we read no data on a socket that selected readable, // it generally means the other end closed the connection gracefully. ScheduleClose (false); //bCloseNow = true; } }
void DatagramDescriptor::Heartbeat() { // Close it if its inactivity timer has expired. if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout)) ScheduleClose (false); //bCloseNow = true; }
void DatagramDescriptor::Heartbeat() { // Close it if its inactivity timer has expired. if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout)) ScheduleClose (false); //bCloseNow = true; }
void ConnectionDescriptor::_DispatchCiphertext() { assert (SslBox); char BigBuf [2048]; bool did_work; do { did_work = false; // try to drain ciphertext while (SslBox->CanGetCiphertext()) { int r = SslBox->GetCiphertext (BigBuf, sizeof(BigBuf)); assert (r > 0); _SendRawOutboundData (BigBuf, r); did_work = true; } // Pump the SslBox, in case it has queued outgoing plaintext // This will return >0 if data was written, // 0 if no data was written, and <0 if there was a fatal error. bool pump; do { pump = false; int w = SslBox->PutPlaintext (NULL, 0); if (w > 0) { did_work = true; pump = true; } else if (w < 0) ScheduleClose (false); } while (pump); // try to put plaintext. INCOMPLETE, doesn't belong here? // In SendOutboundData, we're spooling plaintext directly // into SslBox. That may be wrong, we may need to buffer it // up here! /* const char *ptr; int ptr_length; while (OutboundPlaintext.GetPage (&ptr, &ptr_length)) { assert (ptr && (ptr_length > 0)); int w = SslMachine.PutPlaintext (ptr, ptr_length); if (w > 0) { OutboundPlaintext.DiscardBytes (w); did_work = true; } else break; } */ } while (did_work); }
void ConnectionDescriptor::Heartbeat() { /* Only allow a certain amount of time to go by while waiting * for a pending connect. If it expires, then kill the socket. * For a connected socket, close it if its inactivity timer * has expired. */ if (bConnectPending) { if ((gCurrentLoopTime - CreatedAt) >= PendingConnectTimeout) ScheduleClose (false); //bCloseNow = true; } else { if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout)) ScheduleClose (false); //bCloseNow = true; } }
void ConnectionDescriptor::Write() { /* A socket which is in a pending-connect state will select * writable when the disposition of the connect is known. * At that point, check to be sure there are no errors, * and if none, then promote the socket out of the pending * state. * TODO: I haven't figured out how Windows signals errors on * unconnected sockets. Maybe it does the untraditional but * logical thing and makes the socket selectable for error. * If so, it's unsupported here for the time being, and connect * errors will have to be caught by the timeout mechanism. */ if (bConnectPending) { int error; socklen_t len; len = sizeof(error); #ifdef OS_UNIX int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len); #endif #ifdef OS_WIN32 int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len); #endif if ((o == 0) && (error == 0)) { if (EventCallback) (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_COMPLETED, "", 0); bConnectPending = false; #ifdef HAVE_EPOLL // The callback may have scheduled outbound data. EpollEvent.events = EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0); #endif #ifdef HAVE_KQUEUE MyEventMachine->ArmKqueueReader (this); // The callback may have scheduled outbound data. if (SelectForWrite()) MyEventMachine->ArmKqueueWriter (this); #endif } else ScheduleClose (false); //bCloseNow = true; } else { if (bNotifyWritable) { if (EventCallback) (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_NOTIFY_WRITABLE, NULL, 0); return; } _WriteOutboundData(); } }
void ConnectionDescriptor::HandleError() { if (bWatchOnly) { // An EPOLLHUP | EPOLLIN condition will call Read() before HandleError(), in which case the // socket is already detached and invalid, so we don't need to do anything. if (MySocket == INVALID_SOCKET) return; // HandleError() is called on WatchOnly descriptors by the epoll reactor // when it gets a EPOLLERR | EPOLLHUP. Usually this would show up as a readable and // writable event on other reactors, so we have to fire those events ourselves. if (bNotifyReadable) Read(); if (bNotifyWritable) Write(); } else { ScheduleClose (false); } }
int ConnectionDescriptor::SendOutboundData (const char *data, int length) { #ifdef WITH_SSL if (SslBox) { if (length > 0) { int w = SslBox->PutPlaintext (data, length); if (w < 0) ScheduleClose (false); else _DispatchCiphertext(); } // TODO: What's the correct return value? return 1; // That's a wild guess, almost certainly wrong. } else #endif return _SendRawOutboundData (data, length); }
int ConnectionDescriptor::SendOutboundData (const char *data, int length) { if (bWatchOnly) throw std::runtime_error ("cannot send data on a 'watch only' connection"); if (ProxiedFrom && MaxOutboundBufSize && (unsigned int)(GetOutboundDataSize() + length) > MaxOutboundBufSize) ProxiedFrom->Pause(); #ifdef WITH_SSL if (SslBox) { if (length > 0) { int w = SslBox->PutPlaintext (data, length); if (w < 0) ScheduleClose (false); else _DispatchCiphertext(); } // TODO: What's the correct return value? return 1; // That's a wild guess, almost certainly wrong. } else #endif return _SendRawOutboundData (data, length); }
void ConnectionDescriptor::Read() { /* Read and dispatch data on a socket that has selected readable. * It's theoretically possible to get and dispatch incoming data on * a socket that has already been scheduled for closing or close-after-writing. * In those cases, we'll leave it up the to protocol handler to "do the * right thing" (which probably means to ignore the incoming data). * * 22Aug06: Chris Ochs reports that on FreeBSD, it's possible to come * here with the socket already closed, after the process receives * a ctrl-C signal (not sure if that's TERM or INT on BSD). The application * was one in which network connections were doing a lot of interleaved reads * and writes. * Since we always write before reading (in order to keep the outbound queues * as light as possible), I think what happened is that an interrupt caused * the socket to be closed in ConnectionDescriptor::Write. We'll then * come here in the same pass through the main event loop, and won't get * cleaned up until immediately after. * We originally asserted that the socket was valid when we got here. * To deal properly with the possibility that we are closed when we get here, * I removed the assert. HOWEVER, the potential for an infinite loop scares me, * so even though this is really clunky, I added a flag to assert that we never * come here more than once after being closed. (FCianfrocca) */ int sd = GetSocket(); //assert (sd != INVALID_SOCKET); (original, removed 22Aug06) if (sd == INVALID_SOCKET) { assert (!bReadAttemptedAfterClose); bReadAttemptedAfterClose = true; return; } if (bNotifyReadable) { if (EventCallback) (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_NOTIFY_READABLE, NULL, 0); return; } LastIo = gCurrentLoopTime; int total_bytes_read = 0; char readbuffer [16 * 1024 + 1]; for (int i=0; i < 10; i++) { // Don't read just one buffer and then move on. This is faster // if there is a lot of incoming. // But don't read indefinitely. Give other sockets a chance to run. // NOTICE, we're reading one less than the buffer size. // That's so we can put a guard byte at the end of what we send // to user code. int r = recv (sd, readbuffer, sizeof(readbuffer) - 1, 0); //cerr << "<R:" << r << ">"; if (r > 0) { total_bytes_read += r; LastRead = gCurrentLoopTime; // Add a null-terminator at the the end of the buffer // that we will send to the callback. // DO NOT EVER CHANGE THIS. We want to explicitly allow users // to be able to depend on this behavior, so they will have // the option to do some things faster. Additionally it's // a security guard against buffer overflows. readbuffer [r] = 0; _DispatchInboundData (readbuffer, r); } else if (r == 0) { break; } else { // Basically a would-block, meaning we've read everything there is to read. break; } } if (total_bytes_read == 0) { // If we read no data on a socket that selected readable, // it generally means the other end closed the connection gracefully. ScheduleClose (false); //bCloseNow = true; } }
void ConnectionDescriptor::Write() { /* A socket which is in a pending-connect state will select * writable when the disposition of the connect is known. * At that point, check to be sure there are no errors, * and if none, then promote the socket out of the pending * state. * TODO: I haven't figured out how Windows signals errors on * unconnected sockets. Maybe it does the untraditional but * logical thing and makes the socket selectable for error. * If so, it's unsupported here for the time being, and connect * errors will have to be caught by the timeout mechanism. */ if (bConnectPending) { int error; socklen_t len; len = sizeof(error); #ifdef OS_UNIX int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len); #endif #ifdef OS_WIN32 int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len); #endif if ((o == 0) && (error == 0)) { if (EventCallback) (*EventCallback)(GetBinding(), EM_CONNECTION_COMPLETED, "", 0); // 5May09: Moved epoll/kqueue read/write arming into SetConnectPending, so it can be called // from EventMachine_t::AttachFD as well. SetConnectPending (false); } else ScheduleClose (false); //bCloseNow = true; } else { if (bNotifyWritable) { if (EventCallback) (*EventCallback)(GetBinding(), EM_CONNECTION_NOTIFY_WRITABLE, NULL, 0); _UpdateEvents(false, true); return; } assert(!bWatchOnly); /* 5May09: Kqueue bugs on OSX cause one extra writable event to fire even though we're using EV_ONESHOT. We ignore this extra event once, but only the first time. If it happens again, we should fall through to the assert(nbytes>0) failure to catch any EM bugs which might cause ::Write to be called in a busy-loop. */ #ifdef HAVE_KQUEUE if (MyEventMachine->UsingKqueue()) { if (OutboundDataSize == 0 && !bGotExtraKqueueEvent) { bGotExtraKqueueEvent = true; return; } else if (OutboundDataSize > 0) { bGotExtraKqueueEvent = false; } } #endif _WriteOutboundData(); } }