From 8568b7b74fe962bf6d3252e9967aa3123968615c Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 17 Jul 2012 07:58:38 +0100 Subject: Move DNS resolution into a thread to make the API asyncronous. diff --git a/src/com/isode/stroke/network/PlatformDomainNameResolver.java b/src/com/isode/stroke/network/PlatformDomainNameResolver.java index 45c8dce..ccdee6d 100644 --- a/src/com/isode/stroke/network/PlatformDomainNameResolver.java +++ b/src/com/isode/stroke/network/PlatformDomainNameResolver.java @@ -1,14 +1,14 @@ /* - * Copyright (c) 2010, Isode Limited, London, England. + * Copyright (c) 2010-2012, Isode Limited, London, England. * All rights reserved. */ /* * Copyright (c) 2010, Remko Tronçon. * All rights reserved. */ - package com.isode.stroke.network; +import com.isode.stroke.eventloop.Event.Callback; import com.isode.stroke.eventloop.EventLoop; import com.isode.stroke.eventloop.EventOwner; import java.net.InetAddress; @@ -16,79 +16,44 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; - public class PlatformDomainNameResolver extends DomainNameResolver { private class AddressQuery extends DomainNameAddressQuery implements EventOwner { - AddressQuery(String host, EventLoop eventLoop) { + + private class AddressQueryThread extends Thread { + @Override + public void run() { + final Collection results = new ArrayList(); + try { + results.add(new HostAddress(InetAddress.getByName(hostname))); + } catch (UnknownHostException ex) { + } + eventLoop.postEvent(new Callback() { + public void run() { + onResult.emit(results, results.isEmpty() ? new DomainNameResolveError() : null); + } + }); + } + } + + AddressQuery(String host, EventLoop eventLoop) { hostname = host; this.eventLoop = eventLoop; - //FIXME: port asyncDNS -// thread = null; -// safeToJoin = false; } - public void run() { - //FIXME: port asyncDNS - Collection results = new ArrayList(); - try { - results.add(new HostAddress(InetAddress.getByName(hostname))); - } catch (UnknownHostException ex) { - - } - onResult.emit(results, results.isEmpty() ? new DomainNameResolveError() : null); - -// safeToJoin = false; -// thread = new boost::thread(boost::bind(&AddressQuery::doRun, shared_from_this())); - } -// FIXME: Port async DNS. -// void doRun() { -// //std::cout << "PlatformDomainNameResolver::doRun()" << std::endl; -// boost::asio::ip::tcp::resolver resolver(ioService); -// boost::asio::ip::tcp::resolver::query query(hostname.getUTF8String(), "5222"); -// try { -// //std::cout << "PlatformDomainNameResolver::doRun(): Resolving" << std::endl; -// boost::asio::ip::tcp::resolver::iterator endpointIterator = resolver.resolve(query); -// //std::cout << "PlatformDomainNameResolver::doRun(): Resolved" << std::endl; -// if (endpointIterator == boost::asio::ip::tcp::resolver::iterator()) { -// //std::cout << "PlatformDomainNameResolver::doRun(): Error 1" << std::endl; -// emitError(); -// } -// else { -// std::vector results; -// for ( ; endpointIterator != boost::asio::ip::tcp::resolver::iterator(); ++endpointIterator) { -// boost::asio::ip::address address = (*endpointIterator).endpoint().address(); -// results.push_back(address.is_v4() ? HostAddress(&address.to_v4().to_bytes()[0], 4) : HostAddress(&address.to_v6().to_bytes()[0], 16)); -// } -// -// //std::cout << "PlatformDomainNameResolver::doRun(): Success" << std::endl; -// eventLoop->postEvent( -// boost::bind(boost::ref(onResult), results, boost::optional()), -// shared_from_this()); -// } -// } -// catch (...) { -// //std::cout << "PlatformDomainNameResolver::doRun(): Error 2" << std::endl; -// emitError(); -// } -// safeToJoin = true; -// } -// -// void emitError() { -// eventLoop->postEvent(boost::bind(boost::ref(onResult), std::vector(), boost::optional(DomainNameResolveError())), shared_from_this()); -// } -// -// boost::asio::io_service ioService; - String hostname; - EventLoop eventLoop; -// boost::thread* thread; -// bool safeToJoin; - } + public void run() { + AddressQueryThread thread = new AddressQueryThread(); + thread.setDaemon(true); + thread.start(); + } + final String hostname; + final EventLoop eventLoop; + } public PlatformDomainNameResolver(EventLoop eventLoop) { this.eventLoop = eventLoop; } - + @Override public DomainNameServiceQuery createServiceQuery(String name) { return new PlatformDomainNameServiceQuery(getNormalized(name), eventLoop); @@ -98,6 +63,5 @@ public class PlatformDomainNameResolver extends DomainNameResolver { public DomainNameAddressQuery createAddressQuery(String name) { return new AddressQuery(getNormalized(name), eventLoop); } - private final EventLoop eventLoop; } diff --git a/src/com/isode/stroke/network/PlatformDomainNameServiceQuery.java b/src/com/isode/stroke/network/PlatformDomainNameServiceQuery.java index 17b5478..66a38cc 100644 --- a/src/com/isode/stroke/network/PlatformDomainNameServiceQuery.java +++ b/src/com/isode/stroke/network/PlatformDomainNameServiceQuery.java @@ -1,15 +1,16 @@ /* + * Copyright (c) 2010-2012, Isode Limited, London, England. + * All rights reserved. + */ +/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ -/* - * Copyright (c) 2010, Isode Limited, London, England. - * All rights reserved. - */ package com.isode.stroke.network; +import com.isode.stroke.eventloop.Event.Callback; import com.isode.stroke.eventloop.EventLoop; import com.isode.stroke.eventloop.EventOwner; import java.util.ArrayList; @@ -21,9 +22,46 @@ import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; - public class PlatformDomainNameServiceQuery extends DomainNameServiceQuery implements EventOwner { + private class QueryThread extends Thread { + + @Override + public void run() { + final Collection results = new ArrayList(); + Hashtable env = new Hashtable(); + env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); + env.put("java.naming.provider.url", "dns:"); + try { + DirContext ctx = new InitialDirContext(env); + Attributes attrs = ctx.getAttributes(service, new String[]{"SRV"}); + Attribute attribute = attrs.get("SRV"); + for (int i = 0; attribute != null && i < attribute.size(); i++) { + /* SRV results are going to be returned in the space-separated format + * Priority Weight Port Target + * (See RFC2782) + */ + String[] srvParts = ((String) attribute.get(i)).split(" "); + String host = srvParts[3]; + if (host.endsWith(".")) { + host = host.substring(0, host.length() - 1); + } + Result result = new Result(host, Integer.parseInt(srvParts[2]), Integer.parseInt(srvParts[0]), Integer.parseInt(srvParts[1])); + results.add(result); + } + } catch (NamingException ex) { + /* Turns out that you get the exception just for not finding a result, so we want to fall through to A lookups and ignore.*/ + } + + eventLoop.postEvent(new Callback() { + public void run() { + onResult.emit(results); + } + }); + + } + } + public PlatformDomainNameServiceQuery(String service, EventLoop eventLoop) { this.service = service; this.eventLoop = eventLoop; @@ -31,156 +69,10 @@ public class PlatformDomainNameServiceQuery extends DomainNameServiceQuery imple @Override public void run() { - //TODO: Make async - Collection results = new ArrayList(); - Hashtable env = new Hashtable(); - env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); - env.put("java.naming.provider.url", "dns:"); - try { - DirContext ctx = new InitialDirContext(env); - Attributes attrs = ctx.getAttributes(this.service, new String[]{"SRV"}); - Attribute attribute = attrs.get("SRV"); - for (int i = 0; attribute != null && i < attribute.size(); i++) { - /* SRV results are going to be returned in the space-separated format - * Priority Weight Port Target - * (See RFC2782) - */ - String[] srvParts = ((String) attribute.get(i)).split(" "); - String host = srvParts[3]; - if (host.endsWith(".")) { - host = host.substring(0, host.length() - 1); - } - Result result = new Result(host, Integer.parseInt(srvParts[2]), Integer.parseInt(srvParts[0]), Integer.parseInt(srvParts[1])); - results.add(result); - } - } catch (NamingException ex) { - /* Turns out that you get the exception just for not finding a result, so we want to fall through to A lookups and ignore.*/ - } - - onResult.emit(results); + QueryThread thread = new QueryThread(); + thread.setDaemon(true); + thread.start(); } - - -// void PlatformDomainNameServiceQuery::doRun() { -// std::vector records; -// -//#if defined(SWIFTEN_PLATFORM_WINDOWS) -// DNS_RECORD* responses; -// // FIXME: This conversion doesn't work if unicode is deffed above -// if (DnsQuery(service.getUTF8Data(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &responses, NULL) != ERROR_SUCCESS) { -// emitError(); -// return; -// } -// -// DNS_RECORD* currentEntry = responses; -// while (currentEntry) { -// if (currentEntry->wType == DNS_TYPE_SRV) { -// DomainNameServiceQuery::Result record; -// record.priority = currentEntry->Data.SRV.wPriority; -// record.weight = currentEntry->Data.SRV.wWeight; -// record.port = currentEntry->Data.SRV.wPort; -// -// // The pNameTarget is actually a PCWSTR, so I would have expected this -// // conversion to not work at all, but it does. -// // Actually, it doesn't. Fix this and remove explicit cast -// // Remove unicode undef above as well -// record.hostname = String((const char*) currentEntry->Data.SRV.pNameTarget); -// records.push_back(record); -// } -// currentEntry = currentEntry->pNext; -// } -// DnsRecordListFree(responses, DnsFreeRecordList); -// -//#else -// // Make sure we reinitialize the domain list every time -// res_init(); -// -// //std::cout << "SRV: Querying " << service << std::endl; -// ByteArray response; -// response.resize(NS_PACKETSZ); -// int responseLength = res_query(const_cast(service.getUTF8Data()), ns_c_in, ns_t_srv, reinterpret_cast(response.getData()), response.getSize()); -// if (responseLength == -1) { -// emitError(); -// return; -// } -// -// // Parse header -// HEADER* header = reinterpret_cast(response.getData()); -// unsigned char* messageStart = reinterpret_cast(response.getData()); -// unsigned char* messageEnd = messageStart + responseLength; -// unsigned char* currentEntry = messageStart + NS_HFIXEDSZ; -// -// // Skip over the queries -// int queriesCount = ntohs(header->qdcount); -// while (queriesCount > 0) { -// int entryLength = dn_skipname(currentEntry, messageEnd); -// if (entryLength < 0) { -// emitError(); -// return; -// } -// currentEntry += entryLength + NS_QFIXEDSZ; -// queriesCount--; -// } -// -// // Process the SRV answers -// int answersCount = ntohs(header->ancount); -// while (answersCount > 0) { -// DomainNameServiceQuery::Result record; -// -// int entryLength = dn_skipname(currentEntry, messageEnd); -// currentEntry += entryLength; -// currentEntry += NS_RRFIXEDSZ; -// -// // Priority -// if (currentEntry + 2 >= messageEnd) { -// emitError(); -// return; -// } -// record.priority = ns_get16(currentEntry); -// currentEntry += 2; -// -// // Weight -// if (currentEntry + 2 >= messageEnd) { -// emitError(); -// return; -// } -// record.weight = ns_get16(currentEntry); -// currentEntry += 2; -// -// // Port -// if (currentEntry + 2 >= messageEnd) { -// emitError(); -// return; -// } -// record.port = ns_get16(currentEntry); -// currentEntry += 2; -// -// // Hostname -// if (currentEntry >= messageEnd) { -// emitError(); -// return; -// } -// ByteArray entry; -// entry.resize(NS_MAXDNAME); -// entryLength = dn_expand(messageStart, messageEnd, currentEntry, entry.getData(), entry.getSize()); -// if (entryLength < 0) { -// emitError(); -// return; -// } -// record.hostname = String(entry.getData()); -// records.push_back(record); -// currentEntry += entryLength; -// answersCount--; -// } -//#endif -// -// safeToJoin = true; -// std::sort(records.begin(), records.end(), ResultPriorityComparator()); -// //std::cout << "Sending out " << records.size() << " SRV results " << std::endl; -// eventLoop->postEvent(boost::bind(boost::ref(onResult), records)); -//} - - private final String service; private final EventLoop eventLoop; } -- cgit v0.10.2-6-g49f6