masyo 0.0.1 → 0.0.2

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
+ SHA1:
3
+ metadata.gz: ae12169befcce209c36fdc9159fd4a40a68dbc63
4
+ data.tar.gz: 2245f2c03c5ed7aaadb8aafa6deb3d53a829d469
5
+ SHA512:
6
+ metadata.gz: b29e9d59053d5460a1ed347896ceb8c12e3b9aaa6cdeebcab3806620b7b450b1f1c477aac22b8acd6cf8b5b4c4a7caa57467e9f60190ef2a945391e719e1c4e1
7
+ data.tar.gz: bf8c1af6efbceec09099b08096616b4ac7ba7ec278219c56854160ed7abeac5cb5f25667bdc450d6eaed1a78af7ad505bb8dce5e0571d0e0ab684bc0261ad208
data/README.md CHANGED
@@ -3,10 +3,14 @@
3
3
  ## Description
4
4
  Casual TCP Proxy
5
5
 
6
+ ## Install
7
+
8
+ ```sh
9
+ $ gem install masyo
10
+ ```
11
+
6
12
  ## Usage
7
13
 
8
14
  ```sh
9
- $ cd /path-to-masyo
10
- $ bundle install --path=vendor/bundles
11
- $ bundle exec ruby bin/masyo -listen_port #{LISTEN_PORT} --server_host #{PROXY_TARGET_HOST} --server_port #{PROXY_TARGET_PORT}
15
+ $ masyo -h
12
16
  ```
data/bin/masyo CHANGED
@@ -5,10 +5,10 @@ require "slop"
5
5
  argv = ARGV.dup
6
6
  slop = Slop.new(:strict => true, :help => true)
7
7
  slop.banner "$ bundle exec ruby bin/masyo [options]\n"
8
- slop.on :b, :buffer_size=, "buffer byte size(default is 0)"
9
8
  slop.on :listen_port=, "listen port (default is 2000)"
10
9
  slop.on :server_host=, "target server host (default is \"0.0.0.0\")"
11
10
  slop.on :server_port=, "target server port (default is 24224)"
11
+ slop.on :v, :version, "show masyo version"
12
12
 
13
13
  begin
14
14
  slop.parse!(argv)
@@ -16,18 +16,25 @@ rescue => e
16
16
  puts e
17
17
  exit!
18
18
  end
19
+
19
20
  options = slop.to_hash
21
+
20
22
  unless options[:help]
21
23
  options.delete(:help)
22
24
  options[:listen_port] = (options[:listen_port] || 2000).to_i
23
25
  options[:server_host] ||= "0.0.0.0"
24
26
  options[:server_port] = (options[:server_port] || 24224).to_i
25
- options[:buffer_size] = (options[:buffer_size] || 0).to_i
26
27
 
27
28
  root = File.expand_path("../..", __FILE__)
28
29
  $LOAD_PATH.unshift root
29
30
  $LOAD_PATH.unshift File.join(root, 'lib')
30
31
 
31
32
  require "masyo"
33
+
34
+ if options[:version]
35
+ puts Masyo::VERSION
36
+ exit
37
+ end
38
+
32
39
  Masyo.run options
33
40
  end
@@ -4,38 +4,15 @@ require 'logger'
4
4
  require 'forwardable'
5
5
 
6
6
  require 'masyo/version'
7
- require 'masyo/server'
8
- require 'masyo/client'
9
- require 'masyo/buffer'
7
+ require 'masyo/proxy'
10
8
  require 'extentions/tcp_socket'
11
9
 
12
10
  module Masyo
13
11
  extend self
14
12
 
15
13
  def run(opts = {})
16
- opts = {
17
- listen_port: 2000,
18
- server_host: "0.0.0.0",
19
- server_port: 24224 ,
20
- buffer_size: 0
21
- }.merge(opts)
22
14
  Thread.abort_on_exception = true
23
-
24
- buffer = Buffer.new opts[:buffer_size]
25
- client = Client.new(opts[:server_host], opts[:server_port], buffer)
26
- Server.open(opts[:listen_port]) do |server|
27
- logger.info "listen #{opts[:listen_port]} port."
28
-
29
- server.on_read do |msg|
30
- client.post msg
31
- end
32
-
33
- server.on_close {
34
- client.post buffer.take!
35
- }
36
-
37
- server.start
38
- end
15
+ Proxy.run(opts[:listen_port], opts[:server_host], opts[:server_port])
39
16
  end
40
17
 
41
18
  def logger
