Skip to content

Commit

Permalink
Dont allow unacceptable IO objects in update_with_media
Browse files Browse the repository at this point in the history
We ran into a bug where a 401 error was returned for any File-like
object that didn't have a to_io method defined
(ActionPack::HttpUploadedFile in this case).

Since the contract of this object is to have a to_io method otherwise
it will be included in the oauth signature (cause of 401) then we should
short circuit early on when we know this is supposed to be a File like
object. Later on we aren't sure exactly when we include/exclude params
from the signature.
  • Loading branch information
timlinquist committed Apr 14, 2014
1 parent 3161fc5 commit f22f1e2
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 0 deletions.
7 changes: 7 additions & 0 deletions lib/twitter/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ def initialize(message = '', rate_limit = {}, code = nil)

class ConfigurationError < ::ArgumentError; end

# Raised when a Tweet includes media that doesn't have a to_io method
class UnacceptableIO < StandardError
def initialize
super("The IO object for media must respond to to_io")
end
end

# Raised when Twitter returns a 4xx HTTP status code
class ClientError < self; end

Expand Down
3 changes: 3 additions & 0 deletions lib/twitter/rest/tweets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ def retweet!(*args)
# @rate_limited No
# @authentication Requires user context
# @raise [Twitter::Error::Unauthorized] Error raised when supplied user credentials are not valid.
# @raise [Twitter::Error::UnacceptableIO] Error when the IO object for the media argument does not have a to_io method.
# @return [Twitter::Tweet] The created Tweet.
# @param status [String] The text of your status update, up to 140 characters.
# @param media [File, Hash] A File object with your picture (PNG, JPEG or GIF)
Expand All @@ -214,6 +215,8 @@ def retweet!(*args)
# @option options [String] :display_coordinates Whether or not to put a pin on the exact coordinates a tweet has been sent from.
# @option options [Boolean, String, Integer] :trim_user Each tweet returned in a timeline will include a user object with only the author's numerical ID when set to true, 't' or 1.
def update_with_media(status, media, options = {})
raise Twitter::Error::UnacceptableIO.new unless media.respond_to?(:to_io)

hash = options.dup
hash[:in_reply_to_status_id] = hash.delete(:in_reply_to_status).id unless hash[:in_reply_to_status].nil?
hash[:place_id] = hash.delete(:place).woeid unless hash[:place].nil?
Expand Down
7 changes: 7 additions & 0 deletions spec/twitter/rest/tweets_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,13 @@
expect(a_post('/1.1/statuses/update_with_media.json')).to have_been_made
end
end
context "A non IO object" do
it "raises an error" do
expect {
@client.update_with_media("You always have options", "Unacceptable IO")
}.to raise_error(Twitter::Error::UnacceptableIO)
end
end
context 'already posted' do
before do
stub_post('/1.1/statuses/update_with_media.json').to_return(:status => 403, :body => fixture('already_posted.json'), :headers => {:content_type => 'application/json; charset=utf-8'})
Expand Down

0 comments on commit f22f1e2

Please sign in to comment.