Is this a bug in Rack? - ruby

I'm trying to post multipart content (a file and some strings) to a Sinatra server on localhost using a java client. It seems the server doesn't like the POST message. The stack trace is:
ERROR NoMethodError: undefined method `rewind' for "hi":String
D:/Ruby192/lib/ruby/gems/1.9.1/gems/rack-1.2.3/lib/rack/utils.rb:581:in`block in parse_multipart'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/rack-1.2.3/lib/rack/utils.rb:499:in`loop'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/rack-1.2.3/lib/rack/utils.rb:499:in`parse_multipart'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/rack-1.2.3/lib/rack/request.rb:270:in `parse_multipart'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/rack-1.2.3/lib/rack/request.rb:148:in `POST'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/rack-1.2.3/lib/rack/methodoverride.rb:15:in `call'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:1272:in `block in call'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:1303:in `synchronize'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/sinatra-1.2.6/lib/sinatra/base.rb:1272:in `call'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/rack-1.2.3/lib/rack/content_length.rb:13:in `call'
D:/Ruby192/lib/ruby/gems/1.9.1/gems/rack-1.2.3/lib/rack/handler/webrick.rb:52:in `service'
D:/Ruby192/lib/ruby/1.9.1/webrick/httpserver.rb:111:in `service'
D:/Ruby192/lib/ruby/1.9.1/webrick/httpserver.rb:70:in `run'
D:/Ruby192/lib/ruby/1.9.1/webrick/server.rb:183:in `block in start_thread'
My server prints out the params in the post message. Here's what they are for my java client:
Content-Disposition: form-data; name="file"; filename="fff.jpg"
Content-Type: image/jpeg
Content-Transfer-Encoding: binary
#<File:0xedbc10>
Content-Disposition: form-data; name="jjj"
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
hi
The java code I'm using is:
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://localhost:4567/upload");
File file = new File("D:/My Documents/My Desktop/fff.jpg");
MultipartEntity mpEntity = new MultipartEntity();
ContentBody cbFile = new FileBody(file, "image/jpeg");
mpEntity.addPart("file", cbFile);
ContentBody stringBody = new StringBody("hi", Charset.forName("UTF8"));
mpEntity.addPart("jjj", stringBody);
httppost.setEntity(mpEntity);
HttpResponse response = httpclient.execute(httppost);
So it seems Rack doesn't like dealing with content type when the content type is a string. When I set the content_type variable inside rack/utils.rb to nil when the param is not a file everything works fine. Is this intentional or should it be submitted as a bug?
See also http://blogs.oracle.com/mandy/entry/rack_and_content_type_for.

This is a Rack bug, but you can avoid it in java by doing your upload like this:
HttpPost httpost = new HttpPost(
"http://192.168.140.17:3000/ze/api/documents.xml");
MultipartEntity entity = new MultipartEntity( HttpMultipartMode.BROWSER_COMPATIBLE );
entity.addPart( "folder_id", new StringBody("3", "multipart/form-data", Charset.defaultCharset()) );//f2
entity.addPart( "labels", new StringBody("1,3,4", "form-data", Charset.defaultCharset() ) );
entity.addPart( "uploaded_data", new FileBody(this.file, filename, "application/pdf", null) );
httpost.setEntity(entity);
The catch is creating the MultipartEntity with HttpMultipartMode.BROWSER_COMPATIBLE.

Related

How to send a multi part authorization using the rest-client gem api