@@ -0,0 +1,83 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'socket'
4
+
5
+ module Masyo
6
+ class Proxy
7
+ attr_accessor :proxy
8
+ TIMEOUT = 3
9
+ MAX_RECV_LEN = 1 * 1024 * 1024 * 1024
10
+
11
+ class << self
12
+ def run(listen_port, server_host, server_port)
13
+ p = new(listen_port, server_host, server_port)
14
+ p.run
15
+ rescue Interrupt
16
+ Masyo.logger.info "Stopping..."
17
+ # for sending buffer to receiver before socket close.
18
+ p.stop
19
+ end
20
+ end
21
+
22
+ def initialize(listen_port, server_host, server_port)
23
+ Masyo.logger.info "Proxy 0.0.0.0:#{listen_port} -> #{server_host}:#{server_port}"
24
+
25
+ @server_host = server_host
26
+ @server_port = server_port
27
+ @proxy = ::TCPServer.new(listen_port)
28
+ end
29
+
30
+ def run
31
+ loop {
32
+ break if proxy.closed?
33
+
34
+ Thread.start(proxy.accept) do |client|
35
+ handle client
36
+ end
37
+ }
38
+ end
39
+
40
+ def stop
41
+ proxy.close unless proxy.closed?
42
+ end
43
+
44
+ def handle client
45
+ input = receive_from client
46
+ response = request input
47
+ client.write response unless response.nil?
48
+ ensure
49
+ client.close unless client.closed?
50
+ end
51
+
52
+ def request msg
53
+ ::TCPSocket.open(@server_host, @server_port) { |socket|
54
+ socket.write msg
55
+
56
+ receive_from socket
57
+ }
58
+ rescue Errno::ECONNREFUSED
59
+ Masyo.logger.error "Fail to request to server"
60
+ nil
61
+ end
62
+
63
+ private
64
+
65
+ def receive_from socket
66
+ loop {
67
+ begin
68
+ s = socket.recv_nonblock(MAX_RECV_LEN)
69
+ rescue ::IO::WaitReadable
70
+ if ::IO.select([ socket ], nil, nil, TIMEOUT)
71
+ retry
72
+ else
73
+ # timeout!
74
+ break
75
+ end
76
+ else
77
+ break if !s || s == "" || s == "quit"
78
+ return s
79
+ end
80
+ }
81
+ end
82
+ end
83
+ end
@@ -1,3 +1,3 @@
1
1
  module Masyo
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Masyo::Proxy do
6
+ describe 'request' do
7
+ context 'when fail to connect to server' do
8
+ before do
9
+ ::TCPSocket.stub(:open).and_raise(Errno::ECONNREFUSED)
10
+ ::TCPServer.stub(:new)
11
+ end
12
+
13
+ let(:generate_proxy) do
14
+ proc { described_class.new(7777, "0.0.0.0", 7779) }
15
+ end
16
+
17
+ it 'log error' do
18
+ Masyo.logger.should_receive(:error).with("Fail to request to server")
19
+ generate_proxy.call.request('msg')
20
+ end
21
+ it 'return nil' do
22
+ expect(generate_proxy.call.request('msg')).to eq(nil)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -5,19 +5,12 @@ require 'spec_helper'
5
5
  describe Masyo do
6
6
  describe "#run" do
7
7
  before :each do
8
- Masyo::Buffer.stub(:new)
9
- Masyo::Client.stub(:new)
10
- Masyo::Server.stub(:open)
11
- end
12
-
13
- it "should set Thread.abort_on_exception = true" do
14
- Thread.should_receive(:abort_on_exception=).with(true)
15
- described_class.run
8
+ Masyo::Proxy.stub(:run)
16
9
  end
17
10
 
18
11
  it "should call Server.open with port" do
19
- Masyo::Server.should_receive(:open).with(kind_of(Numeric))
20
- described_class.run
12
+ Masyo::Proxy.should_receive(:run).with(7777, "0.0.0.0", 7779)
13
+ described_class.run(listen_port: 7777, server_host: "0.0.0.0", server_port: 7779)
21
14
  end
22
15
  end
23
16
 
metadata CHANGED
@@ -1,30 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: masyo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 0.0.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Takatoshi Matsumoto
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-12-06 00:00:00.000000000 Z
11
+ date: 2013-09-05 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: slop
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  description: simple tcp proxy server written in ruby
@@ -46,49 +43,40 @@ files:
46
43
  - bin/masyo
47
44
  - lib/extentions/tcp_socket.rb
48
45
  - lib/masyo.rb
49
- - lib/masyo/buffer.rb
50
- - lib/masyo/client.rb
51
- - lib/masyo/event.rb
52
- - lib/masyo/server.rb
46
+ - lib/masyo/proxy.rb
53
47
  - lib/masyo/version.rb
