service-proxy 0.2.12

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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: faf306e26c813332a695bf550b707d53f4c489f213e46e2dd4b658904781587e
4
+ data.tar.gz: dcdde7dbaf1031b60d8cb358678579eba7831c9f25126f3a128f1985a5e83df6
5
+ SHA512:
6
+ metadata.gz: c58f05067a4aa1dd313ddebf14154c1c508fce79b4d21a270e0cb92893e44dc8f88a302b4113fb87df9b5ca0191eddb00a588563207434ed6606111a460667e1
7
+ data.tar.gz: '0256529216e31016783b0fa0c4e2645c1044160e59a331dc150d336fd92362b05abe6cd1cfcae4f2cd512eba26af89245708ddcb24a52df4681142d0683f9663'
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Donald Piret
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,75 @@
1
+ # Service::Proxy
2
+
3
+ When running a service-oriented architecture, it is sometimes desirable to allow your backend services to be mapped to URL paths within the application itself.
4
+
5
+ This allows you for example to be able to share cookies and reduce the need for CORS calls or to expose your services in a way that they are accessible only through a particular front-end application.
6
+
7
+ This gem allows you to easily set up these backend service proxy's through a simple YAML configuration file.
8
+
9
+ ## Installation
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+
14
+ source "https://rubygems.pkg.github.com/sephora-asia" do
15
+ gem 'service-proxy'
16
+ end
17
+
18
+ ```
19
+
20
+ And then execute:
21
+ ```bash
22
+ $ bundle
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ Create a file named ``config/service_proxy.yml`` in your rails application directory. It should look something like this -
28
+
29
+ ```yml
30
+ default: &default
31
+ services:
32
+ address-api:
33
+ path: /api/address
34
+ backend: https://www.my-address-api.com:8888/
35
+
36
+ user-api:
37
+ path: /api/user
38
+ backend: https://www.my-user-api.com/
39
+
40
+ rewards-api:
41
+ path: /api/rewards
42
+ backend: http://www.my-rewards-api.com:9999/
43
+
44
+ ads-api:
45
+ path: /api/ads
46
+ backend: http://www.my-ads-api.com:1101/
47
+ forward_cookies: false
48
+
49
+
50
+ development:
51
+ <<: *default
52
+
53
+ test:
54
+ <<: *default
55
+ ```
56
+
57
+ Each of the `services` block sequence in the `yml` file is given a - `sequence name`, and are expected to contain `path` and `backend` scalar. The request url that the app receives will be matched against each of these `path` and if a match is found, then the request will be forwarded to the corresponding backend. For eg. based on the above config, if our app is responding to https://www.my-app.com, then if a request to https://www.my-app.com/api/rewards?reward_id=1234 is made, it will be matched against the `rewards-api` block sequence and the same request will get forwarded as http://www.my-rewards-api.com:9999/?reward_id=1234.
58
+
59
+ Each block sequence can contain the following scalars -
60
+ - `path` : The path that needs to be matched in the request url. It is a required field.
61
+ - `backend` : This is the destination URI that we need to forward the current request to. This needs to have a valid url scheme `[scheme]://[domain]:[port]/`. Supported `scheme` is either `http` or `https`. `port` is optional and the default value of 80 or 443 is used depending up on whether the scheme is `http` or `https`. This is also a required field.
62
+ - `forward_cookies`: Can be `true` (default value) or `false`. Cookies will not be forwarded to `backend` if this is set to false.
63
+ - `read_timeout`: This sets the proxy timeout. By default it times out in 60 seconds.
64
+ - `verify_ssl`: Can be `true` (default value) or `false`. This tells `Net::HTTP` to not validate certs if set to `false`.
65
+
66
+ ## Contributing
67
+
68
+ Run `make setup` in order to install your dev environment.
69
+
70
+ This project uses the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit naming.
71
+
72
+ Publishing is done automatically when a push to the master branch is detected.
73
+
74
+ ## License
75
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Service::Proxy'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ require 'bundler/gem_tasks'
@@ -0,0 +1,5 @@
1
+ require 'service/proxy/version'
2
+ require 'service/proxy/config'
3
+ require 'service/proxy/middleware'
4
+ require 'service/proxy/service_config'
5
+ require 'service/proxy/railtie' if defined?(Rails)
@@ -0,0 +1,23 @@
1
+ module Service
2
+ module Proxy
3
+ class Config
4
+ attr_reader :config
5
+
6
+ def initialize(config = {})
7
+ @config = config || {}
8
+ end
9
+
10
+ def proxied_paths_matcher
11
+ @proxied_paths_matcher ||= Regexp.union(services.collect(&:path_regexp))
12
+ end
13
+
14
+ def service_matching_path(path)
15
+ services.detect { |service| service.path_regexp =~ path }
16
+ end
17
+
18
+ def services
19
+ @services ||= config.fetch(:services, {}).map { |k, v| ServiceConfig.new(k, v) }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack-proxy'
4
+
5
+ module Service
6
+ module Proxy
7
+ class Middleware < Rack::Proxy
8
+ def perform_request(env)
9
+ request = Rack::Request.new(env)
10
+
11
+ # use rack proxy for anything hitting our host app at /example_service
12
+ if config && request.path =~ config.proxied_paths_matcher
13
+ service = config.service_matching_path(request.path)
14
+
15
+ # most backends required host set properly, but rack-proxy doesn't set this for you automatically
16
+ # even when a backend host is passed in via the options
17
+ env['SERVER_NAME'] = env['HTTP_HOST'] = [service.backend.host, service.backend.port].compact.join(':')
18
+ env['SERVER_PORT'] = service.backend.port.to_s
19
+ env['rack.backend'] = service.backend
20
+ env['rack.url_scheme'] = service.backend.scheme
21
+ env['HTTPS'] = service.backend.scheme.downcase == 'https' ? 'on' : 'off'
22
+
23
+ # This is the only path that needs to be set currently on Rails 5 & greater
24
+ env['PATH_INFO'] = service.backend_path_for(request.path)
25
+
26
+ # don't send your sites cookies to target service, unless it is a trusted internal service that can parse all your cookies
27
+ env['HTTP_COOKIE'] = '' unless service.send_cookies?
28
+
29
+ # other Rack::Proxy opts
30
+ env['rack.ssl_verify_none'] = true unless service.verify_ssl?
31
+ env['http.read_timeout'] = service.read_timeout unless service.read_timeout.nil?
32
+
33
+ env['content-length'] = nil
34
+
35
+ super(env)
36
+ else
37
+ @app.call(env)
38
+ end
39
+ end
40
+
41
+ def rewrite_response(triplet)
42
+ status, headers, body = triplet
43
+ headers['content-length'] = nil
44
+ triplet
45
+ end
46
+
47
+ private
48
+
49
+ def config
50
+ return @config if instance_variable_defined?(:'@config')
51
+
52
+ if File.exist?(Rails.root.join('config/service_proxy.yml'))
53
+ config_data = Rails.application.config_for(:service_proxy)
54
+ @config = Config.new(config_data.deep_symbolize_keys) if config_data
55
+ end
56
+ @config ||= nil
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,9 @@
1
+ module Service
2
+ module Proxy
3
+ class Railtie < ::Rails::Railtie
4
+ initializer 'service.proxy' do |app|
5
+ app.middleware.use Service::Proxy::Middleware
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ module Service
2
+ module Proxy
3
+ class ServiceConfig
4
+ def initialize(name, options)
5
+ @name = name
6
+ @options = options
7
+ end
8
+
9
+ def backend
10
+ @backend ||= URI(@options.fetch(:backend, ''))
11
+ end
12
+
13
+ def backend_path_for(original_path)
14
+ original_path.delete_prefix!(path)
15
+ [backend.path, original_path].compact.join('/').gsub(/\/+/, '/')
16
+ end
17
+
18
+ def path
19
+ @path ||= @options.fetch(:path)
20
+ end
21
+
22
+ def path_regexp
23
+ @path_regexp ||= Regexp.new("^#{Regexp.escape(path)}")
24
+ end
25
+
26
+ def send_cookies?
27
+ @options.fetch(:forward_cookies, true)
28
+ end
29
+
30
+ def verify_ssl?
31
+ @options.fetch(:verify_ssl, true)
32
+ end
33
+
34
+ def read_timeout
35
+ @options.fetch(:read_timeout, nil)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ module Service
2
+ module Proxy
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :service_proxy do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: service-proxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.12
5
+ platform: ruby
6
+ authors:
7
+ - Sephora Asia
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack-proxy
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.6.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.6.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Easily proxy paths in your rails application to other backend services.
70
+ email:
71
+ - dpiret@sephora.sg
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - MIT-LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - lib/service/proxy.rb
80
+ - lib/service/proxy/config.rb
81
+ - lib/service/proxy/middleware.rb
82
+ - lib/service/proxy/railtie.rb
83
+ - lib/service/proxy/service_config.rb
84
+ - lib/service/proxy/version.rb
85
+ - lib/tasks/service/proxy_tasks.rake
86
+ homepage: https://github.com/sephora-asia/service-proxy-rails
87
+ licenses:
88
+ - MIT
89
+ metadata:
90
+ homepage_uri: https://github.com/sephora-asia/service-proxy-rails
91
+ source_code_uri: https://github.com/sephora-asia/service-proxy-rails
92
+ github_repo: ssh://github.com/sephora-asia/service-proxy-rails
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubygems_version: 3.1.2
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Easily proxy paths in your rails application to other backend services.
112
+ test_files: []