tshield 0.11.13.0 → 0.11.18.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 822edc6241a2e6d53abc32da081364b3c56c7804a76bdc5f280cf42229ab36a4
4
- data.tar.gz: de7090a23614e79b2d422665e86218df7cd313c26765660422b7ce8d2c7ab081
3
+ metadata.gz: 36efe1ccd1ea061d66f046d338b76fcb0b9b6f30c740ecadabe6c774939dcd5b
4
+ data.tar.gz: 38559f053d082a577260aa94a115a66937ffb9ce45c52fee3a60339ade9ea32d
5
5
  SHA512:
6
- metadata.gz: 9faf59a02b986ff0ebede6763451fb93157516b4aebe1e809043f8519a65dd9fed61b4a46e9efa57b8be63b841d3861c211cdd0c60fb1c01722c9a62eec710a4
7
- data.tar.gz: c89b1ead5400a31bae4d8a06e807f3e66503e4a2314924a1dc263b7c992524791a7334cd401bbeb0ea20382ebcf8edbf9ab85ae57c62689d4057a73a8d4b3678
6
+ metadata.gz: ba74b2c25265ef74b62b297db19795b387c1bfe4821dce609d17cf99b00d663d58f3f889e134d58a22b679b9f1781bf00c64117fdd1e097111c7fc2cfaab5069
7
+ data.tar.gz: ce7f58cb6bf00a942b56464235cd8fa002c6489aa6643c057f9d201eeebafe3e57be70724f8bca03f8068a5468caf3d62caea846869305d145edca2ed4d19322
data/Gemfile CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  source 'https://rubygems.org'
4
- gem 'sinatra-cross_origin'
5
4
  gemspec
data/README.md CHANGED
@@ -13,9 +13,10 @@ TShield is an open source proxy for mocks API responses.
13
13
  * REST
14
14
  * SOAP
15
15
  * Session manager to separate multiple scenarios (success, error, sucess variation, ...)
16
+ gRPC [EXPERIMENTAL]
16
17
  * Lightweight
17
18
  * MIT license
18
-
19
+
19
20
  ## Table of Contents
20
21
 