54
48
  - masyo.gemspec
55
49
  - sample.god
56
- - spec/masyo/buffer_spec.rb
57
- - spec/masyo/client_spec.rb
58
- - spec/masyo/event_spec.rb
59
- - spec/masyo/server_spec.rb
50
+ - spec/masyo/proxy_spec.rb
60
51
  - spec/masyo_spec.rb
61
52
  - spec/spec_helper.rb
62
53
  - tmp/.gitkeep
63
54
  - vendor/bundles/.gitkeep
64
55
  homepage: https://github.com/ToQoz/Masyo
65
56
  licenses: []
57
+ metadata: {}
66
58
  post_install_message:
67
59
  rdoc_options: []
68
60
  require_paths:
69
61
  - lib
70
62
  required_ruby_version: !ruby/object:Gem::Requirement
71
- none: false
72
63
  requirements:
73
- - - ! '>='
64
+ - - '>='
74
65
  - !ruby/object:Gem::Version
75
66
  version: '0'
76
67
  required_rubygems_version: !ruby/object:Gem::Requirement
77
- none: false
78
68
  requirements:
79
- - - ! '>='
69
+ - - '>='
80
70
  - !ruby/object:Gem::Version
81
71
  version: '0'
82
72
  requirements: []
83
73
  rubyforge_project:
84
- rubygems_version: 1.8.23
74
+ rubygems_version: 2.0.2
85
75
  signing_key:
86
- specification_version: 3
76
+ specification_version: 4
87
77
  summary: masyo
88
78
  test_files:
89
- - spec/masyo/buffer_spec.rb
90
- - spec/masyo/client_spec.rb
91
- - spec/masyo/event_spec.rb
92
- - spec/masyo/server_spec.rb
79
+ - spec/masyo/proxy_spec.rb
93
80
  - spec/masyo_spec.rb
94
81
  - spec/spec_helper.rb
