static void writeALot(Stream::ptr stream) { for (size_t i = 0; i < A_LOT_OF_ITERATIONS; ++i) { MORDOR_TEST_ASSERT_EQUAL(stream->write("t", 1u), 1u); stream->flush(false); } }
MORDOR_UNITTEST(SSLStream, basic) { WorkerPool pool; std::pair<Stream::ptr, Stream::ptr> pipes = pipeStream(); SSLStream::ptr sslserver(new SSLStream(pipes.first, false)); SSLStream::ptr sslclient(new SSLStream(pipes.second, true)); pool.schedule(boost::bind(&accept, sslserver)); sslclient->connect(); pool.dispatch(); Stream::ptr server = sslserver, client = sslclient; char buf[6]; buf[5] = '\0'; client->write("hello"); client->flush(false); MORDOR_TEST_ASSERT_EQUAL(server->read(buf, 5), 5u); MORDOR_TEST_ASSERT_EQUAL((const char *)buf, "hello"); server->write("world"); server->flush(false); MORDOR_TEST_ASSERT_EQUAL(client->read(buf, 5), 5u); MORDOR_TEST_ASSERT_EQUAL((const char *)buf, "world"); }
MORDOR_UNITTEST(SSLStream, forceDuplex) { WorkerPool pool; std::pair<Stream::ptr, Stream::ptr> pipes = pipeStream(); SSLStream::ptr sslserver(new SSLStream(pipes.first, false)); SSLStream::ptr sslclient(new SSLStream(pipes.second, true)); Stream::ptr server = sslserver, client = sslclient; int sequence = 0; pool.schedule(boost::bind(&accept, sslserver)); sslclient->connect(); pool.dispatch(); pool.schedule(boost::bind(&readWorld, client, boost::ref(sequence))); pool.dispatch(); MORDOR_TEST_ASSERT_EQUAL(++sequence, 2); // Read is pending client->write("hello"); client->flush(false); pool.dispatch(); server->write("world"); server->flush(false); pool.dispatch(); MORDOR_TEST_ASSERT_EQUAL(++sequence, 4); }
MORDOR_MAIN(int argc, const char * const argv[]) { try { Config::loadFromEnvironment(); StdoutStream stdoutStream; WorkerPool pool(2); if (argc == 1) { argc = 2; const char * const hyphen[] = { "", "-" }; argv = hyphen; } for (int i = 1; i < argc; ++i) { Stream::ptr inStream; std::string arg(argv[i]); if (arg == "-") { inStream.reset(new StdinStream()); } else { inStream.reset(new FileStream(arg, FileStream::READ)); } transferStream(inStream, stdoutStream); } } catch (const std::exception& ex) { std::cerr << ex.what() << std::endl; } return 0; }
static void basicInFibers(Stream::ptr stream, int &sequence) { MORDOR_TEST_ASSERT_EQUAL(sequence, 1); MORDOR_TEST_ASSERT_EQUAL(stream->write("a"), 1u); stream->close(); stream->flush(); ++sequence; MORDOR_TEST_ASSERT_EQUAL(sequence, 3); }
static void eachConfigVarHTML(ConfigVarBase::ptr var, Stream::ptr stream) { std::string name = var->name(); stream->write("<tr><td align=\"right\">", 22); stream->write(name.c_str(), name.size()); stream->write("=</td><td>", 10); std::string value = var->toString(); stream->write(value.c_str(), value.size()); stream->write("</td></tr>\n", 11); }
static void cancelOnBlockingWriter(Stream::ptr stream, int &sequence) { MORDOR_TEST_ASSERT_EQUAL(++sequence, 2); char buffer[4096]; memset(buffer, 0, sizeof(buffer)); MORDOR_TEST_ASSERT_EXCEPTION( while (true) stream->write(buffer, 4096), OperationAbortedException); MORDOR_TEST_ASSERT_EQUAL(++sequence, 4); MORDOR_TEST_ASSERT_EXCEPTION(stream->write(buffer, 4096), OperationAbortedException); }
Connection::Connection(Stream::ptr stream) : m_stream(stream) { MORDOR_ASSERT(stream); MORDOR_ASSERT(stream->supportsRead()); MORDOR_ASSERT(stream->supportsWrite()); if (!stream->supportsUnread() || !stream->supportsFind()) { BufferedStream *buffered = new BufferedStream(stream); buffered->allowPartialReads(true); m_stream.reset(buffered); } }
MORDOR_UNITTEST(BufferedStream, partiallyBufferedReadRawBuffer) { MemoryStream::ptr baseStream(new MemoryStream("0123456789")); BufferedStream::ptr bufferedStream(new BufferedStream(baseStream)); bufferedStream->bufferSize(3); Stream::ptr stream = bufferedStream; char buffer[3]; buffer[2] = '\0'; MORDOR_TEST_ASSERT_EQUAL(stream->read(buffer, 2), 2u); MORDOR_TEST_ASSERT_EQUAL((const char *)buffer, "01"); MORDOR_TEST_ASSERT_EQUAL(stream->read(buffer, 2), 2u); MORDOR_TEST_ASSERT_EQUAL((const char *)buffer, "23"); }
static void blockingWrite(Stream::ptr stream, int &sequence) { MORDOR_TEST_ASSERT_EQUAL(++sequence, 3); Buffer output; MORDOR_TEST_ASSERT_EQUAL(stream->read(output, 10), 5u); MORDOR_TEST_ASSERT(output == "hello"); }
Multipart::Multipart(Stream::ptr stream, std::string boundary) : m_stream(stream), m_boundary(boundary), m_finished(false) { MORDOR_ASSERT(m_stream); MORDOR_ASSERT(m_stream->supportsRead() || m_stream->supportsWrite()); MORDOR_ASSERT(!(m_stream->supportsRead() && m_stream->supportsWrite())); while (!m_boundary.empty() && m_boundary[m_boundary.size() - 1] == ' ') m_boundary.resize(m_boundary.size() - 1); MORDOR_ASSERT(!m_boundary.empty()); MORDOR_ASSERT(m_boundary.size() <= 70); if (m_boundary.find_first_not_of(allowedBoundaryChars) != std::string::npos) { if (stream->supportsWrite()) { MORDOR_ASSERT(false); } else { MORDOR_THROW_EXCEPTION(InvalidMultipartBoundaryException()); } } m_boundary = "\r\n--" + m_boundary; if (m_stream->supportsRead()) { MORDOR_ASSERT(m_stream->supportsFind()); MORDOR_ASSERT(m_stream->supportsUnread()); } }
static void writeLotsaData(Stream::ptr stream, unsigned long long toTransfer, bool &complete) { RandomStream random; MORDOR_TEST_ASSERT_EQUAL(transferStream(random, stream, toTransfer), toTransfer); stream->flush(); complete = true; }
static void eachConfigVarHTMLWrite(ConfigVarBase::ptr var, Stream::ptr stream) { std::string name = var->name(); stream->write("<tr><td align=\"right\">", 22); stream->write(name.c_str(), name.size()); stream->write("=</td><td><form name=\"", 22); stream->write(name.c_str(), name.size()); stream->write("\" method=\"post\"><input type=\"text\" name=\"", 41); stream->write(name.c_str(), name.size()); stream->write("\" value=\"", 9); std::string value = var->toString(); stream->write(value.c_str(), value.size()); stream->write("\" /><input type=\"submit\" value=\"Change\" /></form></td></tr>\n", 60); }
static void readWorld(Stream::ptr stream, int &sequence) { MORDOR_TEST_ASSERT_EQUAL(++sequence, 1); char buf[6]; buf[5] = '\0'; MORDOR_TEST_ASSERT_EQUAL(stream->read(buf, 5), 5u); MORDOR_TEST_ASSERT_EQUAL(++sequence, 3); MORDOR_TEST_ASSERT_EQUAL((const char *)buf, "world"); }
void threadStress(Stream::ptr stream) { size_t totalRead = 0; size_t totalWritten = 0; size_t buf[64]; Buffer buffer; for (int i = 0; i < 10000; ++i) { if (i % 2) { size_t toRead = 64; size_t read = stream->read(buffer, toRead * sizeof(size_t)); MORDOR_TEST_ASSERT(read % sizeof(size_t) == 0); buffer.copyOut(&buf, read); for (size_t j = 0; read > 0; read -= sizeof(size_t), ++j) { MORDOR_TEST_ASSERT_EQUAL(buf[j], ++totalRead); } buffer.clear(); } else { size_t toWrite = 64; for (size_t j = 0; j < toWrite; ++j) { buf[j] = ++totalWritten; } buffer.copyIn(buf, toWrite * sizeof(size_t)); size_t written = stream->write(buffer, toWrite * sizeof(size_t)); totalWritten -= (toWrite - written / sizeof(size_t)); buffer.clear(); } } stream->close(Stream::WRITE); while (true) { size_t toRead = 64; size_t read = stream->read(buffer, toRead); if (read == 0) break; MORDOR_TEST_ASSERT(read % sizeof(size_t) == 0); buffer.copyOut(&buf, read); for (size_t i = 0; read > 0; read -= sizeof(size_t), ++i) { MORDOR_TEST_ASSERT_EQUAL(buf[i], ++totalRead); } buffer.clear(); } stream->flush(); }
Stream::ptr Connection::getStream(const GeneralHeaders &general, const EntityHeaders &entity, const std::string &method, Status status, boost::function<void()> notifyOnEof, boost::function<void()> notifyOnException, bool forRead) { MORDOR_ASSERT(hasMessageBody(general, entity, method, status)); Stream::ptr stream; if (forRead) { stream.reset(new SingleplexStream(m_stream, SingleplexStream::READ, false)); } else { stream.reset(new SingleplexStream(m_stream, SingleplexStream::WRITE, false)); } Stream::ptr baseStream(stream); for (ParameterizedList::const_reverse_iterator it(general.transferEncoding.rbegin()); it != general.transferEncoding.rend(); ++it) { if (stricmp(it->value.c_str(), "chunked") == 0) { stream.reset(new ChunkedStream(stream)); } else if (stricmp(it->value.c_str(), "deflate") == 0) { stream.reset(new ZlibStream(stream)); } else if (stricmp(it->value.c_str(), "gzip") == 0 || stricmp(it->value.c_str(), "x-gzip") == 0) { stream.reset(new GzipStream(stream)); } else if (stricmp(it->value.c_str(), "compress") == 0 || stricmp(it->value.c_str(), "x-compress") == 0) { MORDOR_ASSERT(false); } else if (stricmp(it->value.c_str(), "identity") == 0) { MORDOR_ASSERT(false); } else { MORDOR_ASSERT(false); } } if (stream != baseStream) { } else if (entity.contentLength != ~0ull) { LimitedStream::ptr limited(new LimitedStream(stream, entity.contentLength)); limited->strict(true); stream = limited; } else if (entity.contentType.type == "multipart") { // Getting stream to pass to multipart; self-delimiting } else { // Delimited by closing the connection } NotifyStream::ptr notify(new NotifyStream(stream)); stream = notify; notify->notifyOnClose = notifyOnEof; notify->notifyOnEof = notifyOnEof; notify->notifyOnException = notifyOnException; return stream; }
static void closeOnBlockingWriter(Stream::ptr stream, int &sequence) { MORDOR_TEST_ASSERT_EQUAL(++sequence, 3); stream->close(); MORDOR_TEST_ASSERT_EQUAL(++sequence, 4); }
Stream::ptr tunnel(HTTP::StreamBroker::ptr streamBroker, const URI &proxy, IPAddress::ptr targetIP, const std::string &targetDomain, unsigned short targetPort, unsigned char version) { MORDOR_ASSERT(version == 4 || version == 5); MORDOR_ASSERT(version == 5 || !targetIP || targetIP->family() == AF_INET); MORDOR_ASSERT(streamBroker); MORDOR_ASSERT(targetIP || !targetDomain.empty()); std::string buffer; buffer.resize(std::max<size_t>(targetDomain.size() + 1u, 16u) + 9); Stream::ptr stream = streamBroker->getStream(proxy); if (version == 5) { buffer[0] = version; buffer[1] = 1; buffer[2] = 0; size_t written = 0; while (written < 3) written += stream->write(buffer.data() + written, 3 - written); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if (buffer[0] != 5) MORDOR_THROW_EXCEPTION(ProtocolViolationException()); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if ((unsigned char)buffer[0] == 0xff) MORDOR_THROW_EXCEPTION(NoAcceptableAuthenticationMethodException()); if (buffer[0] != 0) MORDOR_THROW_EXCEPTION(ProtocolViolationException()); } buffer[0] = version; buffer[1] = 1; size_t size; if (version == 4) { if (targetIP) *(unsigned short *)&buffer[2] = htons(targetIP->port()); else *(unsigned short *)&buffer[2] = htons(targetPort); if (targetIP) *(unsigned int *)&buffer[4] = htonl((unsigned int)(((sockaddr_in *)targetIP->name())->sin_addr.s_addr)); else *(unsigned int *)&buffer[4] = htonl(0x00000001); buffer[8] = 0; if (!targetIP) { memcpy(&buffer[9], targetDomain.c_str(), targetDomain.size()); buffer[9 + targetDomain.size()] = 0; } size = 9 + targetDomain.size() + (targetDomain.empty() ? 0 : 1); } else { buffer[2] = 0; if (targetIP) { if (targetIP->family() == AF_INET) { buffer[3] = 1; *(unsigned int *)&buffer[4] = htonl((unsigned int)(((sockaddr_in *)targetIP->name())->sin_addr.s_addr)); size = 7; } else { buffer[3] = 4; memcpy(&buffer[4], &((sockaddr_in6 *)targetIP->name())->sin6_addr, 16); size = 19; } } else { buffer[3] = 3; buffer[4] = (unsigned char)targetDomain.size(); memcpy(&buffer[5], targetDomain.c_str(), targetDomain.size()); size = 5 + targetDomain.size(); } if (targetIP) *(unsigned short *)&buffer[size] = htons(targetIP->port()); else *(unsigned short *)&buffer[size] = htons(targetPort); size += 2; } size_t written = 0; while (written < size) written += stream->write(buffer.data() + written, size - written); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if ((version == 4 && buffer[0] != 0) || (version == 5 && buffer[0] != 5)) MORDOR_THROW_EXCEPTION(ProtocolViolationException()); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if ((version == 4 && buffer[0] != 0x5a) || (version == 5 && buffer[0] != 0)) MORDOR_THROW_EXCEPTION(InvalidResponseException(buffer[0])); if (version == 4) { size = 0; while (size < 6) { written = stream->read(&buffer[0], 6 - size); if (written == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); size += written; } } else { if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if (buffer[0] != 0) MORDOR_THROW_EXCEPTION(InvalidResponseException(buffer[0])); if (stream->read(&buffer[0], 1) == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); if (buffer[1] == 3) { if (buffer[0] != 0) MORDOR_THROW_EXCEPTION(ProtocolViolationException()); size = buffer[1] + 2; } else if (buffer[1] == 1) { size = 6; } else if (buffer[1] == 4) { size = 18; } else { MORDOR_THROW_EXCEPTION(ProtocolViolationException()); } while (size > 0) { written = stream->read(&buffer[0], size); if (written == 0) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); size -= written; } } return stream; }
static void readALot(Stream::ptr stream) { unsigned char buffer; for (size_t i = 0; i < A_LOT_OF_ITERATIONS; ++i) MORDOR_TEST_ASSERT_EQUAL(stream->read(&buffer, 1u), 1u); }
static void blockingRead(Stream::ptr stream, int &sequence) { MORDOR_TEST_ASSERT_EQUAL(++sequence, 2); MORDOR_TEST_ASSERT_EQUAL(stream->write("hello"), 5u); MORDOR_TEST_ASSERT_EQUAL(++sequence, 3); }
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); } }
static void cancelOnBlockingReader(Stream::ptr stream, int &sequence) { MORDOR_TEST_ASSERT_EQUAL(++sequence, 2); stream->cancelRead(); MORDOR_TEST_ASSERT_EQUAL(++sequence, 3); }
static void copyIn(IOManager *ioManager = NULL) { Connection conn(g_goodConnString, ioManager); conn.execute("CREATE TEMP TABLE stuff (id INTEGER, name TEXT)"); Stream::ptr stream = conn.copyIn("stuff").csv()(); stream->write("1,cody\n"); stream->write("2,tom\n"); stream->write("3,brian\n"); stream->write("4,jeremy\n"); stream->write("5,zach\n"); stream->write("6,paul\n"); stream->write("7,alen\n"); stream->write("8,jt\n"); stream->write("9,jon\n"); stream->write("10,jacob\n"); stream->close(); Result result = conn.execute("SELECT COUNT(*) FROM stuff"); MORDOR_TEST_ASSERT_EQUAL(result.rows(), 1u); MORDOR_TEST_ASSERT_EQUAL(result.columns(), 1u); MORDOR_TEST_ASSERT_EQUAL(result.get<long long>(0, 0), 10); result = conn.execute("SELECT SUM(id) FROM stuff"); MORDOR_TEST_ASSERT_EQUAL(result.rows(), 1u); MORDOR_TEST_ASSERT_EQUAL(result.columns(), 1u); MORDOR_TEST_ASSERT_EQUAL(result.get<long long>(0, 0), 55); }