Отфильтровать любой HTTP-запрос URI?

10

Я хочу отфильтровать любой URI HTTP-запроса через HTTP API.

Случаи использования:

  1. Проверка обновления WordPress идет по адресу http://api.wordpress.org/core/version-check/1.6/ , но https://api.wordpress.org/core/version-check/1.6/ тоже работает, и я хочу использовать это всегда.
  2. Новый файл WordPress взят из http://wordpress.org/wordpress-3.4.2.zip , но https://wordpress.org/wordpress-3.4.2.zip также работает.
  3. Иногда я хочу отладить запросы и перенаправить их временно в пользовательский домен на моем локальном сервере.
  4. Некоторые плагины отправляют запросы на другие серверы, и я хочу заменить эти запросы, когда внешний сервер отключается.

Запросы на обновление являются наиболее важными на данный момент, поскольку все еще существует нефиксированная ошибка 16778 ( дополнительная информация ), а HTTPS-запросы снижают риск атаки «человек посередине».

Я тщательно искал , я изучил основной код ... но закончил как Nacin два года назад:

Я думал, что вы можете отфильтровать URL-адрес HTTP-запроса, но сейчас я не могу его найти.

Что я пропустил? Не так ли? :)

Фуксия
источник
Ссылка на этот ответ здесь для всех, кто ищет отладку cURL в WP.
Кайзер

Ответы:

9

Меньше, чем ответ, но просто список вещей прямо из моего опыта с ним - возможно, вы что-то упустили.

Отладка запроса и его результатов

Без diggin 'слишком глубоко в процессе обновления, но WP HTTP API использует WP_HTTPкласс. Это также предлагает хорошую вещь: крюк отладки.

do_action( 'http_api_debug', $response, 'response', $class, $args, $url );

Где $responseтакже может быть WP_Errorобъект, который, возможно, говорит вам больше.

Примечание. Из краткого теста кажется, что этот фильтр работает (по какой-то причине) только в том случае, если вы поместите его как можно ближе к месту выполнения запроса. Поэтому, возможно, вам нужно вызвать его из-за обратного вызова на одном из приведенных ниже фильтров.

WP_HTTP Классовые аргументы

Аргументы Classes сами по себе фильтруемы, но некоторые из них возвращаются внутренними методами обратно к тому, что WP считает необходимым.

apply_filters( 'http_request_args', $r, $url );

Одним из аргументов является то ssl_verify, что по умолчанию верно (но для меня это вызывает серьезные проблемы при обновлении, например, с GitHub). Редактировать: после отладки тестового запроса я нашел другой аргумент, который проверяет, установлен ли SSL true. Это называется sslverify(без разделения подчеркивания). Не знаю, где это появилось в игре, если оно действительно используется или заброшено, и если у вас есть шанс повлиять на его ценность. Я нашел это с помощью 'http_api_debug'фильтра.

Полностью на заказ

Вы также можете «просто» переопределить все внутренние компоненты и перейти к пользовательской настройке. Для этого есть фильтр.

apply_filters( 'pre_http_request', false, $r, $url );

Первый аргумент должен быть установлен в true. Чем вы можете взаимодействовать с аргументами внутри $rи результатом parse_url( $url );.

полномочие

Еще одна вещь, которая может сработать, это запускать все через собственный прокси. Это требует некоторых настроек в вашем wp-config.php. Я никогда не пробовал этого раньше, но некоторое время назад я просмотрел константы и суммировал некоторые примеры, которые должны работать, и включил некоторые комментарии на случай, если мне это понадобится однажды. Вы должны определить WP_PROXY_HOSTи WP_PROXY_PORTкак мин. установка. В противном случае ничего не будет работать, и это просто обойдет ваш прокси.

# HTTP Proxies
# Used for e.g. in Intranets
# Fixes Feeds as well
# Defines the proxy adresse.
define( 'WP_PROXY_HOST',          '127.0.84.1' );
# Defines the proxy port.
define( 'WP_PROXY_PORT',          '8080' );
# Defines the proxy username.
define( 'WP_PROXY_USERNAME',      'my_user_name' );
# Defines the proxy password.
define( 'WP_PROXY_PASSWORD',      'my_password' );
# Allows you to define some adresses which
# shouldn't be passed through a proxy.
define( 'WP_PROXY_BYPASS_HOSTS',  'localhost, www.example.com' );

