gqtp 1.0.5 → 1.0.6
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 +7 -0
- data/README.md +6 -6
- data/bin/gqtp-proxy +35 -24
- data/doc/text/news.md +18 -0
- data/lib/gqtp/{connection → backend}/coolio.rb +10 -10
- data/lib/gqtp/backend/eventmachine.rb +134 -0
- data/lib/gqtp/{connection → backend}/synchronous.rb +14 -10
- data/lib/gqtp/{connection → backend}/thread.rb +14 -10
- data/lib/gqtp/client.rb +18 -20
- data/lib/gqtp/error.rb +13 -2
- data/lib/gqtp/proxy.rb +26 -23
- data/lib/gqtp/server.rb +15 -14
- data/lib/gqtp/version.rb +2 -2
- data/test/test-client.rb +77 -60
- metadata +25 -41
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a9646329d91fbafd50713cb36cd0a29805f955fc
|
4
|
+
data.tar.gz: d4a1e5c555ee1463d29a4817d5232e05122b20fb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4e97a85e004db92b0d58a9cb624ede027b1ba44d50547526bbec6e0605a96d721b04ad993129ec827959cc050fdad6723ffa0cafedf350cc70c1c1f4fef51646
|
7
|
+
data.tar.gz: 3e6daac1d896ff3ca752bb55a479b6ec1aa7dd4603bc3a28745cd86bdedca95a988da795683e5947edb556326e24d5c5bb8663a4c904d5e0368376b55227549d
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ for high concurrency use.
|
|
22
22
|
|
23
23
|
### Client
|
24
24
|
|
25
|
-
client = GQTP::Client.new(:
|
25
|
+
client = GQTP::Client.new(:host => "192.168.0.1", :port => 10043)
|
26
26
|
request = client.send("status") do |header, body|
|
27
27
|
p body # => "{\"alloc_count\":163,...}"
|
28
28
|
end
|
@@ -30,7 +30,7 @@ for high concurrency use.
|
|
30
30
|
|
31
31
|
### Server
|
32
32
|
|
33
|
-
server = GQTP::Server.new(:
|
33
|
+
server = GQTP::Server.new(:host => "192.168.0.1", :port => 10043)
|
34
34
|
server.on_request do |request, client|
|
35
35
|
body = "{\"alloc_count\":163,...}"
|
36
36
|
header = GQTP::Header.new
|
@@ -45,10 +45,10 @@ for high concurrency use.
|
|
45
45
|
|
46
46
|
### Proxy
|
47
47
|
|
48
|
-
proxy = GQTP::Proxy.new(:
|
49
|
-
:listen_port =>
|
50
|
-
:
|
51
|
-
:upstream_port =>
|
48
|
+
proxy = GQTP::Proxy.new(:listen_host => "127.0.0.1",
|
49
|
+
:listen_port => 10043,
|
50
|
+
:upstream_host => "192.168.0.1",
|
51
|
+
:upstream_port => 10043)
|
52
52
|
proxy.run.wait
|
53
53
|
|
54
54
|
## Dependencies
|
data/bin/gqtp-proxy
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
1
|
+
# -*- coding: utf-8; mode: ruby -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
|
3
|
+
# Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
#
|
5
5
|
# This library is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -22,39 +22,40 @@ require "ostruct"
|
|
22
22
|
require "gqtp"
|
23
23
|
|
24
24
|
options = OpenStruct.new
|
25
|
-
options.
|
26
|
-
options.listen_port =
|
27
|
-
options.
|
28
|
-
options.upstream_port =
|
25
|
+
options.listen_host = "0.0.0.0"
|
26
|
+
options.listen_port = 10043
|
27
|
+
options.upstream_host = nil
|
28
|
+
options.upstream_port = 10043
|
29
29
|
options.backend = :thread
|
30
30
|
|
31
31
|
parser = OptionParser.new
|
32
|
-
parser.on("--listen-
|
33
|
-
"IP
|
34
|
-
"(#{options.
|
35
|
-
options.
|
32
|
+
parser.on("--listen-host=HOST",
|
33
|
+
"IP host or host name to listen",
|
34
|
+
"(#{options.listen_host})") do |host|
|
35
|
+
options.listen_host = host
|
36
36
|
end
|
37
37
|
parser.on("--listen-port=PORT", Integer,
|
38
38
|
"Port number to listen",
|
39
39
|
"(#{options.listen_port})") do |port|
|
40
40
|
options.listen_port = port
|
41
41
|
end
|
42
|
-
parser.on("--upstream-
|
43
|
-
"IP
|
44
|
-
"(#{options.
|
45
|
-
options.
|
42
|
+
parser.on("--upstream-host=HOST",
|
43
|
+
"IP host or host name of upstream",
|
44
|
+
"(#{options.upstream_host})") do |host|
|
45
|
+
options.upstream_host = host
|
46
46
|
end
|
47
47
|
parser.on("--upstream-port=PORT", Integer,
|
48
48
|
"Port number of upstream",
|
49
49
|
"(#{options.upstream_port})") do |port|
|
50
50
|
options.upstream_port = port
|
51
51
|
end
|
52
|
-
available_backends = [
|
53
|
-
|
52
|
+
available_backends = [:thread, :synchronous, :coolio, :eventmachine]
|
53
|
+
available_backends_label = available_backends.join(", ")
|
54
|
+
parser.on("--backend=BACKEND", available_backends,
|
54
55
|
"Use BACKEND for connection",
|
55
|
-
"[#{
|
56
|
+
"[#{available_backends_label}]",
|
56
57
|
"(#{options.backend})") do |backend|
|
57
|
-
options.backend = backend
|
58
|
+
options.backend = backend
|
58
59
|
end
|
59
60
|
|
60
61
|
begin
|
@@ -64,14 +65,24 @@ rescue OptionParser::ParseError
|
|
64
65
|
exit(false)
|
65
66
|
end
|
66
67
|
|
67
|
-
if options.
|
68
|
-
puts("--upstream-
|
68
|
+
if options.upstream_host.nil?
|
69
|
+
puts("--upstream-host is required.")
|
69
70
|
exit(false)
|
70
71
|
end
|
71
72
|
|
72
|
-
proxy = GQTP::Proxy.new(:
|
73
|
+
proxy = GQTP::Proxy.new(:listen_host => options.listen_host,
|
73
74
|
:listen_port => options.listen_port,
|
74
|
-
:
|
75
|
+
:upstream_host => options.upstream_host,
|
75
76
|
:upstream_port => options.upstream_port,
|
76
|
-
:
|
77
|
-
|
77
|
+
:backend => options.backend)
|
78
|
+
begin
|
79
|
+
if options.backend == :eventmachine
|
80
|
+
require "eventmachine"
|
81
|
+
EventMachine.run do
|
82
|
+
proxy.run
|
83
|
+
end
|
84
|
+
else
|
85
|
+
proxy.run.wait
|
86
|
+
end
|
87
|
+
rescue Interrupt
|
88
|
+
end
|
data/doc/text/news.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# News
|
2
2
|
|
3
|
+
## 1.0.6: 2014-03-25
|
4
|
+
|
5
|
+
### Improvements
|
6
|
+
|
7
|
+
* Added EventMachine backend.
|
8
|
+
|
9
|
+
### Changes
|
10
|
+
|
11
|
+
* Changed the default port number to 10043 from 10041 because GQTP
|
12
|
+
server packages use 10043.
|
13
|
+
* Changed error class when unknown connection type is specified to
|
14
|
+
`GQTP::Client.new` to `ArgumentError` from `RuntimeError`.
|
15
|
+
* Wrapped internal error to `GQTP::ConnectionError`.
|
16
|
+
* Changed `:address` keyword to `:host` in `GQTP::Client.new`.
|
17
|
+
`:address` is still usable but it is deprecated.
|
18
|
+
* Changed connection backend module/directory name to `backend` from
|
19
|
+
`connection`.
|
20
|
+
|
3
21
|
## 1.0.5: 2013-09-18
|
4
22
|
|
5
23
|
### Improvements
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
|
3
|
+
# Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
#
|
5
5
|
# This library is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -19,7 +19,7 @@
|
|
19
19
|
require "cool.io"
|
20
20
|
|
21
21
|
module GQTP
|
22
|
-
module
|
22
|
+
module Backend
|
23
23
|
module Coolio
|
24
24
|
class Request
|
25
25
|
def initialize(loop)
|
@@ -83,13 +83,13 @@ module GQTP
|
|
83
83
|
end
|
84
84
|
|
85
85
|
class Client
|
86
|
-
attr_accessor :
|
86
|
+
attr_accessor :host, :port
|
87
87
|
def initialize(options={})
|
88
88
|
@options = options
|
89
|
-
@
|
90
|
-
@port = options[:port] ||
|
89
|
+
@host = options[:host] || "127.0.0.1"
|
90
|
+
@port = options[:port] || 10043
|
91
91
|
@loop = options[:loop] || ::Coolio::Loop.default
|
92
|
-
@socket = Socket.connect(@
|
92
|
+
@socket = Socket.connect(@host, @port)
|
93
93
|
@socket.attach(@loop)
|
94
94
|
end
|
95
95
|
|
@@ -107,16 +107,16 @@ module GQTP
|
|
107
107
|
end
|
108
108
|
|
109
109
|
class Server
|
110
|
-
attr_accessor :
|
110
|
+
attr_accessor :host, :port
|
111
111
|
def initialize(options={})
|
112
112
|
@options = options
|
113
|
-
@
|
114
|
-
@port = options[:port] ||
|
113
|
+
@host = options[:host] || "0.0.0.0"
|
114
|
+
@port = options[:port] || 10043
|
115
115
|
@loop = options[:loop] || ::Coolio::Loop.default
|
116
116
|
end
|
117
117
|
|
118
118
|
def run
|
119
|
-
@server = ::Coolio::TCPServer.new(@
|
119
|
+
@server = ::Coolio::TCPServer.new(@host, @port, Socket) do |client|
|
120
120
|
yield(client)
|
121
121
|
end
|
122
122
|
@server.attach(@loop)
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2014 Kouhei Sutou <kou@clear-code.com>
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
|
19
|
+
require "eventmachine"
|
20
|
+
|
21
|
+
module GQTP
|
22
|
+
module Backend
|
23
|
+
module Eventmachine
|
24
|
+
class Request
|
25
|
+
def wait
|
26
|
+
# Do nothing
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Handler
|
31
|
+
def post_init
|
32
|
+
@read_callbacks = []
|
33
|
+
@buffer = "".force_encoding("ASCII-8BIT")
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(*chunks, &block)
|
37
|
+
chunks.each do |chunk|
|
38
|
+
send_data(chunk)
|
39
|
+
end
|
40
|
+
if block_given?
|
41
|
+
block.call
|
42
|
+
else
|
43
|
+
Request.new
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def read(size, &block)
|
48
|
+
if @buffer.bytesize >= size
|
49
|
+
consume_data(size, block)
|
50
|
+
else
|
51
|
+
@read_callbacks << [size, block]
|
52
|
+
end
|
53
|
+
if block_given?
|
54
|
+
nil
|
55
|
+
else
|
56
|
+
Request.new
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def receive_data(data)
|
61
|
+
@buffer << data
|
62
|
+
until @read_callbacks.empty?
|
63
|
+
size, callback = @read_callbacks.first
|
64
|
+
break if @buffer.bytesize < size
|
65
|
+
@read_callbacks.shift
|
66
|
+
consume_data(size, callback)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def consume_data(size, callback)
|
72
|
+
data = @buffer[0, size]
|
73
|
+
@buffer = @buffer[size..-1]
|
74
|
+
callback.call(data)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Client
|
79
|
+
attr_accessor :host, :port
|
80
|
+
def initialize(options={})
|
81
|
+
@options = options
|
82
|
+
@host = options[:host] || "127.0.0.1"
|
83
|
+
@port = options[:port] || 10043
|
84
|
+
@connection = EventMachine.connect(@host, @port, Handler)
|
85
|
+
end
|
86
|
+
|
87
|
+
def write(*chunks, &block)
|
88
|
+
@connection.write(*chunks, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
def read(size, &block)
|
92
|
+
@connection.read(size, &block)
|
93
|
+
end
|
94
|
+
|
95
|
+
def close
|
96
|
+
@connection.close_connection_after_writing
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
module ServerHandler
|
101
|
+
include Handler
|
102
|
+
|
103
|
+
def initialize(client_handler)
|
104
|
+
super()
|
105
|
+
@client_handler = client_handler
|
106
|
+
end
|
107
|
+
|
108
|
+
def post_init
|
109
|
+
super
|
110
|
+
@client_handler.call(self)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class Server
|
115
|
+
attr_accessor :host, :port
|
116
|
+
def initialize(options={})
|
117
|
+
@options = options
|
118
|
+
@host = options[:host] || "0.0.0.0"
|
119
|
+
@port = options[:port] || 10043
|
120
|
+
end
|
121
|
+
|
122
|
+
def run(&block)
|
123
|
+
@signature =
|
124
|
+
EventMachine.start_server(@host, @port, ServerHandler, block)
|
125
|
+
Request.new
|
126
|
+
end
|
127
|
+
|
128
|
+
def shutdown
|
129
|
+
EventMachine.stop_server(@signature)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
|
3
|
+
# Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
#
|
5
5
|
# This library is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -17,7 +17,7 @@
|
|
17
17
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
18
|
|
19
19
|
module GQTP
|
20
|
-
module
|
20
|
+
module Backend
|
21
21
|
module Synchronous
|
22
22
|
class Request
|
23
23
|
def initialize(data)
|
@@ -58,12 +58,16 @@ module GQTP
|
|
58
58
|
end
|
59
59
|
|
60
60
|
class Client
|
61
|
-
attr_accessor :
|
61
|
+
attr_accessor :host, :port
|
62
62
|
def initialize(options={})
|
63
63
|
@options = options
|
64
|
-
@
|
65
|
-
@port = options[:port] ||
|
66
|
-
|
64
|
+
@host = options[:host] || "127.0.0.1"
|
65
|
+
@port = options[:port] || 10043
|
66
|
+
begin
|
67
|
+
@socket = TCPSocket.open(@host, @port)
|
68
|
+
rescue SystemCallError
|
69
|
+
raise ConnectionError.new(@host, @port, $!)
|
70
|
+
end
|
67
71
|
@io = IO.new(@socket)
|
68
72
|
end
|
69
73
|
|
@@ -81,16 +85,16 @@ module GQTP
|
|
81
85
|
end
|
82
86
|
|
83
87
|
class Server
|
84
|
-
attr_accessor :
|
88
|
+
attr_accessor :host, :port
|
85
89
|
def initialize(options={})
|
86
90
|
@options = options
|
87
|
-
@
|
88
|
-
@port = options[:port] ||
|
91
|
+
@host = options[:host] || "0.0.0.0"
|
92
|
+
@port = options[:port] || 10043
|
89
93
|
@backlog = options[:backlog] || 128
|
90
94
|
end
|
91
95
|
|
92
96
|
def run
|
93
|
-
@server = TCPServer.new(@
|
97
|
+
@server = TCPServer.new(@host, @port)
|
94
98
|
@server.listen(@backlog)
|
95
99
|
loop do
|
96
100
|
client = @server.accept
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
|
3
|
+
# Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
#
|
5
5
|
# This library is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -20,7 +20,7 @@ require "socket"
|
|
20
20
|
require "thread"
|
21
21
|
|
22
22
|
module GQTP
|
23
|
-
module
|
23
|
+
module Backend
|
24
24
|
module Thread
|
25
25
|
class Request
|
26
26
|
def initialize(thread)
|
@@ -69,12 +69,16 @@ module GQTP
|
|
69
69
|
end
|
70
70
|
|
71
71
|
class Client
|
72
|
-
attr_accessor :
|
72
|
+
attr_accessor :host, :port
|
73
73
|
def initialize(options={})
|
74
74
|
@options = options
|
75
|
-
@
|
76
|
-
@port = options[:port] ||
|
77
|
-
|
75
|
+
@host = options[:host] || "127.0.0.1"
|
76
|
+
@port = options[:port] || 10043
|
77
|
+
begin
|
78
|
+
@socket = TCPSocket.open(@host, @port)
|
79
|
+
rescue SystemCallError
|
80
|
+
raise ConnectionError.new(@host, @port, $!)
|
81
|
+
end
|
78
82
|
@io = IO.new(@socket)
|
79
83
|
end
|
80
84
|
|
@@ -92,16 +96,16 @@ module GQTP
|
|
92
96
|
end
|
93
97
|
|
94
98
|
class Server
|
95
|
-
attr_accessor :
|
99
|
+
attr_accessor :host, :port
|
96
100
|
def initialize(options={})
|
97
101
|
@options = options
|
98
|
-
@
|
99
|
-
@port = options[:port] ||
|
102
|
+
@host = options[:host] || "0.0.0.0"
|
103
|
+
@port = options[:port] || 10043
|
100
104
|
@backlog = options[:backlog] || 128
|
101
105
|
end
|
102
106
|
|
103
107
|
def run
|
104
|
-
@server = TCPServer.new(@
|
108
|
+
@server = TCPServer.new(@host, @port)
|
105
109
|
@server.listen(@backlog)
|
106
110
|
thread = ::Thread.new do
|
107
111
|
loop do
|
data/lib/gqtp/client.rb
CHANGED
@@ -21,12 +21,12 @@ require "gqtp/sequential-request"
|
|
21
21
|
|
22
22
|
module GQTP
|
23
23
|
class Client
|
24
|
-
attr_accessor :
|
24
|
+
attr_accessor :host, :port
|
25
25
|
def initialize(options={})
|
26
26
|
@options = options.dup
|
27
|
-
@options[:
|
28
|
-
@options[:port] ||=
|
29
|
-
@
|
27
|
+
@options[:host] ||= @options[:address] || "127.0.0.1"
|
28
|
+
@options[:port] ||= 10043
|
29
|
+
@backend = create_backend
|
30
30
|
end
|
31
31
|
|
32
32
|
def send(body, options={}, &block)
|
@@ -35,13 +35,13 @@ module GQTP
|
|
35
35
|
|
36
36
|
if block_given?
|
37
37
|
sequential_request = SequentialRequest.new
|
38
|
-
write_request = @
|
38
|
+
write_request = @backend.write(header.pack, body) do
|
39
39
|
sequential_request << read(&block)
|
40
40
|
end
|
41
41
|
sequential_request << write_request
|
42
42
|
sequential_request
|
43
43
|
else
|
44
|
-
@
|
44
|
+
@backend.write(header.pack, body)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -51,9 +51,9 @@ module GQTP
|
|
51
51
|
response_body = nil
|
52
52
|
|
53
53
|
sequential_request = SequentialRequest.new
|
54
|
-
read_header_request = @
|
54
|
+
read_header_request = @backend.read(Header.size) do |header|
|
55
55
|
parser << header
|
56
|
-
read_body_request = @
|
56
|
+
read_body_request = @backend.read(parser.header.size) do |body|
|
57
57
|
response_body = body
|
58
58
|
yield(parser.header, response_body) if block_given?
|
59
59
|
end
|
@@ -88,11 +88,8 @@ module GQTP
|
|
88
88
|
sync = !block_given?
|
89
89
|
sequential_request = SequentialRequest.new
|
90
90
|
quit_request = send("quit", :header => header_for_close) do
|
91
|
-
|
92
|
-
|
93
|
-
yield if block_given?
|
94
|
-
end
|
95
|
-
sequential_request << ack_request
|
91
|
+
@backend.close
|
92
|
+
yield if block_given?
|
96
93
|
end
|
97
94
|
sequential_request << quit_request
|
98
95
|
|
@@ -105,18 +102,19 @@ module GQTP
|
|
105
102
|
end
|
106
103
|
|
107
104
|
private
|
108
|
-
def
|
109
|
-
|
105
|
+
def create_backend
|
106
|
+
# :connection is just for backward compatibility.
|
107
|
+
backend = @options[:backend] || @options[:connection] || :thread
|
110
108
|
|
111
109
|
begin
|
112
|
-
require "gqtp/
|
110
|
+
require "gqtp/backend/#{backend}"
|
113
111
|
rescue LoadError
|
114
|
-
raise "unknown
|
112
|
+
raise ArgumentError, "unknown backend: <#{backend.inspect}>: #{$!}"
|
115
113
|
end
|
116
114
|
|
117
|
-
module_name =
|
118
|
-
|
119
|
-
|
115
|
+
module_name = backend.to_s.capitalize
|
116
|
+
backend_module = GQTP::Backend.const_get(module_name)
|
117
|
+
backend_module::Client.new(@options)
|
120
118
|
end
|
121
119
|
|
122
120
|
def header_for_close
|
data/lib/gqtp/error.rb
CHANGED
@@ -16,9 +16,20 @@
|
|
16
16
|
# License along with this library; if not, write to the Free Software
|
17
17
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
18
|
|
19
|
-
require "gqtp/header"
|
20
|
-
|
21
19
|
module GQTP
|
22
20
|
class Error < StandardError
|
23
21
|
end
|
22
|
+
|
23
|
+
class ConnectionError < Error
|
24
|
+
attr_reader :host
|
25
|
+
attr_reader :port
|
26
|
+
attr_reader :detail
|
27
|
+
def initialize(host, port, detail)
|
28
|
+
@host = host
|
29
|
+
@port = port
|
30
|
+
@detail = detail
|
31
|
+
super("Failed to connect to <#{@host}:#{@port}>: " +
|
32
|
+
"#{@detail.message} (#{@detail.class})")
|
33
|
+
end
|
34
|
+
end
|
24
35
|
end
|
data/lib/gqtp/proxy.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012-
|
3
|
+
# Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
#
|
5
5
|
# This library is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -20,29 +20,32 @@ require "gqtp/server"
|
|
20
20
|
|
21
21
|
module GQTP
|
22
22
|
class Proxy
|
23
|
-
attr_accessor :
|
24
|
-
attr_accessor :
|
23
|
+
attr_accessor :listen_host, :listen_port
|
24
|
+
attr_accessor :upstream_host, :upstream_port
|
25
25
|
def initialize(options={})
|
26
26
|
@options = options.dup
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@
|
27
|
+
@listen_host = @options[:listen_host] || @options[:listen_address]
|
28
|
+
@listen_host ||= "0.0.0.0"
|
29
|
+
@listen_port = @options[:listen_port] || 10043
|
30
|
+
@upstream_host = @options[:upstream_host] || @options[:upstream_address]
|
31
|
+
@upstream_host ||= "127.0.0.1"
|
32
|
+
@upstream_port = @options[:upstream_port] || 10043
|
33
|
+
# :connection is just for backward compatibility.
|
34
|
+
@backend = @options[:backend] || @options[:connection] || :thread
|
35
|
+
@server = Server.new(:host => @listen_host,
|
33
36
|
:port => @listen_port,
|
34
|
-
:
|
37
|
+
:backend => @backend)
|
35
38
|
end
|
36
39
|
|
37
40
|
def run
|
38
41
|
@server.on_connect do |client|
|
39
|
-
|
42
|
+
create_backend
|
40
43
|
end
|
41
|
-
@server.on_request do |request, client,
|
42
|
-
|
43
|
-
|
44
|
+
@server.on_request do |request, client, backend|
|
45
|
+
backend.write(request.header.pack, request.body) do
|
46
|
+
backend.read(Header.size) do |header|
|
44
47
|
response_header = Header.parse(header)
|
45
|
-
|
48
|
+
backend.read(response_header.size) do |body|
|
46
49
|
client.write(header, body) do
|
47
50
|
end
|
48
51
|
end
|
@@ -57,18 +60,18 @@ module GQTP
|
|
57
60
|
end
|
58
61
|
|
59
62
|
private
|
60
|
-
def
|
63
|
+
def create_backend
|
61
64
|
begin
|
62
|
-
require "gqtp/
|
65
|
+
require "gqtp/backend/#{@backend}"
|
63
66
|
rescue LoadError
|
64
|
-
raise "unknown
|
67
|
+
raise ArgumentError, "unknown backend: <#{@backend.inspect}>: #{$!}"
|
65
68
|
end
|
66
69
|
|
67
|
-
require "gqtp/
|
68
|
-
module_name = @
|
69
|
-
|
70
|
-
|
71
|
-
|
70
|
+
require "gqtp/backend/#{@backend}"
|
71
|
+
module_name = @backend.to_s.capitalize
|
72
|
+
backend_module = GQTP::Backend::const_get(module_name)
|
73
|
+
backend_module::Client.new(:host => @upstream_host,
|
74
|
+
:port => @upstream_port)
|
72
75
|
end
|
73
76
|
end
|
74
77
|
end
|
data/lib/gqtp/server.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012-
|
3
|
+
# Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
#
|
5
5
|
# This library is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -20,24 +20,24 @@ require "gqtp/header"
|
|
20
20
|
|
21
21
|
module GQTP
|
22
22
|
class Server
|
23
|
-
attr_accessor :
|
23
|
+
attr_accessor :host, :port
|
24
24
|
def initialize(options={})
|
25
25
|
@options = options.dup
|
26
|
-
@options[:
|
27
|
-
@options[:port] ||=
|
26
|
+
@options[:host] ||= @options[:address] || "0.0.0.0"
|
27
|
+
@options[:port] ||= 10043
|
28
28
|
@on_request = nil
|
29
29
|
@on_connect = nil
|
30
30
|
end
|
31
31
|
|
32
32
|
def run
|
33
|
-
@
|
34
|
-
@
|
33
|
+
@backend = create_backend
|
34
|
+
@backend.run do |client|
|
35
35
|
process_request(client, on_connect(client))
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
def shutdown
|
40
|
-
@
|
40
|
+
@backend.shutdown
|
41
41
|
end
|
42
42
|
|
43
43
|
def on_connect(*arguments, &block)
|
@@ -63,18 +63,19 @@ module GQTP
|
|
63
63
|
end
|
64
64
|
|
65
65
|
private
|
66
|
-
def
|
67
|
-
|
66
|
+
def create_backend
|
67
|
+
# :connection is just for backward compatibility.
|
68
|
+
backend = @options[:backend] || @options[:connection] || :thread
|
68
69
|
|
69
70
|
begin
|
70
|
-
require "gqtp/
|
71
|
+
require "gqtp/backend/#{backend}"
|
71
72
|
rescue LoadError
|
72
|
-
raise "unknown
|
73
|
+
raise ArgumentError, "unknown backend: <#{backend.inspect}>: #{$!}"
|
73
74
|
end
|
74
75
|
|
75
|
-
module_name =
|
76
|
-
|
77
|
-
|
76
|
+
module_name = backend.to_s.capitalize
|
77
|
+
backend_module = GQTP::Backend::const_get(module_name)
|
78
|
+
backend_module::Server.new(@options)
|
78
79
|
end
|
79
80
|
|
80
81
|
def process_request(client, connect_info)
|
data/lib/gqtp/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
|
3
|
+
# Copyright (C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
#
|
5
5
|
# This library is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -17,5 +17,5 @@
|
|
17
17
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
18
|
|
19
19
|
module GQTP
|
20
|
-
VERSION = "1.0.
|
20
|
+
VERSION = "1.0.6"
|
21
21
|
end
|
data/test/test-client.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012-
|
3
|
+
# Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
#
|
5
5
|
# This library is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -21,80 +21,97 @@ require "socket"
|
|
21
21
|
require "gqtp/client"
|
22
22
|
|
23
23
|
class ClientTest < Test::Unit::TestCase
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
client = @server.accept
|
33
|
-
@server.close
|
34
|
-
|
35
|
-
process_client(client)
|
24
|
+
class CreateBackendTest < self
|
25
|
+
def test_unknown
|
26
|
+
message = "unknown backend: <\"unknown\">: " +
|
27
|
+
"cannot load such file -- gqtp/backend/unknown"
|
28
|
+
assert_raise(ArgumentError.new(message)) do
|
29
|
+
GQTP::Client.new(:backend => "unknown")
|
30
|
+
end
|
31
|
+
end
|
36
32
|
|
37
|
-
|
33
|
+
def test_no_server
|
34
|
+
server = TCPServer.new("127.0.0.1", 0)
|
35
|
+
free_port = server.addr[1]
|
36
|
+
server.close
|
37
|
+
assert_raise(GQTP::ConnectionError) do
|
38
|
+
GQTP::Client.new(:port => free_port)
|
39
|
+
end
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
class RequestTest < self
|
44
|
+
def setup
|
45
|
+
@host = "127.0.0.1"
|
46
|
+
@server = TCPServer.new(@host, 0)
|
47
|
+
@port = @server.addr[1]
|
44
48
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
49
|
+
@request_body = nil
|
50
|
+
@response_body = nil
|
51
|
+
@thread = Thread.new do
|
52
|
+
client = @server.accept
|
53
|
+
@server.close
|
54
|
+
|
55
|
+
process_client(client)
|
53
56
|
|
54
|
-
|
55
|
-
|
56
|
-
client = GQTP::Client.new(:address => @address, :port => @port)
|
57
|
-
request = client.send("status") do |header, body|
|
58
|
-
assert_equal(["status", @response_body.bytesize, @response_body],
|
59
|
-
[@request_body, header.size, body])
|
57
|
+
client.close
|
58
|
+
end
|
60
59
|
end
|
61
|
-
request.wait
|
62
|
-
end
|
63
60
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
@request_body = client.read(header.size)
|
61
|
+
def teardown
|
62
|
+
@thread.kill
|
63
|
+
end
|
68
64
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
65
|
+
private
|
66
|
+
def process_client(client)
|
67
|
+
header = GQTP::Header.parse(client.read(GQTP::Header.size))
|
68
|
+
@request_body = client.read(header.size)
|
74
69
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
client
|
79
|
-
assert_true(client.close)
|
70
|
+
response_header = GQTP::Header.new
|
71
|
+
response_header.size = @response_body.bytesize
|
72
|
+
client.write(response_header.pack)
|
73
|
+
client.write(@response_body)
|
80
74
|
end
|
81
75
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
76
|
+
class SendTest < self
|
77
|
+
def test_sync
|
78
|
+
@response_body = "[false]"
|
79
|
+
client = GQTP::Client.new(:host => @host, :port => @port)
|
80
|
+
client.send("status")
|
81
|
+
header, body = client.read
|
82
|
+
assert_equal(["status", @response_body.bytesize, @response_body],
|
83
|
+
[@request_body, header.size, body])
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_async
|
87
|
+
@response_body = "[false]"
|
88
|
+
client = GQTP::Client.new(:host => @host, :port => @port)
|
89
|
+
request = client.send("status") do |header, body|
|
90
|
+
assert_equal(["status", @response_body.bytesize, @response_body],
|
91
|
+
[@request_body, header.size, body])
|
92
|
+
end
|
93
|
+
request.wait
|
88
94
|
end
|
89
|
-
assert_false(closed)
|
90
|
-
close_request.wait
|
91
|
-
assert_true(closed)
|
92
95
|
end
|
93
96
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
97
|
+
class CloseTest < self
|
98
|
+
def test_sync
|
99
|
+
@response_body = "[]"
|
100
|
+
client = GQTP::Client.new(:host => @host, :port => @port)
|
101
|
+
assert_true(client.close)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_async
|
105
|
+
@response_body = "[]"
|
106
|
+
client = GQTP::Client.new(:host => @host, :port => @port)
|
107
|
+
closed = false
|
108
|
+
close_request = client.close do
|
109
|
+
closed = true
|
110
|
+
end
|
111
|
+
assert_false(closed)
|
112
|
+
close_request.wait
|
113
|
+
assert_true(closed)
|
114
|
+
end
|
98
115
|
end
|
99
116
|
end
|
100
117
|
end
|
metadata
CHANGED
@@ -1,119 +1,103 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gqtp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.6
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Kouhei Sutou
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-03-25 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bundler
|
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: :development
|
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
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rake
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: test-unit
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - '>='
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - '>='
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: test-unit-notify
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- -
|
59
|
+
- - '>='
|
68
60
|
- !ruby/object:Gem::Version
|
69
61
|
version: '0'
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- -
|
66
|
+
- - '>='
|
76
67
|
- !ruby/object:Gem::Version
|
77
68
|
version: '0'
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: packnga
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
|
-
- -
|
73
|
+
- - '>='
|
84
74
|
- !ruby/object:Gem::Version
|
85
75
|
version: '0'
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
|
-
- -
|
80
|
+
- - '>='
|
92
81
|
- !ruby/object:Gem::Version
|
93
82
|
version: '0'
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
84
|
name: redcarpet
|
96
85
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
86
|
requirements:
|
99
|
-
- -
|
87
|
+
- - '>='
|
100
88
|
- !ruby/object:Gem::Version
|
101
89
|
version: '0'
|
102
90
|
type: :development
|
103
91
|
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
93
|
requirements:
|
107
|
-
- -
|
94
|
+
- - '>='
|
108
95
|
- !ruby/object:Gem::Version
|
109
96
|
version: '0'
|
110
|
-
description:
|
111
|
-
|
97
|
+
description: |
|
98
|
+
Gqtp gem provides both GQTP client, GQTP server and GQTP proxy
|
112
99
|
implementations. They provide asynchronous API. You can use gqtp gem
|
113
|
-
|
114
100
|
for high concurrency use.
|
115
|
-
|
116
|
-
'
|
117
101
|
email:
|
118
102
|
- kou@clear-code.com
|
119
103
|
executables:
|
@@ -131,11 +115,12 @@ files:
|
|
131
115
|
- lib/gqtp/error.rb
|
132
116
|
- lib/gqtp/version.rb
|
133
117
|
- lib/gqtp/proxy.rb
|
118
|
+
- lib/gqtp/backend/coolio.rb
|
119
|
+
- lib/gqtp/backend/thread.rb
|
120
|
+
- lib/gqtp/backend/synchronous.rb
|
121
|
+
- lib/gqtp/backend/eventmachine.rb
|
134
122
|
- lib/gqtp/sequential-request.rb
|
135
123
|
- lib/gqtp/server.rb
|
136
|
-
- lib/gqtp/connection/coolio.rb
|
137
|
-
- lib/gqtp/connection/thread.rb
|
138
|
-
- lib/gqtp/connection/synchronous.rb
|
139
124
|
- lib/gqtp/client.rb
|
140
125
|
- lib/gqtp.rb
|
141
126
|
- doc/text/news.md
|
@@ -149,27 +134,26 @@ files:
|
|
149
134
|
homepage: https://github.com/ranguba/gqtp
|
150
135
|
licenses:
|
151
136
|
- LGPLv2.1 or later
|
137
|
+
metadata: {}
|
152
138
|
post_install_message:
|
153
139
|
rdoc_options: []
|
154
140
|
require_paths:
|
155
141
|
- lib
|
156
142
|
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
-
none: false
|
158
143
|
requirements:
|
159
|
-
- -
|
144
|
+
- - '>='
|
160
145
|
- !ruby/object:Gem::Version
|
161
146
|
version: '0'
|
162
147
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
-
none: false
|
164
148
|
requirements:
|
165
|
-
- -
|
149
|
+
- - '>='
|
166
150
|
- !ruby/object:Gem::Version
|
167
151
|
version: '0'
|
168
152
|
requirements: []
|
169
153
|
rubyforge_project:
|
170
|
-
rubygems_version:
|
154
|
+
rubygems_version: 2.0.14
|
171
155
|
signing_key:
|
172
|
-
specification_version:
|
156
|
+
specification_version: 4
|
173
157
|
summary: Gqtp gem is a [GQTP (Groonga Query Transfer Protocol)](http://groonga.org/docs/spec/gqtp.html)
|
174
158
|
Ruby implementation.
|
175
159
|
test_files:
|