diff --git a/lib/protocol/http/body/buffered.rb b/lib/protocol/http/body/buffered.rb index a8d318d..9d723b2 100644 --- a/lib/protocol/http/body/buffered.rb +++ b/lib/protocol/http/body/buffered.rb @@ -72,6 +72,11 @@ def empty? @index >= @chunks.length end + # A buffered response is always ready. + def ready? + true + end + def read if chunk = @chunks[@index] @index += 1 diff --git a/lib/protocol/http/body/file.rb b/lib/protocol/http/body/file.rb index 7b37751..92cee35 100644 --- a/lib/protocol/http/body/file.rb +++ b/lib/protocol/http/body/file.rb @@ -65,6 +65,10 @@ def empty? @remaining == 0 end + def ready? + true + end + def rewind @file.seek(@offset) end diff --git a/lib/protocol/http/body/head.rb b/lib/protocol/http/body/head.rb index a818e6d..4097804 100644 --- a/lib/protocol/http/body/head.rb +++ b/lib/protocol/http/body/head.rb @@ -42,6 +42,10 @@ def empty? true end + def ready? + true + end + def length @length end diff --git a/lib/protocol/http/body/readable.rb b/lib/protocol/http/body/readable.rb index 3bbe11c..13918aa 100644 --- a/lib/protocol/http/body/readable.rb +++ b/lib/protocol/http/body/readable.rb @@ -44,6 +44,13 @@ def empty? false end + # Whether calling read will block. + # We prefer pessimistic implementation, and thus default to `false`. + # @return [Boolean] + def ready? + false + end + def length nil end diff --git a/lib/protocol/http/body/rewindable.rb b/lib/protocol/http/body/rewindable.rb index db9a2ef..25a1455 100644 --- a/lib/protocol/http/body/rewindable.rb +++ b/lib/protocol/http/body/rewindable.rb @@ -39,6 +39,10 @@ def empty? (@index >= @chunks.size) && super end + def ready? + (@index < @chunks.size) || super + end + # A rewindable body wraps some other body. Convert it to a buffered body def buffered Buffered.new(@chunks) diff --git a/lib/protocol/http/body/wrapper.rb b/lib/protocol/http/body/wrapper.rb index b5c6574..8bd504a 100644 --- a/lib/protocol/http/body/wrapper.rb +++ b/lib/protocol/http/body/wrapper.rb @@ -55,6 +55,10 @@ def empty? @body.empty? end + def ready? + @body.ready? + end + def length @body.length end diff --git a/lib/protocol/http/cookie.rb b/lib/protocol/http/cookie.rb index d905595..dffdc03 100644 --- a/lib/protocol/http/cookie.rb +++ b/lib/protocol/http/cookie.rb @@ -40,7 +40,7 @@ def encoded_value URL.escape(@value) end - def to_str + def to_s buffer = String.new.b buffer << encoded_name << '=' << encoded_value diff --git a/lib/protocol/http/header/authorization.rb b/lib/protocol/http/header/authorization.rb index 0690127..a504d9f 100644 --- a/lib/protocol/http/header/authorization.rb +++ b/lib/protocol/http/header/authorization.rb @@ -26,21 +26,23 @@ module Protocol module HTTP module Header # Used for basic authorization. - # @example headers.add('authorization', Authorization.new("samuel", "password")) - class Authorization - KEY = "Authorization" - - def initialize(username, password) - @username = username - @password = password - end - - def encoded - "#{@username}:#{@password}" + # + # ~~~ ruby + # headers.add('authorization', Authorization.basic("my_username", "my_password")) + # ~~~ + class Authorization < String + # Splits the header and + # @return [Tuple(String, String)] + def credentials + self.split(/\s+/, 2) end - def to_str - 'Basic %s' % Base64.strict_encode64(self.encoded) + def self.basic(username, password) + encoded = "#{username}:#{password}" + + self.new( + "Basic #{Base64.strict_encode64(encoded)}" + ) end end end diff --git a/lib/protocol/http/header/etag.rb b/lib/protocol/http/header/etag.rb index 72b5a3b..39803a9 100644 --- a/lib/protocol/http/header/etag.rb +++ b/lib/protocol/http/header/etag.rb @@ -20,8 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -require_relative 'split' - module Protocol module HTTP module Header diff --git a/lib/protocol/http/headers.rb b/lib/protocol/http/headers.rb index 5e222c6..cf30bc0 100644 --- a/lib/protocol/http/headers.rb +++ b/lib/protocol/http/headers.rb @@ -28,6 +28,7 @@ require_relative 'header/etag' require_relative 'header/etags' require_relative 'header/vary' +require_relative 'header/authorization' module Protocol module HTTP @@ -170,6 +171,7 @@ def extract(keys) end # Add the specified header key value pair. + # # @param key [String] the header key. # @param value [String] the header value to assign. def add(key, value) @@ -216,8 +218,6 @@ def []= key, value 'user-agent' => false, 'referer' => false, 'host' => false, - 'authorization' => false, - 'proxy-authorization' => false, 'if-modified-since' => false, 'if-unmodified-since' => false, 'from' => false, @@ -233,6 +233,10 @@ def []= key, value 'via' => Split, 'x-forwarded-for' => Split, + # Authorization headers: + 'authorization' => Header::Authorization, + 'proxy-authorization' => Header::Authorization, + # Cache validations: 'etag' => Header::ETag, 'if-match' => Header::ETags, diff --git a/lib/protocol/http/methods.rb b/lib/protocol/http/methods.rb index 84fda81..41615d3 100644 --- a/lib/protocol/http/methods.rb +++ b/lib/protocol/http/methods.rb @@ -53,7 +53,7 @@ def self.each self.each do |name, value| define_method(name.downcase) do |location, headers = nil, body = nil| self.call( - Request[value, location.to_str, Headers[headers], body] + Request[value, location.to_s, Headers[headers], body] ) end end diff --git a/lib/protocol/http/reference.rb b/lib/protocol/http/reference.rb index a6c3f56..9e9e5d7 100644 --- a/lib/protocol/http/reference.rb +++ b/lib/protocol/http/reference.rb @@ -110,12 +110,10 @@ def append(buffer) return buffer end - def to_str + def to_s append(String.new) end - alias to_s to_str - # Merges two references as specified by RFC2396, similar to `URI.join`. def + other other = self.class[other] diff --git a/lib/protocol/http/version.rb b/lib/protocol/http/version.rb index d91c35a..dcae857 100644 --- a/lib/protocol/http/version.rb +++ b/lib/protocol/http/version.rb @@ -22,6 +22,6 @@ module Protocol module HTTP - VERSION = "0.18.0" + VERSION = "0.20.0" end end diff --git a/spec/protocol/http/body/buffered_spec.rb b/spec/protocol/http/body/buffered_spec.rb index 1c1d832..b8a3a4c 100644 --- a/spec/protocol/http/body/buffered_spec.rb +++ b/spec/protocol/http/body/buffered_spec.rb @@ -92,6 +92,10 @@ end end + describe '#ready?' do + it {is_expected.to be_ready} + end + describe "#finish" do it "returns self" do expect(subject.finish).to be == subject diff --git a/spec/protocol/http/body/file_spec.rb b/spec/protocol/http/body/file_spec.rb index bfb374c..f7f26e9 100644 --- a/spec/protocol/http/body/file_spec.rb +++ b/spec/protocol/http/body/file_spec.rb @@ -39,6 +39,10 @@ expect(chunk.encoding).to be == Encoding::BINARY end + + describe '#ready?' do + it {is_expected.to be_ready} + end end context 'partial file' do diff --git a/spec/protocol/http/header/authorization_spec.rb b/spec/protocol/http/header/authorization_spec.rb index 380a763..14fc98f 100644 --- a/spec/protocol/http/header/authorization_spec.rb +++ b/spec/protocol/http/header/authorization_spec.rb @@ -21,11 +21,20 @@ # THE SOFTWARE. require 'protocol/http/header/authorization' +require 'protocol/http/headers' RSpec.describe Protocol::HTTP::Header::Authorization do - subject {described_class.new("samuel", "password")} - - it "should generate correct authorization header" do - expect(subject.to_str).to be == "Basic c2FtdWVsOnBhc3N3b3Jk" + context 'with basic username/password' do + subject {described_class.basic("samuel", "password")} + + it "should generate correct authorization header" do + expect(subject).to be == "Basic c2FtdWVsOnBhc3N3b3Jk" + end + + describe '#credentials' do + it "can split credentials" do + expect(subject.credentials).to be == ["Basic", "c2FtdWVsOnBhc3N3b3Jk"] + end + end end end