gqtp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,133 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012 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 "cool.io"
20
+
21
+ module GQTP
22
+ module Connection
23
+ module Coolio
24
+ class Request
25
+ def initialize(loop)
26
+ @loop = loop
27
+ end
28
+
29
+ def wait
30
+ @loop.run
31
+ end
32
+ end
33
+
34
+ class Socket < ::Coolio::TCPSocket
35
+ def initialize(*args)
36
+ super
37
+ @write_callbacks = []
38
+ @read_callbacks = []
39
+ @request = nil
40
+ @buffer = "".force_encoding("ASCII-8BIT")
41
+ end
42
+
43
+ def write(*chunks, &block)
44
+ chunks.each do |chunk|
45
+ super(chunk)
46
+ end
47
+ @write_callbacks << block if block_given?
48
+ Request.new(evloop)
49
+ end
50
+
51
+ def on_write_complete
52
+ write_callbacks, @write_callbacks = @write_callbacks, []
53
+ write_callbacks.each do |callback|
54
+ callback.call
55
+ end
56
+ end
57
+
58
+ def read(size, &block)
59
+ if @buffer.bytesize >= size
60
+ consume_data(size, block)
61
+ else
62
+ @read_callbacks << [size, block]
63
+ end
64
+ Request.new(evloop)
65
+ end
66
+
67
+ def on_read(data)
68
+ @buffer << data
69
+ until @read_callbacks.empty?
70
+ size, callback = @read_callbacks.first
71
+ break if @buffer.bytesize < size
72
+ @read_callbacks.shift
73
+ consume_data(size, callback)
74
+ end
75
+ end
76
+
77
+ private
78
+ def consume_data(size, callback)
79
+ data = @buffer[0, size]
80
+ @buffer = @buffer[size..-1]
81
+ callback.call(data)
82
+ end
83
+ end
84
+
85
+ class Client
86
+ attr_accessor :address, :port
87
+ def initialize(options={})
88
+ @options = options
89
+ @address = options[:address] || "127.0.0.1"
90
+ @port = options[:port] || 10041
91
+ @loop = options[:loop] || ::Coolio::Loop.default
92
+ @socket = Socket.connect(@address, @port)
93
+ @socket.attach(@loop)
94
+ end
95
+
96
+ def write(*chunks, &block)
97
+ @socket.write(*chunks, &block)
98
+ end
99
+
100
+ def read(size, &block)
101
+ @socket.read(size, &block)
102
+ end
103
+
104
+ def close
105
+ @socket.close
106
+ end
107
+ end
108
+
109
+ class Server
110
+ attr_accessor :address, :port
111
+ def initialize(options={})
112
+ @options = options
113
+ @address = options[:address] || "0.0.0.0"
114
+ @port = options[:port] || 10041
115
+ @loop = options[:loop] || ::Coolio::Loop.default
116
+ end
117
+
118
+ def run
119
+ @server = ::Coolio::TCPServer.new(@address, @port, Socket) do |client|
120
+ yield(client)
121
+ end
122
+ @server.attach(@loop)
123
+ @loop.run
124
+ Request.new(@loop)
125
+ end
126
+
127
+ def shutdown
128
+ @server.close
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,108 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012 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
+ module GQTP
20
+ module Connection
21
+ module Synchronous
22
+ class Request
23
+ def initialize(data)
24
+ @data = data
25
+ end
26
+
27
+ def wait
28
+ @data
29
+ end
30
+ end
31
+
32
+ class IO
33
+ def initialize(real_io)
34
+ @real_io = real_io
35
+ end
36
+
37
+ def write(*chunks)
38
+ chunks.each do |chunk|
39
+ until chunk.empty?
40
+ written_bytes = @real_io.write(chunk)
41
+ break if chunk.bytesize == written_bytes
42
+ chunk = chunk[written_bytes..-1]
43
+ end
44
+ end
45
+ yield if block_given?
46
+ Request.new(nil)
47
+ end
48
+
49
+ def read(size=nil)
50
+ data = @real_io.read(size)
51
+ yield(data) if block_given?
52
+ Request.new(data)
53
+ end
54
+
55
+ def close
56
+ @real_io.close
57
+ end
58
+ end
59
+
60
+ class Client
61
+ attr_accessor :address, :port
62
+ def initialize(options={})
63
+ @options = options
64
+ @address = options[:address] || "127.0.0.1"
65
+ @port = options[:port] || 10041
66
+ @socket = TCPSocket.open(@address, @port)
67
+ @io = IO.new(@socket)
68
+ end
69
+
70
+ def write(*chunks, &block)
71
+ @io.write(*chunks, &block)
72
+ end
73
+
74
+ def read(size=nil, &block)
75
+ @io.read(size, &block)
76
+ end
77
+
78
+ def close
79
+ @io.close
80
+ end
81
+ end
82
+
83
+ class Server
84
+ attr_accessor :address, :port
85
+ def initialize(options={})
86
+ @options = options
87
+ @address = options[:address] || "0.0.0.0"
88
+ @port = options[:port] || 10041
89
+ @backlog = options[:backlog] || 128
90
+ end
91
+
92
+ def run
93
+ @server = TCPServer.new(@address, @port)
94
+ @server.listen(@backlog)
95
+ loop do
96
+ client = @server.accept
97
+ yield(IO.new(client))
98
+ end
99
+ Request.new(nil)
100
+ end
101
+
102
+ def shutdown
103
+ @server.shutdown
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,123 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012 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 "socket"
20
+ require "thread"
21
+
22
+ module GQTP
23
+ module Connection
24
+ module Thread
25
+ class Request
26
+ def initialize(thread)
27
+ @thread = thread
28
+ end
29
+
30
+ def wait
31
+ @thread.join
32
+ end
33
+ end
34
+
35
+ class IO
36
+ def initialize(real_io)
37
+ @real_io = real_io
38
+ end
39
+
40
+ def write(*chunks)
41
+ thread = ::Thread.new do
42
+ chunks.each do |chunk|
43
+ until chunk.empty?
44
+ written_bytes = @real_io.write(chunk)
45
+ break if chunk.bytesize == written_bytes
46
+ chunk = chunk[written_bytes..-1]
47
+ end
48
+ end
49
+ yield if block_given?
50
+ end
51
+ Request.new(thread)
52
+ end
53
+
54
+ def read(size=nil)
55
+ thread = ::Thread.new do
56
+ data = @real_io.read(size)
57
+ if block_given?
58
+ yield(data)
59
+ else
60
+ data
61
+ end
62
+ end
63
+ Request.new(thread)
64
+ end
65
+
66
+ def close
67
+ @real_io.close
68
+ end
69
+ end
70
+
71
+ class Client
72
+ attr_accessor :address, :port
73
+ def initialize(options={})
74
+ @options = options
75
+ @address = options[:address] || "127.0.0.1"
76
+ @port = options[:port] || 10041
77
+ @socket = TCPSocket.open(@address, @port)
78
+ @io = IO.new(@socket)
79
+ end
80
+
81
+ def write(*chunks, &block)
82
+ @io.write(*chunks, &block)
83
+ end
84
+
85
+ def read(size=nil, &block)
86
+ @io.read(size, &block)
87
+ end
88
+
89
+ def close
90
+ @io.close
91
+ end
92
+ end
93
+
94
+ class Server
95
+ attr_accessor :address, :port
96
+ def initialize(options={})
97
+ @options = options
98
+ @address = options[:address] || "0.0.0.0"
99
+ @port = options[:port] || 10041
100
+ @backlog = options[:backlog] || 128
101
+ end
102
+
103
+ def run
104
+ @server = TCPServer.new(@address, @port)
105
+ @server.listen(@backlog)
106
+ thread = ::Thread.new do
107
+ loop do
108
+ client = @server.accept
109
+ ::Thread.new do
110
+ yield(IO.new(client))
111
+ end
112
+ end
113
+ end
114
+ Request.new(thread)
115
+ end
116
+
117
+ def shutdown
118
+ @server.shutdown
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
data/lib/gqtp/error.rb ADDED
@@ -0,0 +1,24 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012 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 "gqtp/header"
20
+
21
+ module GQTP
22
+ class Error < StandardError
23
+ end
24
+ end
@@ -0,0 +1,190 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012 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
+ module GQTP
20
+ class Header
21
+ # struct _grn_com_header {
22
+ # uint8_t proto;
23
+ # uint8_t qtype;
24
+ # uint16_t keylen;
25
+ # uint8_t level;
26
+ # uint8_t flags;
27
+ # uint16_t status;
28
+ # uint32_t size;
29
+ # uint32_t opaque;
30
+ # uint64_t cas;
31
+ # };
32
+
33
+ class << self
34
+ def parse(chunk)
35
+ return nil if chunk.bytesize < size
36
+ header = new
37
+ header.proto, header.query_type, header.key_length,
38
+ header.level, header.flags, header.status, header.size,
39
+ header.opaque, cas_high, cas_low = chunk.unpack(pack_format)
40
+ header.cas = cas_high << 32 + cas_low
41
+ header
42
+ end
43
+
44
+ def size
45
+ 24
46
+ end
47
+
48
+ def pack_format
49
+ "CCnCCnNNNN"
50
+ end
51
+ end
52
+
53
+ attr_accessor :proto, :query_type, :key_length, :level, :flags
54
+ attr_accessor :status, :size, :opaque, :cas
55
+ def initialize(values={})
56
+ @proto = values[:proto] || Protocol::GQTP
57
+ @query_type = values[:query_type] || ContentType::NONE
58
+ @key_length = values[:key_length] || 0
59
+ @level = values[:level] || 0
60
+ @flags = values[:flags] || 0
61
+ @status = values[:status] || Status::SUCCESS
62
+ @size = values[:size] || 0
63
+ @opaque = values[:opaque] || 0
64
+ @cas = values[:cas] || 0
65
+ yield(self) if block_given?
66
+ end
67
+
68
+ def pack
69
+ data = [
70
+ @proto, @query_type, @key_length, @level,
71
+ @flags, @status, @size, @opaque, @cas >> 32, @cas & (2 ** 32),
72
+ ]
73
+ data.pack(self.class.pack_format)
74
+ end
75
+
76
+ def ==(other)
77
+ other.is_a?(self.class) and to_hash == other.to_hash
78
+ end
79
+
80
+ def to_hash
81
+ {
82
+ :proto => @proto,
83
+ :query_type => @query_type,
84
+ :key_length => @key_length,
85
+ :level => @level,
86
+ :flags => @flags,
87
+ :status => @status,
88
+ :size => @size,
89
+ :opaque => @opaque,
90
+ :cas => @cas,
91
+ }
92
+ end
93
+
94
+ module Protocol
95
+ GQTP = 0xc7
96
+ end
97
+
98
+ module ContentType
99
+ NONE = 0
100
+ TSV = 1
101
+ JSON = 2
102
+ XML = 3
103
+ MSGPACK = 4
104
+ end
105
+
106
+ module Flag
107
+ MORE = 0x01
108
+ TAIL = 0x02
109
+ HEAD = 0x04
110
+ QUIET = 0x08
111
+ QUIT = 0x10
112
+ end
113
+
114
+ module Status
115
+ SUCCESS = 0
116
+ END_OF_DATA = 1
117
+ UNKNOWN_ERROR = 65535
118
+ OPERATION_NOT_PERMITTED = 65534
119
+ NO_SUCH_FILE_OR_DIRECTORY = 65533
120
+ NO_SUCH_PROCESS = 65532
121
+ INTERRUPTED_FUNCTION_CALL = 65531
122
+ INPUT_OUTPUT_ERROR = 65530
123
+ NO_SUCH_DEVICE_OR_ADDRESS = 65529
124
+ ARG_LIST_TOO_LONG = 65528
125
+ EXEC_FORMAT_ERROR = 65527
126
+ BAD_FILE_DESCRIPTOR = 65526
127
+ NO_CHILD_PROCESSES = 65525
128
+ RESOURCE_TEMPORARILY_UNAVAILABLE = 65524
129
+ NOT_ENOUGH_SPACE = 65523
130
+ PERMISSION_DENIED = 65522
131
+ BAD_ADDRESS = 65521
132
+ RESOURCE_BUSY = 65520
133
+ FILE_EXISTS = 65519
134
+ IMPROPER_LINK = 65518
135
+ NO_SUCH_DEVICE = 65517
136
+ NOT_A_DIRECTORY = 65516
137
+ IS_A_DIRECTORY = 65515
138
+ INVALID_ARGUMENT = 65514
139
+ TOO_MANY_OPEN_FILES_IN_SYSTEM = 65513
140
+ TOO_MANY_OPEN_FILES = 65512
141
+ INAPPROPRIATE_I_O_CONTROL_OPERATION = 65511
142
+ FILE_TOO_LARGE = 65510
143
+ NO_SPACE_LEFT_ON_DEVICE = 65509
144
+ INVALID_SEEK = 65508
145
+ READ_ONLY_FILE_SYSTEM = 65507
146
+ TOO_MANY_LINKS = 65506
147
+ BROKEN_PIPE = 65505
148
+ DOMAIN_ERROR = 65504
149
+ RESULT_TOO_LARGE = 65503
150
+ RESOURCE_DEADLOCK_AVOIDED = 65502
151
+ NO_MEMORY_AVAILABLE = 65501
152
+ FILENAME_TOO_LONG = 65500
153
+ NO_LOCKS_AVAILABLE = 65499
154
+ FUNCTION_NOT_IMPLEMENTED = 65498
155
+ DIRECTORY_NOT_EMPTY = 65497
156
+ ILLEGAL_BYTE_SEQUENCE = 65496
157
+ SOCKET_NOT_INITIALIZED = 65495
158
+ OPERATION_WOULD_BLOCK = 65494
159
+ ADDRESS_IS_NOT_AVAILABLE = 65493
160
+ NETWORK_IS_DOWN = 65492
161
+ NO_BUFFER = 65491
162
+ SOCKET_IS_ALREADY_CONNECTED = 65490
163
+ SOCKET_IS_NOT_CONNECTED = 65489
164
+ SOCKET_IS_ALREADY_SHUTDOWNED = 65488
165
+ OPERATION_TIMEOUT = 65487
166
+ CONNECTION_REFUSED = 65486
167
+ RANGE_ERROR = 65485
168
+ TOKENIZER_ERROR = 65484
169
+ FILE_CORRUPT = 65483
170
+ INVALID_FORMAT = 65482
171
+ OBJECT_CORRUPT = 65481
172
+ TOO_MANY_SYMBOLIC_LINKS = 65480
173
+ NOT_SOCKET = 65479
174
+ OPERATION_NOT_SUPPORTED = 65478
175
+ ADDRESS_IS_IN_USE = 65477
176
+ ZLIB_ERROR = 65476
177
+ LZO_ERROR = 65475
178
+ STACK_OVER_FLOW = 65474
179
+ SYNTAX_ERROR = 65473
180
+ RETRY_MAX = 65472
181
+ INCOMPATIBLE_FILE_FORMAT = 65471
182
+ UPDATE_NOT_ALLOWED = 65470
183
+ TOO_SMALL_OFFSET = 65469
184
+ TOO_LARGE_OFFSET = 65468
185
+ TOO_SMALL_LIMIT = 65467
186
+ CAS_ERROR = 65466
187
+ UNSUPPORTED_COMMAND_VERSION = 65465
188
+ end
189
+ end
190
+ end