I am trying to use the rest-client gem to make api calls for test automation purposes. This requires me to authenticate first with an initial request. Then the following requests must contain the returned token from the first request and the user email. I am currently stuck on how to send the second request properly do this in a post request to create an object.
First request to get token which works fine and returns me the token i need:
request = '{
"user": {
"password" : "abcdefg",
"email" : "example#email.com"
}
}'
response = RestClient.post "https://moviesite.com/sign_in",
request, {
accept: "application/json",
content_type: "application/json"
}
I save the token value like so:
results = JSON.parse(response)
puts results
token_value = results['token']
puts token_value
Second request where I attempt to use the token in order to make an action on an object:
request2 = '{
"movie": {
"title": "new movie",
"status": "release"
}
}'
response2 = RestClient.post "https://moviesite.com/movies",
request2, {
accept: "application/json",
content_type: "application/json",
:Authorization => "Token token=#{token_value}, email='example#email.com'"
}
first request executes just fine and returns the token, i then try to insert the token in the second request but when the second request executes im getting an exception which makes me wonder if i correctly put it together.
Traceback (most recent call last):
9: from APITest.rb:46:in `<main>'
8: from /Users/user/.rvm/gems/ruby-2.6.3/gems/rest-client-2.1.0/lib/restclient.rb:70:in `post'
7: from /Users/user/.rvm/gems/ruby-2.6.3/gems/rest-client-2.1.0/lib/restclient/request.rb:63:in `execute'
6: from /Users/user/.rvm/gems/ruby-2.6.3/gems/rest-client-2.1.0/lib/restclient/request.rb:163:in `execute'
5: from /Users/user/.rvm/gems/ruby-2.6.3/gems/rest-client-2.1.0/lib/restclient/request.rb:727:in `transmit'
4: from /Users/user/.rvm/rubies/ruby-2.6.3/lib/ruby/2.6.0/net/http.rb:920:in `start'
3: from /Users/user/.rvm/gems/ruby-2.6.3/gems/rest-client-2.1.0/lib/restclient/request.rb:743:in `block in transmit'
2: from /Users/user/.rvm/gems/ruby-2.6.3/gems/rest-client-2.1.0/lib/restclient/request.rb:836:in `process_result'
1: from /Users/user/.rvm/gems/ruby-2.6.3/gems/rest-client-2.1.0/lib/restclient/abstract_response.rb:129:in `return!'
/Users/user/.rvm/gems/ruby-2.6.3/gems/rest-client-2.1.0/lib/restclient/abstract_response.rb:249:in `exception_with_response': 404 Not Found (RestClient::NotFound)

Does the shoes gem support HTTPS requests?

I've been looking around on how to make HTTPS requests using the Shoes GUI builder and I'm getting the SSL_connnect connect error and I'm unsure of what to do about it.
I made a test app in this Gist
Here's the Error:
SSL_connect returned=1 errno=0 state=error: certificate verify failed
/Applications/Shoes.app/Contents/MacOS/lib/ruby/2.2.0/net/http.rb:923:in `connect'
/Applications/Shoes.app/Contents/MacOS/lib/ruby/2.2.0/net/http.rb:923:in `block in connect'
/Applications/Shoes.app/Contents/MacOS/lib/ruby/2.2.0/timeout.rb:73:in `timeout'
/Applications/Shoes.app/Contents/MacOS/lib/ruby/2.2.0/net/http.rb:923:in `connect'
/Applications/Shoes.app/Contents/MacOS/lib/ruby/2.2.0/net/http.rb:863:in `do_start'
/Applications/Shoes.app/Contents/MacOS/lib/ruby/2.2.0/net/http.rb:852:in `start'
/Applications/Shoes.app/Contents/MacOS/lib/ruby/2.2.0/net/http.rb:1375:in `request'
shoes.rb:17:in `request'
shoes.rb:54:in `block (3 levels) in <main>'
-e:1:in `call'
I figured it out. Updating the request to strip the token and setting the verify_mode to OpenSSL::SSL::VERIFY_NONE fixed the problem.
token = Base64.encode64("#{user}:#{pass}").strip
uri = URI("https://example.com/")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri)
request["authorization"] = "Bearer #{token}"
request["Content-Type"] = "application/json"
response = http.request(request)
info(response.body)
response

