Commit 23b792a6 authored by Kenton Varda's avatar Kenton Varda

Support DNS lookup.

parent 06fd35c4
......@@ -30,12 +30,12 @@ namespace _ {
namespace {
TEST(EzRpc, Basic) {
EzRpcServer server("127.0.0.1");
EzRpcServer server("localhost");
int callCount = 0;
server.exportCap("cap1", kj::heap<TestInterfaceImpl>(callCount));
server.exportCap("cap2", kj::heap<TestCallOrderImpl>());
EzRpcClient client("127.0.0.1", server.getPort().wait());
EzRpcClient client("localhost", server.getPort().wait());
auto cap = client.importCap<test::TestInterface>("cap1");
auto request = cap.fooRequest();
......
......@@ -102,8 +102,8 @@ struct EzRpcClient::Impl {
Impl(kj::StringPtr serverAddress, uint defaultPort)
: context(EzRpcContext::getThreadLocal()),
setupPromise(context->getIoProvider().getNetwork()
.parseRemoteAddress(serverAddress, defaultPort)
.then([](kj::Own<kj::RemoteAddress>&& addr) {
.parseAddress(serverAddress, defaultPort)
.then([](kj::Own<kj::NetworkAddress>&& addr) {
return addr->connect();
}).then([this](kj::Own<kj::AsyncIoStream>&& stream) {
clientContext = kj::heap<ClientContext>(kj::mv(stream));
......@@ -112,7 +112,7 @@ struct EzRpcClient::Impl {
Impl(struct sockaddr* serverAddress, uint addrSize)
: context(EzRpcContext::getThreadLocal()),
setupPromise(context->getIoProvider().getNetwork()
.getRemoteSockaddr(serverAddress, addrSize)->connect()
.getSockaddr(serverAddress, addrSize)->connect()
.then([this](kj::Own<kj::AsyncIoStream>&& stream) {
clientContext = kj::heap<ClientContext>(kj::mv(stream));
}).fork()) {}
......@@ -196,10 +196,10 @@ struct EzRpcServer::Impl final: public SturdyRefRestorer<Text>, public kj::TaskS
auto paf = kj::newPromiseAndFulfiller<uint>();
portPromise = paf.promise.fork();
tasks.add(context->getIoProvider().getNetwork().parseLocalAddress(bindAddress, defaultPort)
tasks.add(context->getIoProvider().getNetwork().parseAddress(bindAddress, defaultPort)
.then(kj::mvCapture(paf.fulfiller,
[this](kj::Own<kj::PromiseFulfiller<uint>>&& portFulfiller,
kj::Own<kj::LocalAddress>&& addr) {
kj::Own<kj::NetworkAddress>&& addr) {
auto listener = addr->listen();
portFulfiller->fulfill(listener->getPort());
acceptLoop(kj::mv(listener));
......@@ -209,7 +209,7 @@ struct EzRpcServer::Impl final: public SturdyRefRestorer<Text>, public kj::TaskS
Impl(struct sockaddr* bindAddress, uint addrSize)
: context(EzRpcContext::getThreadLocal()), portPromise(nullptr), tasks(*this) {
auto listener = context->getIoProvider().getNetwork()
.getLocalSockaddr(bindAddress, addrSize)->listen();
.getSockaddr(bindAddress, addrSize)->listen();
portPromise = kj::Promise<uint>(listener->getPort()).fork();
acceptLoop(kj::mv(listener));
}
......
......@@ -42,8 +42,8 @@ TEST(AsyncIo, SimpleNetwork) {
auto port = newPromiseAndFulfiller<uint>();
port.promise.then([&](uint portnum) {
return network.parseRemoteAddress("127.0.0.1", portnum);
}).then([&](Own<RemoteAddress>&& result) {
return network.parseAddress("localhost", portnum);
}).then([&](Own<NetworkAddress>&& result) {
return result->connect();
}).then([&](Own<AsyncIoStream>&& result) {
client = kj::mv(result);
......@@ -52,7 +52,7 @@ TEST(AsyncIo, SimpleNetwork) {
ADD_FAILURE() << kj::str(exception).cStr();
});
kj::String result = network.parseLocalAddress("*").then([&](Own<LocalAddress>&& result) {
kj::String result = network.parseAddress("*").then([&](Own<NetworkAddress>&& result) {
listener = result->listen();
port.fulfiller->fulfill(listener->getPort());
return listener->accept();
......@@ -67,28 +67,32 @@ TEST(AsyncIo, SimpleNetwork) {
EXPECT_EQ("foo", result);
}
String tryParseLocal(Network& network, StringPtr text, uint portHint = 0) {
return network.parseLocalAddress(text, portHint).wait()->toString();
}
String tryParseRemote(Network& network, StringPtr text, uint portHint = 0) {
return network.parseRemoteAddress(text, portHint).wait()->toString();
String tryParse(Network& network, StringPtr text, uint portHint = 0) {
return network.parseAddress(text, portHint).wait()->toString();
}
TEST(AsyncIo, AddressParsing) {
auto ioContext = setupAsyncIo();
auto& network = ioContext.provider->getNetwork();
EXPECT_EQ("*:0", tryParseLocal(network, "*"));
EXPECT_EQ("*:123", tryParseLocal(network, "123"));
EXPECT_EQ("*:123", tryParseLocal(network, ":123"));
EXPECT_EQ("[::]:123", tryParseLocal(network, "0::0", 123));
EXPECT_EQ("0.0.0.0:0", tryParseLocal(network, "0.0.0.0"));
EXPECT_EQ("1.2.3.4:5678", tryParseRemote(network, "1.2.3.4", 5678));
EXPECT_EQ("[12ab:cd::34]:321", tryParseRemote(network, "[12ab:cd:0::0:34]:321", 432));
EXPECT_EQ("*:0", tryParse(network, "*"));
EXPECT_EQ("*:123", tryParse(network, "*:123"));
EXPECT_EQ("[::]:123", tryParse(network, "0::0", 123));
EXPECT_EQ("0.0.0.0:0", tryParse(network, "0.0.0.0"));
EXPECT_EQ("1.2.3.4:5678", tryParse(network, "1.2.3.4", 5678));
EXPECT_EQ("[12ab:cd::34]:321", tryParse(network, "[12ab:cd:0::0:34]:321", 432));
EXPECT_EQ("unix:foo/bar/baz", tryParse(network, "unix:foo/bar/baz"));
// We can parse services by name...
EXPECT_EQ("1.2.3.4:80", tryParse(network, "1.2.3.4:http", 5678));
EXPECT_EQ("[::]:80", tryParse(network, "[::]:http", 5678));
EXPECT_EQ("[12ab:cd::34]:80", tryParse(network, "[12ab:cd::34]:http", 5678));
EXPECT_EQ("*:80", tryParse(network, "*:http", 5678));
EXPECT_EQ("unix:foo/bar/baz", tryParseLocal(network, "unix:foo/bar/baz"));
EXPECT_EQ("unix:foo/bar/baz", tryParseRemote(network, "unix:foo/bar/baz"));
// It would be nice to test DNS lookup here but the test would not be very hermetic. Even
// localhost can map to different addresses depending on whether IPv6 is enabled. We do
// connect to "localhost" in a different test, though.
}
TEST(AsyncIo, OneWayPipe) {
......
This diff is collapsed.
......@@ -68,12 +68,19 @@ public:
// specify a port when constructing the LocalAddress -- one will have been assigned automatically.
};
class RemoteAddress {
class NetworkAddress {
// Represents a remote address to which the application can connect.
public:
virtual Promise<Own<AsyncIoStream>> connect() = 0;
// Make a new connection to this address.
//
// The address must not be a wildcard ("*"). If it is an IP address, it must have a port number.
virtual Own<ConnectionReceiver> listen() = 0;
// Listen for incoming connections on this address.
//
// The address must be local.
virtual String toString() = 0;
// Produce a human-readable string which hopefully can be passed to Network::parseRemoteAddress()
......@@ -86,9 +93,6 @@ class LocalAddress {
// Represents a local address on which the application can potentially accept connections.
public:
virtual Own<ConnectionReceiver> listen() = 0;
// Listen for incoming connections on this address.
virtual String toString() = 0;
// Produce a human-readable string which hopefully can be passed to Network::parseRemoteAddress()
// to reproduce this address, although whether or not that works of course depends on the Network
......@@ -105,30 +109,24 @@ class Network {
// LocalAddress and/or RemoteAddress instances directly and work from there, if at all possible.
public:
virtual Promise<Own<LocalAddress>> parseLocalAddress(
StringPtr addr, uint portHint = 0) = 0;
virtual Promise<Own<RemoteAddress>> parseRemoteAddress(
StringPtr addr, uint portHint = 0) = 0;
// Construct a local or remote address from a user-provided string. The format of the address
virtual Promise<Own<NetworkAddress>> parseAddress(StringPtr addr, uint portHint = 0) = 0;
// Construct a network address from a user-provided string. The format of the address
// strings is not specified at the API level, and application code should make no assumptions
// about them. These strings should always be provided by humans, and said humans will know
// what format to use in their particular context.
//
// `portHint`, if provided, specifies the "standard" IP port number for the application-level
// service in play. If the address turns out to be an IP address (v4 or v6), and it lacks a
// port number, this port will be used.
//
// In practice, a local address is usually just a port number (or even an empty string, if a
// reasonable `portHint` is provided), whereas a remote address usually requires a hostname.
// port number, this port will be used. If `addr` lacks a port number *and* `portHint` is
// omitted, then the returned address will only support listen() (not connect()), and a port
// will be chosen when listen() is called.
virtual Own<LocalAddress> getLocalSockaddr(const void* sockaddr, uint len) = 0;
virtual Own<RemoteAddress> getRemoteSockaddr(const void* sockaddr, uint len) = 0;
// Construct a local or remote address from a legacy struct sockaddr.
virtual Own<NetworkAddress> getSockaddr(const void* sockaddr, uint len) = 0;
// Construct a network address from a legacy struct sockaddr.
};
struct OneWayPipe {
// A data pipe with an input end and an output end. The two ends are safe to use in different
// threads. (Typically backed by pipe() system call.)
// A data pipe with an input end and an output end. (Typically backed by pipe() system call.)
Own<AsyncInputStream> in;
Own<AsyncOutputStream> out;
......@@ -136,8 +134,7 @@ struct OneWayPipe {
struct TwoWayPipe {
// A data pipe that supports sending in both directions. Each end's output sends data to the
// other end's input. The ends can be used in separate threads. (Typically backed by
// socketpair() system call.)
// other end's input. (Typically backed by socketpair() system call.)
Own<AsyncIoStream> ends[2];
};
......@@ -175,7 +172,8 @@ public:
// With that said, KJ currently supports the following string address formats:
// - IPv4: "1.2.3.4", "1.2.3.4:80"
// - IPv6: "1234:5678::abcd", "[1234:5678::abcd]:80"
// - Local IP wildcard (local addresses only; covers both v4 and v6): "*", "*:80", ":80", "80"
// - Local IP wildcard (covers both v4 and v6): "*", "*:80"
// - Symbolic names: "example.com", "example.com:80", "example.com:http", "1.2.3.4:http"
// - Unix domain: "unix:/path/to/socket"
struct PipeThread {
......@@ -284,7 +282,7 @@ struct AsyncIoContext {
AsyncIoContext setupAsyncIo();
// Convenience method which sets up the current thread with everything it needs to do async I/O.
// The returned object contains an `EventLoop` which is wrapping an appropriate `EventPort` for
// The returned objects contain an `EventLoop` which is wrapping an appropriate `EventPort` for
// doing I/O on the host system, so everything is ready for the thread to start making async calls
// and waiting on promises.
//
......@@ -292,10 +290,10 @@ AsyncIoContext setupAsyncIo();
// Example:
//
// int main() {
// auto ioSystem = kj::setupIoEventLoop();
// auto ioContext = kj::setupAsyncIo();
//
// // Now we can call an async function.
// Promise<String> textPromise = getHttp(ioSystem->getNetwork(), "http://example.com");
// Promise<String> textPromise = getHttp(*ioContext.provider, "http://example.com");
//
// // And we can wait for the promise to complete. Note that you can only use `wait()`
// // from the top level, not from inside a promise callback.
......
......@@ -40,13 +40,15 @@ Thread::Thread(Function<void()> func): func(kj::mv(func)) {
}
Thread::~Thread() noexcept(false) {
int pthreadResult = pthread_join(*reinterpret_cast<pthread_t*>(&threadId), nullptr);
if (pthreadResult != 0) {
KJ_FAIL_SYSCALL("pthread_join", pthreadResult) { break; }
}
if (!detached) {
int pthreadResult = pthread_join(*reinterpret_cast<pthread_t*>(&threadId), nullptr);
if (pthreadResult != 0) {
KJ_FAIL_SYSCALL("pthread_join", pthreadResult) { break; }
}
KJ_IF_MAYBE(e, exception) {
kj::throwRecoverableException(kj::mv(*e));
KJ_IF_MAYBE(e, exception) {
kj::throwRecoverableException(kj::mv(*e));
}
}
}
......@@ -57,6 +59,14 @@ void Thread::sendSignal(int signo) {
}
}
void Thread::detach() {
int pthreadResult = pthread_detach(*reinterpret_cast<pthread_t*>(&threadId));
if (pthreadResult != 0) {
KJ_FAIL_SYSCALL("pthread_detach", pthreadResult) { break; }
}
detached = true;
}
void* Thread::runThread(void* ptr) {
Thread* thread = reinterpret_cast<Thread*>(ptr);
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]() {
......
......@@ -43,10 +43,14 @@ public:
void sendSignal(int signo);
// Send a Unix signal to the given thread, using pthread_kill or an equivalent.
void detach();
// Don't join the thread in ~Thread().
private:
Function<void()> func;
unsigned long long threadId; // actually pthread_t
kj::Maybe<kj::Exception> exception;
bool detached = false;
static void* runThread(void* ptr);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment