faraday_middleware_safeyaml 0.12.pre.safeyaml

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.
@@ -0,0 +1,87 @@
1
+ require 'faraday'
2
+ require 'forwardable'
3
+
4
+ module FaradayMiddleware
5
+ # Public: Uses the simple_oauth library to sign requests according the
6
+ # OAuth protocol.
7
+ #
8
+ # The options for this middleware are forwarded to SimpleOAuth::Header:
9
+ # :consumer_key, :consumer_secret, :token, :token_secret. All these
10
+ # parameters are optional.
11
+ #
12
+ # The signature is added to the "Authorization" HTTP request header. If the
13
+ # value for this header already exists, it is not overriden.
14
+ #
15
+ # If no Content-Type header is specified, this middleware assumes that
16
+ # request body parameters should be included while signing the request.
17
+ # Otherwise, it only includes them if the Content-Type is
18
+ # "application/x-www-form-urlencoded", as per OAuth 1.0.
19
+ #
20
+ # For better performance while signing requests, this middleware should be
21
+ # positioned before UrlEncoded middleware on the stack, but after any other
22
+ # body-encoding middleware (such as EncodeJson).
23
+ class OAuth < Faraday::Middleware
24
+ dependency 'simple_oauth'
25
+
26
+ AUTH_HEADER = 'Authorization'.freeze
27
+ CONTENT_TYPE = 'Content-Type'.freeze
28
+ TYPE_URLENCODED = 'application/x-www-form-urlencoded'.freeze
29
+
30
+ extend Forwardable
31
+ parser_method = :parse_nested_query
32
+ parser_module = ::Faraday::Utils.respond_to?(parser_method) ? 'Faraday::Utils' : 'Rack::Utils'
33
+ def_delegator parser_module, parser_method
34
+
35
+ def initialize(app, options)
36
+ super(app)
37
+ @options = options
38
+ end
39
+
40
+ def call(env)
41
+ env[:request_headers][AUTH_HEADER] ||= oauth_header(env).to_s if sign_request?(env)
42
+ @app.call(env)
43
+ end
44
+
45
+ def oauth_header(env)
46
+ SimpleOAuth::Header.new env[:method],
47
+ env[:url].to_s,
48
+ signature_params(body_params(env)),
49
+ oauth_options(env)
50
+ end
51
+
52
+ def sign_request?(env)
53
+ !!env[:request].fetch(:oauth, true)
54
+ end
55
+
56
+ def oauth_options(env)
57
+ if extra = env[:request][:oauth] and extra.is_a? Hash and !extra.empty?
58
+ @options.merge extra
59
+ else
60
+ @options
61
+ end
62
+ end
63
+
64
+ def body_params(env)
65
+ if include_body_params?(env)
66
+ if env[:body].respond_to?(:to_str)
67
+ parse_nested_query env[:body]
68
+ else
69
+ env[:body]
70
+ end
71
+ end || {}
72
+ end
73
+
74
+ def include_body_params?(env)
75
+ # see RFC 5849, section 3.4.1.3.1 for details
76
+ !(type = env[:request_headers][CONTENT_TYPE]) or type == TYPE_URLENCODED
77
+ end
78
+
79
+ def signature_params(params)
80
+ params.empty? ? params :
81
+ params.reject {|k,v| v.respond_to?(:content_type) }
82
+ end
83
+ end
84
+ end
85
+
86
+ # deprecated alias
87
+ Faraday::Request::OAuth = FaradayMiddleware::OAuth
@@ -0,0 +1,85 @@
1
+ require 'faraday'
2
+ require 'forwardable'
3
+
4
+ module FaradayMiddleware
5
+ # Public: A simple middleware that adds an access token to each request.
6
+ #
7
+ # By default, the token is added as both "access_token" query parameter and the
8
+ # "Authorization" HTTP request header. It can alternatively be added exclusively
9
+ # as a bearer token "Authorization" header by specifying a "token_type" option
10
+ # of "bearer". However, an explicit "access_token" parameter or "Authorization"
11
+ # header for the current request are not overriden.
12
+ #
13
+ # Examples
14
+ #
15
+ # # configure default token:
16
+ # OAuth2.new(app, 'abc123')
17
+ #
18
+ # # configure query parameter name:
19
+ # OAuth2.new(app, 'abc123', :param_name => 'my_oauth_token')
20
+ #
21
+ # # use bearer token authorization header only
22
+ # OAuth2.new(app, 'abc123', :token_type => 'bearer')
23
+ #
24
+ # # default token value is optional:
25
+ # OAuth2.new(app, :param_name => 'my_oauth_token')
26
+ class OAuth2 < Faraday::Middleware
27
+
28
+ PARAM_NAME = 'access_token'.freeze
29
+ TOKEN_TYPE = 'param'.freeze
30
+ AUTH_HEADER = 'Authorization'.freeze
31
+
32
+ attr_reader :param_name, :token_type
33
+
34
+ extend Forwardable
35
+ def_delegators :'Faraday::Utils', :parse_query, :build_query
36
+
37
+ def call(env)
38
+ params = { param_name => @token }.update query_params(env[:url])
39
+ token = params[param_name]
40
+
41
+ if token.respond_to?(:empty?) && !token.empty?
42
+ case @token_type.downcase
43
+ when 'param'
44
+ env[:url].query = build_query params
45
+ env[:request_headers][AUTH_HEADER] ||= %(Token token="#{token}")
46
+ when 'bearer'
47
+ env[:request_headers][AUTH_HEADER] ||= %(Bearer #{token})
48
+ end
49
+ end
50
+
51
+ @app.call env
52
+ end
53
+
54
+ def initialize(app, token = nil, options = {})
55
+ super(app)
56
+ options, token = token, nil if token.is_a? Hash
57
+ @token = token && token.to_s
58
+ @param_name = options.fetch(:param_name, PARAM_NAME).to_s
59
+ @token_type = options.fetch(:token_type, TOKEN_TYPE).to_s
60
+
61
+ if @token_type == 'param' && @param_name.empty?
62
+ raise ArgumentError, ":param_name can't be blank"
63
+ end
64
+
65
+ if options[:token_type].nil?
66
+ warn "\nWarning: FaradayMiddleware::OAuth2 initialized with default "\
67
+ "token_type - token will be added as both a query string parameter "\
68
+ "and an Authorization header. In the next major release, tokens will "\
69
+ "be added exclusively as an Authorization header by default. Please "\
70
+ "visit https://github.com/lostisland/faraday_middleware/wiki for more information."
71
+ end
72
+ end
73
+
74
+ def query_params(url)
75
+ if url.query.nil? or url.query.empty?
76
+ {}
77
+ else
78
+ parse_query url.query
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ # deprecated alias
85
+ Faraday::Request::OAuth2 = FaradayMiddleware::OAuth2
@@ -0,0 +1,102 @@
1
+ require 'faraday'
2
+ require 'forwardable'
3
+ # fixes normalizing query strings:
4
+ require 'faraday_middleware/addressable_patch' if defined? ::Addressable::URI
5
+
6
+ module FaradayMiddleware
7
+ # Public: Caches GET responses and pulls subsequent ones from the cache.
8
+ class Caching < Faraday::Middleware
9
+ attr_reader :cache
10
+ # Internal: List of status codes that can be cached:
11
+ # * 200 - 'OK'
12
+ # * 203 - 'Non-Authoritative Information'
13
+ # * 300 - 'Multiple Choices'
14
+ # * 301 - 'Moved Permanently'
15
+ # * 302 - 'Found'
16
+ # * 404 - 'Not Found'
17
+ # * 410 - 'Gone'
18
+ CACHEABLE_STATUS_CODES = [200, 203, 300, 301, 302, 404, 410]
19
+
20
+ extend Forwardable
21
+ def_delegators :'Faraday::Utils', :parse_query, :build_query
22
+
23
+ # Public: initialize the middleware.
24
+ #
25
+ # cache - An object that responds to read, write and fetch (default: nil).
26
+ # options - An options Hash (default: {}):
27
+ # :ignore_params - String name or Array names of query params
28
+ # that should be ignored when forming the cache
29
+ # key (default: []).
30
+ #
31
+ # Yields if no cache is given. The block should return a cache object.
32
+ def initialize(app, cache = nil, options = {})
33
+ super(app)
34
+ options, cache = cache, nil if cache.is_a? Hash and block_given?
35
+ @cache = cache || yield
36
+ @options = options
37
+ end
38
+
39
+ def call(env)
40
+ if :get == env[:method]
41
+ if env[:parallel_manager]
42
+ # callback mode
43
+ cache_on_complete(env)
44
+ else
45
+ # synchronous mode
46
+ key = cache_key(env)
47
+ unless response = cache.read(key) and response
48
+ response = @app.call(env)
49
+
50
+ if CACHEABLE_STATUS_CODES.include?(response.status)
51
+ cache.write(key, response)
52
+ end
53
+ end
54
+ finalize_response(response, env)
55
+ end
56
+ else
57
+ @app.call(env)
58
+ end
59
+ end
60
+
61
+ def cache_key(env)
62
+ url = env[:url].dup
63
+ if url.query && params_to_ignore.any?
64
+ params = parse_query url.query
65
+ params.reject! {|k,| params_to_ignore.include? k }
66
+ url.query = params.any? ? build_query(params) : nil
67
+ end
68
+ url.normalize!
69
+ url.request_uri
70
+ end
71
+
72
+ def params_to_ignore
73
+ @params_to_ignore ||= Array(@options[:ignore_params]).map { |p| p.to_s }
74
+ end
75
+
76
+ def cache_on_complete(env)
77
+ key = cache_key(env)
78
+ if cached_response = cache.read(key)
79
+ finalize_response(cached_response, env)
80
+ else
81
+ # response.status is nil at this point, any checks need to be done inside on_complete block
82
+ @app.call(env).on_complete do |response_env|
83
+ if CACHEABLE_STATUS_CODES.include?(response_env.status)
84
+ cache.write(key, response_env.response)
85
+ end
86
+ response_env
87
+ end
88
+ end
89
+ end
90
+
91
+ def finalize_response(response, env)
92
+ response = response.dup if response.frozen?
93
+ env[:response] = response
94
+ unless env[:response_headers]
95
+ env.update response.env
96
+ # FIXME: omg hax
97
+ response.instance_variable_set('@env', env)
98
+ end
99
+ response
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,29 @@
1
+ require 'faraday_middleware/response_middleware'
2
+
3
+ module FaradayMiddleware
4
+ # Public: Parse a Transfer-Encoding: Chunked response to just the original data
5
+ class Chunked < FaradayMiddleware::ResponseMiddleware
6
+ TRANSFER_ENCODING = 'transfer-encoding'.freeze
7
+
8
+ define_parser do |raw_body|
9
+ decoded_body = []
10
+ until raw_body.empty?
11
+ chunk_len, raw_body = raw_body.split("\r\n", 2)
12
+ chunk_len = chunk_len.split(';',2).first.hex
13
+ break if chunk_len == 0
14
+ decoded_body << raw_body[0, chunk_len]
15
+ # The 2 is to strip the extra CRLF at the end of the chunk
16
+ raw_body = raw_body[chunk_len + 2, raw_body.length - chunk_len - 2]
17
+ end
18
+ decoded_body.join('')
19
+ end
20
+
21
+ def parse_response?(env)
22
+ super and chunked_encoding?(env[:response_headers])
23
+ end
24
+
25
+ def chunked_encoding?(headers)
26
+ encoding = headers[TRANSFER_ENCODING] and encoding.split(',').include?('chunked')
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,134 @@
1
+ require 'faraday'
2
+ require 'set'
3
+
4
+ module FaradayMiddleware
5
+ # Public: Exception thrown when the maximum amount of requests is exceeded.
6
+ class RedirectLimitReached < Faraday::Error::ClientError
7
+ attr_reader :response
8
+
9
+ def initialize(response)
10
+ super "too many redirects; last one to: #{response['location']}"
11
+ @response = response
12
+ end
13
+ end
14
+
15
+ # Public: Follow HTTP 301, 302, 303, 307, and 308 redirects.
16
+ #
17
+ # For HTTP 301, 302, and 303, the original GET, POST, PUT, DELETE, or PATCH
18
+ # request gets converted into a GET. With `:standards_compliant => true`,
19
+ # however, the HTTP method after 301/302 remains unchanged. This allows you
20
+ # to opt into HTTP/1.1 compliance and act unlike the major web browsers.
21
+ #
22
+ # This middleware currently only works with synchronous requests; i.e. it
23
+ # doesn't support parallelism.
24
+ #
25
+ # If you wish to persist cookies across redirects, you could use
26
+ # the faraday-cookie_jar gem:
27
+ #
28
+ # Faraday.new(:url => url) do |faraday|
29
+ # faraday.use FaradayMiddleware::FollowRedirects
30
+ # faraday.use :cookie_jar
31
+ # faraday.adapter Faraday.default_adapter
32
+ # end
33
+ class FollowRedirects < Faraday::Middleware
34
+ # HTTP methods for which 30x redirects can be followed
35
+ ALLOWED_METHODS = Set.new [:head, :options, :get, :post, :put, :patch, :delete]
36
+ # HTTP redirect status codes that this middleware implements
37
+ REDIRECT_CODES = Set.new [301, 302, 303, 307, 308]
38
+ # Keys in env hash which will get cleared between requests
39
+ ENV_TO_CLEAR = Set.new [:status, :response, :response_headers]
40
+
41
+ # Default value for max redirects followed
42
+ FOLLOW_LIMIT = 3
43
+
44
+ # Regex that matches characters that need to be escaped in URLs, sans
45
+ # the "%" character which we assume already represents an escaped sequence.
46
+ URI_UNSAFE = /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]%]/
47
+
48
+ # Public: Initialize the middleware.
49
+ #
50
+ # options - An options Hash (default: {}):
51
+ # :limit - A Numeric redirect limit (default: 3)
52
+ # :standards_compliant - A Boolean indicating whether to respect
53
+ # the HTTP spec when following 301/302
54
+ # (default: false)
55
+ # :callback - A callable that will be called on redirects
56
+ # with the old and new envs
57
+ def initialize(app, options = {})
58
+ super(app)
59
+ @options = options
60
+
61
+ @convert_to_get = Set.new [303]
62
+ @convert_to_get << 301 << 302 unless standards_compliant?
63
+ end
64
+
65
+ def call(env)
66
+ perform_with_redirection(env, follow_limit)
67
+ end
68
+
69
+ private
70
+
71
+ def convert_to_get?(response)
72
+ ![:head, :options].include?(response.env[:method]) &&
73
+ @convert_to_get.include?(response.status)
74
+ end
75
+
76
+ def perform_with_redirection(env, follows)
77
+ request_body = env[:body]
78
+ response = @app.call(env)
79
+
80
+ response.on_complete do |response_env|
81
+ if follow_redirect?(response_env, response)
82
+ raise RedirectLimitReached, response if follows.zero?
83
+ new_request_env = update_env(response_env.dup, request_body, response)
84
+ callback.call(response_env, new_request_env) if callback
85
+ response = perform_with_redirection(new_request_env, follows - 1)
86
+ end
87
+ end
88
+ response
89
+ end
90
+
91
+ def update_env(env, request_body, response)
92
+ env[:url] += safe_escape(response['location'] || '')
93
+
94
+ if convert_to_get?(response)
95
+ env[:method] = :get
96
+ env[:body] = nil
97
+ else
98
+ env[:body] = request_body
99
+ end
100
+
101
+ ENV_TO_CLEAR.each {|key| env.delete key }
102
+
103
+ env
104
+ end
105
+
106
+ def follow_redirect?(env, response)
107
+ ALLOWED_METHODS.include? env[:method] and
108
+ REDIRECT_CODES.include? response.status
109
+ end
110
+
111
+ def follow_limit
112
+ @options.fetch(:limit, FOLLOW_LIMIT)
113
+ end
114
+
115
+ def standards_compliant?
116
+ @options.fetch(:standards_compliant, false)
117
+ end
118
+
119
+ def callback
120
+ @options[:callback]
121
+ end
122
+
123
+ # Internal: escapes unsafe characters from an URL which might be a path
124
+ # component only or a fully qualified URI so that it can be joined onto an
125
+ # URI:HTTP using the `+` operator. Doesn't escape "%" characters so to not
126
+ # risk double-escaping.
127
+ def safe_escape(uri)
128
+ uri = uri.split('#')[0] # we want to remove the fragment if present
129
+ uri.to_s.gsub(URI_UNSAFE) { |match|
130
+ '%' + match.unpack('H2' * match.bytesize).join('%').upcase
131
+ }
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,37 @@
1
+ require 'faraday'
2
+
3
+ module FaradayMiddleware
4
+ # Public: Converts parsed response bodies to a Hashie::Mash if they were of
5
+ # Hash or Array type.
6
+ class Mashify < Faraday::Response::Middleware
7
+ attr_accessor :mash_class
8
+
9
+ class << self
10
+ attr_accessor :mash_class
11
+ end
12
+
13
+ dependency do
14
+ require 'hashie/mash'
15
+ self.mash_class = ::Hashie::Mash
16
+ end
17
+
18
+ def initialize(app = nil, options = {})
19
+ super(app)
20
+ self.mash_class = options[:mash_class] || self.class.mash_class
21
+ end
22
+
23
+ def parse(body)
24
+ case body
25
+ when Hash
26
+ mash_class.new(body)
27
+ when Array
28
+ body.map { |item| parse(item) }
29
+ else
30
+ body
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ # deprecated alias
37
+ Faraday::Response::Mashify = FaradayMiddleware::Mashify