user_agent.rb 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. # Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
  2. require 'net/http'
  3. require 'net/https'
  4. require 'net/ftp'
  5. require 'tempfile'
  6. class UserAgent
  7. =begin
  8. get http/https calls
  9. result = UserAgent.get( 'http://host/some_dir/some_file?param1=123' )
  10. result = UserAgent.get(
  11. 'http://host/some_dir/some_file?param1=123',
  12. {
  13. :param1 => 'some value',
  14. },
  15. {
  16. :open_timeout => 2,
  17. :read_timeout => 4,
  18. },
  19. )
  20. get json object
  21. result = UserAgent.get(
  22. 'http://host/some_dir/some_file?param1=123',
  23. {},
  24. {
  25. :json => true,
  26. }
  27. )
  28. returns
  29. result.data # as json object
  30. =end
  31. def self.get(url, params = {}, options = {}, count = 10)
  32. uri = URI.parse(url)
  33. http = get_http(uri, options)
  34. # prepare request
  35. request = Net::HTTP::Get.new( uri, { 'User-Agent' => 'Zammad User Agent' } )
  36. # http basic auth (if needed)
  37. request = set_basic_auth(request, options)
  38. # set params
  39. request = set_params(request, params, options)
  40. # start http call
  41. begin
  42. response = http.request(request)
  43. return process(response, uri, count, params, options)
  44. rescue => e
  45. return Result.new(
  46. error: e.inspect,
  47. success: false,
  48. code: 0,
  49. )
  50. end
  51. end
  52. =begin
  53. post http/https calls
  54. result = UserAgent.post(
  55. 'http://host/some_dir/some_file',
  56. {
  57. :param1 => 1,
  58. :param2 => 2,
  59. },
  60. {
  61. :open_timeout => 2,
  62. :read_timeout => 4,
  63. },
  64. )
  65. returns
  66. result # result object
  67. =end
  68. def self.post(url, params = {}, options = {}, count = 10)
  69. uri = URI.parse(url)
  70. http = get_http(uri, options)
  71. # prepare request
  72. request = Net::HTTP::Post.new( uri, { 'User-Agent' => 'Zammad User Agent' } )
  73. # set params
  74. request = set_params(request, params, options)
  75. # http basic auth (if needed)
  76. request = set_basic_auth(request, options)
  77. # start http call
  78. begin
  79. response = http.request(request)
  80. return process(response, uri, count, params, options)
  81. rescue => e
  82. return Result.new(
  83. error: e.inspect,
  84. success: false,
  85. code: 0,
  86. )
  87. end
  88. end
  89. =begin
  90. put http/https calls
  91. result = UserAgent.put(
  92. 'http://host/some_dir/some_file',
  93. {
  94. :param1 => 1,
  95. :param2 => 2,
  96. },
  97. {
  98. :open_timeout => 2,
  99. :read_timeout => 4,
  100. },
  101. )
  102. returns
  103. result # result object
  104. =end
  105. def self.put(url, params = {}, options = {}, count = 10)
  106. uri = URI.parse(url)
  107. http = get_http(uri, options)
  108. # prepare request
  109. request = Net::HTTP::Put.new( uri, { 'User-Agent' => 'Zammad User Agent' } )
  110. # set params
  111. request = set_params(request, params, options)
  112. # http basic auth (if needed)
  113. request = set_basic_auth(request, options)
  114. # start http call
  115. begin
  116. response = http.request(request)
  117. return process(response, uri, count, params, options)
  118. rescue => e
  119. return Result.new(
  120. error: e.inspect,
  121. success: false,
  122. code: 0,
  123. )
  124. end
  125. end
  126. =begin
  127. delete http/https calls
  128. result = UserAgent.delete(
  129. 'http://host/some_dir/some_file',
  130. {
  131. :open_timeout => 2,
  132. :read_timeout => 4,
  133. },
  134. )
  135. returns
  136. result # result object
  137. =end
  138. def self.delete(url, options = {}, count = 10)
  139. uri = URI.parse(url)
  140. http = get_http(uri, options)
  141. # prepare request
  142. request = Net::HTTP::Delete.new( uri, { 'User-Agent' => 'Zammad User Agent' } )
  143. # http basic auth (if needed)
  144. request = set_basic_auth(request, options)
  145. # start http call
  146. begin
  147. response = http.request(request)
  148. return process(response, uri, count, {}, options)
  149. rescue => e
  150. return Result.new(
  151. error: e.inspect,
  152. success: false,
  153. code: 0,
  154. )
  155. end
  156. end
  157. =begin
  158. perform get http/https/ftp calls
  159. result = UserAgent.request( 'ftp://host/some_dir/some_file.bin' )
  160. result = UserAgent.request( 'http://host/some_dir/some_file.bin' )
  161. result = UserAgent.request( 'https://host/some_dir/some_file.bin' )
  162. # get request
  163. result = UserAgent.request(
  164. 'http://host/some_dir/some_file?param1=123',
  165. {
  166. :open_timeout => 2,
  167. :read_timeout => 4,
  168. },
  169. )
  170. returns
  171. result # result object
  172. =end
  173. def self.request(url, options = {})
  174. uri = URI.parse(url)
  175. case uri.scheme.downcase
  176. when /ftp/
  177. ftp(uri, options)
  178. when /http|https/
  179. get( url, {}, options )
  180. end
  181. end
  182. def self.get_http(uri, options)
  183. http = Net::HTTP.new(uri.host, uri.port)
  184. http.open_timeout = options[:open_timeout] || 4
  185. http.read_timeout = options[:read_timeout] || 10
  186. if uri.scheme =~ /https/i
  187. http.use_ssl = true
  188. # @TODO verify_mode should be configurable
  189. http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  190. end
  191. http
  192. end
  193. def self.set_basic_auth(request, options)
  194. # http basic auth (if needed)
  195. if options[:user] && options[:user] != '' && options[:password] && options[:password] != ''
  196. request.basic_auth options[:user], options[:password]
  197. end
  198. request
  199. end
  200. def self.set_params(request, params, options)
  201. if options[:json]
  202. request.add_field('Content-Type', 'application/json')
  203. if !params.empty?
  204. request.body = params.to_json
  205. end
  206. else
  207. if !params.empty?
  208. request.set_form_data( params )
  209. end
  210. end
  211. request
  212. end
  213. def self.process(response, uri, count, params, options)
  214. if !response
  215. return Result.new(
  216. error: "Can't connect to #{uri}, got no response!",
  217. success: false,
  218. code: 0,
  219. )
  220. end
  221. case response
  222. when Net::HTTPNotFound
  223. return Result.new(
  224. error: "No such file #{uri}, 404!",
  225. success: false,
  226. code: response.code,
  227. )
  228. when Net::HTTPClientError
  229. return Result.new(
  230. error: "Client Error: #{response.inspect}!",
  231. success: false,
  232. code: response.code,
  233. )
  234. when Net::HTTPInternalServerError
  235. return Result.new(
  236. error: "Server Error: #{response.inspect}!",
  237. success: false,
  238. code: response.code,
  239. )
  240. when Net::HTTPRedirection
  241. fail 'Too many redirections for the original URL, halting.' if count <= 0
  242. url = response['location']
  243. return get(url, params, options, count - 1)
  244. when Net::HTTPOK
  245. data = nil
  246. if options[:json] && !options[:jsonParseDisable] && response.body
  247. data = JSON.parse( response.body )
  248. end
  249. return Result.new(
  250. data: data,
  251. body: response.body,
  252. content_type: response['Content-Type'],
  253. success: true,
  254. code: response.code,
  255. )
  256. when Net::HTTPCreated
  257. data = nil
  258. if options[:json] && !options[:jsonParseDisable] && response.body
  259. data = JSON.parse( response.body )
  260. end
  261. return Result.new(
  262. data: data,
  263. body: response.body,
  264. content_type: response['Content-Type'],
  265. success: true,
  266. code: response.code,
  267. )
  268. end
  269. fail "Unable to process http call '#{response.inspect}'"
  270. end
  271. def self.ftp(uri, options)
  272. host = uri.host
  273. filename = File.basename(uri.path)
  274. remote_dir = File.dirname(uri.path)
  275. temp_file = Tempfile.new("download-#{filename}")
  276. temp_file.binmode
  277. begin
  278. Net::FTP.open(host) do |ftp|
  279. ftp.passive = true
  280. if options[:user] && options[:password]
  281. ftp.login( options[:user], options[:password] )
  282. else
  283. ftp.login
  284. end
  285. ftp.chdir(remote_dir) unless remote_dir == '.'
  286. begin
  287. ftp.getbinaryfile( filename, temp_file )
  288. rescue => e
  289. return Result.new(
  290. error: e.inspect,
  291. success: false,
  292. code: '550',
  293. )
  294. end
  295. end
  296. rescue => e
  297. return Result.new(
  298. error: e.inspect,
  299. success: false,
  300. code: 0,
  301. )
  302. end
  303. contents = temp_file.read
  304. temp_file.close
  305. Result.new(
  306. body: contents,
  307. success: true,
  308. code: '200',
  309. )
  310. end
  311. class Result
  312. attr_reader :error
  313. attr_reader :body
  314. attr_reader :data
  315. attr_reader :code
  316. attr_reader :content_type
  317. def initialize(options)
  318. @success = options[:success]
  319. @body = options[:body]
  320. @data = options[:data]
  321. @code = options[:code]
  322. @content_type = options[:content_type]
  323. @error = options[:error]
  324. end
  325. def success?
  326. return true if @success
  327. false
  328. end
  329. end
  330. end