Skip to content

Commit

Permalink
Merge pull request #39 from glennr/gr/improve-error-codes
Browse files Browse the repository at this point in the history
feat: improve response error codes
  • Loading branch information
zaru authored Jul 1, 2017
2 parents 8c5ad71 + ec2fad4 commit 90a352a
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 56 deletions.
16 changes: 15 additions & 1 deletion lib/webpush/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,21 @@ class Error < RuntimeError; end

class ConfigurationError < Error; end

class ResponseError < Error; end
class ResponseError < Error;
attr_reader :response, :host

def initialize(response, host)
@response = response
@host = host
super "host: #{host}, #{@response.inspect}\nbody:\n#{@response.body}"
end
end

class InvalidSubscription < ResponseError; end

class ExpiredSubscription < ResponseError; end

class PayloadTooLarge < ResponseError; end

class TooManyRequests < ResponseError; end
end
12 changes: 9 additions & 3 deletions lib/webpush/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ def perform

if resp.is_a?(Net::HTTPGone) || #Firefox unsubscribed response
(resp.is_a?(Net::HTTPBadRequest) && resp.message == "UnauthorizedRegistration") #Chrome unsubscribed response
raise InvalidSubscription.new(resp.inspect)
elsif !resp.is_a?(Net::HTTPSuccess) #unknown/unhandled response error
raise ResponseError.new "host: #{uri.host}, #{resp.inspect}\nbody:\n#{resp.body}"
raise InvalidSubscription.new(resp, uri.host)
elsif resp.is_a?(Net::HTTPNotFound) # 404
raise ExpiredSubscription.new(resp, uri.host)
elsif resp.is_a?(Net::HTTPRequestEntityTooLarge) # 413
raise PayloadTooLarge.new(resp, uri.host)
elsif resp.is_a?(Net::HTTPTooManyRequests) # 429, try again later!
raise TooManyRequests.new(resp, uri.host)
elsif !resp.is_a?(Net::HTTPSuccess) # unknown/unhandled response error
raise ResponseError.new(resp, uri.host)
end

resp
Expand Down
125 changes: 73 additions & 52 deletions spec/webpush_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,77 @@
expect(Webpush::VERSION).not_to be nil
end

shared_examples 'web push protocol standard error handling' do
it 'raises InvalidSubscription if and only if the combination of status code and message indicate an invalid subscription' do
stub_request(:post, expected_endpoint).
to_return(status: 410, body: "", headers: {})
expect { subject }.to raise_error(Webpush::InvalidSubscription)

stub_request(:post, expected_endpoint).
to_return(status: [400, "UnauthorizedRegistration"], body: "", headers: {})
expect { subject }.to raise_error(Webpush::InvalidSubscription)

stub_request(:post, expected_endpoint).
to_return(status: 400, body: "", headers: {})
expect { subject }.not_to raise_error(Webpush::InvalidSubscription)
end

it 'raises ExpiredSubscription if the API returns a 404 Error' do
stub_request(:post, expected_endpoint).
to_return(status: 404, body: "", headers: {})
expect { subject }.to raise_error(Webpush::ExpiredSubscription)
end

it 'raises PayloadTooLarge if the API returns a 413 Error' do
stub_request(:post, expected_endpoint).
to_return(status: 413, body: "", headers: {})
expect { subject }.to raise_error(Webpush::PayloadTooLarge)
end

it 'raises TooManyRequests if the API returns a 429 Error' do
stub_request(:post, expected_endpoint).
to_return(status: 429, body: "", headers: {})
expect { subject }.to raise_error(Webpush::TooManyRequests)
end

it 'raises ResponseError for unsuccessful status code by default' do
stub_request(:post, expected_endpoint).
to_return(status: 401, body: "", headers: {})

expect { subject }.to raise_error(Webpush::ResponseError)
end

it 'supplies the original status code on the ResponseError' do
stub_request(:post, expected_endpoint).
to_return(status: 401, body: "Oh snap", headers: {})

expect { subject }.to raise_error { |error|
expect(error).to be_a(Webpush::ResponseError)
expect(error.response.code).to eq '401'
expect(error.response.body).to eq 'Oh snap'
}
end

it 'sets the error message to be the host + stringified response' do
stub_request(:post, expected_endpoint).
to_return(status: 401, body: "Oh snap", headers: {})

host = URI.parse(expected_endpoint).host

expect { subject }.to raise_error { |error|
expect(error.message).to eq(
"host: #{host}, #<Net::HTTPUnauthorized 401 readbody=true>\nbody:\nOh snap"
)
}
end

it 'raises exception on error by default' do
stub_request(:post, expected_endpoint).to_raise(StandardError)

expect { subject }.to raise_error
end
end

shared_examples 'request headers with VAPID' do
let(:message) { JSON.generate({ body: 'body' }) }
let(:p256dh) { 'BN4GvZtEZiZuqFxSKVZfSfluwKBD7UxHNBmWkfiZfCtgDE8Bwh-_MtLXbBxTBAWH9r7IPKL0lhdcaqtL1dfxU5E=' }
Expand Down Expand Up @@ -64,32 +135,7 @@
expect(result.code).to eql('201')
end

it 'raises InvalidSubscription if and only if the combination of status code and message indicate an invalid subscription' do
stub_request(:post, expected_endpoint).
to_return(status: 410, body: "", headers: {})
expect { subject }.to raise_error(Webpush::InvalidSubscription)

stub_request(:post, expected_endpoint).
to_return(status: [400, "UnauthorizedRegistration"], body: "", headers: {})
expect { subject }.to raise_error(Webpush::InvalidSubscription)

stub_request(:post, expected_endpoint).
to_return(status: 400, body: "", headers: {})
expect { subject }.not_to raise_error(Webpush::InvalidSubscription)
end

it 'raises ResponseError for unsuccessful status code by default' do
stub_request(:post, expected_endpoint).
to_return(status: 401, body: "", headers: {})

expect { subject }.to raise_error(Webpush::ResponseError)
end

it 'raises exception on error by default' do
stub_request(:post, expected_endpoint).to_raise(StandardError)

expect { subject }.to raise_error
end
include_examples 'web push protocol standard error handling'

it 'message is optional' do
expect(Webpush::Encryption).to_not receive(:encrypt)
Expand Down Expand Up @@ -187,32 +233,7 @@
expect(result.code).to eql('201')
end

it 'raises InvalidSubscription if and only if the combination of status code and message indicate an invalid subscription' do
stub_request(:post, expected_endpoint).
to_return(status: 410, body: "", headers: {})
expect { subject }.to raise_error(Webpush::InvalidSubscription)

stub_request(:post, expected_endpoint).
to_return(status: [400, "UnauthorizedRegistration"], body: "", headers: {})
expect { subject }.to raise_error(Webpush::InvalidSubscription)

stub_request(:post, expected_endpoint).
to_return(status: 400, body: "", headers: {})
expect { subject }.not_to raise_error(Webpush::InvalidSubscription)
end

it 'raises ResponseError for unsuccessful status code by default' do
stub_request(:post, expected_endpoint).
to_return(status: 401, body: "", headers: {})

expect { subject }.to raise_error(Webpush::ResponseError)
end

it 'raises exception on error by default' do
stub_request(:post, expected_endpoint).to_raise(StandardError)

expect { subject }.to raise_error
end
include_examples 'web push protocol standard error handling'

it 'message and encryption keys are optional' do
expect(Webpush::Encryption).to_not receive(:encrypt)
Expand Down

0 comments on commit 90a352a

Please sign in to comment.