21
22
  * [Basic Usage](#basic-usage)
@@ -26,7 +27,7 @@ TShield is an open source proxy for mocks API responses.
26
27
  * [Features](#features)
27
28
  * [Examples](#examples)
28
29
  * [Contributing](#contributing)
29
-
30
+
30
31
  ## Basic Usage
31
32
  ### Install
32
33
 
@@ -37,7 +38,7 @@ TShield is an open source proxy for mocks API responses.
37
38
  To run server execute this command
38
39
 
39
40
  tshield
40
-
41
+
41
42
  Default port is `4567`
42
43
 
43
44
  #### Command Line Options
@@ -102,7 +103,7 @@ To register stub into a session create an object with following attributes:
102
103
  * **session**: name of session.
103
104
  * **stubs**: an array with objects described above.
104
105
 
105
- ### Example of matching configuration
106
+ ### Example of HTTP matching configuration
106
107
 
107
108
  ```json
108
109
  [
@@ -163,7 +164,7 @@ To register stub into a session create an object with following attributes:
163
164
  ]
164
165
  ```
165
166
 
166
- ## Config options for VCR
167
+ ## Config options for HTTP VCR
167
168
  ```yaml
168
169
  request:
169
170
  timeout: 8
@@ -227,7 +228,7 @@ You can use TShield sessions to separate multiple scenarios for your mocks
227
228
  By default TShield save request/response into
228
229
 
229
230
  requests/<<domain_name>>/<<resource_with_param>>/<<http_verb>>/<<index_based.content and json>>
230
-
231
+
231
232
  If you start a session a folder with de **session_name** will be placed between **"requests/"** and **"<<domain_name>>"**
232
233
 
233
234
  ### Start TShield session
@@ -249,6 +250,36 @@ _DELETE_ to http://localhost:4567/sessions
249
250
  curl -X DELETE \
250
251
  http://localhost:4567/sessions
251
252
  ```
253
+ ## [Experimental] Config options for gRPC
254
+
255
+ ```yaml
256
+ grpc:
257
+ port: 5678
258
+ proto_dir: 'proto'
259
+ services:
260
+ 'helloworld_services_pb':
261
+ module: 'Helloworld::Greeter'
262
+ hostname: '0.0.0.0:50051'
263
+ ```
264
+
265
+
266
+ ### Not Implemented Yet
267
+
268
+ - Sessions
269
+ - Matching
270
+
271
+ ### Configuration
272
+
273
+ First, generate ruby files from proto files. Use `grpc_tools_ruby_protoc`
274
+ present in the gem `grpc-tools`. Example:
275
+
276
+ `grpc_tools_ruby_protoc -I proto --ruby_out=proto --grpc_out=proto proto/<INPUT>.proto`
277
+
278
+ Call example in component_tests using [grpcurl](https://github.com/fullstorydev/grpcurl):
279
+
280
+ `grpcurl -plaintext -import-path component_tests/proto -proto helloworld.proto -d '{"name": "teste"}' localhost:5678 helloworld.Greeter/SayHello`
281
+
282
+ ### Using in VCR mode
252
283
 
253
284
  ## Custom controllers
254
285
 
data/Rakefile CHANGED
@@ -32,5 +32,5 @@ end
32
32
 
33
33
  task :server do
34
34
  $LOAD_PATH.unshift File.dirname('./lib/tshield.rb')
35
- exec 'bin/tshield'
35
+ Thread.new { exec 'bin/tshield' }
36
36
  end
@@ -5,14 +5,6 @@ require 'tshield/options'
5
5
  TShield::Options.init
6
6
 
7
7
  require 'tshield'
8
- tshield = Thread.new { TShield::Server.run! }
9
8
 
10
- configuration = TShield::Configuration.load_configuration
11
- (configuration.tcp_servers || []).each do |tcp_server|
12
- puts "initializing #{tcp_server['name']}"
13
- require "./servers/#{tcp_server['file']}"
14
- klass = Object.const_get(tcp_server['name'])
15
- Thread.new { klass.new.listen(tcp_server['port']) }
16
- end
17
-
18
- tshield.join
9
+ Thread.new { TShield::Grpc.run! }
10
+ TShield::Server.run!
@@ -1,4 +1,11 @@
1
1
  ---
2
+ grpc:
3
+ port: 5678
4
+ proto_dir: 'proto'
5
+ services:
6
+ 'helloworld_services_pb':
7
+ module: 'Helloworld::Greeter'
8
+ hostname: '0.0.0.0:50051'
2
9
  request:
3
10
  timeout: 10
4
11
  domains:
@@ -2,8 +2,8 @@
2
2
 
3
3
  require 'tshield/extensions/string_extensions'
4
4
  require 'tshield/options'
5
- require 'tshield/simple_tcp_server'
6
5
  require 'tshield/server'
6
+ require 'tshield/grpc'
7
7
 
8
8
  # TShield: API mocks for development and testing
9
9
  module TShield
@@ -103,6 +103,11 @@ module TShield
103
103
  session_path || '/sessions'
104
104
  end
105
105
 
106
+ def grpc
107
+ defaults = { 'port' => 5678, 'proto_dir' => 'proto', 'services' => {} }
108
+ defaults.merge(@grpc || {})
109
+ end
110
+
106
111
  def self.get_url_for_domain_by_path(path, config)
107
112
  config['paths'].select { |pattern| path =~ Regexp.new(pattern) }[0]
108
113
  end
@@ -5,6 +5,14 @@ module StringExtensions
5
5
  def to_rack_name
6
6
  "HTTP_#{upcase.tr('-', '_')}"
7
7
  end
8
+
9
+ def underscore
10
+ gsub(/::/, '/')
11
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
12
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
13
+ .tr('-', '_')
14
+ .downcase
15
+ end
8
16
  end
9
17
 
10
18
  String.include StringExtensions
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'grpc'
4
+
5
+ require 'tshield/configuration'
6
+ require 'tshield/grpc/vcr'
7
+
8
+ module TShield
9
+ module Grpc
10
+ module RequestHandler
11
+ include TShield::Grpc::VCR
12
+ def handler(method_name, request, parameters)
13
+ options = self.class.options
14
+ handler_in_vcr_mode(method_name, request, parameters, options)
15
+ end
16
+ end
17
+ def self.run!
18
+ @configuration = TShield::Configuration.singleton.grpc
19
+
20
+ lib_dir = File.join(Dir.pwd, @configuration['proto_dir'])
21
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
22
+
23
+ TShield.logger.info("loading proto files from #{lib_dir}")
24
+
25
+ bind = "0.0.0.0:#{@configuration['port']}"
26
+ TShield.logger.info("Starting gRPC server in #{bind}")
27
+
28
+ server = GRPC::RpcServer.new
29
+ server.add_http2_port(bind, :this_port_is_insecure)
30
+
31
+ services = load_services(@configuration['services'])
32
+ services.each do |class_service|
33
+ class_service.include RequestHandler
34
+ server.handle(class_service)
35
+ end
36
+
37
+ server.run_till_terminated_or_interrupted([1, 'int', 'SIGQUIT']) unless services.empty?
38
+ end
39
+
40
+ def self.load_services(services)
41
+ handlers = []
42
+ number_of_handlers = 0
43
+ services.each do |file, options|
44
+ require file
45
+
46
+ base = Object.const_get("#{options['module']}::Service")
47
+ number_of_handlers += 1
48
+
49
+ implementation = build_handler(base, base.rpc_descs, number_of_handlers, options)
50
+ handlers << implementation
51
+ end
52
+ handlers
53
+ end
54
+
55
+ def self.build_handler(base, descriptions, number_of_handlers, options)
56
+ handler = Class.new(base) do
57
+ class << self
58
+ attr_writer :options
59
+ attr_reader :options
60
+ end
61
+ descriptions.each do |service_name, description|
62
+ puts description
63
+ method_name = service_name.to_s.underscore.to_sym
64
+ define_method(method_name) do |request, parameters|
65
+ handler(__method__, request, parameters)
66
+ end
67
+ end
68
+ end
69
+ handler.options = options
70
+ TShield::Grpc.const_set "GrpcService#{number_of_handlers}", handler
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tshield/sessions'
4
+
5
+ module TShield
6
+ module Grpc
7
+ module VCR
8
+ def handler_in_vcr_mode(method_name, request, parameters, options)
9
+ parameters.peer =~ /\[(.+?)\]/
10
+ @session = TShield::Sessions.current(Regexp.last_match(1))
11
+
12
+ TShield.logger.info("grpc using session #{@session || 'default'}")
13
+ module_name = options['module']
14
+
15
+ path = create_destiny(module_name, method_name, request)
16
+ response = saved_response(path)
17
+ if response
18
+ TShield.logger.info("returning saved response for request #{request.to_json} saved into #{hexdigest(request)}")
19
+ return response
20
+ end
21
+
22
+ TShield.logger.info("calling server to get response for #{request.to_json}")
23
+ client_class = Object.const_get("#{module_name}::Stub")
24
+ client_instance = client_class.new(options['hostname'], :this_channel_is_insecure)
25
+ response = client_instance.send(method_name, request)
26
+ save_request_and_response(path, request, response)
27
+ response
28
+ end
29
+
30
+ def saved_response(path)
31
+ response_file = File.join(path, 'response')
32
+ return false unless File.exist? response_file
33
+
34
+ content = JSON.parse File.open(response_file).read
35
+ response_class = File.open(File.join(path, 'response_class')).read.strip
36
+ Kernel.const_get(response_class).new(content)
37
+ end
38
+
39
+ def save_request_and_response(path, request, response)
40
+ save_request(path, request)
41
+ save_response(path, response)
42
+ end
43
+
44
+ def save_request(path, request)
45
+ file = File.open(File.join(path, 'original_request'), 'w')
46
+ file.puts request.to_json
47
+ file.close
48
+ end
49
+
50
+ def save_response(path, response)
51
+ file = File.open(File.join(path, 'response'), 'w')
52
+ file.puts response.to_json
53
+ file.close
54
+
55
+ response_class = File.open(File.join(path, 'response_class'), 'w')
56
+ response_class.puts response.class.to_s
57
+ response_class.close
58
+ end
59
+
60
+ def complete_path(module_name, method_name, request)
61
+ @session_name = (@session || {})[:name]
62
+ path = ['requests', 'grpc', @session_name, module_name, method_name.to_s, hexdigest(request)].compact
63
+ path
64
+ end
65
+
66
+ def create_destiny(module_name, method_name, request)
67
+ current_path = []
68
+
69
+ path = complete_path(module_name, method_name, request)
70
+ TShield.logger.info("using path #{path}")
71
+ path.each do |path|
72
+ current_path << path
73
+ destiny = File.join current_path
74
+ Dir.mkdir destiny unless File.exist? destiny
75
+ end
76
+ path
77
+ end
78
+
79
+ def hexdigest(request)
80
+ Digest::SHA1.hexdigest request.to_json
81
+ end
82
+ end
83
+ end
84
+ end
@@ -17,6 +17,7 @@ module TShield
17
17
  set :views, File.join(File.dirname(__FILE__), 'views')
18
18
  set :bind, '0.0.0.0'
19
19
  set :port, TShield::Options.instance.port
20
+ set :server, :puma
20
21
 
21
22
  configure do
22
23
  enable :cross_origin
@@ -56,7 +57,6 @@ module TShield
56
57
 
57
58
  def self.run!
58
59
  register_resources
59
- require 'byebug'
60
60
  super
61
61
  end
62
62
  end
@@ -5,7 +5,7 @@ module TShield
5
5
  class Version
6
6
  MAJOR = 0
7
7
  MINOR = 11
8
- PATCH = 13
8
+ PATCH = 18
9
9
  PRE = 0
10
10
 
11
11
  class << self
@@ -15,9 +15,4 @@ require 'webmock/rspec'
15
15
  require 'tshield/extensions/string_extensions'
16
16
 
17
17
  RSpec.configure do |config|
18
- config.before(:each) do
19
- allow(File).to receive(:join).and_return(
20
- 'spec/tshield/fixtures/config/tshield.yml'
21
- )
22
- end
23
18
  end
@@ -35,6 +35,12 @@ describe TShield::Configuration do
35
35
  )
36
36
  end
37
37
 
38
+ context 'on grpc configuration' do
39
+ it 'recover server port' do
40
+ expect(@configuration.grpc['port']).to(eq(5678))
41
+ end
42
+ end
43
+
38
44
  context 'on load filters' do
39
45
  it 'recover filters for a domain' do
40
46
  expect(@configuration.get_filters('example.org')).to eq([ExampleFilter])
@@ -72,4 +78,17 @@ describe TShield::Configuration do
72
78
  expect { TShield::Configuration.singleton }.to raise_error RuntimeError
73
79
  end
74
80
  end
81
+
82
+ context 'on config exists without grpc entry' do
83
+ before :each do
84
+ options_instance = double
85
+ allow(options_instance).to receive(:configuration_file)
86
+ .and_return('spec/tshield/fixtures/config/tshield-without-grpc.yml')
87
+ allow(TShield::Options).to receive(:instance).and_return(options_instance)
88
+ @configuration = TShield::Configuration.singleton
89
+ end
90
+ it 'should set default value for port' do
91
+ expect(@configuration.grpc).to eql('port' => 5678, 'proto_dir' => 'proto', 'services' => {})
92
+ end
93
+ end
75
94
  end
@@ -0,0 +1,17 @@
1
+ ---
2
+ request:
3
+ timeout: 0
4
+ domains:
5
+ 'example.org':
6
+ name: 'example.org'
7
+ filters:
8
+ - 'ExampleFilter'
9
+ paths:
10
+ - '/api/one'
11
+ - '/api/two'
12
+ skip_query_params:
13
+ - 'a'
14
+ 'example.com':
15
+ name: 'example.com'
16
+ paths:
17
+ - '/api/three'
@@ -1,4 +1,6 @@
1
1
  ---
2
+ grpc:
3
+ port: 5678
2
4
  request:
3
5
  timeout: 0
4
6
  domains:
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestServices
4
+ class Service
5
+ def self.rpc_descs
6
+ { 'ServiceMethod' => {} }
7
+ end
8
+ end
9
+
10
+ class Stub
11
+ def initialize(attributes, options = {}); end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'tshield/grpc'
6
+
7
+ describe TShield::Grpc do
8
+ context 'on load services' do
9
+ before :each do
10
+ lib_dir = File.join(__dir__, 'fixtures/proto')
11
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
12
+
13
+ @services = {
14
+ 'test_services_pb' => { 'module' => 'TestServices', 'hostname' => '0.0.0.0:5678' }
15
+ }
16
+ end
17
+
18
+ it 'should implement a service from options' do
19
+ implementation = TShield::Grpc.load_services(@services).first
20
+ instance = implementation.new
21
+ expect(instance.respond_to?(:service_method)).to be_truthy
22
+ end
23
+ end
24
+ end
@@ -25,8 +25,11 @@ Gem::Specification.new do |s|
25
25
  s.required_ruby_version = '>= 2.3'
26
26
 
27
27
  s.add_dependency('byebug', '~> 11.0', '>= 11.0.1')
28
+ s.add_dependency('grpc', '~> 1.28', '>= 1.28.0')
29
+ s.add_dependency('grpc-tools', '~> 1.28', '>= 1.28.0')
28
30
  s.add_dependency('httparty', '~> 0.14', '>= 0.14.0')
29
31
  s.add_dependency('json', '~> 2.0', '>= 2.0')
32
+ s.add_dependency('puma', '~> 4.3', '>= 4.3.3')
30
33
  s.add_dependency('sinatra', '~> 1.4', '>= 1.4.0')
31
34
  s.add_dependency('sinatra-cross_origin', '~> 0.4.0', '>= 0.4')
32
35
  s.add_development_dependency('coveralls')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tshield
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.13.0
4
+ version: 0.11.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Diego Rubin
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-04-03 00:00:00.000000000 Z
12
+ date: 2020-07-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: byebug
@@ -31,6 +31,46 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 11.0.1
34
+ - !ruby/object:Gem::Dependency
35
+ name: grpc
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.28.0
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.28'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 1.28.0
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.28'
54
+ - !ruby/object:Gem::Dependency
55
+ name: grpc-tools
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.28.0
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '1.28'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 1.28.0
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.28'
34
74
  - !ruby/object:Gem::Dependency
35
75
  name: httparty
36
76
  requirement: !ruby/object:Gem::Requirement
@@ -71,6 +111,26 @@ dependencies:
71
111
  - - "~>"
72
112
  - !ruby/object:Gem::Version
73
113
  version: '2.0'
114
+ - !ruby/object:Gem::Dependency
115
+ name: puma
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: '4.3'
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 4.3.3
124
+ type: :runtime
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '4.3'
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: 4.3.3
74
134
  - !ruby/object:Gem::Dependency
75
135
  name: sinatra
76
136
  requirement: !ruby/object:Gem::Requirement
@@ -367,6 +427,8 @@ files:
367
427
  - lib/tshield/controllers/sessions.rb
368
428
  - lib/tshield/counter.rb
369
429
  - lib/tshield/extensions/string_extensions.rb
430
+ - lib/tshield/grpc.rb
431
+ - lib/tshield/grpc/vcr.rb
370
432
  - lib/tshield/logger.rb
371
433
  - lib/tshield/matching/filters.rb
372
434
  - lib/tshield/options.rb
@@ -376,15 +438,17 @@ files:
376
438
  - lib/tshield/response.rb
377
439
  - lib/tshield/server.rb
378
440
  - lib/tshield/sessions.rb
379
- - lib/tshield/simple_tcp_server.rb
380
441
  - lib/tshield/version.rb
381
442
  - spec/spec_helper.rb
382
443
  - spec/tshield/after_filter_spec.rb
383
444
  - spec/tshield/configuration_spec.rb
384
445
  - spec/tshield/controllers/requests_spec.rb
446
+ - spec/tshield/fixtures/config/tshield-without-grpc.yml
385
447
  - spec/tshield/fixtures/config/tshield.yml
386
448
  - spec/tshield/fixtures/filters/example_filter.rb
387
449
  - spec/tshield/fixtures/matching/example.json
450
+ - spec/tshield/fixtures/proto/test_services_pb.rb
451
+ - spec/tshield/grpc_spec.rb
388
452
  - spec/tshield/options_spec.rb
389
453
  - spec/tshield/request_matching_spec.rb
390
454
  - spec/tshield/request_vcr_spec.rb
@@ -415,11 +479,14 @@ summary: Proxy for mocks API responses
415
479
  test_files:
416
480
  - spec/spec_helper.rb
417
481
  - spec/tshield/request_matching_spec.rb
482
+ - spec/tshield/grpc_spec.rb
418
483
  - spec/tshield/configuration_spec.rb
419
484
  - spec/tshield/request_vcr_spec.rb
420
485
  - spec/tshield/controllers/requests_spec.rb
421
486
  - spec/tshield/options_spec.rb
422
487
  - spec/tshield/after_filter_spec.rb
423
488
  - spec/tshield/fixtures/matching/example.json
489
+ - spec/tshield/fixtures/proto/test_services_pb.rb
424
490
  - spec/tshield/fixtures/filters/example_filter.rb
425
491
  - spec/tshield/fixtures/config/tshield.yml
492
+ - spec/tshield/fixtures/config/tshield-without-grpc.yml
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'socket'
4
-
5
- module TShield
6
- class SimpleTCPServer
7
- def initialize
8
- @running = true
9
- end
10
-
11
- def on_connect(_client)
12
- raise 'should implement method on_connect'
13
- end
14
-
15
- def close
16
- @running = false
17
- end
18
-
19
- def listen(port)
20
- puts "listening #{port}"
21
- @server = TCPServer.new(port)
22
- while @running
23
- client = @server.accept
24
- on_connect(client)
25
- end
26
- end
27
- end
28
- end