masyo 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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