Skip to content

Commit

Permalink
feat: implement CustomHostNameResolver for custom DNS resolution in t…
Browse files Browse the repository at this point in the history
…ests

- progress towards fixing aim42#331
  • Loading branch information
RehanChalana committed Oct 12, 2024
1 parent 9dc29e1 commit 076194d
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import org.aim42.htmlsanitycheck.Configuration
import org.aim42.htmlsanitycheck.collect.SingleCheckResults
import org.aim42.htmlsanitycheck.html.HtmlConst
import org.aim42.htmlsanitycheck.html.HtmlPage
import org.aim42.htmlsanitycheck.tools.Web
import org.aim42.htmlsanitycheck.test.dns.CustomHostNameResolver
import org.wiremock.integrations.testcontainers.WireMockContainer
import spock.lang.Ignore
import spock.lang.IgnoreIf
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll

import java.lang.reflect.Field
import java.lang.reflect.Proxy


// see end-of-file for license information


Expand All @@ -29,26 +33,54 @@ class BrokenHttpLinksCheckerSpec extends Specification {
.withMappingFromResource("mappings.json")
.withExposedPorts(8080)

@Shared
CustomHostNameResolver customHostNameResolver = new CustomHostNameResolver()

/** executed once before all specs are executed **/
def setupSpec() {
wireMockServer.start()
port = wireMockServer.getMappedPort(8080)
}

/** executed once after all specs are executed **/
def cleanupSpec() {
wireMockServer.stop()
registerCustomDnsResolver()
}

/* executed before every single spec */

def setup() {
myConfig = new Configuration()
brokenHttpLinksChecker = new BrokenHttpLinksChecker( myConfig )

collector = new SingleCheckResults()
}


/** executed once after all specs are executed **/
def cleanupSpec() {
wireMockServer.stop()
}


// Custom method to register the DNS resolver
private void registerCustomDnsResolver() {
try {
Field implField = InetAddress.class.getDeclaredField("impl");
implField.setAccessible(true);
Object currentImpl = implField.get(null);

Proxy newImpl = (Proxy) Proxy.newProxyInstance(
currentImpl.getClass().getClassLoader(),
currentImpl.getClass().getInterfaces(),
(proxy, method, args) -> {
if ("lookupAllHostAddr".equals(method.getName()) && args.length == 1 && args[0] instanceof String) {
return customHostNameResolver.resolve((String) args[0]);
}
return method.invoke(currentImpl, args);
}
);

implField.set(null, newImpl);
} catch (Exception e) {
throw new RuntimeException("Failed to register custom DNS resolver", e);
}
}
/**
* checking for internet connectivity is a somewhat brittle - as there's no such thing as "the internet"
* (the checker will most likely use google.com as a proxy for "internet"
Expand Down Expand Up @@ -80,7 +112,7 @@ class BrokenHttpLinksCheckerSpec extends Specification {
def "one syntactically correct http URL is ok"() {
given: "an HTML page with a single correct anchor/link"
String HTML = """$HtmlConst.HTML_HEAD
<a href="http://localhost:$port/google">google</a>
<a href="http://${CustomHostNameResolver.WIREMOCK_HOST}:$port/google">google</a>
$HtmlConst.HTML_END """

htmlPage = new HtmlPage(HTML)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.aim42.htmlsanitycheck.test.dns;

import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class CustomHostNameResolver {

public static final String WIREMOCK_HOST = "my.custom.mocked.host";
private static Object originalResolver;

static {
try {
Field implField = InetAddress.class.getDeclaredField("impl");
implField.setAccessible(true);
originalResolver = implField.get(null);
} catch (Exception e) {
throw new RuntimeException("Failed to setup fallback DNS resolver", e);
}
}

public InetAddress[] resolve(String hostname) throws UnknownHostException {
// Custom DNS resolution logic
if (WIREMOCK_HOST.equals(hostname)) {
return new InetAddress[]{InetAddress.getByAddress("localhost", new byte[]{127, 0, 0, 1})};
}
// Fallback to original resolver using reflection
try {
return (InetAddress[]) originalResolver.getClass()
.getMethod("lookupAllHostAddr", String.class)
.invoke(originalResolver, hostname);
} catch (Exception e) {
throw new UnknownHostException("Failed to resolve hostname: " + hostname);
}
}
}

0 comments on commit 076194d

Please sign in to comment.