fetching tweets with Ruby returns `open_http': 400 Bad Request (OpenURI::HTTPError)

I am new to Ruby. I have starting reading first chapter in Bastards Book Ruby i.e. Tweet fetching but the tutorial uses Twitter API v1. The response I got told me to use v1.1. So, I followed tutorials and wrote a program:
require 'oauth'
def prepare_access_token(oauth_token, oauth_token_secret)
consumer = OAuth::Consumer.new("apiKey", "APISecret", { :site => "https://api.twitter.com", :scheme => :header })
token_hash = { :oauth_token => oauth_token, :oauth_token_secret => oauth_token_secret }
access_token = OAuth::AccessToken.from_hash(consumer, token_hash )
return access_token
end
access_token = prepare_access_token("AccessToken", "AccessTokenSecret")
response = access_token.request(:get, "https://api.twitter.com/1.1/statuses/home_timeline.json")
puts response
Output: #<Net::HTTPOK:0x007fb7eb0885b8>
If I run program until here. It runs just fine. But, then if I add below content to fetch tweets and write to file. I get errors
twitter_user = "iAbhimanyuAryan"
remote_base_url = "https://api.twitter.com/1.1/statuses/user_timeline.json?count=100&screen_name="
remote_full_url = remote_base_url + twitter_user
tweets = open(remote_full_url).read
my_local_filename = twitter_user + "-tweets.json"
my_local_file = open(my_local_filename, "w")
my_local_file.write(tweets)
my_local_file.close
Errors:
❯ ruby twitter.rb
#<Net::HTTPOK:0x007fbbfc9d75c8>
/Users/abhimanyuaryan/.rbenv/versions/2.2.3/lib/ruby/2.2.0/open-uri.rb:358:in `open_http': 400 Bad Request (OpenURI::HTTPError)
from /Users/abhimanyuaryan/.rbenv/versions/2.2.3/lib/ruby/2.2.0/open-uri.rb:736:in `buffer_open'
from /Users/abhimanyuaryan/.rbenv/versions/2.2.3/lib/ruby/2.2.0/open-uri.rb:211:in `block in open_loop'
from /Users/abhimanyuaryan/.rbenv/versions/2.2.3/lib/ruby/2.2.0/open-uri.rb:209:in `catch'
from /Users/abhimanyuaryan/.rbenv/versions/2.2.3/lib/ruby/2.2.0/open-uri.rb:209:in `open_loop'
from /Users/abhimanyuaryan/.rbenv/versions/2.2.3/lib/ruby/2.2.0/open-uri.rb:150:in `open_uri'
from /Users/abhimanyuaryan/.rbenv/versions/2.2.3/lib/ruby/2.2.0/open-uri.rb:716:in `open'
from /Users/abhimanyuaryan/.rbenv/versions/2.2.3/lib/ruby/2.2.0/open-uri.rb:34:in `open'
from twitter.rb:27:in `<main>'
Looking at the man pages for the Twitter API, this request requires authentication.
You will need to use your AccessToken object to make the request. Something like this:
# response is a [Net::HTTPResponse][3]
response = access_token.get(remote_full_url)
# Get the entire body of the response
tweets = response.body
Note, this is a very basic way of doing it. If you were putting such code into production, you would add proper checking and error handling.

Ruby post not working

I'm trying to replicate the curl request on this page using Ruby. When I run the curl request from my system it works fine, but Ruby gives me an error. I've tried a few different ways of doing Ruby posts but none of them work. Here's my code:
hr_args = { 'type' => 'todo',
'text' => 'tyy'
}
hr_hd = {"Content-Type"=>"application/json",
'x-api-user'=> habit_user,
'x-api-key' => habit_token
}
url = URI.parse('https://habitrpg.com:443/api/v2/user/tasks')
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url.request_uri, hr_hd)
request.body = hr_args.to_json
response = http.request(request)
This is the error I get:
/usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/protocol.rb:153:in `read_nonblock': end of file reached (EOFError)
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/protocol.rb:153:in `rbuf_fill'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/protocol.rb:134:in `readuntil'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/protocol.rb:144:in `readline'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/http/response.rb:39:in `read_status_line'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/http/response.rb:28:in `read_new'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/http.rb:1408:in `block in transport_request'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/http.rb:1405:in `catch'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/http.rb:1405:in `transport_request'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/http.rb:1378:in `request'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/http.rb:1371:in `block in request'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/http.rb:853:in `start'
from /usr/local/rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/net/http.rb:1369:in `request'
from todo.rb:38:in `<main>'
What am I doing wrong?
The problem was that I needed to set http.use_ssl = true.
try this
...
require 'openssl'
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
...
...

