From 8179b8ff68a400bd497dd5e21aa0b45d1674240e Mon Sep 17 00:00:00 2001 From: Sean Reinhardt Date: Mon, 6 Apr 2020 21:51:05 -0700 Subject: [PATCH] [Http] add default User-Agent string to cURL requests, unless one is provided by :headers --- CHANGELOG.md | 4 +- lib/cocoapods-downloader/base.rb | 12 ++++++ lib/cocoapods-downloader/http.rb | 12 ++++++ spec/base_spec.rb | 14 ++++++ spec/http_spec.rb | 74 ++++++++++++++++++++++++++++++++ spec/remote_file_spec.rb | 1 + 6 files changed, 116 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dce17fb..5bb7afb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ ##### Enhancements -* None. +* Add User-Agent to `cURL` requests when downloading source via the `:http` download strategy, unless one was provided by the `:headers` option. + [Sean Reinhardt](https://github.com/seanreinhardtapps) + [#9619](https://github.com/CocoaPods/CocoaPods/issues/9619) ##### Bug Fixes diff --git a/lib/cocoapods-downloader/base.rb b/lib/cocoapods-downloader/base.rb index 13d4ad5..8e2d38a 100644 --- a/lib/cocoapods-downloader/base.rb +++ b/lib/cocoapods-downloader/base.rb @@ -121,6 +121,18 @@ def checkout_options raise 'Abstract method' end + # Returns a User-Agent string that itentifies http network requests as + # originating from CocoaPods. + # Contains version numbers from the CocoaPods Gem and the cocoapods-downloader Gem. + # + # @param [module] base_module The Base CocoaPods Module to retrieve the version number from. + # @return [String] the User-Agent string. + # + def self.user_agent_string(base_module = Pod) + pods_version = base_module.const_defined?('VERSION') ? "CocoaPods/#{base_module::VERSION} " : '' + "#{pods_version}cocoapods-downloader/#{Pod::Downloader::VERSION}" + end + #-----------------------------------------------------------------------# # Defines two methods for an executable, based on its name. The bang diff --git a/lib/cocoapods-downloader/http.rb b/lib/cocoapods-downloader/http.rb index 4ba7f0e..90e13dd 100644 --- a/lib/cocoapods-downloader/http.rb +++ b/lib/cocoapods-downloader/http.rb @@ -3,12 +3,16 @@ module Pod module Downloader class Http < RemoteFile + USER_AGENT_HEADER = 'User-Agent'.freeze + private executable :curl def download_file(full_filename) parameters = ['-f', '-L', '-o', full_filename, url, '--create-dirs', '--netrc-optional', '--retry', '2'] + parameters << user_agent_argument if headers.nil? || + headers.none? { |header| header.casecmp(USER_AGENT_HEADER).zero? } headers.each do |h| parameters << '-H' @@ -17,6 +21,14 @@ def download_file(full_filename) curl! parameters end + + # Returns a cURL command flag to add the CocoaPods User-Agent. + # + # @return [String] cURL command -A flag and User-Agent. + # + def user_agent_argument + "-A '#{Http.user_agent_string}'" + end end end end diff --git a/spec/base_spec.rb b/spec/base_spec.rb index 929146f..b7870b6 100644 --- a/spec/base_spec.rb +++ b/spec/base_spec.rb @@ -14,6 +14,20 @@ module Downloader new_options = Base.preprocess_options(options) new_options.should == options end + + it 'defines a user agent with the cocoapods-downloader version' do + module TestModuleNoVersion + end + Base.user_agent_string(TestModuleNoVersion).should == "cocoapods-downloader/#{Pod::Downloader::VERSION}" + end + + it 'defines a user agent containing CocoaPods downloader versions when available' do + module TestModuleWithVersion + VERSION = 'a.b.c'.freeze + end + Base.user_agent_string(TestModuleWithVersion).should == + "CocoaPods/#{TestModuleWithVersion::VERSION} cocoapods-downloader/#{Pod::Downloader::VERSION}" + end end end end diff --git a/spec/http_spec.rb b/spec/http_spec.rb index c785a68..533beb8 100644 --- a/spec/http_spec.rb +++ b/spec/http_spec.rb @@ -3,9 +3,11 @@ module Pod module Downloader describe 'HTTP' do + mock_user_agent = 'mock_user_agent'.freeze before do tmp_folder.rmtree if tmp_folder.exist? @fixtures_url = 'file://' + fixture('http').to_s + Http.stubs(:user_agent_string).returns(mock_user_agent) end it 'download file and unzip it' do @@ -75,6 +77,78 @@ module Downloader downloader.download end end + + it 'passes User-Agent to cURL' do + options = { :http => "#{@fixtures_url}/lib.zip" } + downloader = Downloader.for_target(tmp_folder, options) + downloader.expects(:curl!).with( + includes("-A \'#{mock_user_agent}\'"), + ) + should.raise DownloaderError do + downloader.download + end + end + + it 'passes default User-Agent to cURL with other request headers' do + options = { :http => "#{@fixtures_url}/lib.zip", + :headers => ['Accept: application/json'], + } + downloader = Downloader.for_target(tmp_folder, options) + downloader.expects(:curl!).with( + includes("-A \'#{mock_user_agent}\'"), + ) + should.raise DownloaderError do + downloader.download + end + end + + it 'prefers User-Agent provided in headers over default User-Agent' do + options = { + :http => "#{@fixtures_url}/lib.zip", + :headers => ['Accept: application/json', 'User-Agent: custom_user_agent'], + } + downloader = Downloader.for_target(tmp_folder, options) + downloader.expects(:curl!).with( + all_of( + includes('-H'), + includes('Accept: application/json'), + includes('-H'), + includes('User-Agent: custom_user_agent'), + ), + Not( + includes("-A \'#{mock_user_agent}\'"), + ), + ) + should.raise DownloaderError do + downloader.download + end + end + + it 'prefers case insensitive User-Agent provided in headers' do + options = { + :http => "#{@fixtures_url}/lib.zip", + :headers => ['Accept: application/json', 'user-agent: custom_user_agent'], + } + downloader = Downloader.for_target(tmp_folder, options) + downloader.expects(:curl!).with( + all_of( + includes('-H'), + includes('Accept: application/json'), + includes('-H'), + includes('user-agent: custom_user_agent'), + ), + Not( + includes("-A \'#{mock_user_agent}\'"), + ), + ) + should.raise DownloaderError do + downloader.download + end + end + + it 'supplies User-Agent argument for cURL' do + Http.new('', '', {}).instance_eval { user_agent_argument }.should.match /-A '#{mock_user_agent}'/ + end end end end diff --git a/spec/remote_file_spec.rb b/spec/remote_file_spec.rb index eca4eab..bfc9447 100644 --- a/spec/remote_file_spec.rb +++ b/spec/remote_file_spec.rb @@ -14,6 +14,7 @@ def download_file(full_filename) before do tmp_folder.rmtree if tmp_folder.exist? @fixtures_url = 'file://' + fixture('remote_file').to_s + RemoteFile.stubs(:user_agent_string).returns('mock_user_agent') end it 'download file and unzip it' do