httparty-responsibly 0.17.0.r1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +18 -0
  3. data/.gitignore +13 -0
  4. data/.rubocop.yml +92 -0
  5. data/.rubocop_todo.yml +124 -0
  6. data/.simplecov +1 -0
  7. data/.travis.yml +11 -0
  8. data/CONTRIBUTING.md +23 -0
  9. data/Changelog.md +502 -0
  10. data/Gemfile +23 -0
  11. data/Guardfile +16 -0
  12. data/MIT-LICENSE +20 -0
  13. data/README.md +78 -0
  14. data/Rakefile +10 -0
  15. data/bin/httparty +123 -0
  16. data/cucumber.yml +1 -0
  17. data/docs/README.md +106 -0
  18. data/examples/README.md +86 -0
  19. data/examples/aaws.rb +32 -0
  20. data/examples/basic.rb +28 -0
  21. data/examples/body_stream.rb +14 -0
  22. data/examples/crack.rb +19 -0
  23. data/examples/custom_parsers.rb +68 -0
  24. data/examples/delicious.rb +37 -0
  25. data/examples/google.rb +16 -0
  26. data/examples/headers_and_user_agents.rb +10 -0
  27. data/examples/logging.rb +36 -0
  28. data/examples/microsoft_graph.rb +52 -0
  29. data/examples/multipart.rb +22 -0
  30. data/examples/nokogiri_html_parser.rb +19 -0
  31. data/examples/peer_cert.rb +9 -0
  32. data/examples/rescue_json.rb +17 -0
  33. data/examples/rubyurl.rb +14 -0
  34. data/examples/stackexchange.rb +24 -0
  35. data/examples/stream_download.rb +26 -0
  36. data/examples/tripit_sign_in.rb +44 -0
  37. data/examples/twitter.rb +31 -0
  38. data/examples/whoismyrep.rb +10 -0
  39. data/httparty-responsibly.gemspec +27 -0
  40. data/lib/httparty.rb +684 -0
  41. data/lib/httparty/connection_adapter.rb +244 -0
  42. data/lib/httparty/cookie_hash.rb +21 -0
  43. data/lib/httparty/exceptions.rb +33 -0
  44. data/lib/httparty/hash_conversions.rb +69 -0
  45. data/lib/httparty/logger/apache_formatter.rb +45 -0
  46. data/lib/httparty/logger/curl_formatter.rb +91 -0
  47. data/lib/httparty/logger/logger.rb +28 -0
  48. data/lib/httparty/logger/logstash_formatter.rb +59 -0
  49. data/lib/httparty/module_inheritable_attributes.rb +56 -0
  50. data/lib/httparty/net_digest_auth.rb +136 -0
  51. data/lib/httparty/parser.rb +150 -0
  52. data/lib/httparty/request.rb +386 -0
  53. data/lib/httparty/request/body.rb +84 -0
  54. data/lib/httparty/request/multipart_boundary.rb +11 -0
  55. data/lib/httparty/response.rb +140 -0
  56. data/lib/httparty/response/headers.rb +33 -0
  57. data/lib/httparty/response_fragment.rb +19 -0
  58. data/lib/httparty/text_encoder.rb +70 -0
  59. data/lib/httparty/utils.rb +11 -0
  60. data/lib/httparty/version.rb +3 -0
  61. data/script/release +42 -0
  62. data/website/css/common.css +47 -0
  63. data/website/index.html +73 -0
  64. metadata +138 -0
