sapoci-connect 0.1.7 → 0.1.9

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.
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