82
+ has_rdoc:
@@ -1,33 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- module Masyo
4
- class BufferOverflowException < StandardError; end
5
-
6
- class Buffer
7
- attr_accessor :maxlen, :buffer
8
-
9
- def initialize(maxlen = 0)
10
- @maxlen = maxlen
11
- @buffer = ""
12
- end
13
-
14
- def take!
15
- b = buffer
16
- clear!
17
- b
18
- end
19
-
20
- def push(str)
21
- if buffer.bytesize + str.bytesize <= maxlen
22
- self.buffer += str
23
- else
24
- raise BufferOverflowException
25
- end
26
- end
27
- alias_method :<<, :push
28
-
29
- def clear!
30
- self.buffer = ""
31
- end
32
- end
33
- end
@@ -1,50 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'socket'
4
-
5
- module Masyo
6
- class Client
7
- def initialize(host, port, buffer)
8
- @host = host
9
- @port = port
10
- @buffer = buffer
11
-
12
- if buffer.maxlen > 0
13
- extend BufferedClient
14
- else
15
- extend PlainClient
16
- end
17
- end
18
-
19
- extend ::Forwardable
20
- def_delegators :Masyo, :logger
21
- end
22
-
23
- module PlainClient
24
- def post(msg)
25
- ::TCPSocket.open(@host, @port) { |socket|
26
- socket.write msg
27
- }
28
- end
29
- end
30
-
31
- module BufferedClient
32
- def post(msg)
33
- ::TCPSocket.open(@host, @port) { |socket|
34
- begin
35
- buffer << msg
36
- rescue BufferOverflowException
37
- # clear buffer
38
- socket.write buffer.take!
39
- begin
40
- buffer << msg
41
- rescue BufferOverflowException
42
- # post without using buffer
43
- #
44
- socket.write msg
45
- end
46
- end
47
- }
48
- end
49
- end
50
- end
@@ -1,29 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- module Masyo
4
- module Event
5
-
6
- def self.included(base)
7
- base.event_types.each do |e|
8
- base.class_eval {
9
- define_method("on_#{e}") do |&handler|
10
- events[e] ||= []
11
- events[e] << handler
12
- end
13
- }
14
- end
15
- end
16
-
17
- def events
18
- @events ||= {}
19
- end
20
-
21
- def trigger_event(name, *arg)
22
- if events[name]
23
- events[name].each do |f|
24
- f.call *arg
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,73 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'socket'
4
- require 'masyo/event'
5
-
6
- module Masyo
7
- class Server
8
- attr_accessor :tcp_server, :socket_to_client
9
- TO_CLIENT_SOCKET_TIMEOUT = 3
10
-
11
- class << self
12
- def event_types
13
- [ :read, :close ]
14
- end
15
-
16
- def open(*args)
17
- raise ArgumentError, "wrong number of arguments (#{args.size} for 1..2)" if args.size <= 0 || args.size >= 2
18
-
19
- server = new(args.first)
20
- return server unless block_given?
21
- begin
22
- yield server
23
- ensure
24
- # for sending buffer to server before socket close.
25
- server.trigger_event :close
26
- server.close unless server.closed?
27
- end
28
- end
29
- end
30
-
31
- include Event
32
-
33
- def initialize(port)
34
- raise ArgumentError, "#{port} is not a Integer" unless port.is_a? Integer
35
- @tcp_server = ::TCPServer.new(port)
36
- end
37
-
38
- def start
39
- loop {
40
- Thread.start(tcp_server.accept) do |to_client|
41
- handle_request to_client
42
- end
43
- }
44
- end
45
-
46
- def handle_request(to_client)
47
- begin
48
- loop {
49
- begin
50
- input = to_client.recv_nonblock(2048)
51
- rescue ::IO::WaitReadable
52
- if ::IO.select([ to_client ], nil, nil, TO_CLIENT_SOCKET_TIMEOUT)
53
- retry
54
- else
55
- # timeout!
56
- break
57
- end
58
- else
59
- break if !input || input == "" || input == "quit"
60
-
61
- trigger_event :read, input
62
- end
63
- }
64
- ensure
65
- to_client.close_immediately unless to_client.closed?
66
- end
67
- end
68
-
69
- extend ::Forwardable
70
- def_delegators :tcp_server, :close, :closed?
71
- def_delegators :Masyo, :logger
72
- end
73
- end
@@ -1,54 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'spec_helper'
4
-
5
- describe Masyo::Buffer do
6
- let (:instance) { described_class.new 100 }
7
-
8
- describe '#initialize' do
9
- context 'when given a number for args' do
10
- it 'should set it to maxlen' do
11
- described_class.new(100).maxlen.should eq(100)
12
- end
13
- end
14
- context 'when given none for args' do
15
- it 'should set 0 to maxlen' do
16
- described_class.new.maxlen.should eq(0)
17
- end
18
- end
19
- end
20
-
21
- describe '#clear!' do
22
- it 'should clear buffer' do
23
- instance.buffer = "toqoz.should be_cool."
24
- instance.clear!
25
- instance.buffer.should eq("")
26
-
27
- end
28
- end
29
-
30
- describe '#take!' do
31
- it 'should return buffer and clear buffer' do
32
- instance.buffer = "toqoz.should be_cool."
33
- instance.take!.should eq("toqoz.should be_cool.")
34
- instance.buffer.should eq("")
35
- end
36
- end
37
-
38
- describe '#push' do
39
- context 'when given string `don\'t` over buffer' do
40
- it 'should add string as buffer' do
41
- str = Array.new(100, "a").join("")
42
- instance.push str
43
- instance.buffer.should eq(str)
44
- end
45
- end
46
- context 'when given string over buffer' do
47
- it 'raise error' do
48
- proc {
49
- instance.push Array.new(101, "a").join("")
50
- }.should raise_error(Masyo::BufferOverflowException)
51
- end
52
- end
53
- end
54
- end
@@ -1,36 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'spec_helper'
4
-
5
- describe Masyo::Client do
6
- describe '#initialize' do
7
- let(:buffered_client) {
8
- buffer = double(maxlen: 100)
9
- described_class.new("0.0.0.0", 8080, buffer)
10
- }
11
- let(:plain_client) {
12
- buffer = double(maxlen: 0)
13
- described_class.new("0.0.0.0", 8080, buffer)
14
- }
15
-
16
- context 'when buffer_size > 0' do
17
- it 'should use BufferedClient#post as #post' do
18
- buffered_client.method("post").owner.should eq(Masyo::BufferedClient)
19
- end
20
- end
21
-
22
- context 'when buffer_size == 0' do
23
- it 'should use PlainClient#post as #post' do
24
- plain_client.method("post").owner.should eq(Masyo::PlainClient)
25
- end
26
- end
27
- end
28
-
29
- describe '#post' do
30
- let (:instance) { described_class.new("0.0.0.0", 8212, double(maxlen: 10)) }
31
- it 'should call TCPSocket.open' do
32
- TCPSocket.should_receive(:open).with(kind_of(String), kind_of(Numeric))
33
- instance.post("dummy message")
34
- end
35
- end
36
- end
@@ -1,65 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'spec_helper'
4
-
5
- describe Masyo::Event do
6
- let(:instance) do
7
- mod = described_class
8
- Class.new {
9
- class << self
10
- def event_types
11
- [ :xxx ]
12
- end
13
- end
14
-
15
- include mod
16
- }.new
17
- end
18
-
19
- describe '.included' do
20
- context 'when included' do
21
- it 'define methods for event_types(return by base class methods)' do
22
- instance.respond_to?(:on_xxx).should be_true
23
- end
24
- end
25
- end
26
-
27
- describe '#on_*' do
28
- describe '#on_xxx' do
29
- it 'should store given block as event callback' do
30
- callback = proc {}
31
- instance.on_xxx(&callback)
32
- instance.events.should eq({ xxx: [ callback ] })
33
- end
34
- end
35
- context '#on_invalid_event' do
36
- it 'should `not` store given block as event callback' do
37
- proc {
38
- callback = proc {}
39
- instance.on_invalid_event(&callback)
40
- }.should raise_error(NoMethodError)
41
-
42
- instance.events.should eq({})
43
- end
44
- end
45
- end
46
-
47
- describe '#trigger_event' do
48
- context 'when given exist event anme' do
49
- it 'should trigger all event for #{name}(first args)' do
50
- callback = proc {}
51
- callback.should_receive(:call)
52
- instance.on_xxx(&callback)
53
- instance.trigger_event :xxx
54
- end
55
- end
56
- context 'when given `not` exist event anme' do
57
- it 'should not trigger all event for #{name}(first args)' do
58
- callback = proc {}
59
- callback.should_not_receive(:call)
60
- instance.on_invalid_event(&callback) rescue
61
- instance.trigger_event :invalid_event
62
- end
63
- end
64
- end
65
- end
@@ -1,87 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'spec_helper'
4
-
5
- describe Masyo::Server do
6
- let (:instance) { described_class.new(8183) }
7
- let (:logger) { double(info: true, error: true) }
8
-
9
- after(:each) do
10
- instance.close
11
- end
12
-
13
- describe '.open' do
14
- context 'when given over 2 args' do
15
- it 'should recieve ArgumentError' do
16
- proc { described_class.open(8183, "hoge", "foo") }.should raise_error(ArgumentError)
17
- end
18
- end
19
- context 'when given non-number value as first arg' do
20
- it 'should recieve ArgumentError' do
21
- proc { described_class.open("9090") }.should raise_error(ArgumentError)
22
- end
23
- end
24
- end
25
-
26
- describe '#initialize' do
27
- it 'store ::TCPServer as instance variable' do
28
- instance.tcp_server.is_a?(::TCPServer).should be_true
29
- end
30
- end
31
-
32
- describe '#handle_request' do
33
- let (:socket) { double(close_immediately: nil, closed?: false) }
34
-
35
- context 'when BasicSocket#recv_nonblock raise IO::WaitReadable' do
36
- before :each do
37
- socket.stub(:recv_nonblock).and_raise(StandardError.new.extend(IO::WaitReadable))
38
- end
39
-
40
- it 'should call IO.select.' do
41
- IO.should_receive(:select).
42
- with([ socket ], nil, nil, described_class::TO_CLIENT_SOCKET_TIMEOUT).
43
- and_return(false)
44
- instance.handle_request(socket)
45
- end
46
- end
47
-
48
- context "when BasicSocket#recv_nonblock don't raise IO::WaitReadable" do
49
- let (:input) { "dummy_input" }
50
-
51
- before :each do
52
- socket.stub(:recv_nonblock).and_return(input, "")
53
- end
54
-
55
- context 'and when input is not false, nil, "", "quit"' do
56
- it 'should call trigger_event with :read as first arg, and input as second arg' do
57
- instance.should_receive(:trigger_event).with(:read, input)
58
- proc {
59
- instance.handle_request(socket)
60
- }.should_not raise_error
61
- end
62
- end
63
- end
64
-
65
- context 'when raise Exception except IO::WaitReadable' do
66
- it 'should close socket and not catch Exceptions.' do
67
- socket.stub(:recv_nonblock).and_raise(StandardError)
68
- socket.should_receive(:close_immediately)
69
-
70
- proc {
71
- instance.handle_request(socket)
72
- }.should raise_error(StandardError)
73
- end
74
- end
75
- end
76
- end
77
-
78
- describe ::TCPServer do
79
- describe '#close_immediately' do
80
- it 'should receive setsockopt with relavant options and close' do
81
- socket = described_class.new(8183)
82
- socket.should_receive(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_LINGER, [1,0].pack('ii'))
83
- socket.should_receive(:close)
84
- socket.close_immediately
85
- end
86
- end
87
- end