Установка заголовка запроса в селене

Я пытаюсь установить заголовок запроса "Referer", чтобы подделать запрос, поступающий с другого сайта. Нам нужен тест на возможность использования определенного реферера, который возвращает конкретную форму пользователю, в противном случае предоставляется альтернативная форма.

Я могу сделать это в полтергейсте:

page.driver.headers = {"Referer" => referer_string}

но я не могу найти эквивалентную функциональность для драйвера селемия.

Как я могу установить заголовки запроса в драйвере селена капибара?

13 ответов

Решение

Webdriver не содержит API для этого. Смотрите выпуск 141 от Selenium tracker для получения дополнительной информации. В названии проблемы говорится, что речь идет о заголовках ответов, но было решено, что Selenium не будет содержать API для заголовков запросов в рамках этой проблемы. Несколько проблем с добавлением API для установки заголовков запросов были помечены как дубликаты: первый, второй, третий.

Вот несколько возможностей, которые я могу предложить:

  1. Используйте другой драйвер / библиотеку вместо селена
  2. Напишите специфичный для браузера плагин (или найдите существующий), который позволит вам добавить заголовок для запроса.
  3. Используйте browsermob-proxy или какой - либо другой прокси.

Я бы выбрал вариант 3 в большинстве случаев. Это не трудно.

Обратите внимание, что Ghostdriver имеет API для него, но он не поддерживается другими драйверами.

Для тех, кто использует Python, вы можете рассмотреть возможность использования Selenium Wire, которая может устанавливать заголовки запросов, а также предоставлять вам возможность проверять запросы и ответы.

from seleniumwire import webdriver  # Import from seleniumwire

# Create a new instance of the Firefox driver
driver = webdriver.Firefox()

driver.header_overrides = {
    'Referer': 'referer_string',
}

# All subsequent requests will now contain the Referer

Я была такая же проблема. Я решил это, загрузив модификацию заголовков Firefox и активировав ее с помощью селена.

Код в Python следующий

fp = webdriver.FirefoxProfile()
path_modify_header = 'C:/xxxxxxx/modify_headers-0.7.1.1-fx.xpi'
fp.add_extension(path_modify_header)

fp.set_preference("modifyheaders.headers.count", 1)
fp.set_preference("modifyheaders.headers.action0", "Add")
fp.set_preference("modifyheaders.headers.name0", "Name_of_header") # Set here the name of the header
fp.set_preference("modifyheaders.headers.value0", "value_of_header") # Set here the value of the header
fp.set_preference("modifyheaders.headers.enabled0", True)
fp.set_preference("modifyheaders.config.active", True)
fp.set_preference("modifyheaders.config.alwaysOn", True)

driver = webdriver.Firefox(firefox_profile=fp)

У меня была та же проблема сегодня, за исключением того, что мне нужно было установить другого реферера для каждого теста. Я использовал промежуточное программное обеспечение и класс для передачи ему заголовков. Мысль, которой я поделюсь (или, может быть, есть более чистое решение?)

lib/request_headers.rb:

class CustomHeadersHelper
  cattr_accessor :headers
end

class RequestHeaders
  def initialize(app, helper = nil)
    @app, @helper = app, helper
  end

  def call(env)
    if @helper
      headers = @helper.headers

      if headers.is_a?(Hash)
        headers.each do |k,v|
          env["HTTP_#{k.upcase.gsub("-", "_")}"] = v
        end
      end
    end

    @app.call(env)
  end
end

config/initializers/middleware.rb

require 'request_headers'

if %w(test cucumber).include?(Rails.env)
  Rails.application.config.middleware.insert_before Rack::Lock, "RequestHeaders", CustomHeadersHelper
end

spec/support/capybara_headers.rb

require 'request_headers'

module CapybaraHeaderHelpers
  shared_context "navigating within the site" do
    before(:each) { add_headers("Referer" => Capybara.app_host + "/") }
  end

  def add_headers(custom_headers)
    if Capybara.current_driver == :rack_test
      custom_headers.each do |name, value|
        page.driver.browser.header(name, value)
      end
    else
      CustomHeadersHelper.headers = custom_headers
    end
  end
end

spec/spec_helper.rb