РЕДАКТИРОВАТЬ

WP_HTTPКласс обычно выступает в качестве базового класса (будет расширен для различных сценариев). Выступающие WP_HTTP_*классы Fsockopen, Streams, Curl, Proxy, Cookie, Encoding. Если вы подключите обратный вызов к 'http_api_debug'-action, то третий аргумент сообщит вам, какой класс использовался для вашего запроса.

Внутри WP_HTTP_curlкласса вы найдете request()метод. Этот метод предлагает два фильтра для перехвата поведения SSL: один для локальных запросов 'https_local_ssl_verify'и один для удаленных запросов 'https_ssl_verify'. WP, скорее всего, определит localкак localhostи что вы получите взамен get_option( 'siteurl' );.

Поэтому я бы попробовал выполнить следующее прямо перед тем, как выполнить этот запрос (или из обратного вызова, который подключен к ближайшему запросу:

add_filter( 'https_ssl_verify', '__return_true' );

# Local requests should be checked with something like
# 'localhost' === $_SERVER['HTTP_HOST'] or similar
# add_filter( 'https_local_ssl_verify', '__return_true' );

Sidenote: в большинстве случаев WP_HTTP_curlбудет использоваться для работы с прокси.

кайзер
источник
1
Ах, я думаю, что вы ответили на мой вопрос косвенно: я могу подключиться pre_http_request, отменить запрос и отправить его с правильным URL-адресом. Попробую это сегодня вечером.
fuxia
8

Основываясь на полезном ответе @ kaiser, я написал код, который, кажется, работает хорошо. Вот почему я отметил это как Ответ.

Позвольте мне объяснить мое решение ...

Логика

Когда выполняется запрос, отправленный через API WP_Http::request(). Это метод с ...

@todo Рефакторинг этого кода.

... в заголовке. Я не мог согласиться больше.

Теперь есть несколько фильтров. Я решил неправильно использовать pre_http_requestдля моих нужд:

add_filter( 'pre_http_request', 't5_update_wp_per_https', 10, 3 );

Мы получаем три аргумента здесь: false, $r, $url.

  • falseожидаемое возвращаемое значение для apply_filters(). Если мы отправим что-нибудь еще назад, WordPress немедленно остановится, и исходный запрос не будет отправлен.

  • $rэто массив аргументов для этого запроса. Мы должны изменить это тоже через минуту.

  • $urlэто - сюрприз! - URL.

Таким образом , в нашем обратном вызове t5_update_wp_per_https()мы посмотрим на URL, и если это URL мы хотим фильтр, мы говорим НЕТ на WordPress с помощью не сказать «нет» ( false).

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

Примечание: из этого следует, что вы можете предотвратить все HTTP-запросы:
add_filter( 'pre_http_request', '__return_true' );

Вместо этого мы запускаем наш собственный запрос с более точным URL-адресом и слегка скорректированными аргументами ( $rпереименованы $argsдля удобства чтения)

Код

Пожалуйста, прочитайте встроенные комментарии, они важны.

<?php
/**
 * Plugin Name: T5 Update WP per HTTPS
 * Description: Forces update checks and downloads for WP to use HTTPS.
 * Plugin URI:  http://wordpress.stackexchange.com/questions/72529/filter-any-http-request-uri
 * Version:     2012.11.14
 * Author:      Thomas Scholz
 * Author URI:  http://toscho.de
 * Licence:     MIT
 * License URI: http://opensource.org/licenses/MIT
 */

add_filter( 'pre_http_request', 't5_update_wp_per_https', 10, 3 );

/**
 * Force HTTPS requests for update checks and new WP version downloads.
 *
 * @wp-hook pre_http_request
 * @param   bool   $false
 * @param   array  $args
 * @param   string $url
 * @return  FALSE|array|object FALSE if everything is okay, an array of request
 *                            results or an WP_Error instance.
 */
function t5_update_wp_per_https( $false, $args, $url )
{
    // Split the URL into useful parts.
    $url_data = parse_url( $url );

    // It is already HTTPS.
    if ( 'https' === strtolower( $url_data['scheme'] ) )
        return FALSE;

    // Not our host.
    if ( FALSE === stripos( $url_data['host'], 'wordpress.org' ) )
        return FALSE;

    // Make that an HTTPS request.
    $new_url = substr_replace( $url, 'https', 0, 4 );

    // WP_Http cannot verify the wordpress.org certificate.
    $args['sslverify'] = FALSE;

    // It is slow. We wait at least 30 seconds.
    30 > $args['timeout'] and $args['timeout'] = 30;

    // Get an instance of WP_Http.
    $http    = _wp_http_get_object();

    // Get the result.
    $result = $http->request( $new_url, $args );

    /* prepend this line with a '#' to debug like a boss.
    print '<pre>'
    . htmlspecialchars( print_r( $result, TRUE ), ENT_QUOTES, 'utf-8', FALSE )
    . '</pre>';
    die();
    /**/

    return $result;
}

Тесты

Без этого плагина WordPress используется:

  • http://api.wordpress.org/core/version-check/1.6/ для проверки обновлений и
  • http://wordpress.org/wordpress-3.4.2.zip скачать новые файлы.

Я тестировал его с двумя локальными установками, в одном месте и установкой нескольких сайтов на Win 7. Для того, чтобы заставить набор обновлений I $wp_versionв wp-includes/version.phpдо 1и версии TwentyEleven к 1.3.

Для наблюдения за сетевым трафиком я использовал Wireshark : он бесплатный, работает на Windows и Linux и предлагает несколько впечатляющих инструментов фильтрации.

Смотреть HTTPS немного сложно: вы видите только зашифрованные данные ... в конце концов, это идея. Чтобы увидеть, сделал ли мой плагин то, что он должен делать, я сначала посмотрел незашифрованный трафик и заметил IP-адрес, используемый для подключения к wordpress.org. Это было 72.233.56.138иногда 72.233.56.139.
Не удивительно, что есть балансировщик нагрузки и, возможно, множество других инструментов, поэтому мы не можем полагаться на один IP-адрес.

Затем я набрал ip.addr == 72.233.56.138в маске фильтра, активировал плагин, зашел wp-admin/update-core.phpи наблюдал за трафиком в Wireshark. Зеленые линии - это запросы в виде простого текста - именно то, чего мы не хотим. Красные и черные линии являются признаком успеха.

Wireshark

Проверка обновлений прошла нормально: обнаружены «более новые» версии. Актуальные обновления для темы и ядра тоже прошли нормально. Именно то, что мне было нужно.

И все же ... это может быть проще, если бы существовал простой фильтр для URL.

Фуксия
источник
1
+1 /* /**/, просто потому что это гений. И (если бы я мог) еще один +1 для Чарльза Бронсона. И тогда должен быть еще один +1 для подробного объяснения, комментариев и скриншотов.
Кайзер
3
    add_filter('http_request_args', 'http_request_args_custom', 10,2);
    function http_request_args_custom($request,$url){
            if (strpos($url, 'wordpress.org') !== false){
                    global $replaced_url;
                    $replaced_url = 'http://wordpress.local';
            }
            return $request;
    }

    add_action('http_api_curl', 'http_api_curl_custom');
    function http_api_curl_custom(&$handle){
            global $replaced_url;
            if (!is_null($replaced_url))
                    curl_setopt( $handle, CURLOPT_URL, $replaced_url);
    }

    $http = new WP_Http();
    $response = $http->request('http://wordpress.org', array());

    var_dump($response);
Олег Бутузов
источник
1
Следует отметить, что в этом примере кода в функции http_api_curl_custom глобальная переменная $ replace_url должна быть установлена ​​в NULL после ее использования. В противном случае, после первого появления «wordpress.org» в URL, все другие URL будут заменены.
MihanEntalpo