HttpURLConnection — https:// против http://

Я пытаюсь получить значок URL-адреса, который вводит пользователь, например

_url = "google.com";

Я использую HttpUrlConnection, чтобы получить растровое изображение значка из расширения /favicon.ico с URL-адреса хоста.

        String faviconString = Uri.parse(_url).getHost() + "/favicon.ico";
        URL faviconUrl = null;
        Bitmap favicon = null;
        try
        {
            faviconString = "http://" + faviconString;
            faviconUrl = new URL(faviconString);
            HttpURLConnection connection = (HttpURLConnection) faviconUrl.openConnection();
            connection.setDoInput(true);
            connection.connect();
            favicon = BitmapFactory.decodeStream(connection.getInputStream());
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return favicon;

Однако, поскольку пользователь, вероятно, не укажет http:// или https://, мне придется добавить его самому. У меня проблема в том, что если я добавлю http:// перед URL-адресом, все будет работать нормально, но для https:// некоторые сайты вернут значок, другие просто дадут мне ноль. Как узнать, на какой странице используется https? Должен ли я просто добавлять http:// для каждого случая? Существуют ли какие-либо веб-сайты, которые строго ограничивают https и возвращают значение null при использовании http?


person Jason Hu    schedule 20.09.2013    source источник
comment
Это не очень надежный метод, так как фавиконы могут быть определены внутри страницы вот так <link rel="shortcut icon" href="/a/different/path.ico" type="image/x-icon" />   -  person Emiel    schedule 20.09.2013
comment
Есть ли другие способы, которые вы могли бы предложить? WebIconDatabase и WebView.getFavicon() были бы идеальными, но WebIconDatabase устарела   -  person Jason Hu    schedule 20.09.2013
comment
Извините, я не так хорошо знаком с Android, но это звучит как довольно хороший подход.   -  person Emiel    schedule 20.09.2013
comment
Подсказка: в дополнение к вышесказанному, некоторые веб-сайты могут возвращать null для http и работать на https.   -  person Sherif elKhatib    schedule 24.09.2013
comment
@SherifelKhatib, по сути, это то, о чем этот вопрос   -  person Jason Hu    schedule 24.09.2013
comment
@JasonHu Вы только что отредактировали свой вопрос, чтобы сказать, что это, по сути, то, о чем этот вопрос! Я имел в виду тот факт, что веб-сайт может выбрать одну из двух схем или обе. И мой комментарий задолго до вашего редактирования!   -  person Sherif elKhatib    schedule 24.09.2013
comment
@SherifelKhatib Я только что добавил немного в конце, чтобы прояснить вопрос. Если бы я не знал, что http вернет null, зачем мне вообще возиться с https, зачем мне вообще задавать этот вопрос.   -  person Jason Hu    schedule 24.09.2013
comment
Should I just add http:// for every case? вот почему я сказал то, что сказал. В любом случае, жестко закодировать этот Uri.parse(_url).getHost() + "/favicon.ico" очень нереально. Однако, если вы настаиваете на получении только этого пути, вы можете сделать запрос HEAD, чтобы узнать подробности о ресурсе, даже не получая данные. проверьте это здесь: w3.org/Protocols/rfc2616/rfc2616- sec9.html#sec9.4   -  person Sherif elKhatib    schedule 24.09.2013
comment
@SherifelKhatib звучит хорошо. Просто я всегда не решаюсь делать что-то вручную, когда на самом деле в API Android, вероятно, есть что-то, что обрабатывает это, о чем я не знал, отсюда и вопросы :(   -  person Jason Hu    schedule 24.09.2013
comment
@JasonHu, в случае, если вы получаете null, каков код состояния HTTP? Вы проверили, является ли это ошибкой 404, перенаправлением или чем-то еще? Если это перенаправление, следует ли за ними API, который вы используете?   -  person nloko    schedule 30.09.2013


Ответы (6)


Если вы не воспользуетесь идеей пользователя 2558882 или не существует какого-либо другого инструмента, который просто создаст для вас значок веб-сайта, вы вам придется проверить как URL-адреса http, так и https. Другого способа сделать это нет. Это часть сложности использования Интернета.

Возможно, было бы немного лучше посмотреть на свой код по-другому и разбить то, что вы пытаетесь сделать, на более мелкие, более управляемые части?

public void getFavicon(String host) {

    URL httpUrl = this.getHttpUrl(host + "/favicon.ico");

    Bitmap favicon = this.getBitmap(httpUrl);

    if (favicon == null) {

        URL httpsUrl = this.getHttpsUrl(host + "/favicon.ico");

        favicon = this.getBitmap(httpsUrl);
    }

    if (favicon == null) {

        throw new FaviconMissingException("Unable to find favicon for host: " + host);
    }

    return favicon;
}

public URL getHttpUrl(String uri) throws MalformedURLException {

    // There are better ways of building a url then string concationation.
    return new URL("http://" + uri);
}

public URL getHttpsUrl(String uri) throws MalformedURLException {

    // There are better ways of building a url then string concationation.
    return new URL("https://" + uri);
}

public Bitmap getBitmap(URL url) {

    InputStream inputStream = getInputStream(url);

    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

    return bitmap
}

public InputStream getInputStream(URL url) {

    // Please use a real connection library like HTTPClient here!
    // HttpClient will handle timeouts, redirects, and things like that for you.
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setDoInput(true);
    connection.connect();

    return connection.getInputStream();
}

Кстати, забота об одном или двух соединениях занимает больше времени, чем написание кода для выполнения двух запросов. Я почти гарантирую, что Google делает два запроса по мере необходимости. И если это достаточно хорошо для Google, это достаточно хорошо для меня.

Наконец, если вы начинаете видеть, что выполнение двух запросов действительно занимает слишком много времени, примите меры для повышения производительности.

person hooknc    schedule 27.09.2013

Примечание. Я не уверен, насколько полезным будет мой ответ.

Вы можете получить фавикон с помощью Google:

http://www.google.com/s2/favicons?domain=stackoverflow.com

возвращает:

введите здесь описание изображения

Вам не нужно указывать http или https.

 http://www.google.com/s2/favicons?domain=my.yorku.ca ===>> (https://my.yorku.ca)

возвращает:

введите здесь описание изображения

Но это не та иконка, которую использует https://my.yorku.ca. Итак, я думаю, Google возвращает значение по умолчанию для сайтов, которые не предоставляют доступ к своим фавиконам.

InputStream is = null;

String urlPrefix = "http://www.google.com/s2/favicons?domain=";

String _url = "google.com";

Bitmap favicon = null;

try {

    is = (InputStream) new URL(urlPrefix + _url).getContent();

} catch (MalformedURLException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

favicon = BitmapFactory.decodeStream(is);

На самом деле вы можете сохранить копию значка по умолчанию и проверить, если:

if (defaultBitmap.sameAs(favicon)) {
    // favicon wasn't available
}
person Vikram    schedule 24.09.2013
comment
Это классный подход, но в примере с yorku yorku.com/favicon.ico действительно возвращает правильный значок. Так что я, вероятно, все же выберу альтернативу /favicon.ico. - person Jason Hu; 24.09.2013
comment
@JasonHu Просто хочу убедиться, что вы используете действительные тестовые примеры. yorku.com/favicon.ico недействителен, домен http://yorku.ca. В своем ответе я использовал https://my.yorku.ca. Когда я попытался использовать https://my.yorku.ca/favicon.ico, я получил нулевое значение. Подход Google прекрасно работает для yorku.ca. - person Vikram; 24.09.2013
comment
Моя ошибка в первом комментарии, я использовал yorku.ca в своем тесте, который сработал. Я думаю, что возврат null — это нормально, за исключением случая, когда использование /favicon.ico возвращает null, а google возвращает что-то, что не является значком по умолчанию. Когда я вернусь null, я тоже использую значок по умолчанию на своем конце - person Jason Hu; 24.09.2013
comment
@JasonHu Да, оба подхода работают для yorku.ca. Но подход Google не работает для my.yorku.ca. Ваш подход работает для этого? - person Vikram; 24.09.2013
comment
Нет, он возвращает null, и в этом случае я просто покажу значок по умолчанию на своем конце. - person Jason Hu; 24.09.2013
comment
@JasonHu Попробуйте с _url = eff.org. URL-адрес Google вернет значок значка, а /favicon.ico вернет ноль. Так что я думаю, вы должны найти случай, когда: подход Google ломается и /favicon.ico работает. - person Vikram; 24.09.2013

Как насчет того, чтобы проверить, возвращает ли веб-сайт ноль или фавикон?

Я надеюсь, это поможет вам

person Sebastian Walla    schedule 23.09.2013
comment
Вы имеете в виду сначала попробовать https:// и проверить, является ли возвращаемый значок null, а затем попробовать http://? Это своего рода грубая сила, я бы предпочел не продолжать так, если это возможно. В конце концов, мне потребовалось бы открывать соединение дважды, что далеко не идеально. - person Jason Hu; 23.09.2013

Другой ответ, который еще «легче».

Просто заставьте пользователя ввести URL-адрес (включая протокол) для своего значка и убедитесь, что URL возвращает значок. Если нет, отобразите конечному пользователю ошибку проверки.

Следуя принципам Agile, выполняйте наименьший объем работы и смотрите, что работает. Если один план не работает, попробуйте что-то другое.

person hooknc    schedule 29.09.2013

Попробуйте это, когда URL-адрес начинается с «https»:

              TrustManager[] trustAllCerts = new TrustManager[]
               {
                 new X509TrustManager()
                  {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers()  { return null; }
                    public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType)  {}
                    public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType)  {}
                  }
                 };
              try
                {
                  SSLContext sc = SSLContext.getInstance( "SSL"); // "TLS" "SSL"
                  sc.init( null, trustAllCerts, null);
                  HttpsURLConnection.setDefaultSSLSocketFactory( sc.getSocketFactory());
                  HttpsURLConnection.setDefaultHostnameVerifier( 
                   new HostnameVerifier() 
                    {
                      public boolean verify( String hostname, SSLSession session) { return true; }
                    } );
                }
               catch( Exception e)
person Tapa Save    schedule 30.09.2013

person    schedule
comment
testUrlHttps.getProtocol() просто в значительной степени анализирует протокольную часть строки downloadURL, так что это не слишком помогает. Проблема заключалась в том, что когда пользователь не указывает протокол (либо http, либо https) при вводе URL-адреса. - person Jason Hu; 24.09.2013
comment
Что означает https.setHostnameVerifier(DO_NOT_VERYFY); ? - person user3469203; 15.06.2018