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; } } }
bool Timer::reset(unsigned long long us, bool fromNow) { boost::mutex::scoped_lock lock(m_manager->m_mutex); if (!m_dg) return false; // No change if (us == m_us && !fromNow) return true; std::set<Timer::ptr, Timer::Comparator>::iterator it = m_manager->m_timers.find(shared_from_this()); MORDOR_ASSERT(it != m_manager->m_timers.end()); m_manager->m_timers.erase(it); unsigned long long start; if (fromNow) start = TimerManager::now(); else start = m_next - m_us; m_us = us; m_next = start + m_us; it = m_manager->m_timers.insert(shared_from_this()).first; bool atFront = (it == m_manager->m_timers.begin()) && !m_manager->m_tickled; if (atFront) m_manager->m_tickled = true; lock.unlock(); MORDOR_LOG_DEBUG(g_log) << this << " reset to " << m_us; if (atFront) m_manager->onTimerInsertedAtFront(); return true; }
void ZlibStream::flush(int flush) { flushBuffer(); while (true) { if (m_outBuffer.writeAvailable() == 0) m_outBuffer.reserve(m_bufferSize); struct iovec outbuf = m_outBuffer.writeBuffer(~0u, false); MORDOR_ASSERT(m_strm.avail_in == 0); m_strm.next_out = (Bytef*)outbuf.iov_base; m_strm.avail_out = outbuf.iov_len; int rc = deflate(&m_strm, flush); MORDOR_ASSERT(m_strm.avail_in == 0); MORDOR_LOG_DEBUG(g_log) << this << " deflate((0, " << outbuf.iov_len << "), " << flush << "): " << rc << " (0, " << m_strm.avail_out << ")"; m_outBuffer.produce(outbuf.iov_len - m_strm.avail_out); MORDOR_ASSERT(flush == Z_FINISH || rc != Z_STREAM_END); switch (rc) { case Z_STREAM_END: m_closed = true; deflateEnd(&m_strm); flushBuffer(); return; case Z_OK: break; case Z_BUF_ERROR: flushBuffer(); return; default: MORDOR_NOTREACHED(); } } }
size_t SSLStream::read(void *buffer, size_t length) { const int toRead = (int)std::min<size_t>(0x0fffffff, length); while (true) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_read, m_ssl.get(), buffer, toRead), &error); if (result > 0) { return result; } MORDOR_LOG_DEBUG(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ")"; switch (error) { case SSL_ERROR_NONE: return result; case SSL_ERROR_ZERO_RETURN: // Received close_notify message MORDOR_ASSERT(result == 0); return 0; case SSL_ERROR_WANT_READ: wantRead(); continue; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_read"); ; } MORDOR_LOG_WARNING(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ")"; if (result == 0) { return 0; } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_read"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_read(" << m_ssl.get() << ", " << toRead << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_read"); ; } default: MORDOR_NOTREACHED(); } } }
void EventLoop::idle() { while (!stopping()) { MORDOR_LOG_DEBUG(g_log) << m_messageWindow << " Starting new message pump"; schedule(boost::bind(&EventLoop::messagePump, this)); Fiber::yield(); } }
void EventLoop::messagePump() { MORDOR_LOG_DEBUG(g_log) << m_messageWindow << " starting message pump"; while (!hasWorkToDo() && !stopping()) { MSG msg; BOOL bRet = GetMessageW(&msg, NULL, 0, 0); if (bRet < 0) MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("GetMessageW"); if (bRet == 0) { stop(); return; } TranslateMessage(&msg); DispatchMessageW(&msg); } MORDOR_LOG_DEBUG(g_log) << m_messageWindow << " exiting message pump"; }
unsigned long long TimerManager::nextTimer() { boost::mutex::scoped_lock lock(m_mutex); m_tickled = false; if (m_timers.empty()) { MORDOR_LOG_DEBUG(g_log) << this << " nextTimer(): ~0ull"; return ~0ull; } const Timer::ptr &next = *m_timers.begin(); unsigned long long nowUs = now(); unsigned long long result; if (nowUs >= next->m_next) result = 0; else result = next->m_next - nowUs; MORDOR_LOG_DEBUG(g_log) << this << " nextTimer(): " << result; return result; }
void Scheduler::dispatch() { MORDOR_LOG_DEBUG(g_log) << this << " dispatching"; MORDOR_ASSERT(m_rootThread == gettid() && m_threadCount == 0); m_stopping = true; m_autoStop = true; yieldTo(); m_autoStop = false; }
static void stubOnTimer( boost::weak_ptr<void> weakCond, boost::function<void ()> dg) { boost::shared_ptr<void> temp = weakCond.lock(); if (temp) { dg(); } else { MORDOR_LOG_DEBUG(g_log) << " Conditionally skip in stubOnTimer!"; } }
int Logger::writeLog(std::string& msg, Mordor::Log::Level log_level) { //show all g_logs Mordor::Log::visit(boost::bind(&printLogger, &(std::cout), _1)); MORDOR_LOG_ERROR(g_app_log)<<msg; MORDOR_LOG_INFO(g_app_log)<<msg; MORDOR_LOG_DEBUG(g_app_log)<<msg; //can not see it by default. should provide some config? return 0; }
void Scheduler::switchTo(tid_t thread) { MORDOR_ASSERT(Scheduler::getThis() != NULL); if (Scheduler::getThis() == this) { if (thread == emptytid() || thread == gettid()) return; } MORDOR_LOG_DEBUG(g_log) << this << " switching to thread " << thread; schedule(Fiber::getThis(), thread); Scheduler::yieldTo(); }
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; }
bool Scheduler::scheduleNoLock(boost::function<void ()> dg, tid_t thread) { MORDOR_LOG_DEBUG(g_log) << this << " scheduling " << dg << " on thread " << thread; MORDOR_ASSERT(dg); // Not thread-targeted, or this scheduler owns the targetted thread MORDOR_ASSERT(thread == emptytid() || thread == m_rootThread || contains(m_threads, thread)); FiberAndThread ft = {Fiber::ptr(), dg, thread }; bool tickleMe = m_fibers.empty(); m_fibers.push_back(ft); return tickleMe; }
bool Timer::cancel() { MORDOR_LOG_DEBUG(g_log) << this << " cancel"; boost::mutex::scoped_lock lock(m_manager->m_mutex); if (m_dg) { m_dg = NULL; std::set<Timer::ptr, Timer::Comparator>::iterator it = m_manager->m_timers.find(shared_from_this()); MORDOR_ASSERT(it != m_manager->m_timers.end()); m_manager->m_timers.erase(it); return true; } return false; }
void Scheduler::yieldTo() { Scheduler *self = Scheduler::getThis(); MORDOR_ASSERT(self); MORDOR_LOG_DEBUG(g_log) << self << " yielding to scheduler"; MORDOR_ASSERT(t_fiber.get()); if (self->m_rootThread == gettid() && (t_fiber->state() == Fiber::INIT || t_fiber->state() == Fiber::TERM)) { self->m_callingFiber = Fiber::getThis(); self->yieldTo(true); } else { self->yieldTo(false); } }
bool Timer::refresh() { boost::mutex::scoped_lock lock(m_manager->m_mutex); if (!m_dg) return false; std::set<Timer::ptr, Timer::Comparator>::iterator it = m_manager->m_timers.find(shared_from_this()); MORDOR_ASSERT(it != m_manager->m_timers.end()); m_manager->m_timers.erase(it); m_next = TimerManager::now() + m_us; m_manager->m_timers.insert(shared_from_this()); lock.unlock(); MORDOR_LOG_DEBUG(g_log) << this << " refresh"; return true; }
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; }
Timer::ptr TimerManager::registerTimer(unsigned long long us, boost::function<void ()> dg, bool recurring) { MORDOR_ASSERT(dg); Timer::ptr result(new Timer(us, dg, recurring, this)); boost::mutex::scoped_lock lock(m_mutex); std::set<Timer::ptr, Timer::Comparator>::iterator it = m_timers.insert(result).first; bool atFront = (it == m_timers.begin()) && !m_tickled; if (atFront) m_tickled = true; lock.unlock(); MORDOR_LOG_DEBUG(g_log) << result.get() << " registerTimer(" << us << ", " << recurring << "): " << atFront; if (atFront) onTimerInsertedAtFront(); return result; }
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(); }
size_t ZlibStream::write(const Buffer &buffer, size_t length) { MORDOR_ASSERT(!m_closed); flushBuffer(); while (true) { if (m_outBuffer.writeAvailable() == 0) m_outBuffer.reserve(m_bufferSize); struct iovec inbuf = buffer.readBuffer(length, false); struct iovec outbuf = m_outBuffer.writeBuffer(~0u, false); m_strm.next_in = (Bytef*)inbuf.iov_base; m_strm.avail_in = inbuf.iov_len; m_strm.next_out = (Bytef*)outbuf.iov_base; m_strm.avail_out = outbuf.iov_len; int rc = deflate(&m_strm, Z_NO_FLUSH); MORDOR_LOG_DEBUG(g_log) << this << " deflate((" << inbuf.iov_len << ", " << outbuf.iov_len << "), Z_NO_FLUSH): " << rc << " (" << m_strm.avail_in << ", " << m_strm.avail_out << ")"; // We are always providing both input and output MORDOR_ASSERT(rc != Z_BUF_ERROR); // We're not doing Z_FINISH, so we shouldn't get EOF MORDOR_ASSERT(rc != Z_STREAM_END); size_t result; switch(rc) { case Z_OK: result = inbuf.iov_len - m_strm.avail_in; if (result == 0) continue; m_outBuffer.produce(outbuf.iov_len - m_strm.avail_out); try { flushBuffer(); } catch (std::runtime_error) { // Swallow it } return result; default: MORDOR_NOTREACHED(); } } }
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; }
void Scheduler::stop() { // Already stopped if (m_rootFiber && m_threadCount == 0 && (m_rootFiber->state() == Fiber::TERM || m_rootFiber->state() == Fiber::INIT)) { MORDOR_LOG_VERBOSE(g_log) << this << " stopped"; m_stopping = true; // A derived class may inhibit stopping while it has things to do in // its idle loop, so we can't break early if (stopping()) return; } bool exitOnThisFiber = false; if (m_rootThread != emptytid()) { // A thread-hijacking scheduler must be stopped // from within itself to return control to the // original thread MORDOR_ASSERT(Scheduler::getThis() == this); if (Fiber::getThis() == m_callingFiber) { exitOnThisFiber = true; // First switch to the correct thread MORDOR_LOG_DEBUG(g_log) << this << " switching to root thread to stop"; switchTo(m_rootThread); } if (!m_callingFiber) exitOnThisFiber = true; } else { // A spawned-threads only scheduler cannot be stopped from within // itself... who would get control? MORDOR_ASSERT(Scheduler::getThis() != this); } m_stopping = true; for (size_t i = 0; i < m_threadCount; ++i) tickle(); if (m_rootFiber && (m_threadCount != 0u || Scheduler::getThis() != this)) tickle(); // Wait for all work to stop on this thread if (exitOnThisFiber) { while (!stopping()) { // Give this thread's run fiber a chance to kill itself off MORDOR_LOG_DEBUG(g_log) << this << " yielding to this thread to stop"; yieldTo(true); } } // Wait for other threads to stop if (exitOnThisFiber || Scheduler::getThis() != this) { MORDOR_LOG_DEBUG(g_log) << this << " waiting for other threads to stop"; std::vector<boost::shared_ptr<Thread> > threads; { boost::mutex::scoped_lock lock(m_mutex); threads.swap(m_threads); } for (std::vector<boost::shared_ptr<Thread> >::const_iterator it (threads.begin()); it != threads.end(); ++it) { (*it)->join(); } } MORDOR_LOG_VERBOSE(g_log) << this << " stopped"; }
void Scheduler::run() { t_scheduler = this; if (gettid() != m_rootThread) { // Running in own thread t_fiber = Fiber::getThis().get(); } else { // Hijacked a thread MORDOR_ASSERT(t_fiber.get() == Fiber::getThis().get()); } Fiber::ptr idleFiber(new Fiber(boost::bind(&Scheduler::idle, this))); MORDOR_LOG_VERBOSE(g_log) << this << " starting thread with idle fiber " << idleFiber; Fiber::ptr dgFiber; // use a vector for O(1) .size() std::vector<FiberAndThread> batch(m_batchSize); bool isActive = false; while (true) { batch.clear(); bool dontIdle = false; bool tickleMe = false; { boost::mutex::scoped_lock lock(m_mutex); // Kill ourselves off if needed if (m_threads.size() > m_threadCount && gettid() != m_rootThread) { // Accounting if (isActive) --m_activeThreadCount; // Kill off the idle fiber try { throw boost::enable_current_exception( OperationAbortedException()); } catch(...) { idleFiber->inject(boost::current_exception()); } // Detach our thread for (std::vector<boost::shared_ptr<Thread> > ::iterator it = m_threads.begin(); it != m_threads.end(); ++it) if ((*it)->tid() == gettid()) { m_threads.erase(it); if (m_threads.size() > m_threadCount) tickle(); return; } MORDOR_NOTREACHED(); } std::list<FiberAndThread>::iterator it(m_fibers.begin()); while (it != m_fibers.end()) { // If we've met our batch size, and we're not checking to see // if we need to tickle another thread, then break if ( (tickleMe || m_activeThreadCount == threadCount()) && batch.size() == m_batchSize) break; if (it->thread != emptytid() && it->thread != gettid()) { MORDOR_LOG_DEBUG(g_log) << this << " skipping item scheduled for thread " << it->thread; // Wake up another thread to hopefully service this tickleMe = true; dontIdle = true; ++it; continue; } MORDOR_ASSERT(it->fiber || it->dg); // This fiber is still executing; probably just some race // race condition that it needs to yield on one thread // before running on another thread if (it->fiber && it->fiber->state() == Fiber::EXEC) { MORDOR_LOG_DEBUG(g_log) << this << " skipping executing fiber " << it->fiber; ++it; dontIdle = true; continue; } // We were just checking if there is more work; there is, so // set the flag and don't actually take this piece of work if (batch.size() == m_batchSize) { tickleMe = true; break; } batch.push_back(*it); it = m_fibers.erase(it); if (!isActive) { ++m_activeThreadCount; isActive = true; } } if (batch.empty() && isActive) { --m_activeThreadCount; isActive = false; } } if (tickleMe) tickle(); MORDOR_LOG_DEBUG(g_log) << this << " got " << batch.size() << " fiber/dgs to process (max: " << m_batchSize << ", active: " << isActive << ")"; MORDOR_ASSERT(isActive == !batch.empty()); if (!batch.empty()) { std::vector<FiberAndThread>::iterator it; for (it = batch.begin(); it != batch.end(); ++it) { Fiber::ptr f = it->fiber; boost::function<void ()> dg = it->dg; try { if (f && f->state() != Fiber::TERM) { MORDOR_LOG_DEBUG(g_log) << this << " running " << f; f->yieldTo(); } else if (dg) { if (!dgFiber) dgFiber.reset(new Fiber(dg)); dgFiber->reset(dg); MORDOR_LOG_DEBUG(g_log) << this << " running " << dg; dgFiber->yieldTo(); if (dgFiber->state() != Fiber::TERM) dgFiber.reset(); else dgFiber->reset(NULL); } } catch (...) { MORDOR_LOG_FATAL(Log::root()) << boost::current_exception_diagnostic_information(); throw; } } continue; } if (dontIdle) continue; if (idleFiber->state() == Fiber::TERM) { MORDOR_LOG_DEBUG(g_log) << this << " idle fiber terminated"; if (gettid() == m_rootThread) m_callingFiber.reset(); // Unblock the next thread if (threadCount() > 1) tickle(); return; } MORDOR_LOG_DEBUG(g_log) << this << " idling"; idleFiber->call(); } }
void SSLStream::close(CloseType type) { MORDOR_ASSERT(type == BOTH); if (!(sslCallWithLock(std::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_SENT_SHUTDOWN)) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_shutdown, m_ssl.get()), &error); if (result <= 0) { MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; switch (error) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result == 0) break; MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_shutdown"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } default: MORDOR_NOTREACHED(); } } flush(false); } while (!(sslCallWithLock(std::bind(SSL_get_shutdown, m_ssl.get()), NULL) & SSL_RECEIVED_SHUTDOWN)) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_shutdown, m_ssl.get()), &error); MORDOR_LOG_DEBUG(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result > 0) { break; } switch (error) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: break; case SSL_ERROR_WANT_READ: flush(); wantRead(); continue; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } MORDOR_LOG_WARNING(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result == 0) { break; } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_shutdown"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_shutdown(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_shutdown"); ; } default: MORDOR_NOTREACHED(); } break; } parent()->close(); }
void SSLStream::connect() { while (true) { unsigned long error = SSL_ERROR_NONE; const int result = sslCallWithLock(std::bind(SSL_connect, m_ssl.get()), &error); MORDOR_LOG_DEBUG(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result > 0) { flush(false); return; } switch (error) { case SSL_ERROR_NONE: flush(false); return; case SSL_ERROR_ZERO_RETURN: // Received close_notify message return; case SSL_ERROR_WANT_READ: flush(); wantRead(); continue; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: MORDOR_NOTREACHED(); case SSL_ERROR_SYSCALL: if (hasOpenSSLError()) { std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_connect"); ; } MORDOR_LOG_ERROR(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ")"; if (result == 0) { MORDOR_THROW_EXCEPTION(UnexpectedEofException()); } MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("SSL_connect"); case SSL_ERROR_SSL: { MORDOR_ASSERT(hasOpenSSLError()); std::string message = getOpenSSLErrorMessage(); MORDOR_LOG_ERROR(g_log) << this << " SSL_connect(" << m_ssl.get() << "): " << result << " (" << error << ", " << message << ")"; MORDOR_THROW_EXCEPTION(OpenSSLException(message)) // << boost::errinfo_api_function("SSL_connect"); ; } default: MORDOR_NOTREACHED(); } } }
size_t ZlibStream::read(Buffer &buffer, size_t length) { if (m_closed) return 0; struct iovec outbuf = buffer.writeBuffer(length, false); m_strm.next_out = (Bytef*)outbuf.iov_base; m_strm.avail_out = outbuf.iov_len; while (true) { std::vector<iovec> inbufs = m_inBuffer.readBuffers(); size_t avail_in; if (!inbufs.empty()) { m_strm.next_in = (Bytef*)inbufs[0].iov_base; avail_in = inbufs[0].iov_len; m_strm.avail_in = inbufs[0].iov_len; } else { m_strm.next_in = NULL; m_strm.avail_in = 0; } int rc = inflate(&m_strm, Z_NO_FLUSH); MORDOR_LOG_DEBUG(g_log) << this << " inflate((" << (inbufs.empty() ? 0 : inbufs[0].iov_len) << ", " << outbuf.iov_len << ")): " << rc << " (" << m_strm.avail_in << ", " << m_strm.avail_out << ")"; if (!inbufs.empty()) m_inBuffer.consume(inbufs[0].iov_len - m_strm.avail_in); size_t result; switch (rc) { case Z_STREAM_END: // May have still produced output result = outbuf.iov_len - m_strm.avail_out; buffer.produce(result); inflateEnd(&m_strm); m_closed = true; return result; case Z_OK: result = outbuf.iov_len - m_strm.avail_out; // It consumed input, but produced no output... DON'T return eof if (result == 0) continue; buffer.produce(result); return result; case Z_BUF_ERROR: // no progress... we need to provide more input (since we're // guaranteed to provide output) MORDOR_ASSERT(m_strm.avail_in == 0); MORDOR_ASSERT(inbufs.empty()); result = parent()->read(m_inBuffer, m_bufferSize); if (result == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); break; case Z_MEM_ERROR: throw std::bad_alloc(); case Z_NEED_DICT: MORDOR_THROW_EXCEPTION(NeedPresetDictionaryException()); case Z_DATA_ERROR: MORDOR_THROW_EXCEPTION(CorruptedZlibStreamException()); default: MORDOR_NOTREACHED(); } } }