Net::HTTP 와 http_proxy

회사에서
루비 개발을 하는 많은 사람들은 처음에 proxy로 인해 혼란을 겪는다. 요즘 대부분의 기업 네트워크는 보안 등을 위해서 프록시를 사용하며, 따라서 오픈 API 요청과 같이 네트워크 기능을 사용하는 루비 코드에서는 반드시 프록시 사용을 위한 부분이 추가되어야 한다. 그런데 이를 미처 생각하지 못한 사람들은 코드가 전혀 동작하지 않는다고 생각하게 되는 것이다.
예를 들어, 다음의 코드는 proxy 환경에서는 동작하지 않는다.
(소스 1)
- require 'net/http'
h = Net::HTTP.new('www.google.co.kr') - h.get('index.html')
제대로 동작하게 하려면 다음과 같이 수정되어야 한다.
(소스 2)
- require 'net/http'
h = Net::HTTP.new('www.google.co.kr', '192.168.10.23', '8080') - h.get('index.html')
위에서 HTTP.new 의 2,3번째 파라미터는 물론 http proxy의 주소와 포트번호이다.
그런데
위의 코드는 바람직하지 않다. 프록시의 주소와 포트 번호가 하드코딩되어 있으므로 프록시 주소가 다른 곳 또는 프록시를 사용하지 않는 곳에서는 코드를 수정해야한다.
이 문제를 해결하기 위해서는 일반적으로 많이 사용되는 http_proxy라는 환경 변수를 이용하여 아래와 같이 고칠 수 있다.
(혹시 생소한 사람을 위해 예를들면, http_proxy는 'http:192.168.10.23:8080' 과 같이 정의되어 있다.)
(소스 3)
- require 'net/http'
- host, p_port = if ENV['http_proxy']
- proxy = URI.parse(ENV['http_proxy')
- [host, proxy.port]
- else
- [nil,nil]
- end
- h = Net::HTTP.new('www.google.co.kr', p_host, p_port)
- h.get('index.html')
하지만 내가 작성한 코드가 아니라 다른 사람들이 작성한 라이브러리를 사용하는데, 그 라이브러리가 proxy를 전현 고려하지 않고 작성되어 있다면 어떻게 해야 할까?
실제로 이런 경우는 꽤 많다. 예를 들어 최근에 내가 사용하려고 했던 googlecalendar 라는 gem도 그런 경우였다. 물론 ruby는 열린 언어이므로 해당 코드를 직접 고칠 수 있다. 하지만 googlecalendar gem만 해도 수정이 필요한 부분이 꽤 많아서, 모두를 찾아서 고치는 일이 쉽지 않다.
이 문제의 근본 원인(?)을 따져보면 루비의 기본 라이브러리인 Net::HTTP 가 환경변수 http_proxy의 값을 자동으로 적용하지 않기 때문이다. 이렇게 구현한 데에도 나름의 이유가 있겠지만 개인적으로는 아쉽게 생각한다.
반면 REST기반의 API 구현에 자주 사용되는 rest-open-uri 라는 gem은 http_proxy를 자동으로 적용해주기 때문에 매우 편리하다. (rme2day라는 미투데이 API gem을 개발할 때도 이것을 사용했다.)
결국
나는 악의 뿌리(^^) Net::HTTP를 수정해서 이 문제를 해결해보기로 했다.
net/http.rb 소스코드를 들여다본 결과, 다행히 HTTP.new 함수만 건드려주면 된다는 것을 알게되었다.
net/http.rb를 직접 수정하는 것은 여러가지로 상당히 위험스럽고, 바람직하지도 않은 일이므로, 기존 코드는 그대로 두고 proxy 처리만을 위한 부분을 따로 떼내어, 다음과 같이 use_http_proxy.rb 를 작성했다.
(소스 4)
- ## use_http_proxy.rb
- module Net
class HTTP - #기존의 new 를 alias 해둔다.
class << HTTP
alias org_new new
end - # proxy_addr가 명시적으로 지정되지 않은 경우에는
- # http_proxy환경변수에 정의된 proxy를 사용한다.
- # proxy정보 적용 후에는 원래의 new인 org_new를 호출한다.
def HTTP.new(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil)
p_addr, p_port, p_user, p_pass = env_proxy unless p_addr
org_new(address, port, p_addr, p_port, p_user, p_pass)
end
- # http_proxy 읽어오기
def HTTP.env_proxy
uri = URI.parse(ENV['http_proxy'] || ENV['HTTP_PROXY'])
p_user = p_pass = nil
p_user, p_pass = uri.userinfo.split(/:/) if uri.userinfo
[uri.host, uri.port, p_user, p_pass]
end
end
end
이제, 맨 앞에 나왔던 (소스 1)과 같이 proxy가 고려되지 않은 코드에서 proxy를 사용하기 위해서는 아래처럼 require 'use_http_proxy' 한 줄만 추가하면 된다.
- require 'net/http'
- require 'use_http_proxy'
h = Net::HTTP.new('www.google.co.kr') - h.get('index.html')
Comments
-
We have been an ebay power seller and paypal confirmed seller of <a href=http://www.wowgoldprice.com>wow gold</a> for years.We also offer cheap <a href=http://www.wowgoldprice.org>wow gold</a>. by wow gold
-
I know some <a href=http://www.wowgoldshopping.com>wow gold</a> in wow,i love wow,love <a href=http://www.wowgold-shopping.com>wow gold</a>,love wowgoldshopping.com,too. by wow gold
-
We have been an ebay power seller and paypal confirmed seller of <a href=http://www.wowgoldprice.org>wow gold</a> for years.We also offer cheap <a href=http://www.wowgoldprice.com>wow gold</a>. by wow gold