@@ -0,0 +1,84 @@
1
+ require_relative 'multipart_boundary'
2
+
3
+ module HTTParty
4
+ class Request
5
+ class Body
6
+ def initialize(params, query_string_normalizer: nil, force_multipart: false)
7
+ @params = params
8
+ @query_string_normalizer = query_string_normalizer
9
+ @force_multipart = force_multipart
10
+ end
11
+
12
+ def call
13
+ if params.respond_to?(:to_hash)
14
+ multipart? ? generate_multipart : normalize_query(params)
15
+ else
16
+ params
17
+ end
18
+ end
19
+
20
+ def boundary
21
+ @boundary ||= MultipartBoundary.generate
22
+ end
23
+
24
+ def multipart?
25
+ params.respond_to?(:to_hash) && (force_multipart || has_file?(params))
26
+ end
27
+
28
+ private
29
+
30
+ def generate_multipart
31
+ normalized_params = params.flat_map { |key, value| HashConversions.normalize_keys(key, value) }
32
+
33
+ multipart = normalized_params.inject('') do |memo, (key, value)|
34
+ memo += "--#{boundary}\r\n"
35
+ memo += %(Content-Disposition: form-data; name="#{key}")
36
+ # value.path is used to support ActionDispatch::Http::UploadedFile
37
+ # https://github.com/jnunemaker/httparty/pull/585
38
+ memo += %(; filename="#{file_name(value)}") if file?(value)
39
+ memo += "\r\n"
40
+ memo += "Content-Type: #{content_type(value)}\r\n" if file?(value)
41
+ memo += "\r\n"
42
+ memo += file?(value) ? value.read : value.to_s
43
+ memo += "\r\n"
44
+ end
45
+
46
+ multipart += "--#{boundary}--\r\n"
47
+ end
48
+
49
+ def has_file?(value)
50
+ if value.respond_to?(:to_hash)
51
+ value.to_hash.any? { |_, v| has_file?(v) }
52
+ elsif value.respond_to?(:to_ary)
53
+ value.to_ary.any? { |v| has_file?(v) }
54
+ else
55
+ file?(value)
56
+ end
57
+ end
58
+
59
+ def file?(object)
60
+ object.respond_to?(:path) && object.respond_to?(:read)
61
+ end
62
+
63
+ def normalize_query(query)
64
+ if query_string_normalizer
65
+ query_string_normalizer.call(query)
66
+ else
67
+ HashConversions.to_params(query)
68
+ end
69
+ end
70
+
71
+ def content_type(object)
72
+ return object.content_type if object.respond_to?(:content_type)
73
+ mime = MIME::Types.type_for(object.path)
74
+ mime.empty? ? 'application/octet-stream' : mime[0].content_type
75
+ end
76
+
77
+ def file_name(object)
78
+ object.respond_to?(:original_filename) ? object.original_filename : File.basename(object.path)
79
+ end
80
+
81
+ attr_reader :params, :query_string_normalizer, :force_multipart
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,11 @@
1
+ require 'securerandom'
2
+
3
+ module HTTParty
4
+ class Request
5
+ class MultipartBoundary
6
+ def self.generate
7
+ "------------------------#{SecureRandom.urlsafe_base64(12)}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,140 @@
1
+ module HTTParty
2
+ class Response < Object
3
+ def self.underscore(string)
4
+ string.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z])([A-Z])/, '\1_\2').downcase
5
+ end
6
+
7
+ def self._load(data)
8
+ req, resp, parsed_resp, resp_body = Marshal.load(data)
9
+
10
+ new(req, resp, -> { parsed_resp }, body: resp_body)
11
+ end
12
+
13
+ attr_reader :request, :response, :body, :headers
14
+
15
+ def initialize(request, response, parsed_block, options = {})
16
+ @request = request
17
+ @response = response
18
+ @body = options[:body] || response.body
19
+ @parsed_block = parsed_block
20
+ @headers = Headers.new(response.to_hash)
21
+
22
+ if request.options[:logger]
23
+ logger = ::HTTParty::Logger.build(
24
+ request.options[:logger],
25
+ request.options[:log_level],
26
+ request.options[:log_format]
27
+ )
28
+ logger.format(request, self)
29
+ end
30
+
31
+ throw_exception
32
+ end
33
+
34
+ def parsed_response
35
+ @parsed_response ||= @parsed_block.call
36
+ end
37
+
38
+ def code
39
+ response.code.to_i
40
+ end
41
+
42
+ def http_version
43
+ response.http_version
44
+ end
45
+
46
+ def tap
47
+ yield self
48
+ self
49
+ end
50
+
51
+ def inspect
52
+ inspect_id = ::Kernel::format "%x", (object_id * 2)
53
+ %(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
54
+ end
55
+
56
+ CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
57
+
58
+ CODES_TO_OBJ.each do |response_code, klass|
59
+ name = klass.name.sub("Net::HTTP", '')
60
+ name = "#{underscore(name)}?".to_sym
61
+
62
+ define_method(name) do
63
+ klass === response
64
+ end
65
+ end
66
+
67
+ # Support old multiple_choice? method from pre 2.0.0 era.
68
+ if ::RUBY_VERSION >= "2.0.0" && ::RUBY_PLATFORM != "java"
69
+ alias_method :multiple_choice?, :multiple_choices?
70
+ end
71
+
72
+ # Support old status codes method from pre 2.6.0 era.
73
+ if ::RUBY_VERSION >= "2.6.0" && ::RUBY_PLATFORM != "java"
74
+ alias_method :gateway_time_out?, :gateway_timeout?
75
+ alias_method :request_entity_too_large?, :payload_too_large?
76
+ alias_method :request_time_out?, :request_timeout?
77
+ alias_method :request_uri_too_long?, :uri_too_long?
78
+ alias_method :requested_range_not_satisfiable?, :range_not_satisfiable?
79
+ end
80
+
81
+ def nil?
82
+ response.nil? || response.body.nil? || response.body.empty?
83
+ end
84
+
85
+ def to_s
86
+ if !response.nil? && !response.body.nil? && response.body.respond_to?(:to_s)
87
+ response.body.to_s
88
+ else
89
+ inspect
90
+ end
91
+ end
92
+
93
+ def pretty_print(pp)
94
+ if !parsed_response.nil? && parsed_response.respond_to?(:pretty_print)
95
+ parsed_response.pretty_print(pp)
96
+ else
97
+ super
98
+ end
99
+ end
100
+
101
+ def display(port=$>)
102
+ if !parsed_response.nil? && parsed_response.respond_to?(:display)
103
+ parsed_response.display(port)
104
+ elsif !response.nil? && !response.body.nil? && response.body.respond_to?(:display)
105
+ response.body.display(port)
106
+ else
107
+ port.write(inspect)
108
+ end
109
+ end
110
+
111
+ def respond_to_missing?(name, *args)
112
+ return true if super
113
+ parsed_response.respond_to?(name) || response.respond_to?(name)
114
+ end
115
+
116
+ def _dump(_level)
117
+ Marshal.dump([request, response, parsed_response, body])
118
+ end
119
+
120
+ protected
121
+
122
+ def method_missing(name, *args, &block)
123
+ if parsed_response.respond_to?(name)
124
+ parsed_response.send(name, *args, &block)
125
+ elsif response.respond_to?(name)
126
+ response.send(name, *args, &block)
127
+ else
128
+ super
129
+ end
130
+ end
131
+
132
+ def throw_exception
133
+ if @request.options[:raise_on] && @request.options[:raise_on].include?(code)
134
+ ::Kernel.raise ::HTTParty::ResponseError.new(@response), "Code #{code} - #{body}"
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ require 'httparty/response/headers'
@@ -0,0 +1,33 @@
1
+ require 'delegate'
2
+
3
+ module HTTParty
4
+ class Response #:nodoc:
5
+ class Headers < ::SimpleDelegator
6
+ include ::Net::HTTPHeader
7
+
8
+ def initialize(header_values = nil)
9
+ @header = {}
10
+ if header_values
11
+ header_values.each_pair do |k,v|
12
+ if v.is_a?(Array)
13
+ v.each do |sub_v|
14
+ add_field(k, sub_v)
15
+ end
16
+ else
17
+ add_field(k, v)
18
+ end
19
+ end
20
+ end
21
+ super(@header)
22
+ end
23
+
24
+ def ==(other)
25
+ if other.is_a?(::Net::HTTPHeader)
26
+ @header == other.instance_variable_get(:@header)
27
+ elsif other.is_a?(Hash)
28
+ @header == other || @header == Headers.new(other).instance_variable_get(:@header)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ require 'delegate'
2
+
3
+ module HTTParty
4
+ # Allow access to http_response and code by delegation on fragment
5
+ class ResponseFragment < SimpleDelegator
6
+ attr_reader :http_response, :connection
7
+
8
+ def code
9
+ @http_response.code.to_i
10
+ end
11
+
12
+ def initialize(fragment, http_response, connection)
13
+ @fragment = fragment
14
+ @http_response = http_response
15
+ @connection = connection
16
+ super fragment
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,70 @@
1
+ module HTTParty
2
+ class TextEncoder
3
+ attr_reader :text, :content_type, :assume_utf16_is_big_endian
4
+
5
+ def initialize(text, assume_utf16_is_big_endian: true, content_type: nil)
6
+ @text = text.dup
7
+ @content_type = content_type
8
+ @assume_utf16_is_big_endian = assume_utf16_is_big_endian
9
+ end
10
+
11
+ def call
12
+ if can_encode?
13
+ encoded_text
14
+ else
15
+ text
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def can_encode?
22
+ ''.respond_to?(:encoding) && charset
23
+ end
24
+
25
+ def encoded_text
26
+ if 'utf-16'.casecmp(charset) == 0
27
+ encode_utf_16
28
+ else
29
+ encode_with_ruby_encoding
30
+ end
31
+ end
32
+
33
+ def encode_utf_16
34
+ if text.bytesize >= 2
35
+ if text.getbyte(0) == 0xFF && text.getbyte(1) == 0xFE
36
+ return text.force_encoding("UTF-16LE")
37
+ elsif text.getbyte(0) == 0xFE && text.getbyte(1) == 0xFF
38
+ return text.force_encoding("UTF-16BE")
39
+ end
40
+ end
41
+
42
+ if assume_utf16_is_big_endian # option
43
+ text.force_encoding("UTF-16BE")
44
+ else
45
+ text.force_encoding("UTF-16LE")
46
+ end
47
+ end
48
+
49
+ def encode_with_ruby_encoding
50
+ # NOTE: This will raise an argument error if the
51
+ # charset does not exist
52
+ encoding = Encoding.find(charset)
53
+ text.force_encoding(encoding.to_s)
54
+ rescue ArgumentError
55
+ text
56
+ end
57
+
58
+ def charset
59
+ return nil if content_type.nil?
60
+
61
+ if (matchdata = content_type.match(/;\s*charset\s*=\s*([^=,;"\s]+)/i))
62
+ return matchdata.captures.first
63
+ end
64
+
65
+ if (matchdata = content_type.match(/;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i))
66
+ return matchdata.captures.first.gsub(/\\(.)/, '\1')
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,11 @@
1
+ module HTTParty
2
+ module Utils
3
+ def self.stringify_keys(hash)
4
+ return hash.transform_keys(&:to_s) if hash.respond_to?(:transform_keys)
5
+
6
+ hash.each_with_object({}) do |(key, value), new_hash|
7
+ new_hash[key.to_s] = value
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module HTTParty
2
+ VERSION = "0.17.0"
3
+ end
@@ -0,0 +1,42 @@
1
+ #!/bin/sh
2
+ #/ Usage: release
3
+ #/
4
+ #/ Tag the version in the repo and push the gem.
5
+ #/
6
+
7
+ set -e
8
+ cd $(dirname "$0")/..
9
+
10
+ [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
11
+ grep '^#/' <"$0"| cut -c4-
12
+ exit 0
13
+ }
14
+
15
+ gem_name=httparty
16
+
17
+ # Build a new gem archive.
18
+ rm -rf $gem_name-*.gem
19
+ gem build -q $gem_name.gemspec
20
+
21
+ # Make sure we're on the master branch.
22
+ (git branch | grep -q '* master') || {
23
+ echo "Only release from the master branch."
24
+ exit 1
25
+ }
26
+
27
+ # Figure out what version we're releasing.
28
+ tag=v`ls $gem_name-*.gem | sed "s/^$gem_name-\(.*\)\.gem$/\1/"`
29
+
30
+ echo "Releasing $tag"
31
+
32
+ # Make sure we haven't released this version before.
33
+ git fetch -t origin
34
+
35
+ (git tag -l | grep -q "$tag") && {
36
+ echo "Whoops, there's already a '${tag}' tag."
37
+ exit 1
38
+ }
39
+
40
+ # Tag it and bag it.
41
+ gem push $gem_name-*.gem && git tag "$tag" &&
42
+ git push origin master && git push origin "$tag"
@@ -0,0 +1,47 @@
1
+ @media screen, projection {
2
+ /*
3
+ Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4
+ Code licensed under the BSD License:
5
+ http://developer.yahoo.net/yui/license.txt
6
+ version: 2.2.0
7
+ */
8
+ body {font:13px arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}table {font-size:inherit;font:100%;}select, input, textarea {font:99% arial,helvetica,clean,sans-serif;}pre, code {font:115% monospace;*font-size:100%;}body * {line-height:1.22em;}
9
+ body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}/*ol,ul {list-style:none;}*/caption,th {text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;}
10
+ /* end of yahoo reset and fonts */
11
+
12
+ body {color:#333; background:#4b1a1a; line-height:1.3;}
13
+ p {margin:0 0 20px;}
14
+ a {color:#4b1a1a;}
15
+ a:hover {text-decoration:none;}
16
+ strong {font-weight:bold;}
17
+ em {font-style:italics;}
18
+ h1,h2,h3,h4,h5,h6 {font-weight:bold;}
19
+ h1 {font-size:197%; margin:30px 0; color:#4b1a1a;}
20
+ h2 {font-size:174%; margin:20px 0; color:#b8111a;}
21
+ h3 {font-size:152%; margin:10px 0;}
22
+ h4 {font-size:129%; margin:10px 0;}
23
+ pre {background:#eee; margin:0 0 20px; padding:20px; border:1px solid #ccc; font-size:100%; overflow:auto;}
24
+ code {font-size:100%; margin:0; padding:0;}
25
+ ul, ol {margin:10px 0 10px 25px;}
26
+ ol li {margin:0 0 10px;}
27
+
28
+
29
+
30
+
31
+
32
+ div#wrapper {background:#fff; width:560px; margin:0 auto; padding:20px; border:10px solid #bc8c46; border-width:0 10px;}
33
+ div#header {position:relative; border-bottom:1px dotted; margin:0 0 10px; padding:0 0 10px;}
34
+ div#header p {margin:0; padding:0;}
35
+ div#header h1 {margin:0; padding:0;}
36
+ ul#nav {position:absolute; top:0; right:0; list-style:none; margin:0; padding:0;}
37
+ ul#nav li {display:inline; padding:0 0 0 5px;}
38
+ ul#nav li a {}
39
+ div#content {}
40
+ div#footer {margin:40px 0 0; border-top:1px dotted; padding:10px 0 0;}
41
+
42
+
43
+
44
+
45
+
46
+
47
+ }