void SSLStream::flush(bool flushParent) { static const int WRITE_BUF_LENGTH = 4096; char writeBuf[WRITE_BUF_LENGTH]; int toWrite = 0; do { toWrite = BIO_read(m_writeBio, (void *)writeBuf, WRITE_BUF_LENGTH); if (toWrite > 0) { m_writeBuffer.copyIn((const void *)writeBuf, toWrite); } } while (toWrite > 0); if (m_writeBuffer.readAvailable() == 0) return; while (m_writeBuffer.readAvailable()) { MORDOR_LOG_TRACE(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << ")"; size_t written = parent()->write(m_writeBuffer, m_writeBuffer.readAvailable()); MORDOR_LOG_TRACE(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << "): " << written; m_writeBuffer.consume(written); } if (flushParent) parent()->flush(flushParent); }
ptrdiff_t BufferedStream::find(const std::string &str, size_t sanitySize, bool throwIfNotFound) { if (supportsSeek()) flush(false); if (sanitySize == (size_t)~0) sanitySize = 2 * m_bufferSize; sanitySize += str.size(); while (true) { size_t readAvailable = m_readBuffer.readAvailable(); if (readAvailable > 0) { ptrdiff_t result = m_readBuffer.find(str, std::min(sanitySize, readAvailable)); if (result != -1) { return result; } } if (readAvailable >= sanitySize) { if (throwIfNotFound) MORDOR_THROW_EXCEPTION(BufferOverflowException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << m_bufferSize << ")"; size_t result = parent()->read(m_readBuffer, m_bufferSize); MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << m_bufferSize << "): " << result; if (result == 0) { // EOF if (throwIfNotFound) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } } }
void EventLoop::tickle() { MORDOR_LOG_TRACE(g_log) << m_messageWindow << " tickling"; if (!PostMessage(m_messageWindow, g_tickleMessage, 0, 0)) MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("PostMessage"); }
IOManagerEPoll::~IOManagerEPoll() { stop(); close(m_epfd); MORDOR_LOG_TRACE(g_log) << this << " close(" << m_epfd << ")"; close(m_tickleFds[0]); MORDOR_LOG_VERBOSE(g_log) << this << " close(" << m_tickleFds[0] << ")"; close(m_tickleFds[1]); }
std::vector<boost::function<void ()> > TimerManager::processTimers() { std::vector<Timer::ptr> expired; std::vector<boost::function<void ()> > result; unsigned long long nowUs = now(); { boost::mutex::scoped_lock lock(m_mutex); if (m_timers.empty()) return result; bool rollover = detectClockRollover(nowUs); if (!rollover && (*m_timers.begin())->m_next > nowUs) return result; Timer nowTimer(nowUs); Timer::ptr nowTimerPtr(&nowTimer, &nop<Timer *>); // Find all timers that are expired std::set<Timer::ptr, Timer::Comparator>::iterator it = rollover ? m_timers.end() : m_timers.lower_bound(nowTimerPtr); while (it != m_timers.end() && (*it)->m_next == nowUs ) ++it; // Copy to expired, remove from m_timers; expired.insert(expired.begin(), m_timers.begin(), it); m_timers.erase(m_timers.begin(), it); result.reserve(expired.size()); // Look at expired timers and re-register recurring timers // (while under the same lock) for (std::vector<Timer::ptr>::iterator it2(expired.begin()); it2 != expired.end(); ++it2) { Timer::ptr &timer = *it2; MORDOR_ASSERT(timer->m_dg); result.push_back(timer->m_dg); if (timer->m_recurring) { MORDOR_LOG_TRACE(g_log) << timer << " expired and refreshed"; timer->m_next = nowUs + timer->m_us; m_timers.insert(timer); } else { MORDOR_LOG_TRACE(g_log) << timer << " expired"; timer->m_dg = NULL; } } } return result; }
LRESULT CALLBACK EventLoop::foregroundIdleProc(int nCode, WPARAM wParam, LPARAM lParam) { EventLoop *self = (EventLoop *)Scheduler::getThis(); MORDOR_LOG_TRACE(g_log) << self->m_messageWindow << " message pump idle"; if (self->hasWorkToDo()) self->tickle(); return CallNextHookEx(self->m_idleHook, nCode, wParam, lParam); }
void SSLStream::wantRead() { if (m_readBuffer.readAvailable() == 0) { MORDOR_LOG_TRACE(g_log) << this << " parent()->read(32768)"; const size_t result = parent()->read(m_readBuffer, 32768); MORDOR_LOG_TRACE(g_log) << this << " parent()->read(32768): " << result; if (result == 0) { BIO_set_mem_eof_return(m_readBio, 0); return; } } MORDOR_ASSERT(m_readBuffer.readAvailable()); const iovec iov = m_readBuffer.readBuffer(~0, false); MORDOR_ASSERT(iov.iov_len > 0); const int written = BIO_write(m_readBio, (char *)iov.iov_base, iov.iov_len); MORDOR_ASSERT(written > 0); if (written > 0) { m_readBuffer.consume(written); } MORDOR_LOG_DEBUG(g_log) << this << " wantRead(): " << written; }
LRESULT CALLBACK EventLoop::wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { EventLoop *self = (EventLoop *)Scheduler::getThis(); MORDOR_ASSERT(self->m_messageWindow == hWnd || self->m_messageWindow == NULL); if (uMsg == g_tickleMessage) { MORDOR_LOG_TRACE(g_log) << hWnd << " received tickle"; Scheduler::yield(); return 0; } switch (uMsg) { case WM_TIMER: { MORDOR_LOG_TRACE(g_log) << hWnd << " processing timers"; std::vector<boost::function<void ()> > expired = self->processTimers(); if (!expired.empty()) self->schedule(expired.begin(), expired.end()); unsigned long long nextTimer = self->nextTimer(); if (nextTimer != ~0ull) { UINT uElapse = (UINT)((nextTimer / 1000) + 1); uElapse = std::max<UINT>(USER_TIMER_MINIMUM, uElapse); uElapse = std::min<UINT>(USER_TIMER_MAXIMUM, uElapse); if (!SetTimer(hWnd, 1, uElapse, NULL)) MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SetTimer"); } else { if (!KillTimer(hWnd, 1)) MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("KillTimer"); } return 0; } default: return DefWindowProcW(hWnd, uMsg, wParam, lParam); } }
size_t BufferedStream::readInternal(T &buffer, size_t length) { if (supportsSeek()) flush(false); size_t remaining = length; size_t buffered = std::min(m_readBuffer.readAvailable(), remaining); m_readBuffer.copyOut(buffer, buffered); m_readBuffer.consume(buffered); remaining -= buffered; MORDOR_LOG_VERBOSE(g_log) << this << " read(" << length << "): " << buffered << " read from buffer"; if (remaining == 0) return length; if (buffered == 0 || !m_allowPartialReads) { size_t result; do { // Read enough to satisfy this request, plus up to a multiple of // the buffer size size_t todo = ((remaining - 1) / m_bufferSize + 1) * m_bufferSize; try { MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << todo << ")"; result = parent()->read(m_readBuffer, todo); MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << todo << "): " << result; } catch (...) { if (remaining == length) { MORDOR_LOG_VERBOSE(g_log) << this << " forwarding exception"; throw; } else { MORDOR_LOG_VERBOSE(g_log) << this << " swallowing exception"; // Swallow the exception return length - remaining; } } buffered = std::min(m_readBuffer.readAvailable(), remaining); m_readBuffer.copyOut(buffer, buffered); m_readBuffer.consume(buffered); advance(buffer, buffered); remaining -= buffered; } while (remaining > 0 && !m_allowPartialReads && result != 0); } return length - remaining; }
size_t BufferedStream::flushWrite(size_t length) { while (m_writeBuffer.readAvailable() >= m_bufferSize) { size_t result; try { if (m_readBuffer.readAvailable() && supportsSeek()) { parent()->seek(-(long long)m_readBuffer.readAvailable(), CURRENT); m_readBuffer.clear(); } size_t toWrite = m_writeBuffer.readAvailable(); if (m_flushMultiplesOfBuffer) toWrite = toWrite / m_bufferSize * m_bufferSize; MORDOR_LOG_TRACE(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << ")"; result = parent()->write(m_writeBuffer, toWrite); MORDOR_LOG_DEBUG(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << "): " << result; m_writeBuffer.consume(result); } catch (...) { // If this entire write is still in our buffer, // back it out and report the error if (m_writeBuffer.readAvailable() >= length) { MORDOR_LOG_VERBOSE(g_log) << this << " forwarding exception"; Buffer tempBuffer; tempBuffer.copyIn(m_writeBuffer, m_writeBuffer.readAvailable() - length); m_writeBuffer.clear(); m_writeBuffer.copyIn(tempBuffer); throw; } else { // Otherwise we have to say we succeeded, // because we're not allowed to have a partial // write, and we can't report an error because // the caller will think he needs to repeat // the entire write MORDOR_LOG_VERBOSE(g_log) << this << " swallowing exception"; return length; } } } return length; }
void BufferedStream::flush(bool flushParent) { while (m_writeBuffer.readAvailable()) { if (m_readBuffer.readAvailable() && supportsSeek()) { parent()->seek(-(long long)m_readBuffer.readAvailable(), CURRENT); m_readBuffer.clear(); } MORDOR_LOG_TRACE(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << ")"; size_t result = parent()->write(m_writeBuffer, m_writeBuffer.readAvailable()); MORDOR_LOG_DEBUG(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << "): " << result; MORDOR_ASSERT(result > 0); m_writeBuffer.consume(result); } if (flushParent) parent()->flush(); }
bool NegotiateAuth::authorize(const AuthParams &challenge, AuthParams &authorization, const URI &uri) { SECURITY_STATUS status; std::wstring packageW = toUtf16(challenge.scheme); std::string param = challenge.base64; std::string outboundBuffer; SecBufferDesc outboundBufferDesc; SecBuffer outboundSecBuffer; TimeStamp lifetime; ULONG contextAttributes; outboundBuffer.resize(4096); outboundBufferDesc.ulVersion = 0; outboundBufferDesc.cBuffers = 1; outboundBufferDesc.pBuffers = &outboundSecBuffer; outboundSecBuffer.BufferType = SECBUFFER_TOKEN; outboundSecBuffer.pvBuffer = &outboundBuffer[0]; outboundSecBuffer.cbBuffer = (unsigned long)outboundBuffer.size(); if (param.empty()) { // No response from server; we're starting a new session if (SecIsValidHandle(&m_creds)) return false; SEC_WINNT_AUTH_IDENTITY_W id; id.User = (unsigned short *)m_username.c_str(); id.UserLength = (unsigned long)m_username.size(); id.Domain = (unsigned short *)m_domain.c_str(); id.DomainLength = (unsigned long)m_domain.size(); id.Password = (unsigned short *)m_password.c_str(); id.PasswordLength = (unsigned long)m_password.size(); id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; status = AcquireCredentialsHandleW(NULL, (wchar_t *)packageW.c_str(), SECPKG_CRED_OUTBOUND, NULL, m_username.empty() ? NULL : &id, NULL, NULL, &m_creds, &lifetime); MORDOR_LOG_TRACE(g_log) << "AcquireCredentialsHandleW(" << challenge.scheme << ", " << toUtf8(m_username) << "): (" << status << ")"; if (!SUCCEEDED(status)) MORDOR_THROW_EXCEPTION_FROM_ERROR_API(status, "AcquireCredentialsHandleW"); status = InitializeSecurityContextW( &m_creds, NULL, (wchar_t *)toUtf16(uri.toString()).c_str(), ISC_REQ_CONFIDENTIALITY, 0, SECURITY_NATIVE_DREP, NULL, 0, &m_secCtx, &outboundBufferDesc, &contextAttributes, &lifetime); MORDOR_LOG_TRACE(g_log) << "InitializeSecurityContextW(" << uri << ", {0}): {" << outboundSecBuffer.cbBuffer << "} (" << status << ")"; } else { // Prepare the response from the server std::string inboundBuffer = base64decode(param); SecBufferDesc inboundBufferDesc; SecBuffer inboundSecBuffer; inboundBufferDesc.ulVersion = 0; inboundBufferDesc.cBuffers = 1; inboundBufferDesc.pBuffers = &inboundSecBuffer; inboundSecBuffer.BufferType = SECBUFFER_TOKEN; inboundSecBuffer.pvBuffer = &inboundBuffer[0]; inboundSecBuffer.cbBuffer = (unsigned long)inboundBuffer.size(); status = InitializeSecurityContextW( &m_creds, &m_secCtx, (wchar_t *)toUtf16(uri.toString()).c_str(), ISC_REQ_CONFIDENTIALITY, 0, SECURITY_NATIVE_DREP, &inboundBufferDesc, 0, &m_secCtx, &outboundBufferDesc, &contextAttributes, &lifetime); MORDOR_LOG_TRACE(g_log) << "InitializeSecurityContextW(" << uri << ", {" << inboundSecBuffer.cbBuffer << "}): {" << outboundSecBuffer.cbBuffer << "} (" << status << ")"; } if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) { status = CompleteAuthToken(&m_secCtx, &outboundBufferDesc); MORDOR_LOG_TRACE(g_log) << "CompleteAuthToken(): {" << outboundSecBuffer.cbBuffer << "} (" << status << ")"; } if (!SUCCEEDED(status)) MORDOR_THROW_EXCEPTION_FROM_ERROR(status); outboundBuffer.resize(outboundSecBuffer.cbBuffer); authorization.scheme = challenge.scheme; authorization.base64 = base64encode(outboundBuffer); authorization.parameters.clear(); return true; }
void IOManagerEPoll::idle() { epoll_event events[64]; while (true) { unsigned long long nextTimeout; if (stopping(nextTimeout)) return; int rc = -1; errno = EINTR; while (rc < 0 && errno == EINTR) { int timeout = -1; if (nextTimeout != ~0ull) timeout = (int)(nextTimeout / 1000) + 1; rc = epoll_wait(m_epfd, events, 64, timeout); if (rc < 0 && errno == EINTR) nextTimeout = nextTimer(); } MORDOR_LOG_LEVEL(g_log, rc < 0 ? Log::ERROR : Log::VERBOSE) << this << " epoll_wait(" << m_epfd << "): " << rc << " (" << errno << ")"; if (rc < 0) MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("epoll_wait"); std::vector<boost::function<void ()> > expired = processTimers(); schedule(expired.begin(), expired.end()); for(int i = 0; i < rc; ++i) { epoll_event &event = events[i]; if (event.data.fd == m_tickleFds[0]) { unsigned char dummy; int rc2 = read(m_tickleFds[0], &dummy, 1); MORDOR_VERIFY(rc2 == 1); MORDOR_LOG_VERBOSE(g_log) << this << " received tickle"; continue; } bool err = event.events & (EPOLLERR | EPOLLHUP); boost::mutex::scoped_lock lock(m_mutex); std::map<int, AsyncEvent>::iterator it = m_pendingEvents.find(event.data.fd); if (it == m_pendingEvents.end()) continue; AsyncEvent &e = it->second; MORDOR_LOG_TRACE(g_log) << " epoll_event {" << (epoll_events_t)event.events << ", " << event.data.fd << "}, registered for " << (epoll_events_t)e.event.events; if ((event.events & EPOLLERR) && (e.event.events & EPOLLERR)) { if (e.m_dgError) e.m_schedulerError->schedule(e.m_dgError); else e.m_schedulerError->schedule(e.m_fiberError); // Block other events from firing e.m_dgError = NULL; e.m_fiberError.reset(); e.m_dgIn = NULL; e.m_fiberIn.reset(); e.m_dgOut = NULL; e.m_fiberOut.reset(); event.events = 0; e.event.events = 0; } if ((event.events & EPOLLHUP) && (e.event.events & EPOLLHUP)) { if (e.m_dgClose) e.m_schedulerError->schedule(e.m_dgClose); else e.m_schedulerError->schedule(e.m_fiberClose); // Block write event from firing e.m_dgOut = NULL; e.m_fiberOut.reset(); e.m_dgClose = NULL; e.m_fiberClose.reset(); event.events &= EPOLLOUT; e.event.events &= EPOLLOUT; err = false; } if (((event.events & EPOLLIN) || err) && (e.event.events & EPOLLIN)) { if (e.m_dgIn) e.m_schedulerIn->schedule(e.m_dgIn); else e.m_schedulerIn->schedule(e.m_fiberIn); e.m_dgIn = NULL; e.m_fiberIn.reset(); event.events |= EPOLLIN; } if (((event.events & EPOLLOUT) || err) && (e.event.events & EPOLLOUT)) { if (e.m_dgOut) e.m_schedulerOut->schedule(e.m_dgOut); else e.m_schedulerOut->schedule(e.m_fiberOut); e.m_dgOut = NULL; e.m_fiberOut.reset(); event.events |= EPOLLOUT; } e.event.events &= ~event.events; int op = e.event.events == 0 ? EPOLL_CTL_DEL : EPOLL_CTL_MOD; int rc2 = epoll_ctl(m_epfd, op, event.data.fd, &e.event); MORDOR_LOG_LEVEL(g_log, rc2 ? Log::ERROR : Log::VERBOSE) << this << " epoll_ctl(" << m_epfd << ", " << (epoll_ctl_op_t)op << ", " << event.data.fd << ", " << (epoll_events_t)e.event.events << "): " << rc2 << " (" << errno << ")"; if (rc2) MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("epoll_ctl"); if (op == EPOLL_CTL_DEL) m_pendingEvents.erase(it); } Fiber::yield(); } }