gqtp 1.0.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.
@@ -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