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 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(); } } }
long long CatStream::seek(long long offset, Anchor anchor) { if (offset == 0 && anchor == CURRENT) return m_pos; MORDOR_ASSERT(m_seekable); std::vector<Stream::ptr>::iterator it = m_it; long long itOffset = 0; switch (anchor) { case BEGIN: it = m_streams.begin(); if (it != m_streams.end()) itOffset = (*it)->tell(); break; case CURRENT: break; case END: it = m_streams.end(); break; default: MORDOR_NOTREACHED(); } long long pos = m_pos; while (offset != 0) { if (offset < 0) { if (itOffset == 0) { if (it == m_streams.begin()) throw std::invalid_argument("Can't seek below 0"); --it; itOffset = (*it)->size(); } long long toChange = std::min(-offset, itOffset); itOffset -= toChange; pos -= toChange; offset += toChange; } else { long long toChange = offset; if (it != m_streams.end()) toChange = std::min(offset, (*it)->size() - itOffset); itOffset += toChange; pos += toChange; offset -= toChange; if (it != m_streams.end() && itOffset == (*it)->size()) { ++it; itOffset = 0; } } } if (it != m_streams.end() && it != m_it) (*it)->seek(itOffset); m_it = it; if (it == m_streams.end()) pos = m_size + itOffset; return m_pos = pos; }
long long BufferedStream::seek(long long offset, Anchor anchor) { MORDOR_ASSERT(parent()->supportsTell()); long long parentPos = parent()->tell(); long long bufferedPos = parentPos - m_readBuffer.readAvailable() + m_writeBuffer.readAvailable(); long long parentSize = parent()->supportsSize() ? -1ll : parent()->size(); // Check for no change in position if ((offset == 0 && anchor == CURRENT) || (offset == bufferedPos && anchor == BEGIN) || (parentSize != -1ll && offset + parentSize == bufferedPos && anchor == END)) return bufferedPos; MORDOR_ASSERT(supportsSeek()); flush(false); MORDOR_ASSERT(m_writeBuffer.readAvailable() == 0u); switch (anchor) { case BEGIN: MORDOR_ASSERT(offset >= 0); if (offset >= bufferedPos && offset <= parentPos) { m_readBuffer.consume((size_t)(offset - bufferedPos)); return offset; } m_readBuffer.clear(); break; case CURRENT: if (offset > 0 && offset <= (long long)m_readBuffer.readAvailable()) { m_readBuffer.consume((size_t)offset); return bufferedPos + offset; } offset -= m_readBuffer.readAvailable(); m_readBuffer.clear(); break; case END: if (parentSize == -1ll) throw std::invalid_argument("Can't seek from end without known size"); if (parentSize + offset >= bufferedPos && parentSize + offset <= parentPos) { m_readBuffer.consume((size_t)(parentSize + offset - bufferedPos)); return parentSize + offset; } m_readBuffer.clear(); break; default: MORDOR_NOTREACHED(); } return parent()->seek(offset, anchor); }
static void convertNode(JSON::Value &value, yaml_node_t *node, yaml_document_t &document) { switch (node->type) { case YAML_SCALAR_NODE: value = std::string((char *)node->data.scalar.value, node->data.scalar.length); break; case YAML_SEQUENCE_NODE: { value = JSON::Array(); JSON::Array &array = value.get<JSON::Array>(); yaml_node_item_t *item = node->data.sequence.items.start; array.resize(node->data.sequence.items.top - item); JSON::Array::iterator it = array.begin(); while (item < node->data.sequence.items.top) { convertNode(*it, yaml_document_get_node(&document, *item), document); ++it; ++item; } break; } case YAML_MAPPING_NODE: { value = JSON::Object(); JSON::Object &object = value.get<JSON::Object>(); yaml_node_pair_t *pair = node->data.mapping.pairs.start; while (pair < node->data.mapping.pairs.top) { yaml_node_t *keyNode = yaml_document_get_node(&document, pair->key); yaml_node_t *valueNode = yaml_document_get_node(&document, pair->value); if (keyNode->type != YAML_SCALAR_NODE) MORDOR_THROW_EXCEPTION(std::runtime_error("Can't use a non-string as a key")); std::string key((char *)keyNode->data.scalar.value, keyNode->data.scalar.length); convertNode(object.insert(std::make_pair(key, JSON::Value()))->second, valueNode, document); ++pair; } break; } default: MORDOR_NOTREACHED(); } }
void ZlibStream::reset() { m_inBuffer.clear(); m_outBuffer.clear(false); if (!m_closed) { if (supportsRead()) { inflateEnd(&m_strm); } else { deflateEnd(&m_strm); } m_closed = true; } int rc; memset(&m_strm, 0, sizeof(z_stream)); if (supportsRead()) { rc = inflateInit2(&m_strm, m_windowBits); } else { rc = deflateInit2(&m_strm, m_level, Z_DEFLATED, m_windowBits, m_memlevel, (int)m_strategy); } switch (rc) { case Z_OK: m_closed = false; break; case Z_MEM_ERROR: throw std::bad_alloc(); case Z_STREAM_ERROR: { std::string message(m_strm.msg ? m_strm.msg : ""); if (supportsRead()) { inflateEnd(&m_strm); } else { deflateEnd(&m_strm); } throw std::runtime_error(message); } default: MORDOR_NOTREACHED(); } }
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 Config::request(ServerRequest::ptr request, Access access) { const std::string &method = request->request().requestLine.method; if (method == POST) { if (access != READWRITE) { respondError(request, FORBIDDEN); return; } if (request->request().entity.contentType.type != "application" || request->request().entity.contentType.subtype != "x-www-form-urlencoded") { respondError(request, UNSUPPORTED_MEDIA_TYPE); return; } Stream::ptr requestStream = request->requestStream(); requestStream.reset(new LimitedStream(requestStream, 65536)); MemoryStream requestBody; transferStream(requestStream, requestBody); std::string queryString; queryString.resize(requestBody.buffer().readAvailable()); requestBody.buffer().copyOut(&queryString[0], requestBody.buffer().readAvailable()); bool failed = false; URI::QueryString qs(queryString); for (URI::QueryString::const_iterator it = qs.begin(); it != qs.end(); ++it) { ConfigVarBase::ptr var = Mordor::Config::lookup(it->first); if (var && !var->fromString(it->second)) failed = true; } if (failed) { respondError(request, HTTP::FORBIDDEN, "One or more new values were not accepted"); return; } // Fall through } if (method == GET || method == HEAD || method == POST) { Format format = HTML; URI::QueryString qs; if (request->request().requestLine.uri.queryDefined()) qs = request->request().requestLine.uri.queryString(); URI::QueryString::const_iterator it = qs.find("alt"); if (it != qs.end() && it->second == "json") format = JSON; // TODO: use Accept to indicate JSON switch (format) { case HTML: { request->response().status.status = OK; request->response().entity.contentType = MediaType("text", "html"); if (method == HEAD) { if (request->request().requestLine.ver == Version(1, 1) && isAcceptable(request->request().request.te, "chunked", true)) { request->response().general.transferEncoding.push_back("chunked"); } return; } Stream::ptr response = request->responseStream(); response.reset(new BufferedStream(response)); response->write("<html><body><table>\n", 20); Mordor::Config::visit(boost::bind(access == READWRITE ? &eachConfigVarHTMLWrite : &eachConfigVarHTML, _1, response)); response->write("</table></body></html>", 22); response->close(); break; } case JSON: { JSON::Object root; Mordor::Config::visit(boost::bind(&eachConfigVarJSON, _1, boost::ref(root))); std::ostringstream os; os << root; std::string str = os.str(); request->response().status.status = OK; request->response().entity.contentType = MediaType("application", "json"); request->response().entity.contentLength = str.size(); if (method != HEAD) { request->responseStream()->write(str.c_str(), str.size()); request->responseStream()->close(); } break; } default: MORDOR_NOTREACHED(); } } else { respondError(request, METHOD_NOT_ALLOWED); } }
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(); } }
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(); } } }
bool getCredentialsFromKeychain(const URI &uri, ClientRequest::ptr priorRequest, std::string &scheme, std::string &realm, std::string &username, std::string &password, size_t attempts) { if (attempts != 1) return false; bool proxy = priorRequest->response().status.status == PROXY_AUTHENTICATION_REQUIRED; const ChallengeList &challengeList = proxy ? priorRequest->response().response.proxyAuthenticate : priorRequest->response().response.wwwAuthenticate; if (isAcceptable(challengeList, "Basic")) scheme = "Basic"; else if (isAcceptable(challengeList, "Digest")) scheme = "Digest"; else return false; std::vector<SecKeychainAttribute> attrVector; std::string host = uri.authority.host(); attrVector.push_back((SecKeychainAttribute){kSecServerItemAttr, host.size(), (void *)host.c_str()}); UInt32 port = 0; if (uri.authority.portDefined()) { port = uri.authority.port(); attrVector.push_back((SecKeychainAttribute){kSecPortItemAttr, sizeof(UInt32), &port}); } SecProtocolType protocol; if (proxy && priorRequest->request().requestLine.method == CONNECT) protocol = kSecProtocolTypeHTTPSProxy; else if (proxy) protocol = kSecProtocolTypeHTTPProxy; else if (uri.scheme() == "https") protocol = kSecProtocolTypeHTTPS; else if (uri.scheme() == "http") protocol = kSecProtocolTypeHTTP; else MORDOR_NOTREACHED(); attrVector.push_back((SecKeychainAttribute){kSecProtocolItemAttr, sizeof(SecProtocolType), &protocol}); ScopedCFRef<SecKeychainSearchRef> search; SecKeychainAttributeList attrList; attrList.count = (UInt32)attrVector.size(); attrList.attr = &attrVector[0]; OSStatus status = SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, &attrList, &search); if (status != 0) return false; ScopedCFRef<SecKeychainItemRef> item; status = SecKeychainSearchCopyNext(search, &item); if (status != 0) return false; SecKeychainAttributeInfo info; SecKeychainAttrType tag = kSecAccountItemAttr; CSSM_DB_ATTRIBUTE_FORMAT format = CSSM_DB_ATTRIBUTE_FORMAT_STRING; info.count = 1; info.tag = (UInt32 *)&tag; info.format = (UInt32 *)&format; SecKeychainAttributeList *attrs; UInt32 passwordLength = 0; void *passwordBytes = NULL; status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attrs, &passwordLength, &passwordBytes); if (status != 0) return false; try { username.assign((const char *)attrs->attr[0].data, attrs->attr[0].length); password.assign((const char *)passwordBytes, passwordLength); } catch (...) { SecKeychainItemFreeContent(attrs, passwordBytes); throw; } SecKeychainItemFreeContent(attrs, passwordBytes); return true; }
Result PreparedStatement::execute() { PGconn *conn = m_conn.lock().get(); boost::shared_ptr<PGresult> result, next; int nParams = (int)m_params.size(); Oid *paramTypes = NULL; int *paramLengths = NULL, *paramFormats = NULL; const char **params = NULL; if (nParams) { if (m_name.empty()) paramTypes = &m_paramTypes[0]; params = &m_params[0]; paramLengths = &m_paramLengths[0]; paramFormats = &m_paramFormats[0]; } const char *api = NULL; #ifndef WINDOWS SchedulerSwitcher switcher(m_scheduler); #endif if (m_name.empty()) { #ifndef WINDOWS if (m_scheduler) { api = "PQsendQueryParams"; if (!PQsendQueryParams(conn, m_command.c_str(), nParams, paramTypes, params, paramLengths, paramFormats, m_resultFormat)) throwException(conn); flush(conn, m_scheduler); next.reset(nextResult(conn, m_scheduler), &PQclear); while (next) { result = next; next.reset(nextResult(conn, m_scheduler), &PQclear); if (next) { ExecStatusType status = PQresultStatus(next.get()); MORDOR_LOG_VERBOSE(g_log) << conn << "PQresultStatus(" << next.get() << "): " << PQresStatus(status); switch (status) { case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: break; default: throwException(next.get()); MORDOR_NOTREACHED(); } } } } else #endif { api = "PQexecParams"; result.reset(PQexecParams(conn, m_command.c_str(), nParams, paramTypes, params, paramLengths, paramFormats, m_resultFormat), &PQclear); } } else { #ifndef WINDOWS if (m_scheduler) { api = "PQsendQueryPrepared"; if (!PQsendQueryPrepared(conn, m_name.c_str(), nParams, params, paramLengths, paramFormats, 1)) throwException(conn); flush(conn, m_scheduler); next.reset(nextResult(conn, m_scheduler), &PQclear); while (next) { result = next; next.reset(nextResult(conn, m_scheduler), &PQclear); if (next) { ExecStatusType status = PQresultStatus(next.get()); MORDOR_LOG_VERBOSE(g_log) << conn << "PQresultStatus(" << next.get() << "): " << PQresStatus(status); switch (status) { case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: break; default: throwException(next.get()); MORDOR_NOTREACHED(); } } } } else #endif { api = "PQexecPrepared"; result.reset(PQexecPrepared(conn, m_name.c_str(), nParams, params, paramLengths, paramFormats, m_resultFormat), &PQclear); } } if (!result) throwException(conn); ExecStatusType status = PQresultStatus(result.get()); MORDOR_ASSERT(api); MORDOR_LOG_VERBOSE(g_log) << conn << " " << api << "(\"" << m_command << m_name << "\", " << nParams << "), PQresultStatus(" << result.get() << "): " << PQresStatus(status); switch (status) { case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: return Result(result); default: throwException(result.get()); MORDOR_NOTREACHED(); } }
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(); } } }
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(); }