Is there a way to attach Ruby Net::HTTP request to a specific IP address / network interface?
Asked Answered
B

4

13

Im looking a way to use different IP addresses for each GET request with standard Net::HTTP library. Server has 5 ip addresses and assuming that some API`s are blocking access when request limit per IP is reached. So, only way to do it - use another server. I cant find anything about it in ruby docs.

For example, curl allows you to attach it to specific ip address (in PHP):

$req = curl_init($url)
curl_setopt($req, CURLOPT_INTERFACE, 'ip.address.goes.here';
$result = curl_exec($req);

Is there any way to do it with Net::HTTP library? As alternative - CURB (ruby curl binding). But it will be the last thing i`ll try.

Suggestions / Ideas?

P.S. The solution with CURB (with dirty tests, ip`s being replaced):

require 'rubygems'
require 'curb'

ip_addresses = [
  '1.1.1.1',
  '2.2.2.2',
  '3.3.3.3',
  '4.4.4.4',
  '5.5.5.5'
]

ip_addresses.each do |address|
  url = 'http://www.ip-adress.com/'
  c = Curl::Easy.new(url)
  c.interface = address
  c.perform
  ip = c.body_str.scan(/<h2>My IP address is: ([\d\.]{1,})<\/h2>/).first
  puts "for #{address} got response: #{ip}"
end
Bellbird answered 9/6, 2010 at 23:24 Comment(0)
E
1

Doesn't look like you can do it with Net:HTTP. Here's the source

http://github.com/ruby/ruby/blob/trunk/lib/net/http.rb

Line 644 is where the connection is opened

  s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }

The third and fourth arguments to TCPSocket.open are local_address and local_port, and since they're not specified, it's not possible. Looks like you'll have to go with curb.

Exurbia answered 9/6, 2010 at 23:46 Comment(3)
yeah, i also dug into the http.rb file to find is there anything that might be overriden. well, that sucks because i have to rewrite my core request library.. anyway thanks for an advice :)Bellbird
got a simple curb solution. will find out if the performance is still the same.Bellbird
FYI for anyone who comes across this answer now -- this has been fixed. Untested but it looks like you can do x = Net::HTTP.new('google.com'); x.local_host = '10.0.0.1'.Purree
B
9

I know this is old, but hopefully someone else finds this useful, as I needed this today. You can do the following:

http = Net::HTTP.new(uri.host, uri.port)
http.local_host = ip
response = http.request(request)

Note that you I don't believe you can use Net::HTTP.start, as it doesn't accept local_host as an option.

Barbarian answered 22/7, 2014 at 19:26 Comment(1)
I get Errno::EADDRNOTAVAIL: Cannot assign requested address - bind(2) for "[ip]" port [port]. I tried setting http.local_port too, but nothing. Ruby 2.6.3Exogamy
M
4

There is in fact a way to do this if you monkey patch TCPSocket:

https://gist.github.com/800214

Curb is awesome but won't work with Jruby so I've been looking into alternatives...

Mainsheet answered 28/1, 2011 at 16:31 Comment(0)
E
1

Doesn't look like you can do it with Net:HTTP. Here's the source

http://github.com/ruby/ruby/blob/trunk/lib/net/http.rb

Line 644 is where the connection is opened

  s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }

The third and fourth arguments to TCPSocket.open are local_address and local_port, and since they're not specified, it's not possible. Looks like you'll have to go with curb.

Exurbia answered 9/6, 2010 at 23:46 Comment(3)
yeah, i also dug into the http.rb file to find is there anything that might be overriden. well, that sucks because i have to rewrite my core request library.. anyway thanks for an advice :)Bellbird
got a simple curb solution. will find out if the performance is still the same.Bellbird
FYI for anyone who comes across this answer now -- this has been fixed. Untested but it looks like you can do x = Net::HTTP.new('google.com'); x.local_host = '10.0.0.1'.Purree
S
-3

Of course you can. I did as below:

# remote_host can be IP or hostname
uri     = URI.parse( "http://" + remote_host )
http    = Net::HTTP.new( uri.host, uri.port )
request = Net::HTTP::Get.new(uri.request_uri)
request.initialize_http_header( { "Host" => domain })
response = http.request( request )
Sales answered 3/1, 2019 at 6:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.