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

Support DNS lookup.

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