sapoci-connect 0.1.7 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ *2012-05-03 (0.1.9)*
2
+
3
+ * Require Faraday version 0.8.0 or later.
4
+ * Fixed handling of HTTP GET on HTTP redirect, especially 302:
5
+ There was a problem with echoing the body with HTTP GET, which
6
+ some proxies don't like (Squid).
7
+ * Fixed cookie handling: Some clients replace cookie values in
8
+ subsequent requests and, up until now, we simply concatenated
9
+ them.
10
+ * Used middleware from FaradayMiddleware project, but changed/fixed
11
+ some issues, e.g. cookie handling and handling the case where
12
+ a client wants to redirect, but does not provide a Location header.
13
+
14
+
15
+ *2012-02-08 (0.1.8)*
16
+
17
+ * Hardened sapoci-search and put up a banner in optparse
18
+ * Updated sapoci gem to 0.1.7: Accept (and replace) comma by dot in
19
+ numeric fields
20
+ * Timeout by Faraday is special: `Faraday::Error::TimeoutError`
21
+ instead of `Timeout::Error`
22
+
1
23
  *2011-10-15 (0.1.7)*
2
24
 
3
25
  * Accept GET and POST on ./bin/sapoci-search
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007-2011 Oliver Eilhard
1
+ Copyright (c) 2007-2012 Oliver Eilhard
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -10,10 +10,9 @@ comply to the SAP OCI 4.0 specification.
10
10
  It's as simple as this:
11
11
 
12
12
  conn = Faraday.new("http://onlineshop.com/path", :params => {"token" => "123"}) do |builder|
13
- builder.use SAPOCI::Connect::Middleware::FollowRedirects
14
- builder.use SAPOCI::Connect::Middleware::PassCookies
15
- builder.use SAPOCI::Connect::Middleware::BackgroundSearch
16
- builder.adapter :net_http
13
+ builder.response :follow_redirects, :cookies => :all, :limit => 5
14
+ builder.response :background_search
15
+ builder.adapter :net_http
17
16
  end
18
17
 
19
18
  conn.options[:timeout] = 5
@@ -25,7 +24,7 @@ It's as simple as this:
25
24
  puts resp.env[:raw_body] # => "<html>...</html>"
26
25
 
