| 
									
										
										
										
											2017-03-05 19:26:56 +01:00
										 |  |  | describe "download strategies", :cask do | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |   let(:url) { "http://example.com/cask.dmg" } | 
					
						
							| 
									
										
										
										
											2017-05-29 18:24:52 +01:00
										 |  |  |   let(:url_options) { {} } | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |   let(:cask) { | 
					
						
							|  |  |  |     instance_double(Hbc::Cask, token:   "some-cask", | 
					
						
							| 
									
										
										
										
											2018-07-30 19:48:20 +02:00
										 |  |  |                                url:     URL.new(url, **url_options), | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |                                version: "1.2.3.4") | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe Hbc::CurlDownloadStrategy do | 
					
						
							|  |  |  |     let(:downloader) { Hbc::CurlDownloadStrategy.new(cask) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       allow(downloader.temporary_path).to receive(:rename) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "properly assigns a name and uri based on the Cask" do | 
					
						
							|  |  |  |       expect(downloader.name).to eq("some-cask") | 
					
						
							|  |  |  |       expect(downloader.url).to eq("http://example.com/cask.dmg") | 
					
						
							|  |  |  |       expect(downloader.version.to_s).to eq("1.2.3.4") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "calls curl with default arguments for a simple Cask" do | 
					
						
							|  |  |  |       allow(downloader).to receive(:curl) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |       downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |       expect(downloader).to have_received(:curl).with( | 
					
						
							| 
									
										
										
										
											2017-08-08 18:10:13 +02:00
										 |  |  |         "--location", | 
					
						
							|  |  |  |         "--remote-time", | 
					
						
							|  |  |  |         "--continue-at", "-", | 
					
						
							|  |  |  |         "--output", kind_of(Pathname), | 
					
						
							| 
									
										
										
										
											2017-08-08 22:28:08 +02:00
										 |  |  |         cask.url.to_s, | 
					
						
							| 
									
										
										
										
											2017-08-08 18:10:13 +02:00
										 |  |  |         user_agent: :default | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |       ) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with an explicit user agent" do | 
					
						
							|  |  |  |       let(:url_options) { { user_agent: "Mozilla/25.0.1" } } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it "adds the appropriate curl args" do | 
					
						
							| 
									
										
										
										
											2018-07-30 10:11:00 +02:00
										 |  |  |         expect(downloader).to receive(:system_command!) { |*, args:, **| | 
					
						
							| 
									
										
										
										
											2017-08-08 18:10:13 +02:00
										 |  |  |           expect(args.each_cons(2)).to include(["--user-agent", "Mozilla/25.0.1"]) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |         downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with a generalized fake user agent" do | 
					
						
							| 
									
										
										
										
											2017-08-08 18:10:13 +02:00
										 |  |  |       alias_matcher :a_string_matching, :match | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |       let(:url_options) { { user_agent: :fake } } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it "adds the appropriate curl args" do | 
					
						
							| 
									
										
										
										
											2018-07-30 10:11:00 +02:00
										 |  |  |         expect(downloader).to receive(:system_command!) { |*, args:, **| | 
					
						
							| 
									
										
										
										
											2017-08-08 18:10:13 +02:00
										 |  |  |           expect(args.each_cons(2).to_a).to include(["--user-agent", a_string_matching(/Mozilla.*Mac OS X 10.*AppleWebKit/)]) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |         downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with cookies set" do | 
					
						
							|  |  |  |       let(:url_options) { | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           cookies: { | 
					
						
							| 
									
										
										
										
											2016-10-14 20:33:16 +02:00
										 |  |  |             coo: "kie", | 
					
						
							|  |  |  |             mon: "ster", | 
					
						
							|  |  |  |           }, | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it "adds curl args for cookies" do | 
					
						
							|  |  |  |         curl_args = [] | 
					
						
							|  |  |  |         allow(downloader).to receive(:curl) { |*args| curl_args = args } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |         downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         expect(curl_args.each_cons(2)).to include(["-b", "coo=kie;mon=ster"]) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with referer set" do | 
					
						
							|  |  |  |       let(:url_options) { { referer: "http://somehost/also" } } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it "adds curl args for referer" do | 
					
						
							|  |  |  |         curl_args = [] | 
					
						
							|  |  |  |         allow(downloader).to receive(:curl) { |*args| curl_args = args } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |         downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         expect(curl_args.each_cons(2)).to include(["-e", "http://somehost/also"]) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
											  
											
												Add tests for CurlDownloadStrategy#tarball_path
Sometimes, `brew cask fetch`/`install` fails with an error message
similar to this:
```
==> Downloading https://w3g3a5v6.ssl.hwcdn.net/upload2/game/214692/735
Error: Download failed on Cask 'steamed-hams' with message: Operation
not supported @ rb_sysopen -
/Users/claudia/Documents/dev/brew/var/homebrew/locks/steamed-hams--1.0
.com&Expires=1520937180&Signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9y
VZUE0uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8LBLKnz%2B2X%2B
iq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHCyzMmyNe5giEKJNW8WF0KXriULhzLTWL
SA3ZTLCIofAdRiiGje1kNYY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7S
LvEDBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXNN4o1mW%2FMayy2t
TY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2BPK%2B4Sg==&hwexp=1520937440&hwsi
g=55bc66884b925ef22f8673c33bfcc33b.incomplete.lock
```
To reproduce the issue, check out this branch and run the
`cask/download_strategy` test suite:
```
brew tests --only=cask/download_strategy
```
Or take a real-life example, which would be a Cask whose URL has
**1.** no `.` character anywhere in the URL path itself (outside of
the domain name), **and 2.** at least one query parameter with a `.`
character in it; **and 3.** other query parameters following that
with a combined length of more than 255 characters.
This combination may be uncommon but it exists, especially in
[URLs that change on every visit](https://github.com/caskroom/homebrew-cask/blob/1002d41242216ce7c71597765dc0a86cabd1fcf7/doc/cask_language_reference/stanzas/url.md#urls-that-change-on-every-visit),
for example
[`steamed-hams`](https://github.com/claui/homebrew-cask-games/blob/9d7df499cdc3665ca2c68a555e9ff9826a2200e7/Casks/steamed-hams.rb)
from my `claui/cask-games` tap:
    $ brew tap claui/cask-games
    $ brew cask fetch steamed-hams
In a nutshell, **URL query strings sometimes look like a very long
file extension to Homebrew,** which it then proceeds to use as a file
name.
In `CurlDownloadStrategy`, Homebrew seems to apply a heuristic to the
cask URL in order to figure out a possibly meaningful file extension.
Sometimes this heuristic produces immensely long file extensions,
especially when there’s a query parameter with a `.` character in it
but not in the URL path itself (outside of the domain name).
Homebrew then believes that everything after the `.` is a file
extension. In one of the later steps, it tries to create a lock file
containing that extension, which fails because HFS+ cannot handle
files whose base file name has more than 255 characters.
One solution would be to improve Homebrew’s extension detector a bit
so that it won’t cross individual URL query param boundaries any
longer. This is done by adding `&` as a stop character:
```
def ext
  Pathname.new(@url).extname[/[^?&]+/]
end
```
This appears to fix the issue for most (if not all) practical
purposes.
											
										 
											2018-03-13 16:37:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     context "with a file name trailing the URL path" do | 
					
						
							|  |  |  |       describe "#tarball_path" do | 
					
						
							|  |  |  |         subject { downloader.tarball_path } | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Add tests for CurlDownloadStrategy#tarball_path
Sometimes, `brew cask fetch`/`install` fails with an error message
similar to this:
```
==> Downloading https://w3g3a5v6.ssl.hwcdn.net/upload2/game/214692/735
Error: Download failed on Cask 'steamed-hams' with message: Operation
not supported @ rb_sysopen -
/Users/claudia/Documents/dev/brew/var/homebrew/locks/steamed-hams--1.0
.com&Expires=1520937180&Signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9y
VZUE0uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8LBLKnz%2B2X%2B
iq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHCyzMmyNe5giEKJNW8WF0KXriULhzLTWL
SA3ZTLCIofAdRiiGje1kNYY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7S
LvEDBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXNN4o1mW%2FMayy2t
TY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2BPK%2B4Sg==&hwexp=1520937440&hwsi
g=55bc66884b925ef22f8673c33bfcc33b.incomplete.lock
```
To reproduce the issue, check out this branch and run the
`cask/download_strategy` test suite:
```
brew tests --only=cask/download_strategy
```
Or take a real-life example, which would be a Cask whose URL has
**1.** no `.` character anywhere in the URL path itself (outside of
the domain name), **and 2.** at least one query parameter with a `.`
character in it; **and 3.** other query parameters following that
with a combined length of more than 255 characters.
This combination may be uncommon but it exists, especially in
[URLs that change on every visit](https://github.com/caskroom/homebrew-cask/blob/1002d41242216ce7c71597765dc0a86cabd1fcf7/doc/cask_language_reference/stanzas/url.md#urls-that-change-on-every-visit),
for example
[`steamed-hams`](https://github.com/claui/homebrew-cask-games/blob/9d7df499cdc3665ca2c68a555e9ff9826a2200e7/Casks/steamed-hams.rb)
from my `claui/cask-games` tap:
    $ brew tap claui/cask-games
    $ brew cask fetch steamed-hams
In a nutshell, **URL query strings sometimes look like a very long
file extension to Homebrew,** which it then proceeds to use as a file
name.
In `CurlDownloadStrategy`, Homebrew seems to apply a heuristic to the
cask URL in order to figure out a possibly meaningful file extension.
Sometimes this heuristic produces immensely long file extensions,
especially when there’s a query parameter with a `.` character in it
but not in the URL path itself (outside of the domain name).
Homebrew then believes that everything after the `.` is a file
extension. In one of the later steps, it tries to create a lock file
containing that extension, which fails because HFS+ cannot handle
files whose base file name has more than 255 characters.
One solution would be to improve Homebrew’s extension detector a bit
so that it won’t cross individual URL query param boundaries any
longer. This is done by adding `&` as a stop character:
```
def ext
  Pathname.new(@url).extname[/[^?&]+/]
end
```
This appears to fix the issue for most (if not all) practical
purposes.
											
										 
											2018-03-13 16:37:27 +01:00
										 |  |  |         its(:extname) { is_expected.to eq(".dmg") } | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with no discernible file name in it" do | 
					
						
							|  |  |  |       let(:url) { "http://example.com/download" } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       describe "#tarball_path" do | 
					
						
							|  |  |  |         subject { downloader.tarball_path } | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Add tests for CurlDownloadStrategy#tarball_path
Sometimes, `brew cask fetch`/`install` fails with an error message
similar to this:
```
==> Downloading https://w3g3a5v6.ssl.hwcdn.net/upload2/game/214692/735
Error: Download failed on Cask 'steamed-hams' with message: Operation
not supported @ rb_sysopen -
/Users/claudia/Documents/dev/brew/var/homebrew/locks/steamed-hams--1.0
.com&Expires=1520937180&Signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9y
VZUE0uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8LBLKnz%2B2X%2B
iq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHCyzMmyNe5giEKJNW8WF0KXriULhzLTWL
SA3ZTLCIofAdRiiGje1kNYY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7S
LvEDBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXNN4o1mW%2FMayy2t
TY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2BPK%2B4Sg==&hwexp=1520937440&hwsi
g=55bc66884b925ef22f8673c33bfcc33b.incomplete.lock
```
To reproduce the issue, check out this branch and run the
`cask/download_strategy` test suite:
```
brew tests --only=cask/download_strategy
```
Or take a real-life example, which would be a Cask whose URL has
**1.** no `.` character anywhere in the URL path itself (outside of
the domain name), **and 2.** at least one query parameter with a `.`
character in it; **and 3.** other query parameters following that
with a combined length of more than 255 characters.
This combination may be uncommon but it exists, especially in
[URLs that change on every visit](https://github.com/caskroom/homebrew-cask/blob/1002d41242216ce7c71597765dc0a86cabd1fcf7/doc/cask_language_reference/stanzas/url.md#urls-that-change-on-every-visit),
for example
[`steamed-hams`](https://github.com/claui/homebrew-cask-games/blob/9d7df499cdc3665ca2c68a555e9ff9826a2200e7/Casks/steamed-hams.rb)
from my `claui/cask-games` tap:
    $ brew tap claui/cask-games
    $ brew cask fetch steamed-hams
In a nutshell, **URL query strings sometimes look like a very long
file extension to Homebrew,** which it then proceeds to use as a file
name.
In `CurlDownloadStrategy`, Homebrew seems to apply a heuristic to the
cask URL in order to figure out a possibly meaningful file extension.
Sometimes this heuristic produces immensely long file extensions,
especially when there’s a query parameter with a `.` character in it
but not in the URL path itself (outside of the domain name).
Homebrew then believes that everything after the `.` is a file
extension. In one of the later steps, it tries to create a lock file
containing that extension, which fails because HFS+ cannot handle
files whose base file name has more than 255 characters.
One solution would be to improve Homebrew’s extension detector a bit
so that it won’t cross individual URL query param boundaries any
longer. This is done by adding `&` as a stop character:
```
def ext
  Pathname.new(@url).extname[/[^?&]+/]
end
```
This appears to fix the issue for most (if not all) practical
purposes.
											
										 
											2018-03-13 16:37:27 +01:00
										 |  |  |         its(:to_path) { is_expected.to end_with("some-cask--1.2.3.4") } | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with a file name trailing the first query parameter" do | 
					
						
							|  |  |  |       let(:url) { "http://example.com/download?file=cask.zip&a=1" } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       describe "#tarball_path" do | 
					
						
							|  |  |  |         subject { downloader.tarball_path } | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Add tests for CurlDownloadStrategy#tarball_path
Sometimes, `brew cask fetch`/`install` fails with an error message
similar to this:
```
==> Downloading https://w3g3a5v6.ssl.hwcdn.net/upload2/game/214692/735
Error: Download failed on Cask 'steamed-hams' with message: Operation
not supported @ rb_sysopen -
/Users/claudia/Documents/dev/brew/var/homebrew/locks/steamed-hams--1.0
.com&Expires=1520937180&Signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9y
VZUE0uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8LBLKnz%2B2X%2B
iq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHCyzMmyNe5giEKJNW8WF0KXriULhzLTWL
SA3ZTLCIofAdRiiGje1kNYY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7S
LvEDBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXNN4o1mW%2FMayy2t
TY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2BPK%2B4Sg==&hwexp=1520937440&hwsi
g=55bc66884b925ef22f8673c33bfcc33b.incomplete.lock
```
To reproduce the issue, check out this branch and run the
`cask/download_strategy` test suite:
```
brew tests --only=cask/download_strategy
```
Or take a real-life example, which would be a Cask whose URL has
**1.** no `.` character anywhere in the URL path itself (outside of
the domain name), **and 2.** at least one query parameter with a `.`
character in it; **and 3.** other query parameters following that
with a combined length of more than 255 characters.
This combination may be uncommon but it exists, especially in
[URLs that change on every visit](https://github.com/caskroom/homebrew-cask/blob/1002d41242216ce7c71597765dc0a86cabd1fcf7/doc/cask_language_reference/stanzas/url.md#urls-that-change-on-every-visit),
for example
[`steamed-hams`](https://github.com/claui/homebrew-cask-games/blob/9d7df499cdc3665ca2c68a555e9ff9826a2200e7/Casks/steamed-hams.rb)
from my `claui/cask-games` tap:
    $ brew tap claui/cask-games
    $ brew cask fetch steamed-hams
In a nutshell, **URL query strings sometimes look like a very long
file extension to Homebrew,** which it then proceeds to use as a file
name.
In `CurlDownloadStrategy`, Homebrew seems to apply a heuristic to the
cask URL in order to figure out a possibly meaningful file extension.
Sometimes this heuristic produces immensely long file extensions,
especially when there’s a query parameter with a `.` character in it
but not in the URL path itself (outside of the domain name).
Homebrew then believes that everything after the `.` is a file
extension. In one of the later steps, it tries to create a lock file
containing that extension, which fails because HFS+ cannot handle
files whose base file name has more than 255 characters.
One solution would be to improve Homebrew’s extension detector a bit
so that it won’t cross individual URL query param boundaries any
longer. This is done by adding `&` as a stop character:
```
def ext
  Pathname.new(@url).extname[/[^?&]+/]
end
```
This appears to fix the issue for most (if not all) practical
purposes.
											
										 
											2018-03-13 16:37:27 +01:00
										 |  |  |         its(:extname) { is_expected.to eq(".zip") } | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with a file name trailing the second query parameter" do | 
					
						
							|  |  |  |       let(:url) { "http://example.com/dl?a=1&file=cask.zip&b=2" } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       describe "#tarball_path" do | 
					
						
							|  |  |  |         subject { downloader.tarball_path } | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Add tests for CurlDownloadStrategy#tarball_path
Sometimes, `brew cask fetch`/`install` fails with an error message
similar to this:
```
==> Downloading https://w3g3a5v6.ssl.hwcdn.net/upload2/game/214692/735
Error: Download failed on Cask 'steamed-hams' with message: Operation
not supported @ rb_sysopen -
/Users/claudia/Documents/dev/brew/var/homebrew/locks/steamed-hams--1.0
.com&Expires=1520937180&Signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9y
VZUE0uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8LBLKnz%2B2X%2B
iq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHCyzMmyNe5giEKJNW8WF0KXriULhzLTWL
SA3ZTLCIofAdRiiGje1kNYY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7S
LvEDBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXNN4o1mW%2FMayy2t
TY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2BPK%2B4Sg==&hwexp=1520937440&hwsi
g=55bc66884b925ef22f8673c33bfcc33b.incomplete.lock
```
To reproduce the issue, check out this branch and run the
`cask/download_strategy` test suite:
```
brew tests --only=cask/download_strategy
```
Or take a real-life example, which would be a Cask whose URL has
**1.** no `.` character anywhere in the URL path itself (outside of
the domain name), **and 2.** at least one query parameter with a `.`
character in it; **and 3.** other query parameters following that
with a combined length of more than 255 characters.
This combination may be uncommon but it exists, especially in
[URLs that change on every visit](https://github.com/caskroom/homebrew-cask/blob/1002d41242216ce7c71597765dc0a86cabd1fcf7/doc/cask_language_reference/stanzas/url.md#urls-that-change-on-every-visit),
for example
[`steamed-hams`](https://github.com/claui/homebrew-cask-games/blob/9d7df499cdc3665ca2c68a555e9ff9826a2200e7/Casks/steamed-hams.rb)
from my `claui/cask-games` tap:
    $ brew tap claui/cask-games
    $ brew cask fetch steamed-hams
In a nutshell, **URL query strings sometimes look like a very long
file extension to Homebrew,** which it then proceeds to use as a file
name.
In `CurlDownloadStrategy`, Homebrew seems to apply a heuristic to the
cask URL in order to figure out a possibly meaningful file extension.
Sometimes this heuristic produces immensely long file extensions,
especially when there’s a query parameter with a `.` character in it
but not in the URL path itself (outside of the domain name).
Homebrew then believes that everything after the `.` is a file
extension. In one of the later steps, it tries to create a lock file
containing that extension, which fails because HFS+ cannot handle
files whose base file name has more than 255 characters.
One solution would be to improve Homebrew’s extension detector a bit
so that it won’t cross individual URL query param boundaries any
longer. This is done by adding `&` as a stop character:
```
def ext
  Pathname.new(@url).extname[/[^?&]+/]
end
```
This appears to fix the issue for most (if not all) practical
purposes.
											
										 
											2018-03-13 16:37:27 +01:00
										 |  |  |         its(:extname) { is_expected.to eq(".zip") } | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with an unusually long query string" do | 
					
						
							|  |  |  |       let(:url) do | 
					
						
							|  |  |  |         [ | 
					
						
							|  |  |  |           "https://node49152.ssl.fancycdn.example.com", | 
					
						
							|  |  |  |           "/fancycdn/node/49152/file/upload/download", | 
					
						
							|  |  |  |           "?cask_class=zf920df", | 
					
						
							|  |  |  |           "&cask_group=2348779087242312", | 
					
						
							|  |  |  |           "&cask_archive_file_name=cask.zip", | 
					
						
							|  |  |  |           "&signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9yVZUE0", | 
					
						
							|  |  |  |           "uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8L", | 
					
						
							|  |  |  |           "BLKnz%2B2X%2Biq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHC", | 
					
						
							|  |  |  |           "yzMmyNe5giEKJNW8WF0KXriULhzLTWLSA3ZTLCIofAdRiiGje1kN", | 
					
						
							|  |  |  |           "YY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7SLvE", | 
					
						
							|  |  |  |           "DBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXN", | 
					
						
							|  |  |  |           "N4o1mW%2FMayy2tTY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2", | 
					
						
							|  |  |  |           "BPK%2B4Sg==", | 
					
						
							| 
									
										
										
										
											2018-06-14 22:48:37 +02:00
										 |  |  |         ].join | 
					
						
							| 
									
										
											  
											
												Add tests for CurlDownloadStrategy#tarball_path
Sometimes, `brew cask fetch`/`install` fails with an error message
similar to this:
```
==> Downloading https://w3g3a5v6.ssl.hwcdn.net/upload2/game/214692/735
Error: Download failed on Cask 'steamed-hams' with message: Operation
not supported @ rb_sysopen -
/Users/claudia/Documents/dev/brew/var/homebrew/locks/steamed-hams--1.0
.com&Expires=1520937180&Signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9y
VZUE0uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8LBLKnz%2B2X%2B
iq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHCyzMmyNe5giEKJNW8WF0KXriULhzLTWL
SA3ZTLCIofAdRiiGje1kNYY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7S
LvEDBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXNN4o1mW%2FMayy2t
TY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2BPK%2B4Sg==&hwexp=1520937440&hwsi
g=55bc66884b925ef22f8673c33bfcc33b.incomplete.lock
```
To reproduce the issue, check out this branch and run the
`cask/download_strategy` test suite:
```
brew tests --only=cask/download_strategy
```
Or take a real-life example, which would be a Cask whose URL has
**1.** no `.` character anywhere in the URL path itself (outside of
the domain name), **and 2.** at least one query parameter with a `.`
character in it; **and 3.** other query parameters following that
with a combined length of more than 255 characters.
This combination may be uncommon but it exists, especially in
[URLs that change on every visit](https://github.com/caskroom/homebrew-cask/blob/1002d41242216ce7c71597765dc0a86cabd1fcf7/doc/cask_language_reference/stanzas/url.md#urls-that-change-on-every-visit),
for example
[`steamed-hams`](https://github.com/claui/homebrew-cask-games/blob/9d7df499cdc3665ca2c68a555e9ff9826a2200e7/Casks/steamed-hams.rb)
from my `claui/cask-games` tap:
    $ brew tap claui/cask-games
    $ brew cask fetch steamed-hams
In a nutshell, **URL query strings sometimes look like a very long
file extension to Homebrew,** which it then proceeds to use as a file
name.
In `CurlDownloadStrategy`, Homebrew seems to apply a heuristic to the
cask URL in order to figure out a possibly meaningful file extension.
Sometimes this heuristic produces immensely long file extensions,
especially when there’s a query parameter with a `.` character in it
but not in the URL path itself (outside of the domain name).
Homebrew then believes that everything after the `.` is a file
extension. In one of the later steps, it tries to create a lock file
containing that extension, which fails because HFS+ cannot handle
files whose base file name has more than 255 characters.
One solution would be to improve Homebrew’s extension detector a bit
so that it won’t cross individual URL query param boundaries any
longer. This is done by adding `&` as a stop character:
```
def ext
  Pathname.new(@url).extname[/[^?&]+/]
end
```
This appears to fix the issue for most (if not all) practical
purposes.
											
										 
											2018-03-13 16:37:27 +01:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       describe "#tarball_path" do | 
					
						
							|  |  |  |         subject { downloader.tarball_path } | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Add tests for CurlDownloadStrategy#tarball_path
Sometimes, `brew cask fetch`/`install` fails with an error message
similar to this:
```
==> Downloading https://w3g3a5v6.ssl.hwcdn.net/upload2/game/214692/735
Error: Download failed on Cask 'steamed-hams' with message: Operation
not supported @ rb_sysopen -
/Users/claudia/Documents/dev/brew/var/homebrew/locks/steamed-hams--1.0
.com&Expires=1520937180&Signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9y
VZUE0uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8LBLKnz%2B2X%2B
iq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHCyzMmyNe5giEKJNW8WF0KXriULhzLTWL
SA3ZTLCIofAdRiiGje1kNYY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7S
LvEDBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXNN4o1mW%2FMayy2t
TY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2BPK%2B4Sg==&hwexp=1520937440&hwsi
g=55bc66884b925ef22f8673c33bfcc33b.incomplete.lock
```
To reproduce the issue, check out this branch and run the
`cask/download_strategy` test suite:
```
brew tests --only=cask/download_strategy
```
Or take a real-life example, which would be a Cask whose URL has
**1.** no `.` character anywhere in the URL path itself (outside of
the domain name), **and 2.** at least one query parameter with a `.`
character in it; **and 3.** other query parameters following that
with a combined length of more than 255 characters.
This combination may be uncommon but it exists, especially in
[URLs that change on every visit](https://github.com/caskroom/homebrew-cask/blob/1002d41242216ce7c71597765dc0a86cabd1fcf7/doc/cask_language_reference/stanzas/url.md#urls-that-change-on-every-visit),
for example
[`steamed-hams`](https://github.com/claui/homebrew-cask-games/blob/9d7df499cdc3665ca2c68a555e9ff9826a2200e7/Casks/steamed-hams.rb)
from my `claui/cask-games` tap:
    $ brew tap claui/cask-games
    $ brew cask fetch steamed-hams
In a nutshell, **URL query strings sometimes look like a very long
file extension to Homebrew,** which it then proceeds to use as a file
name.
In `CurlDownloadStrategy`, Homebrew seems to apply a heuristic to the
cask URL in order to figure out a possibly meaningful file extension.
Sometimes this heuristic produces immensely long file extensions,
especially when there’s a query parameter with a `.` character in it
but not in the URL path itself (outside of the domain name).
Homebrew then believes that everything after the `.` is a file
extension. In one of the later steps, it tries to create a lock file
containing that extension, which fails because HFS+ cannot handle
files whose base file name has more than 255 characters.
One solution would be to improve Homebrew’s extension detector a bit
so that it won’t cross individual URL query param boundaries any
longer. This is done by adding `&` as a stop character:
```
def ext
  Pathname.new(@url).extname[/[^?&]+/]
end
```
This appears to fix the issue for most (if not all) practical
purposes.
											
										 
											2018-03-13 16:37:27 +01:00
										 |  |  |         its(:extname) { is_expected.to eq(".zip") } | 
					
						
							|  |  |  |         its("to_path.length") { is_expected.to be_between(0, 255) } | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe Hbc::CurlPostDownloadStrategy do | 
					
						
							|  |  |  |     let(:downloader) { Hbc::CurlPostDownloadStrategy.new(cask) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       allow(downloader.temporary_path).to receive(:rename) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with :using and :data specified" do | 
					
						
							|  |  |  |       let(:url_options) { | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           using: :post, | 
					
						
							|  |  |  |           data:  { | 
					
						
							| 
									
										
										
										
											2016-10-14 20:33:16 +02:00
										 |  |  |             form: "data", | 
					
						
							|  |  |  |             is:   "good", | 
					
						
							|  |  |  |           }, | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it "adds curl args for post arguments" do | 
					
						
							|  |  |  |         curl_args = [] | 
					
						
							|  |  |  |         allow(downloader).to receive(:curl) { |*args| curl_args = args } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |         downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         expect(curl_args.each_cons(2)).to include(["-d", "form=data"]) | 
					
						
							|  |  |  |         expect(curl_args.each_cons(2)).to include(["-d", "is=good"]) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with :using but no :data" do | 
					
						
							|  |  |  |       let(:url_options) { { using: :post } } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it "adds curl args for a POST request" do | 
					
						
							|  |  |  |         curl_args = [] | 
					
						
							|  |  |  |         allow(downloader).to receive(:curl) { |*args| curl_args = args } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |         downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         expect(curl_args.each_cons(2)).to include(["-X", "POST"]) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe Hbc::SubversionDownloadStrategy do | 
					
						
							|  |  |  |     let(:url_options) { { using: :svn } } | 
					
						
							| 
									
										
										
										
											2018-07-19 23:56:51 +02:00
										 |  |  |     let(:fake_system_command) { class_double(SystemCommand) } | 
					
						
							| 
									
										
										
										
											2017-08-08 18:10:01 +02:00
										 |  |  |     let(:downloader) { Hbc::SubversionDownloadStrategy.new(cask, command: fake_system_command) } | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |     before do | 
					
						
							|  |  |  |       allow(fake_system_command).to receive(:run!) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "returns a tarball path on fetch" do | 
					
						
							|  |  |  |       allow(downloader).to receive(:compress) | 
					
						
							|  |  |  |       allow(downloader).to receive(:fetch_repo) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-08 18:10:01 +02:00
										 |  |  |       expect(downloader.fetch).to equal(downloader.cached_location) | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "calls fetch_repo with default arguments for a simple Cask" do | 
					
						
							|  |  |  |       allow(downloader).to receive(:compress) | 
					
						
							|  |  |  |       allow(downloader).to receive(:fetch_repo) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |       downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |       expect(downloader).to have_received(:fetch_repo).with( | 
					
						
							|  |  |  |         downloader.cached_location, | 
					
						
							| 
									
										
										
										
											2017-02-12 15:06:54 +00:00
										 |  |  |         cask.url.to_s, | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |       ) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "calls svn with default arguments for a simple Cask" do | 
					
						
							|  |  |  |       allow(downloader).to receive(:compress) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |       downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |       expect(fake_system_command).to have_received(:run!).with( | 
					
						
							|  |  |  |         "/usr/bin/svn", | 
					
						
							|  |  |  |         hash_including(args: [ | 
					
						
							| 
									
										
										
										
											2016-10-14 20:33:16 +02:00
										 |  |  |                          "checkout", | 
					
						
							|  |  |  |                          "--force", | 
					
						
							|  |  |  |                          "--config-option", | 
					
						
							|  |  |  |                          "config:miscellany:use-commit-times=yes", | 
					
						
							|  |  |  |                          cask.url.to_s, | 
					
						
							|  |  |  |                          downloader.cached_location, | 
					
						
							| 
									
										
										
										
											2017-02-12 15:06:54 +00:00
										 |  |  |                        ]), | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |       ) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with trust_cert set on the URL" do | 
					
						
							|  |  |  |       let(:url_options) { | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           using:      :svn, | 
					
						
							|  |  |  |           trust_cert: true, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it "adds svn arguments for :trust_cert" do | 
					
						
							|  |  |  |         allow(downloader).to receive(:compress) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |         downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         expect(fake_system_command).to have_received(:run!).with( | 
					
						
							|  |  |  |           "/usr/bin/svn", | 
					
						
							|  |  |  |           hash_including(args: [ | 
					
						
							| 
									
										
										
										
											2016-10-14 20:33:16 +02:00
										 |  |  |                            "checkout", | 
					
						
							|  |  |  |                            "--force", | 
					
						
							|  |  |  |                            "--config-option", | 
					
						
							|  |  |  |                            "config:miscellany:use-commit-times=yes", | 
					
						
							|  |  |  |                            "--trust-server-cert", | 
					
						
							|  |  |  |                            "--non-interactive", | 
					
						
							|  |  |  |                            cask.url.to_s, | 
					
						
							|  |  |  |                            downloader.cached_location, | 
					
						
							| 
									
										
										
										
											2017-02-12 15:06:54 +00:00
										 |  |  |                          ]), | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |         ) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with :revision set on url" do | 
					
						
							|  |  |  |       let(:url_options) { | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           using:    :svn, | 
					
						
							|  |  |  |           revision: "10", | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it "adds svn arguments for :revision" do | 
					
						
							|  |  |  |         allow(downloader).to receive(:compress) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 19:55:05 +02:00
										 |  |  |         downloader.fetch | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         expect(fake_system_command).to have_received(:run!).with( | 
					
						
							|  |  |  |           "/usr/bin/svn", | 
					
						
							|  |  |  |           hash_including(args: [ | 
					
						
							| 
									
										
										
										
											2016-10-14 20:33:16 +02:00
										 |  |  |                            "checkout", | 
					
						
							|  |  |  |                            "--force", | 
					
						
							|  |  |  |                            "--config-option", | 
					
						
							|  |  |  |                            "config:miscellany:use-commit-times=yes", | 
					
						
							|  |  |  |                            cask.url.to_s, | 
					
						
							|  |  |  |                            downloader.cached_location, | 
					
						
							|  |  |  |                            "-r", | 
					
						
							|  |  |  |                            "10", | 
					
						
							| 
									
										
										
										
											2017-02-12 15:06:54 +00:00
										 |  |  |                          ]), | 
					
						
							| 
									
										
										
										
											2016-08-18 22:11:42 +03:00
										 |  |  |         ) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |