user_agent.rb 8.4 KB

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