berkeley_library-util 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.idea/util.iml +7 -7
- data/.rubocop.yml +4 -0
- data/CHANGES.md +7 -0
- data/lib/berkeley_library/util/module_info.rb +1 -1
- data/lib/berkeley_library/util/uris/requester.rb +36 -9
- data/lib/berkeley_library/util/uris.rb +68 -3
- data/spec/berkeley_library/util/uris/requester_spec.rb +64 -0
- data/spec/berkeley_library/util/uris_spec.rb +95 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9eac060e7b0956b965455bddb8cc6c9721d5af784ac421766c2d791d99632927
|
4
|
+
data.tar.gz: a68fecb0b685ddffa3cb0fff6a46180e377efeb71b1cc5d23a389117c27be72d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 052c93d1c09d569479d4bec70ffa9413e3ab0404f19482955b89552891c7058ab76ec11191456aa79016e1e2c98403eb355c89982ffe31f31d37f650517970b2
|
7
|
+
data.tar.gz: 530542deae99ffbd089cfc294b614d9560e2298ed4f7a20161970185935155a620b578bd99066a219e7d0410305d525e354c9b811162adbefdf3dda35c904427
|
data/.idea/util.iml
CHANGED
@@ -8,9 +8,9 @@
|
|
8
8
|
<content url="file://$MODULE_DIR$" />
|
9
9
|
<orderEntry type="jdk" jdkName="RVM: ruby-2.7.4" jdkType="RUBY_SDK" />
|
10
10
|
<orderEntry type="sourceFolder" forTests="false" />
|
11
|
-
<orderEntry type="library" scope="PROVIDED" name="actionpack (v7.0.
|
12
|
-
<orderEntry type="library" scope="PROVIDED" name="actionview (v7.0.
|
13
|
-
<orderEntry type="library" scope="PROVIDED" name="activesupport (v7.0.
|
11
|
+
<orderEntry type="library" scope="PROVIDED" name="actionpack (v7.0.3, RVM: ruby-2.7.4) [gem]" level="application" />
|
12
|
+
<orderEntry type="library" scope="PROVIDED" name="actionview (v7.0.3, RVM: ruby-2.7.4) [gem]" level="application" />
|
13
|
+
<orderEntry type="library" scope="PROVIDED" name="activesupport (v7.0.3, RVM: ruby-2.7.4) [gem]" level="application" />
|
14
14
|
<orderEntry type="library" scope="PROVIDED" name="addressable (v2.8.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
15
15
|
<orderEntry type="library" scope="PROVIDED" name="amazing_print (v1.4.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
16
16
|
<orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
@@ -35,13 +35,13 @@
|
|
35
35
|
<orderEntry type="library" scope="PROVIDED" name="http-cookie (v1.0.4, RVM: ruby-2.7.4) [gem]" level="application" />
|
36
36
|
<orderEntry type="library" scope="PROVIDED" name="i18n (v1.10.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
37
37
|
<orderEntry type="library" scope="PROVIDED" name="lograge (v0.12.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
38
|
-
<orderEntry type="library" scope="PROVIDED" name="loofah (v2.
|
38
|
+
<orderEntry type="library" scope="PROVIDED" name="loofah (v2.18.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
39
39
|
<orderEntry type="library" scope="PROVIDED" name="method_source (v1.0.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
40
40
|
<orderEntry type="library" scope="PROVIDED" name="mime-types (v3.4.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
41
41
|
<orderEntry type="library" scope="PROVIDED" name="mime-types-data (v3.2022.0105, RVM: ruby-2.7.4) [gem]" level="application" />
|
42
42
|
<orderEntry type="library" scope="PROVIDED" name="minitest (v5.15.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
43
43
|
<orderEntry type="library" scope="PROVIDED" name="netrc (v0.11.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
44
|
-
<orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.13.
|
44
|
+
<orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.13.6, RVM: ruby-2.7.4) [gem]" level="application" />
|
45
45
|
<orderEntry type="library" scope="PROVIDED" name="oj (v3.13.11, RVM: ruby-2.7.4) [gem]" level="application" />
|
46
46
|
<orderEntry type="library" scope="PROVIDED" name="ougai (v1.9.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
47
47
|
<orderEntry type="library" scope="PROVIDED" name="parallel (v1.22.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
@@ -52,7 +52,7 @@
|
|
52
52
|
<orderEntry type="library" scope="PROVIDED" name="rack-test (v1.1.0, RVM: ruby-2.7.4) [gem]" level="application" />
|
53
53
|
<orderEntry type="library" scope="PROVIDED" name="rails-dom-testing (v2.0.3, RVM: ruby-2.7.4) [gem]" level="application" />
|
54
54
|
<orderEntry type="library" scope="PROVIDED" name="rails-html-sanitizer (v1.4.2, RVM: ruby-2.7.4) [gem]" level="application" />
|
55
|
-
<orderEntry type="library" scope="PROVIDED" name="railties (v7.0.
|
55
|
+
<orderEntry type="library" scope="PROVIDED" name="railties (v7.0.3, RVM: ruby-2.7.4) [gem]" level="application" />
|
56
56
|
<orderEntry type="library" scope="PROVIDED" name="rainbow (v3.1.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
57
57
|
<orderEntry type="library" scope="PROVIDED" name="rake (v13.0.6, RVM: ruby-2.7.4) [gem]" level="application" />
|
58
58
|
<orderEntry type="library" scope="PROVIDED" name="regexp_parser (v2.3.1, RVM: ruby-2.7.4) [gem]" level="application" />
|
@@ -98,7 +98,7 @@
|
|
98
98
|
</RakeTaskImpl>
|
99
99
|
<RakeTaskImpl description="Run all specs in spec directory, with coverage" fullCommand="coverage" id="coverage" />
|
100
100
|
<RakeTaskImpl description="Run tests, check test coverage, check code style, check for vulnerabilities, build gem" fullCommand="default" id="default" />
|
101
|
-
<RakeTaskImpl description="Build berkeley_library-util.gemspec as berkeley_library-util-0.1.
|
101
|
+
<RakeTaskImpl description="Build berkeley_library-util.gemspec as berkeley_library-util-0.1.4.gem" fullCommand="gem" id="gem" />
|
102
102
|
<RakeTaskImpl description="Run RuboCop with auto-correct, and output results to console" fullCommand="ra" id="ra" />
|
103
103
|
<RakeTaskImpl description="Run rubocop with HTML output" fullCommand="rubocop" id="rubocop" />
|
104
104
|
<RakeTaskImpl id="rubocop">
|
data/.rubocop.yml
CHANGED
@@ -55,6 +55,10 @@ Style/CommentedKeyword:
|
|
55
55
|
Style/Documentation:
|
56
56
|
Enabled: false
|
57
57
|
|
58
|
+
# You say "cryptic", I say "compact"
|
59
|
+
Style/FormatString:
|
60
|
+
EnforcedStyle: percent
|
61
|
+
|
58
62
|
# Adding more line noise to format strings will not improve them
|
59
63
|
Style/FormatStringToken:
|
60
64
|
Enabled: false
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# 0.1.5 (2022-09-16)
|
2
|
+
|
3
|
+
- Adds `URIs#path_escape` to escape URL path segments
|
4
|
+
- Adds `URIs#head` and `URIs#head_response`, to make a HEAD request and return the HTTP status code
|
5
|
+
and raw response object, respectively
|
6
|
+
- Clarifies documentation for `URIs#get` and `URIs#get_response` regarding unsuccessful requests
|
7
|
+
|
1
8
|
# 0.1.4 (2022-07-20)
|
2
9
|
|
3
10
|
- Adds `URIs#safe_parse_uri`, which returns `nil` for invalid URLs (unlike `URIs#uri_or_nil`, which
|
@@ -7,7 +7,7 @@ module BerkeleyLibrary
|
|
7
7
|
SUMMARY = 'Miscellaneous Ruby utilities for the UC Berkeley Library'.freeze
|
8
8
|
DESCRIPTION = 'A collection of miscellaneous Ruby routines for the UC Berkeley Library.'.freeze
|
9
9
|
LICENSE = 'MIT'.freeze
|
10
|
-
VERSION = '0.1.
|
10
|
+
VERSION = '0.1.5'.freeze
|
11
11
|
HOMEPAGE = 'https://github.com/BerkeleyLibrary/util'.freeze
|
12
12
|
end
|
13
13
|
end
|
@@ -16,29 +16,56 @@ module BerkeleyLibrary
|
|
16
16
|
# @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
|
17
17
|
# @param headers [Hash] the request headers.
|
18
18
|
# @return [String] the body as a string.
|
19
|
-
# @raise [RestClient::Exception] in the event of an
|
19
|
+
# @raise [RestClient::Exception] in the event of an unsuccessful request.
|
20
20
|
def get(uri, params: {}, headers: {})
|
21
|
-
resp =
|
21
|
+
resp = make_request(:get, uri, params, headers)
|
22
22
|
resp.body
|
23
23
|
end
|
24
24
|
|
25
|
-
# Performs a
|
25
|
+
# Performs a HEAD request and returns the response status as an integer.
|
26
|
+
# Note that unlike {Requester#get}, this does not raise an error in the
|
27
|
+
# event of an unsuccessful request.
|
28
|
+
#
|
29
|
+
# @param uri [URI, String] the URI to HEAD
|
30
|
+
# @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
|
31
|
+
# @param headers [Hash] the request headers.
|
32
|
+
# @return [Integer] the response code as an integer.
|
33
|
+
def head(uri, params: {}, headers: {})
|
34
|
+
head_response(uri, params: params, headers: headers).code
|
35
|
+
end
|
36
|
+
|
37
|
+
# Performs a GET request and returns the response, even in the event of
|
38
|
+
# a failed request.
|
26
39
|
#
|
27
40
|
# @param uri [URI, String] the URI to GET
|
28
41
|
# @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
|
29
42
|
# @param headers [Hash] the request headers.
|
30
43
|
# @return [RestClient::Response] the body as a string.
|
31
44
|
def get_response(uri, params: {}, headers: {})
|
32
|
-
|
45
|
+
make_request(:get, uri, params, headers)
|
46
|
+
rescue RestClient::Exception => e
|
47
|
+
e.response
|
48
|
+
end
|
49
|
+
|
50
|
+
# Performs a HEAD request and returns the response, even in the event of
|
51
|
+
# a failed request.
|
52
|
+
#
|
53
|
+
# @param uri [URI, String] the URI to HEAD
|
54
|
+
# @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
|
55
|
+
# @param headers [Hash] the request headers.
|
56
|
+
# @return [RestClient::Response] the body as a string.
|
57
|
+
def head_response(uri, params: {}, headers: {})
|
58
|
+
make_request(:head, uri, params, headers)
|
33
59
|
rescue RestClient::Exception => e
|
34
60
|
e.response
|
35
61
|
end
|
36
62
|
|
37
63
|
private
|
38
64
|
|
39
|
-
|
65
|
+
# @return [RestClient::Response]
|
66
|
+
def make_request(method, uri, params, headers)
|
40
67
|
url_str = url_str_with_params(uri, params)
|
41
|
-
|
68
|
+
req_resp_or_raise(method, url_str, headers)
|
42
69
|
end
|
43
70
|
|
44
71
|
def url_str_with_params(uri, params)
|
@@ -57,15 +84,15 @@ module BerkeleyLibrary
|
|
57
84
|
end
|
58
85
|
|
59
86
|
# @return [RestClient::Response]
|
60
|
-
def
|
61
|
-
resp = RestClient.
|
87
|
+
def req_resp_or_raise(method, url_str, headers)
|
88
|
+
resp = RestClient::Request.execute(method: method, url: url_str, headers: headers)
|
62
89
|
begin
|
63
90
|
return resp if (status = resp.code) == 200
|
64
91
|
|
65
92
|
raise(exception_for(resp, status))
|
66
93
|
ensure
|
67
94
|
# noinspection RubyMismatchedReturnType
|
68
|
-
logger.info("
|
95
|
+
logger.info("#{method.to_s.upcase} #{url_str} returned #{status}")
|
69
96
|
end
|
70
97
|
end
|
71
98
|
|
@@ -8,6 +8,8 @@ module BerkeleyLibrary
|
|
8
8
|
module URIs
|
9
9
|
include BerkeleyLibrary::Logging
|
10
10
|
|
11
|
+
UTF_8 = Encoding::UTF_8
|
12
|
+
|
11
13
|
class << self
|
12
14
|
include URIs
|
13
15
|
end
|
@@ -24,18 +26,31 @@ module BerkeleyLibrary
|
|
24
26
|
Appender.new(uri, *elements).to_uri
|
25
27
|
end
|
26
28
|
|
27
|
-
# Performs a GET request.
|
29
|
+
# Performs a GET request and returns the response body as a string.
|
28
30
|
#
|
29
31
|
# @param uri [URI, String] the URI to GET
|
30
32
|
# @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
|
31
33
|
# @param headers [Hash] the request headers.
|
32
34
|
# @return [String] the body as a string.
|
33
|
-
# @raise [RestClient::Exception] in the event of an
|
35
|
+
# @raise [RestClient::Exception] in the event of an unsuccessful request.
|
34
36
|
def get(uri, params: {}, headers: {})
|
35
37
|
Requester.get(uri, params: params, headers: headers)
|
36
38
|
end
|
37
39
|
|
38
|
-
# Performs a
|
40
|
+
# Performs a HEAD request and returns the response status as an integer.
|
41
|
+
# Note that unlike {Requester#get}, this does not raise an error in the
|
42
|
+
# event of an unsuccessful request.
|
43
|
+
#
|
44
|
+
# @param uri [URI, String] the URI to HEAD
|
45
|
+
# @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
|
46
|
+
# @param headers [Hash] the request headers.
|
47
|
+
# @return [Integer] the response code as an integer.
|
48
|
+
def head(uri, params: {}, headers: {})
|
49
|
+
Requester.head(uri, params: params, headers: headers)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Performs a GET request and returns the response, even in the event of
|
53
|
+
# a failed request.
|
39
54
|
#
|
40
55
|
# @param uri [URI, String] the URI to GET
|
41
56
|
# @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
|
@@ -45,6 +60,17 @@ module BerkeleyLibrary
|
|
45
60
|
Requester.get_response(uri, params: params, headers: headers)
|
46
61
|
end
|
47
62
|
|
63
|
+
# Performs a HEAD request and returns the response, even in the event of
|
64
|
+
# a failed request.
|
65
|
+
#
|
66
|
+
# @param uri [URI, String] the URI to HEAD
|
67
|
+
# @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
|
68
|
+
# @param headers [Hash] the request headers.
|
69
|
+
# @return [RestClient::Response] the body as a string.
|
70
|
+
def head_response(uri, params: {}, headers: {})
|
71
|
+
Requester.head_response(uri, params: params, headers: headers)
|
72
|
+
end
|
73
|
+
|
48
74
|
# Returns the specified URL as a URI, or `nil` if the URL is `nil`.
|
49
75
|
# @param url [String, URI, nil] the URL.
|
50
76
|
# @return [URI] the URI, or `nil`.
|
@@ -54,6 +80,19 @@ module BerkeleyLibrary
|
|
54
80
|
Validator.uri_or_nil(url)
|
55
81
|
end
|
56
82
|
|
83
|
+
# Escapes the specified string so that it can be used as a URL path segment,
|
84
|
+
# replacing disallowed characters (including /) with percent-encodings as needed.
|
85
|
+
def path_escape(s)
|
86
|
+
raise ArgumentError, "Can't escape #{s.inspect}: not a string" unless s.respond_to?(:encoding)
|
87
|
+
raise ArgumentError, "Can't escape #{s.inspect}: expected #{UTF_8}, was #{s.encoding}" unless s.encoding == UTF_8
|
88
|
+
|
89
|
+
''.tap do |escaped|
|
90
|
+
s.bytes.each do |b|
|
91
|
+
escaped << (should_escape?(b, :path_segment) ? '%%%02X' % b : b.chr)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
57
96
|
# Returns the specified URL as a URI, or `nil` if the URL cannot
|
58
97
|
# be parsed.
|
59
98
|
# @param url [Object, nil] the URL.
|
@@ -65,6 +104,32 @@ module BerkeleyLibrary
|
|
65
104
|
logger.warn("Error parsing URL #{url.inspect}", e)
|
66
105
|
nil
|
67
106
|
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# TODO: extend to cover other modes - host, zone, path, password, query, fragment
|
111
|
+
# cf. https://github.com/golang/go/blob/master/src/net/url/url.go
|
112
|
+
ALLOWED_BYTES_BY_MODE = {
|
113
|
+
path_segment: [0x24, 0x26, 0x2b, 0x3a, 0x3d, 0x40] # @ & = + $
|
114
|
+
}.freeze
|
115
|
+
|
116
|
+
def should_escape?(b, mode)
|
117
|
+
return false if unreserved?(b)
|
118
|
+
return false if ALLOWED_BYTES_BY_MODE[mode]&.include?(b)
|
119
|
+
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
123
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
124
|
+
def unreserved?(byte)
|
125
|
+
return true if byte >= 0x41 && byte <= 0x5a # A-Z
|
126
|
+
return true if byte >= 0x61 && byte <= 0x7a # a-z
|
127
|
+
return true if byte >= 0x30 && byte <= 0x39 # 0-9
|
128
|
+
return true if [0x2d, 0x2e, 0x5f, 0x7e].include?(byte) # - . _ ~
|
129
|
+
|
130
|
+
false
|
131
|
+
end
|
132
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
68
133
|
end
|
69
134
|
end
|
70
135
|
end
|
@@ -66,6 +66,70 @@ module BerkeleyLibrary
|
|
66
66
|
expect(result).to eq(expected_body)
|
67
67
|
end
|
68
68
|
end
|
69
|
+
|
70
|
+
describe :head do
|
71
|
+
it 'returns an HTTP status code for a URL string' do
|
72
|
+
url = 'https://example.org/'
|
73
|
+
expected_status = 203
|
74
|
+
stub_request(:head, url).to_return(status: expected_status)
|
75
|
+
|
76
|
+
result = Requester.head(url)
|
77
|
+
expect(result).to eq(expected_status)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'returns an HTTP response body for a URI' do
|
81
|
+
uri = URI.parse('https://example.org/')
|
82
|
+
expected_status = 203
|
83
|
+
stub_request(:head, uri).to_return(status: expected_status)
|
84
|
+
|
85
|
+
result = Requester.head(uri)
|
86
|
+
expect(result).to eq(expected_status)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'appends query parameters' do
|
90
|
+
url = 'https://example.org/'
|
91
|
+
params = { p1: 1, p2: 2 }
|
92
|
+
url_with_query = "#{url}?#{URI.encode_www_form(params)}"
|
93
|
+
expected_status = 203
|
94
|
+
stub_request(:head, url_with_query).to_return(status: expected_status)
|
95
|
+
|
96
|
+
result = Requester.head(url, params: params)
|
97
|
+
expect(result).to eq(expected_status)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'sends request headers' do
|
101
|
+
url = 'https://example.org/'
|
102
|
+
headers = { 'X-help' => 'I am trapped in a unit test' }
|
103
|
+
expected_status = 203
|
104
|
+
stub_request(:head, url).with(headers: headers).to_return(status: expected_status)
|
105
|
+
|
106
|
+
result = Requester.head(url, headers: headers)
|
107
|
+
expect(result).to eq(expected_status)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'returns the status even for unsuccessful requests' do
|
111
|
+
aggregate_failures 'responses' do
|
112
|
+
[207, 400, 401, 403, 404, 405, 418, 451, 500, 503].each do |expected_status|
|
113
|
+
url = "http://example.edu/#{expected_status}"
|
114
|
+
stub_request(:head, url).to_return(status: expected_status)
|
115
|
+
|
116
|
+
result = Requester.head(url)
|
117
|
+
expect(result).to eq(expected_status)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'handles redirects' do
|
123
|
+
url1 = 'https://example.org/'
|
124
|
+
url2 = 'https://example.edu/'
|
125
|
+
stub_request(:head, url1).to_return(status: 302, headers: { 'Location' => url2 })
|
126
|
+
expected_status = 203
|
127
|
+
stub_request(:head, url2).to_return(status: expected_status)
|
128
|
+
|
129
|
+
result = Requester.head(url1)
|
130
|
+
expect(result).to eq(expected_status)
|
131
|
+
end
|
132
|
+
end
|
69
133
|
end
|
70
134
|
end
|
71
135
|
end
|
@@ -16,16 +16,14 @@ module BerkeleyLibrary::Util
|
|
16
16
|
expect(new_uri).to eq(expected_uri)
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
xit "doesn't append to a bare URI when there's nothing to append" do
|
19
|
+
it 'returns a bare URI when there\'s nothing to append' do
|
21
20
|
original_url = 'https://example.org'
|
22
21
|
new_uri = URIs.append(original_url)
|
23
22
|
expected_uri = URI(original_url)
|
24
23
|
expect(new_uri).to eq(expected_uri)
|
25
24
|
end
|
26
25
|
|
27
|
-
|
28
|
-
xit "doesn't append to a bare URI when there's only a query string" do
|
26
|
+
it 'appends to a bare URI even when there\'s only a query string' do
|
29
27
|
original_url = 'https://example.org'
|
30
28
|
new_uri = URIs.append(original_url, '?foo=bar')
|
31
29
|
expected_uri = URI("#{original_url}?foo=bar")
|
@@ -136,6 +134,19 @@ module BerkeleyLibrary::Util
|
|
136
134
|
new_uri = URIs.append(original_uri, '?qux=corge', '&grault=plugh#xyzzy')
|
137
135
|
expect(new_uri).to eq(URI('https://example.org/foo/bar?qux=corge&grault=plugh#xyzzy'))
|
138
136
|
end
|
137
|
+
|
138
|
+
it 'rejects invalid characters' do
|
139
|
+
original_uri = URI('https://example.org/')
|
140
|
+
expect { URIs.append(original_uri, '精力善用') }.to raise_error(URI::InvalidComponentError)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'accepts percent-encoded path segments' do
|
144
|
+
original_uri = URI('https://example.org/')
|
145
|
+
encoded_segment = URIs.path_escape('精力善用')
|
146
|
+
new_uri = URIs.append(original_uri, encoded_segment, 'foo.html')
|
147
|
+
expected_url = "https://example.org/#{encoded_segment}/foo.html"
|
148
|
+
expect(new_uri).to eq(URI(expected_url))
|
149
|
+
end
|
139
150
|
end
|
140
151
|
|
141
152
|
describe 'requests' do
|
@@ -177,6 +188,42 @@ module BerkeleyLibrary::Util
|
|
177
188
|
expect(response.code).to eq(404)
|
178
189
|
end
|
179
190
|
end
|
191
|
+
|
192
|
+
describe :head do
|
193
|
+
it 'makes a HEAD request' do
|
194
|
+
expected_status = 200
|
195
|
+
stub_request(:head, url_with_query).with(headers: headers).to_return(status: expected_status)
|
196
|
+
|
197
|
+
result = URIs.head(url, params: params, headers: headers)
|
198
|
+
expect(result).to eq(expected_status)
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'returns the status even for unsuccessful requests' do
|
202
|
+
expected_status = 404
|
203
|
+
stub_request(:head, url_with_query).with(headers: headers).to_return(status: expected_status)
|
204
|
+
|
205
|
+
result = URIs.head(url, params: params, headers: headers)
|
206
|
+
expect(result).to eq(expected_status)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe :head_response do
|
211
|
+
it 'makes a HEAD request' do
|
212
|
+
stub_request(:head, url_with_query).with(headers: headers).to_return(body: expected_body)
|
213
|
+
|
214
|
+
response = URIs.head_response(url, params: params, headers: headers)
|
215
|
+
expect(response.body).to eq(expected_body)
|
216
|
+
expect(response.code).to eq(200)
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'returns the response even for errors' do
|
220
|
+
stub_request(:head, url_with_query).with(headers: headers).to_return(status: 404, body: expected_body)
|
221
|
+
|
222
|
+
response = URIs.head_response(url, params: params, headers: headers)
|
223
|
+
expect(response.body).to eq(expected_body)
|
224
|
+
expect(response.code).to eq(404)
|
225
|
+
end
|
226
|
+
end
|
180
227
|
end
|
181
228
|
|
182
229
|
describe :safe_parse_uri do
|
@@ -210,5 +257,49 @@ module BerkeleyLibrary::Util
|
|
210
257
|
end
|
211
258
|
end
|
212
259
|
end
|
260
|
+
|
261
|
+
describe :path_escape do
|
262
|
+
let(:in_out) do
|
263
|
+
{
|
264
|
+
'' => '',
|
265
|
+
'corge' => 'corge',
|
266
|
+
'foo+bar' => 'foo+bar',
|
267
|
+
'qux/quux' => 'qux%2Fquux',
|
268
|
+
'foo bar baz' => 'foo%20bar%20baz',
|
269
|
+
'25%' => '25%25',
|
270
|
+
"\t !\"#$%&'()*+,/:;<=>?@[\\]^`{|}☺" => '%09%20%21%22%23$%25&%27%28%29%2A+%2C%2F:%3B%3C=%3E%3F@%5B%5C%5D%5E%60%7B%7C%7D%E2%98%BA',
|
271
|
+
'精力善用' => '%E7%B2%BE%E5%8A%9B%E5%96%84%E7%94%A8'
|
272
|
+
}
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'escapes a path segment' do
|
276
|
+
aggregate_failures do
|
277
|
+
in_out.each do |in_str, out_str|
|
278
|
+
expect(URIs.path_escape(in_str)).to eq(out_str)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'rejects non-strings' do
|
284
|
+
str = in_out.keys.last
|
285
|
+
expect { URIs.path_escape(str.bytes) }.to raise_error(ArgumentError)
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'rejects non-UTF-8 strings' do
|
289
|
+
str = in_out.keys.last
|
290
|
+
expect { URIs.path_escape(str.encode(Encoding::Shift_JIS)) }.to raise_error(ArgumentError)
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'accepts non-UTF-8 strings converted to UTF-8' do
|
294
|
+
in_str = in_out.keys.last
|
295
|
+
out_str = in_out[in_str]
|
296
|
+
|
297
|
+
# OK, we're really just testing String#encode here, but
|
298
|
+
# it's useful for documentation
|
299
|
+
in_str_sjis = in_str.encode(Encoding::Shift_JIS)
|
300
|
+
in_str_utf8 = in_str_sjis.encode(Encoding::UTF_8)
|
301
|
+
expect(URIs.path_escape(in_str_utf8)).to eq(out_str)
|
302
|
+
end
|
303
|
+
end
|
213
304
|
end
|
214
305
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: berkeley_library-util
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Moles
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: berkeley_library-logging
|