...
config.include CapybaraHeaderHelpers

Затем я могу включить общий контекст, где мне нужно, или передать другие заголовки в другой before блок. Я не тестировал его ни с чем, кроме Selenium и RackTest, но он должен быть прозрачным, поскольку внедрение заголовка выполняется до того, как запрос действительно попадет в приложение.

Если вы используете javacsript и хотите реализовать только на Chrome, Puppeteer - лучший вариант, поскольку он имеет встроенную поддержку для изменения заголовков. Проверьте это: https://pptr.dev/#?product=Puppeteer&version=v10.1.0&show=api-pagesetextrahttpheadersheaders

Хотя для кросс-браузерного использования вы можете проверить @requestly/seleniumпакет npm. Это оболочка расширения дляrequestly, обеспечивающая простую интеграцию с selenium-webdriver. Расширение может изменять заголовки. Проверить: https://www.npmjs.com/package/@requestly/selenium

Установка заголовков запросов в веб-драйвере напрямую не работает. Это правда.

Однако вы можете обойти эту проблему, используя инструменты разработчика браузера (я тестировал с помощью edge & chrome), и это отлично работает.

Согласно документации, у вас есть возможность добавлять собственные заголовки:https://chromedevtools.github.io/devtools-protocol/tot/Network/

Ниже приведен пример.

          [Test]
    public async Task AuthenticatedRequest()
    {
        await LogMessage("=== starting the test ===");

        EdgeOptions options = new EdgeOptions {UseChromium = true};
        options.AddArgument("no-sandbox");
        var driver = new RemoteWebDriver(new Uri(_testsSettings.GridUrl), options.ToCapabilities(), TimeSpan.FromMinutes(3));

        //Get DevTools
        IDevTools devTools = driver;

        //DevTools Session
        var session = devTools.GetDevToolsSession();

        var devToolsSession = session.GetVersionSpecificDomains<DevToolsSessionDomains>();
        await devToolsSession.Network.Enable(new Network.EnableCommandSettings());
        
        var extraHeader = new Network.Headers();
        var data = await Base64KerberosTicket();
        var headerValue = $"Negotiate {data}";
        
        await LogMessage($"header values is {headerValue}");

        extraHeader.Add("Authorization", headerValue);
        await devToolsSession.Network.SetExtraHTTPHeaders(new Network.SetExtraHTTPHeadersCommandSettings
        {
            Headers = extraHeader
        });

        driver.Url = _testsSettings.TestUrl;

        driver.Navigate();
        driver.Quit();

        await LogMessage("=== ending the test ===");
    }

Это пример, написанный на C#, но он, вероятно, будет работать с java, python, а также с основными платформами.

Надеюсь, это поможет сообществу.

Я хотел что-то более тонкое для RSpec/Ruby, чтобы пользовательский код находился только в одном месте. Вот мое решение:

/spec/support/selenium.rb
...
RSpec.configure do |config|
  config.after(:suite) do
    $custom_headers = nil
  end
end

module RequestWithExtraHeaders
  def headers
    $custom_headers.each do |key, value|
      self.set_header "HTTP_#{key}", value
    end if $custom_headers

    super
  end
end
class ActionDispatch::Request
  prepend RequestWithExtraHeaders
end

Тогда в моих спецификациях:

/specs/features/something_spec.rb
...
$custom_headers = {"Referer" => referer_string}

Если вы используете HtmlUnitDriverВы можете установить заголовки запроса, изменив WebClient, вот так:

final case class Header(name: String, value: String)

final class HtmlUnitDriverWithHeaders(headers: Seq[Header]) extends HtmlUnitDriver {
  super.modifyWebClient {
    val client = super.getWebClient
    headers.foreach(h => client.addRequestHeader(h.name, h.value))
    client
  }
}

Заголовки будут тогда на всех запросах, сделанных веб-браузером.

В C# Selenium можно использовать IWebDriver и добавить сетевые перехватчики, которые соответствующим образом обновляют заголовки.

      driver.Manage().Network.AddRequestHandler(new NetworkRequestHandler()
{
  RequestMatcher = (u) => true,
  RequestTransformer = (u) =>
  {
    u.Headers["my-header"] = "some-value";
    return u;
  }
});

Из уже рассмотренных выше решений наиболее надежным является использование Browsermob-Proxy

Но при работе с удаленным сетевым компьютером Browsermob-proxy бесполезен.

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

  1. Добавьте расширение ModHeader в браузер Chrome

Как скачать Modheader? Ссылка

ChromeOptions options = new ChromeOptions();
options.addExtensions(new File(C://Downloads//modheader//modheader.crx));

// Set the Desired capabilities 
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(ChromeOptions.CAPABILITY, options);

// Instantiate the chrome driver with capabilities
WebDriver driver = new RemoteWebDriver(new URL(YOUR_HUB_URL), options);
  1. Перейдите в расширения браузера и запишите идентификатор контекста локального хранилища ModHeader.

  1. Перейдите к URL-адресу ModHeader, чтобы установить контекст локального хранилища.

.

// set the context on the extension so the localStorage can be accessed
driver.get("chrome-extension://idgpnmonknjnojddfkpgkljpfnnfcklj/_generated_background_page.html");

Where `idgpnmonknjnojddfkpgkljpfnnfcklj` is the value captured from the Step# 2
  1. Теперь добавьте заголовки в запрос, используя Javascript

.

   ((Javascript)driver).executeScript(
         "localStorage.setItem('profiles', JSON.stringify([{  title: 'Selenium', hideComment: true, appendMode: '', 
             headers: [                        
               {enabled: true, name: 'token-1', value: 'value-1', comment: ''},
               {enabled: true, name: 'token-2', value: 'value-2', comment: ''}
             ],                          
             respHeaders: [],
             filters: []
          }]));");

где token-1, value-1, token-2, value-2 - заголовки и значения запроса, которые необходимо добавить.

  1. Теперь перейдите к нужному веб-приложению.

    driver.get("your-desired-website");

Проверить это: chrome_options = Options()

chrome_options.add_argument('--headless')
chrome_options.add_argument('user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"')

Задача решена!

Установка дополнительных заголовков запросов возможна с помощью браузеров DevTools (по крайней мере, в Chrome и Edge)!

Ниже приведен пример, в котором я установил заголовок безопасности для аутентификации Kerberos.

Это пример dotnet, но вы можете добиться этого на java, python и т. Д.

      using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.DevTools;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.Remote;
using DevToolsSessionDomains = OpenQA.Selenium.DevTools.V91.DevToolsSessionDomains;
using Network = OpenQA.Selenium.DevTools.V91.Network;

...

          private async Task CallAKerberosProtectedWebpage()
    {
        await LogMessage("=== starting the test ===");
        EdgeOptions options = new EdgeOptions {UseChromium = true};
        options.AddArgument("no-sandbox");
        var driver =
            new RemoteWebDriver(new Uri(_testsSettings.GridUrl), options.ToCapabilities(), TimeSpan.FromMinutes(3));

        IDevTools devTools = driver;
        var session = devTools.GetDevToolsSession();

        var devToolsSession = session.GetVersionSpecificDomains<DevToolsSessionDomains>();
        await devToolsSession.Network.Enable(new Network.EnableCommandSettings());

        var extraHeader = new Network.Headers();
        var data = await Base64KerberosTicket();
        var headerValue = $"Negotiate {data}";

        await LogMessage($"header values is {headerValue}");

        extraHeader.Add("Authorization", headerValue);
        await devToolsSession.Network.SetExtraHTTPHeaders(new Network.SetExtraHTTPHeadersCommandSettings
        {
            Headers = extraHeader
        });

        driver.Url = _testsSettings.TestUrl;
        driver.Navigate();
        driver.Quit();
        await LogMessage("=== ending the test ===");
    }

Вы можете сделать это с PhantomJSDriver.

PhantomJSDriver pd = ((PhantomJSDriver) ((WebDriverFacade) getDriver()).getProxiedDriver());
pd.executePhantomJS(
            "this.onResourceRequested = function(request, net) {" +
            "   net.setHeader('header-name', 'header-value')" +
            "};");

Используя объект запроса, вы также можете фильтровать, чтобы заголовок не был установлен для каждого запроса.

Другие вопросы по тегам