Ruby undefined method `bytesize' for #<Hash:0x2954fe8>

i have the following Ruby Code, for a tracking website in sandbox mode:
require "net/http"
require "net/https"
require "uri"
xml = <<XML
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?><data appname="dhl_entwicklerportal" language-code="de" password="Dhl_123!" request="get-status-for-public-user"><data piece-code="00340433836536550280"></data></data>
XML
uri = URI('https://cig.dhl.de/services/sandbox/rest/sendungsverfolgung')
nhttp = Net::HTTP.new(uri.host, uri.port)
nhttp.use_ssl=true
nhttp.verify_mode=OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri)
request.basic_auth 'xpackageWP', 'hidden'
response = nhttp.start {|http|
http.request(request, xml:xml)
}
puts response.body
I always get the error :
d:/Ruby200/lib/ruby/2.0.0/net/http/generic_request.rb:179:in `send_request_with_body' undefined method `bytesize' for #<Hash:0x2954fe8> (NoMethodError)
from d:/Ruby200/lib/ruby/2.0.0/net/http/generic_request.rb:130:in `exec'
from d:/Ruby200/lib/ruby/2.0.0/net/http.rb:1404:in `block in transport_request'
from d:/Ruby200/lib/ruby/2.0.0/net/http.rb:1403:in `catch'
from d:/Ruby200/lib/ruby/2.0.0/net/http.rb:1403:in `transport_request'
from d:/Ruby200/lib/ruby/2.0.0/net/http.rb:1376:in `request'
from D:/Dropbox_5BHIF/Dropbox/TempDHL/Main.rb:17:in `block in <main>'
from d:/Ruby200/lib/ruby/2.0.0/net/http.rb:852:in `start'
from D:/Dropbox_5BHIF/Dropbox/TempDHL/Main.rb:16:in `<main>'
I tried really hard to solve this, but i cannot think of any problem.
When i test it in the Browser with the Link and then a ?xml= it works perfectly, so it seems to be a problem with my Ruby Code.
You're sending post data as a hash. You should encode it as string.
For example Using URI::encode_www_form:
request = Net::HTTP::Post.new(uri)
...
response = nhttp.start do |http|
post_data = URI.encode_www_form({xml: xml})
http.request(request, post_data)
end
UPDATE If you want GET request, append the query string to the url.
post_data = URI.encode_www_form({xml: xml})
uri = URI('https://cig.dhl.de/services/sandbox/rest/sendungsverfolgung?' +
post_data)
...
response = nhttp.start do |http|
http.request(request)
end
request expects a string for its second argument, on which it invokes bytesize. You're giving it a hash, which doesn't respond to bytesize.
Use POST and not GET request
xml = <<XML
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?><data
appname="dhl_entwicklerportal" language-code="de" password="Dhl_123!" request="get-status-for-public-user"><data piece-code="00340433836536550280"></data></data>
XML
uri = URI('https://cig.dhl.de/services/sandbox/rest/sendungsverfolgung')
nhttp = Net::HTTP.new(uri.host, uri.port)
nhttp.use_ssl=true
nhttp.verify_mode=OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(uri)
request.body = xml
request.basic_auth 'xpackageWP', 'hidden'
response = nhttp.start {|http|
http.request(request)
}
I solved by using .to_s, it returns a 200.
#client.job.create_or_update(job_name, job_xml.get_xml.to_s)
This is Jenkins API client what I'm currently using:
https://github.com/arangamani/jenkins_api_client

Resources