Разрешение URL с помощью Java дает мне неправильно закодированные символы в URL - PullRequest
0 голосов
/ 09 апреля 2011

Когда я делаю следующее:

try {
    URL url = new URL(urlAsString);
    //using proxy may increase latency
    HttpURLConnection hConn = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
    // force no follow
    hConn.setInstanceFollowRedirects(false);
    // the program doesn't care what the content actually is       
    hConn.setRequestMethod("HEAD");
    // default is 0 => infinity waiting
    hConn.setConnectTimeout(timeout);
    hConn.setReadTimeout(timeout);
    hConn.connect();
    int responseCode = hConn.getResponseCode();
    hConn.getInputStream().close();
    if (responseCode == HttpURLConnection.HTTP_OK)
        return urlAsString;

    String loc = hConn.getHeaderField("Location");
    if (responseCode == HttpURLConnection.HTTP_MOVED_PERM && loc != null)
        return loc.replaceAll(" ", "+");

} catch (Exception ex) {
}
return "";

для этого URL: http://bit.ly/gek1qK Я получаю

http://blog.tweetsmarter.com/twitter-downtime/twitter-redesignsâthen-everything-breaks/

что не так. Firefox разрешает до

http://blog.tweetsmarter.com/twitter-downtime/twitter-redesigns%E2%80%94then-everything-breaks/

Что не так в коде?

Ответы [ 2 ]

3 голосов
/ 09 апреля 2011

Согласно RFC 2616, раздел 2.2 , значения заголовка HTTP обычно должны кодироваться с использованием ISO-8859-1.

Здесь bit.ly отправляет неверный ответ - заголовок Location: кодируется с использованием UTF-8, поэтому символ em-dash представлен тремя отдельными байтами (0xe2, 0x80, 0x94).

HttpURLConnection декодирует байты с использованием ISO-8859-1, поэтому они становятся тремя символами (â и двумя неопределенными символами), , но выглядит так, как будто вы перекодируете их с использованием UTF-8 (производя 2 байт на символ, поскольку все три имеют значения> = 0x80) перед применением кодировки URL .

Firefox, скорее всего, обрабатывает данные как ISO-8859-1 на всем протяжении; затем проблема устраняется, если позже применяется кодировка URL.

Вы можете сделать то же самое путем URL-кодирования значения, возвращаемого getHeaderField(); поскольку диапазон Unicode от U + 0080 до U + 00FF идентичен диапазону байтов ISO-8859-1 0x80-0xFF, символы, не входящие в ASCII, можно кодировать, приводя их к значениям int:

/**
 * Takes a URI that was decoded as ISO-8859-1 and applies percent-encoding
 * to non-ASCII characters. Workaround for broken origin servers that send
 * UTF-8 in the Location: header.
 */
static String encodeUriFromHeader(String uri) {
    StringBuilder sb = new StringBuilder();

    for(char ch : badLocation.toCharArray()) {
        if(ch < (char)128) {
            sb.append(ch);
        } else {
            // this is ONLY valid if the uri was decoded using ISO-8859-1
            sb.append(String.format("%%%02X", (int)ch));
        }
    }

    return sb.toString();
}
1 голос
/ 09 апреля 2011

В этом нет ничего плохого. Разница в том, что m-Dash по-разному обозначается в разных кодировках. Поэтому, если Firefox использует кодировку, отличную от той, что используется в вашей программе, вы увидите другой символ.

И то и другое правильно, в вашем случае. Это просто вопрос кодирования. В Java вы используете UTF-8, который Рекомендация Консорциума World Wide Web ; хотя кажется, что в FF вы видите ISO-8859.

Если вы хотите сгенерировать тот же результат, что и Firefox в Java, попробуйте следующее:

System.out.print(URLEncoder.encode(loc.replace(" ", "+"), "ISO-8859-1"));

Он напечатает то, что вы видите в Firefox. (очевидно, он будет также кодировать / и :. Но только для демонстрации)

...