27
26
  Review [Faraday](https://github.com/technoweenie/faraday) for details on
28
- connection initiation.
27
+ connection initiation. We require Faraday version 0.8.0 or later.
29
28
 
30
29
  ## Testing
31
30
 
@@ -46,6 +45,8 @@ To test external servers, use the REMOTE environment variable:
46
45
  Standing on the shoulder of giants, where giants include:
47
46
 
48
47
  * Rick Olson for [faraday](https://github.com/technoweenie/faraday),
48
+ * Erik Michaels-Ober, Wynn Netherland, et al. for
49
+ [faraday_middleware](https://github.com/pengwynn/faraday_middleware),
49
50
  * Ilya Grigorik for [em-synchrony](https://github.com/igrigorik/em-synchrony),
50
51
  [em-http-request](https://github.com/igrigorik/em-http-request) and stuff,
51
52
  * David Balatero and Paul Dix for [typhoeus](https://github.com/dbalatero/typhoeus)
data/bin/sapoci-search CHANGED
@@ -13,6 +13,8 @@ require 'active_support/core_ext'
13
13
 
14
14
  options = OpenStruct.new
15
15
  parser = OptionParser.new do |opts|
16
+ opts.banner = "Usage: #{$0} [options] url keywords"
17
+
16
18
  opts.on("-d", "--[no-]debug", "Show raw response") do |o|
17
19
  options.debug = o
18
20
  end
@@ -20,6 +22,10 @@ parser = OptionParser.new do |opts|
20
22
  opts.on("-p", "--post", "Use POST instead of GET") do |o|
21
23
  options.http_post = o
22
24
  end
25
+
26
+ opts.on("-P", "--proxy [uri]", "Set proxy server URI") do |uri|
27
+ options.proxy_uri = uri
28
+ end
23
29
 
24
30
  opts.separator ""
25
31
  opts.separator "Common options:"
@@ -33,6 +39,12 @@ end
33
39
  begin
34
40
  parser.parse!(ARGV)
35
41
 
42
+ # url and keyword required
43
+ if ARGV.size < 2
44
+ puts parser.to_s
45
+ exit 1
46
+ end
47
+
36
48
  # Parameters
37
49
  url = ARGV[0]
38
50
  keywords = ARGV[1]
@@ -44,13 +56,16 @@ begin
44
56
 
45
57
  # Setup
46
58
  conn = Faraday.new(uri.to_s, :ssl => {:verify => true}) do |builder|
47
- builder.use SAPOCI::Connect::Middleware::FollowRedirects
48
- builder.use SAPOCI::Connect::Middleware::PassCookies
49
- builder.use SAPOCI::Connect::Middleware::BackgroundSearch
59
+ builder.response :follow_redirects, :cookies => :all, :limit => 5
60
+ builder.response :background_search
50
61
  builder.use Faraday::Response::Logger if options.debug
51
- builder.adapter :net_http
62
+ builder.adapter Faraday.default_adapter
52
63
  end
53
64
 
65
+ # Respect proxy settings
66
+ options.proxy_uri ||= ENV['SAPOCI_PROXY_URI']
67
+ conn.proxy({:uri => options.proxy_uri}) if options.proxy_uri
68
+
54
69
  # Execute
55
70
  method = options.http_post ? :post : :get
56
71
  resp = SAPOCI::Connect.search(method, conn, keywords, "http://return.to/me", params)
@@ -1,22 +1,27 @@
1
1
  require 'faraday'
2
2
  require 'sapoci'
3
3
  require 'sapoci/connect/middleware/follow_redirects'
4
- require 'sapoci/connect/middleware/pass_cookies'
5
4
  require 'sapoci/connect/middleware/background_search'
6
5
 
7
6
  module SAPOCI
8
7
  module Connect
9
8
 
9
+ # Register Faraday middleware
10
+ if Faraday.respond_to?(:register_middleware)
11
+ Faraday.register_middleware :response,
12
+ :follow_redirects => lambda { SAPOCI::Connect::Middleware::FollowRedirects },
13
+ :background_search => lambda { SAPOCI::Connect::Middleware::BackgroundSearch }
14
+ end
15
+
10
16
  # Perform an OCI background search.
11
17
  #
12
18
  # If you need to follow redirects and pass cookies along, you should
13
19
  # initialize and use Faraday with this pattern:
14
20
  #
15
21
  # conn = Faraday.new("http://shop.com/path", :params => {"optional" => "value"}) do |builder|
16
- # builder.use SAPOCI::Connect::Middleware::FollowRedirects
17
- # builder.use SAPOCI::Connect::Middleware::PassCookies
18
- # builder.use SAPOCI::Connect::Middleware::BackgroundSearch
19
- # builder.adapter :net_http
22
+ # builder.response :follow_redirects, :cookies => :all, :limit => 5, :standards_compliant => true
23
+ # builder.response :background_search
24
+ # builder.adapter Faraday.default_adapter
20
25
  # end
21
26
  # conn.options[:timeout] = 3
22
27
  # conn.options[:open_timeout] = 5
@@ -33,7 +38,7 @@ module SAPOCI
33
38
  params.update(extra_params) if extra_params
34
39
 
35
40
  unless connection.builder.handlers.include?(SAPOCI::Connect::Middleware::BackgroundSearch)
36
- connection.use SAPOCI::Connect::Middleware::BackgroundSearch
41
+ connection.response :background_search
37
42
  end
38
43
 
39
44
  case method.to_sym
@@ -2,18 +2,11 @@ module SAPOCI
2
2
  module Connect
3
3
  module Middleware
4
4
  class BackgroundSearch < Faraday::Response::Middleware
5
- def initialize(app, options = {})
6
- @options = options
7
- super(app)
8
- end
9
-
10
- def call(env)
11
- @app.call(env).on_complete do |resp|
12
- case env[:status]
13
- when 200
14
- env[:sapoci] = SAPOCI::Document.from_html(env[:body])
15
- else
16
- end
5
+ def on_complete(env)
6
+ case env[:status]
7
+ when 200
8
+ env[:sapoci] = SAPOCI::Document.from_html(env[:body])
9
+ else
17
10
  end
18
11
  end
19
12
  end
@@ -1,6 +1,10 @@
1
+ require 'faraday'
2
+ require 'set'
3
+
1
4
  module SAPOCI
2
5
  module Connect
3
6
  module Middleware
7
+ # Public: Exception thrown when the maximum amount of requests is exceeded.
4
8
  class RedirectLimitReached < Faraday::Error::ClientError
5
9
  attr_reader :response
6
10
 
@@ -10,43 +14,139 @@ module SAPOCI
10
14
  end
11
15
  end
12
16
 
17
+ # Public: Exception thrown when client returns an empty location header
13
18
  class RedirectWithoutLocation < Faraday::Error::ClientError
14
19
  attr_reader :response
15
20
 
16
21
  def initialize(response)
17
- super "redirect without setting HTTP Location header"
22
+ super "redirect with empty location header"
18
23
  @response = response
19
24
  end
20
25
  end
21
26
 
27
+ # Public: Follow HTTP 301, 302, 303, and 307 redirects for GET, PATCH, POST,
28
+ # PUT, and DELETE requests.
29
+ #
30
+ # This middleware does not follow the HTTP specification for HTTP 302, by
31
+ # default, in that it follows the improper implementation used by most major
32
+ # web browsers which forces the redirected request to become a GET request
33
+ # regardless of the original request method.
34
+ #
35
+ # For HTTP 301, 302, and 303, the original request is transformed into a
36
+ # GET request to the response Location, by default. However, with standards
37
+ # compliance enabled, a 302 will instead act in accordance with the HTTP
38
+ # specification, which will replay the original request to the received
39
+ # Location, just as with a 307.
40
+ #
41
+ # For HTTP 307, the original request is replayed to the response Location,
42
+ # including original HTTP request method (GET, POST, PUT, DELETE, PATCH),
43
+ # original headers, and original body.
44
+ #
45
+ # This middleware currently only works with synchronous requests; in other
46
+ # words, it doesn't support parallelism.
22
47
  class FollowRedirects < Faraday::Middleware
23
- REDIRECTS = [301, 302, 303]
24
- FOLLOW_LIMIT = 5
48
+ # HTTP methods for which 30x redirects can be followed
49
+ ALLOWED_METHODS = Set.new [:get, :post, :put, :patch, :delete]
50
+ # HTTP redirect status codes that this middleware implements
51
+ REDIRECT_CODES = Set.new [301, 302, 303, 307]
52
+ # Keys in env hash which will get cleared between requests
53
+ ENV_TO_CLEAR = Set.new [:status, :response, :response_headers]
54
+
55
+ # Default value for max redirects followed
56
+ FOLLOW_LIMIT = 3
25
57
 
58
+ # Public: Initialize the middleware.
59
+ #
60
+ # options - An options Hash (default: {}):
61
+ # limit - A Numeric redirect limit (default: 3)
62
+ # standards_compliant - A Boolean indicating whether to respect
63
+ # the HTTP spec when following 302
64
+ # (default: false)
65
+ # cookie - Use either an array of strings
66
+ # (e.g. ['cookie1', 'cookie2']) to choose kept cookies
67
+ # or :all to keep all cookies.
26
68
  def initialize(app, options = {})
27
69
  super(app)
28
70
  @options = options
29
- @limit = options[:limit] || FOLLOW_LIMIT
71
+
72
+ @replay_request_codes = Set.new [307]
73
+ @replay_request_codes << 302 if standards_compliant?
30
74
  end
31
75
 
32
76
  def call(env)
33
- process(@app.call(env), @limit)
77
+ perform_with_redirection(env, follow_limit)
34
78
  end
35
79
 
36
- def process(response, limit)
80
+ private
81
+
82
+ def transform_into_get?(response)
83
+ !@replay_request_codes.include? response.status
84
+ end
85
+
86
+ def perform_with_redirection(env, follows)
87
+ request_body = env[:body]
88
+ response = @app.call(env)
89
+
37
90
  response.on_complete do |env|
38
- if redirect?(response)
39
- raise RedirectLimitReached, response if limit.zero?
40
- raise RedirectWithoutLocation, response if response['location'].blank?
41
- env[:url] += response['location']
42
- env[:method] = :get
43
- response = process(@app.call(env), limit - 1)
91
+ if follow_redirect?(env, response)
92
+ raise RedirectLimitReached, response if follows.zero?
93
+ env = update_env(env, request_body, response)
94
+ response = perform_with_redirection(env, follows - 1)
95
+ end
96
+ end
97
+ response
98
+ end
99
+
100
+ def update_env(env, request_body, response)
101
+ location = response['location']
102
+ raise RedirectWithoutLocation, response if location.blank?
103
+ env[:url] += location
104
+ if @options[:cookies] && captured_cookies = keep_cookies(env)
105
+ env[:request_headers]['Cookie'] = captured_cookies
106
+ end
107
+
108
+ if transform_into_get?(response)
109
+ env[:method] = :get
110
+ env[:body] = nil
111
+ else
112
+ env[:body] = request_body
113
+ end
114
+
115
+ ENV_TO_CLEAR.each {|key| env.delete key }
116
+
117
+ env
118
+ end
119
+
120
+ def follow_redirect?(env, response)
121
+ ALLOWED_METHODS.include? env[:method] and
122
+ REDIRECT_CODES.include? response.status
123
+ end
124
+
125
+ def follow_limit
126
+ @options.fetch(:limit, FOLLOW_LIMIT)
127
+ end
128
+
129
+ def keep_cookies(env)
130
+ cookies = @options.fetch(:cookies, [])
131
+ response_cookies = env[:response_headers]['Set-Cookie'] #[:cookies]
132
+ cookies == :all ? response_cookies : selected_request_cookies(response_cookies)
133
+ end
134
+
135
+ def selected_request_cookies(cookies)
136
+ selected_cookies(cookies)[0...-1]
137
+ end
138
+
139
+ def selected_cookies(cookies)
140
+ "".tap do |cookie_string|
141
+ @options[:cookies].each do |cookie|
142
+ string = /#{cookie}=?[^;]*/.match(cookies)[0] + ';'
143
+ cookie_string << string
44
144
  end
45
145
  end
46
146
  end
47
147
 
48
- def redirect?(response)
49
- REDIRECTS.include? response.status
148
+ def standards_compliant?
149
+ @options.fetch(:standards_compliant, false)
50
150
  end
51
151
  end
52
152
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sapoci-connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,55 +9,38 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-15 00:00:00.000000000 Z
12
+ date: 2012-05-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
16
- requirement: &70144890289320 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 0.7.4
21
+ version: 0.8.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70144890289320
25
- - !ruby/object:Gem::Dependency
26
- name: em-synchrony
27
- requirement: &70144890288740 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ~>
31
- - !ruby/object:Gem::Version
32
- version: 1.0.0
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: *70144890288740
36
- - !ruby/object:Gem::Dependency
37
- name: em-http-request
38
- requirement: &70144890288140 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
39
25
  none: false
40
26
  requirements:
41
- - - ~>
27
+ - - ! '>='
42
28
  - !ruby/object:Gem::Version
43
- version: 1.0.0
44
- type: :runtime
45
- prerelease: false
46
- version_requirements: *70144890288140
29
+ version: 0.8.0
47
30
  - !ruby/object:Gem::Dependency
48
- name: typhoeus
49
- requirement: &70144890287460 !ruby/object:Gem::Requirement
31
+ name: rack
32
+ requirement: !ruby/object:Gem::Requirement
50
33
  none: false
51
34
  requirements:
52
35
  - - ! '>='
53
36
  - !ruby/object:Gem::Version
54
- version: 0.2.4
37
+ version: 1.1.0
38
+ - - <
39
+ - !ruby/object:Gem::Version
40
+ version: '2'
55
41
  type: :runtime
56
42
  prerelease: false
57
- version_requirements: *70144890287460
58
- - !ruby/object:Gem::Dependency
59
- name: rack
60
- requirement: &70144890286820 !ruby/object:Gem::Requirement
43
+ version_requirements: !ruby/object:Gem::Requirement
61
44
  none: false
62
45
  requirements:
63
46
  - - ! '>='
@@ -66,23 +49,25 @@ dependencies:
66
49
  - - <
67
50
  - !ruby/object:Gem::Version
68
51
  version: '2'
69
- type: :runtime
70
- prerelease: false
71
- version_requirements: *70144890286820
72
52
  - !ruby/object:Gem::Dependency
73
53
  name: sapoci
74
- requirement: &70144890285840 !ruby/object:Gem::Requirement
54
+ requirement: !ruby/object:Gem::Requirement
75
55
  none: false
76
56
  requirements:
77
57
  - - ! '>='
78
58
  - !ruby/object:Gem::Version
79
- version: 0.1.4
59
+ version: 0.1.7
80
60
  type: :runtime
81
61
  prerelease: false
82
- version_requirements: *70144890285840
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: 0.1.7
83
68
  - !ruby/object:Gem::Dependency
84
69
  name: bundler
85
- requirement: &70144890285160 !ruby/object:Gem::Requirement
70
+ requirement: !ruby/object:Gem::Requirement
86
71
  none: false
87
72
  requirements:
88
73
  - - ~>
@@ -90,10 +75,15 @@ dependencies:
90
75
  version: '1.0'
91
76
  type: :development
92
77
  prerelease: false
93
- version_requirements: *70144890285160
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: '1.0'
94
84
  - !ruby/object:Gem::Dependency
95
85
  name: rdoc
96
- requirement: &70144890284560 !ruby/object:Gem::Requirement
86
+ requirement: !ruby/object:Gem::Requirement
97
87
  none: false
98
88
  requirements:
99
89
  - - ~>
@@ -101,10 +91,15 @@ dependencies:
101
91
  version: '2.5'
102
92
  type: :development
103
93
  prerelease: false
104
- version_requirements: *70144890284560
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ~>
98
+ - !ruby/object:Gem::Version
99
+ version: '2.5'
105
100
  - !ruby/object:Gem::Dependency
106
101
  name: rake
107
- requirement: &70144890283900 !ruby/object:Gem::Requirement
102
+ requirement: !ruby/object:Gem::Requirement
108
103
  none: false
109
104
  requirements:
110
105
  - - ! '>='
@@ -112,10 +107,15 @@ dependencies:
112
107
  version: 0.9.2
113
108
  type: :development
114
109
  prerelease: false
115
- version_requirements: *70144890283900
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: 0.9.2
116
116
  - !ruby/object:Gem::Dependency
117
117
  name: sinatra
118
- requirement: &70144890283300 !ruby/object:Gem::Requirement
118
+ requirement: !ruby/object:Gem::Requirement
119
119
  none: false
120
120
  requirements:
121
121
  - - ~>
@@ -123,7 +123,12 @@ dependencies:
123
123
  version: '1.2'
124
124
  type: :development
125
125
  prerelease: false
126
- version_requirements: *70144890283300
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ~>
130
+ - !ruby/object:Gem::Version
131
+ version: '1.2'
127
132
  description: HTTP client library for working with SAP OCI compliant servers.
128
133
  email:
129
134
  - oliver.eilhard@gmail.com
@@ -139,7 +144,6 @@ files:
139
144
  - lib/sapoci/connect.rb
140
145
  - lib/sapoci/connect/middleware/background_search.rb
141
146
  - lib/sapoci/connect/middleware/follow_redirects.rb
142
- - lib/sapoci/connect/middleware/pass_cookies.rb
143
147
  - CHANGELOG.md
144
148
  - LICENSE
145
149
  - README.md
@@ -164,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
168
  version: 1.3.6
165
169
  requirements: []
166
170
  rubyforge_project:
167
- rubygems_version: 1.8.9
171
+ rubygems_version: 1.8.23
168
172
  signing_key:
169
173
  specification_version: 3
170
174
  summary: HTTP client library for working with SAP OCI compliant servers.
@@ -1,25 +0,0 @@
1
- module SAPOCI
2
- module Connect
3
- module Middleware
4
- class PassCookies < Faraday::Response::Middleware
5
- def initialize(app, options = {})
6
- @options = options
7
- @cookies = []
8
- super(app)
9
- end
10
-
11
- def call(env)
12
- unless @cookies.empty?
13
- cookies = @cookies.compact.uniq.join("; ").squeeze(";")
14
- env[:request_headers]['Cookie'] = cookies
15
- end
16
- @app.call(env).on_complete do |resp|
17
- if cookie = resp[:response_headers]['Set-Cookie']
18
- @cookies << cookie
19
- end
20
- end
21
- end # call
22
- end
23
- end
24
- end
25
- end