abhinav-phi commented on code in PR #2122:
URL: https://github.com/apache/stormcrawler/pull/2122#discussion_r3996891837


##########
core/src/main/java/org/apache/stormcrawler/util/URLUtil.java:
##########
@@ -253,6 +254,31 @@ public static String getHost(String url) {
         }
     }
 
+    /**
+     * Returns the host in the form the HTTP client will connect to it: 
percent-escapes decoded,
+     * lowercased and without a trailing dot. Host strings which only differ 
in escaping or case
+     * reach the same server, so politeness queues and robots.txt caches must 
key on the same
+     * value, otherwise one server is fetched under several queue ids and its 
robots.txt is
+     * downloaded once per spelling.
+     *
+     * @param url The url to check.
+     * @return String The canonical host for the url, or null if the url is 
not well formed or has
+     *     no host.
+     */
+    public static String getCanonicalHost(URL url) {
+        String host = url.getHost();
+        if (host == null) {
+            return null;
+        }
+        // okhttp percent-decodes the host when it parses the URL; do the same
+        // so keys derived from the URL string agree with what it connects to
+        String decoded = URLDecoder.decode(host, StandardCharsets.UTF_8);

Review Comment:
   Fixed in bbb945e8 — `URLDecoder` is gone. `percentDecodeHost` decodes only 
`%XX` pairs, never maps `+` to space, and keeps malformed escapes like `%zz` as 
literal characters, so a hostile host string cannot throw (the javadoc states 
the contract). `URLUtilNormaliseHostTest` pins the escaping, idempotence and 
multi-byte cases.



##########
core/src/main/java/org/apache/stormcrawler/util/URLUtil.java:
##########
@@ -253,6 +254,31 @@ public static String getHost(String url) {
         }
     }
 
+    /**
+     * Returns the host in the form the HTTP client will connect to it: 
percent-escapes decoded,

Review Comment:
   Reworded in bbb945e8 as suggested: the javadoc now says "the form of the 
host used to key politeness queues and the robots.txt cache: what okhttp 
connects to, with the root label normalised away". 
`HostAliasCacheKeyTest.okhttpCollapsesHostAliases` keeps the trailing-dot 
divergence documented.



##########
core/src/main/java/org/apache/stormcrawler/bolt/FetcherBolt.java:
##########
@@ -186,7 +186,7 @@ public static FetchItem create(URL u, String url, Tuple t, 
String queueMode) {
                     key = u.getHost();
                 }
             } else {
-                key = u.getHost();
+                key = URLUtil.getCanonicalHost(u);

Review Comment:
   Fixed in bbb945e8 — `canonicalHost` is computed once at the top of the 
method (FetcherBolt.java:177) and used in all three branches: `byIP` → 
`getByName(canonicalHost)`, `byDomain` → `getPLD(canonicalHost)`, `byHost` → 
the key itself.



##########
core/src/main/java/org/apache/stormcrawler/bolt/SimpleFetcherBolt.java:
##########
@@ -628,7 +628,7 @@ private String getPolitenessKey(URL u) {
                 key = u.getHost();
             }
         } else {
-            key = u.getHost();
+            key = URLUtil.getCanonicalHost(u);

Review Comment:
   Fixed in bbb945e8 — same here: canonicalHost is computed once at the top 
(SimpleFetcherBolt.java:617) and used in all three modes.



##########
core/src/main/java/org/apache/stormcrawler/util/URLUtil.java:
##########
@@ -253,6 +254,134 @@ public static String getHost(String url) {
         }
     }
 
+    /**
+     * Returns the form of the host used to key politeness queues and the 
robots.txt cache: what
+     * okhttp connects to, with the root label normalised away. Host strings 
which only differ in
+     * escaping or case reach the same server, so both spellings must end up 
under one key,
+     * otherwise one server is fetched under several queue ids and its 
robots.txt is downloaded once
+     * per spelling.
+     *
+     * @param url The url to check.
+     * @return String The canonical host for the url, or null if the url is 
not well formed or has
+     *     no host.
+     */
+    public static String getCanonicalHost(URL url) {
+        String host = url.getHost();
+        if (host == null) {
+            return null;
+        }
+        // okhttp percent-decodes the host when it parses the URL; do the same
+        // so keys derived from the URL string agree with what it connects to.
+        // The decoder never throws: crawled content is hostile input, and a
+        // malformed escape falls back to the raw spelling rather than blowing
+        // up the caller
+        String decoded = percentDecodeHost(host);
+        if (decoded.endsWith(".")) {
+            decoded = decoded.substring(0, decoded.length() - 1);
+        }
+        return decoded.toLowerCase(Locale.ROOT);
+    }
+
+    /**
+     * Percent-decodes a host string, leaving {@code +} alone and keeping 
malformed escapes as
+     * literal characters. Unlike {@link URLDecoder#decode}, this never throws.
+     */
+    private static String percentDecodeHost(String host) {
+        if (!host.contains("%")) {
+            return host;
+        }
+        StringBuilder sb = new StringBuilder(host.length());
+        for (int i = 0; i < host.length(); i++) {
+            char c = host.charAt(i);
+            if (c == '%' && i + 2 < host.length()) {
+                int hi = Character.digit(host.charAt(i + 1), 16);
+                int lo = Character.digit(host.charAt(i + 2), 16);
+                if (hi != -1 && lo != -1) {
+                    sb.append((char) ((hi << 4) | lo));

Review Comment:
   Good catch — fixed in 57f76f7c: the decoded octets are collected and 
interpreted as one UTF-8 sequence, so `%C3%BC` becomes `ü`, not `ü`. Pinned by 
`URLUtilNormaliseHostTest.multiByteEscapeDecodesAsUtf8`; a host normalised via 
`status.updater.normalise.hosts` can no longer resolve to a different host than 
its queue key.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to