fluent-plugin-http-ex 0.0.1
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 +15 -0
- data/.gitignore +17 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +13 -0
- data/README.md +279 -0
- data/Rakefile +10 -0
- data/examples/chunked.rb +27 -0
- data/examples/json.rb +23 -0
- data/examples/json_list.rb +12 -0
- data/examples/msgpack.rb +22 -0
- data/examples/msgpack_list.rb +23 -0
- data/examples/nc_chunked.rb +44 -0
- data/examples/sample.conf +11 -0
- data/fluent-plugin-http-ex.gemspec +21 -0
- data/lib/fluent/plugin/in_http_ex.rb +252 -0
- data/test/helper.rb +23 -0
- data/test/in_http_ex_test.rb +100 -0
- data/test/plugin/test_in_http_ex.rb +347 -0
- metadata +91 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MGZlNmQ3NTVmNDkyNDRjNTMyYjYzMWFkNzJjYzBjZTdkYzNlZDI1Yw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NTFkNjIyN2MyNDgwNmY2ZmRkMDNhMDAxYjZlNmU0ZmRmMTQ0ZmY4Ng==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MjFiNWVjNDI2YTYzNjNkZjZlY2Y5ZDJjMGQ5YzMyYjQ1ZmRjNTExZjBkYmNj
|
10
|
+
MGRmMDlhNmY3ZDc4ZjE5OTk1ZWVlZTg4MGM2MzBlNjNhZDMyOTQwNGFkZjVj
|
11
|
+
NTM5MWFkM2RiYjY5YzEyZTE0MDM2YzZjZWY5YzJiNDlkMTEyN2E=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MDUwZGEwN2YxZjcyMmMxYmUxYzk0YmJmNzRmOTk2ZDVlY2RiMDNlNjNmMGQz
|
14
|
+
NDlhZGRiNDNkMGJjZWRhNDlmNjZkMzg4YzMxNTVlNjQxNWY5MDg4ZjRmZTkx
|
15
|
+
NWUyNjJkODU1NmYyMGZhZGE3ZjIzNThkNGI1M2Q1NGMyMjM2NGI=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (C) 2013 hiro-su
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
# Fluent::Plugin::Http::Ex
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
This plugin takes JSON or MessagePack of events as input via a single, list or chunked
|
6
|
+
HTTP POST request and emits each as an individual event to your output plugins.
|
7
|
+
If you're sending a lot of events this simplifies your client's code and eliminates
|
8
|
+
the overhead of creating a lot of brief connections.
|
9
|
+
|
10
|
+
## Configuration
|
11
|
+
|
12
|
+
The ExHttpInput plugin uses the same settings you would use for the standard
|
13
|
+
HTTP input plugin. Example:
|
14
|
+
|
15
|
+
<source>
|
16
|
+
type http_ex
|
17
|
+
port 8888
|
18
|
+
bind 0.0.0.0
|
19
|
+
body_size_limit 32m
|
20
|
+
keepalive_timeout 300s #0s is not timeout
|
21
|
+
</source>
|
22
|
+
|
23
|
+
Like the HTTP input plugin, the tag is determined by the URL used, which means
|
24
|
+
all events in one request must have the same tag.
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
Have your logging system send JSON or Message of events. Example:
|
29
|
+
|
30
|
+
Base URL
|
31
|
+
|
32
|
+
http://localhost:8888
|
33
|
+
|
34
|
+
### json
|
35
|
+
#### case 1
|
36
|
+
|
37
|
+
resource
|
38
|
+
|
39
|
+
j or null
|
40
|
+
|
41
|
+
header
|
42
|
+
|
43
|
+
Content-type: application/x-www-form-urlencoded
|
44
|
+
|
45
|
+
body
|
46
|
+
|
47
|
+
json=<json data>
|
48
|
+
|
49
|
+
sample
|
50
|
+
|
51
|
+
$ curl -X POST -d 'json={"action":"login","user":2}' \
|
52
|
+
http://localhost:8888/j/test.tag.here;
|
53
|
+
|
54
|
+
$ curl -X POST -d 'json={"action":"login","user":2}' \
|
55
|
+
http://localhost:8888/test.tag.here;
|
56
|
+
|
57
|
+
#### case 2
|
58
|
+
|
59
|
+
resource
|
60
|
+
|
61
|
+
j or null
|
62
|
+
|
63
|
+
header
|
64
|
+
|
65
|
+
Content-type: application/json
|
66
|
+
|
67
|
+
body
|
68
|
+
|
69
|
+
<json data>
|
70
|
+
|
71
|
+
sample
|
72
|
+
|
73
|
+
$ curl -X POST -H 'Content-Type: application/json' -d '{"action":"login","user":2}' \
|
74
|
+
http://localhost:8888/j/test.tag.here;
|
75
|
+
|
76
|
+
$ curl -X POST -H 'Content-Type: application/json' -d '{"action":"login","user":2}' \
|
77
|
+
http://localhost:8888/test.tag.here;
|
78
|
+
|
79
|
+
### json list
|
80
|
+
#### case 1
|
81
|
+
|
82
|
+
resource
|
83
|
+
|
84
|
+
js
|
85
|
+
|
86
|
+
header
|
87
|
+
|
88
|
+
Content-type: application/x-www-form-urlencoded
|
89
|
+
|
90
|
+
body
|
91
|
+
|
92
|
+
json=<json list data>
|
93
|
+
|
94
|
+
sample
|
95
|
+
|
96
|
+
$ curl -X POST -d 'json=[{"action":"login","user":2},{"action":"login","user":2}]' \
|
97
|
+
http://localhost:8888/js/test.tag.here;
|
98
|
+
|
99
|
+
#### case 2
|
100
|
+
|
101
|
+
resource
|
102
|
+
|
103
|
+
js
|
104
|
+
|
105
|
+
header
|
106
|
+
|
107
|
+
Content-type: application/json
|
108
|
+
|
109
|
+
body
|
110
|
+
|
111
|
+
json=<json list data>
|
112
|
+
|
113
|
+
sample
|
114
|
+
|
115
|
+
$ curl -X POST -d '[{"action":"login","user":2},{"action":"login","user":2}]' \
|
116
|
+
http://localhost:8888/js/test.tag.here;
|
117
|
+
|
118
|
+
### msgpack
|
119
|
+
#### case 1
|
120
|
+
|
121
|
+
resource
|
122
|
+
|
123
|
+
m or null
|
124
|
+
|
125
|
+
header
|
126
|
+
|
127
|
+
Content-type: application/x-www-form-urlencoded
|
128
|
+
|
129
|
+
body
|
130
|
+
|
131
|
+
msgpack=<hash msgpack data>
|
132
|
+
hash.to_msgpack
|
133
|
+
|
134
|
+
#### case2
|
135
|
+
|
136
|
+
resource
|
137
|
+
|
138
|
+
m or null
|
139
|
+
|
140
|
+
header
|
141
|
+
|
142
|
+
Content-type: application/x-msgpack
|
143
|
+
|
144
|
+
body
|
145
|
+
|
146
|
+
<msgpack data>
|
147
|
+
hash.to_msgpack
|
148
|
+
|
149
|
+
### msgpack list
|
150
|
+
#### case 1
|
151
|
+
|
152
|
+
resource
|
153
|
+
|
154
|
+
ms
|
155
|
+
|
156
|
+
header
|
157
|
+
|
158
|
+
Content-type: application/x-www-form-urlencoded
|
159
|
+
|
160
|
+
body
|
161
|
+
|
162
|
+
msgpack=<msgpack list data>
|
163
|
+
[hash,hash,hash].to_msgpack
|
164
|
+
|
165
|
+
#### case 2
|
166
|
+
|
167
|
+
resource
|
168
|
+
|
169
|
+
ms
|
170
|
+
|
171
|
+
header
|
172
|
+
|
173
|
+
Content-type: application/x-msgpack
|
174
|
+
|
175
|
+
body
|
176
|
+
|
177
|
+
msgpack=<msgpack list data>
|
178
|
+
[hash,hash,hash].to_msgpack
|
179
|
+
|
180
|
+
### msgpack chunked
|
181
|
+
|
182
|
+
resource
|
183
|
+
|
184
|
+
ms
|
185
|
+
|
186
|
+
header
|
187
|
+
|
188
|
+
Content-type: application/x-msgpack
|
189
|
+
Transfer-Encoding: chunked
|
190
|
+
|
191
|
+
body
|
192
|
+
|
193
|
+
<msgpack chunk data>
|
194
|
+
"#{hash.to_msgpack}#{hash.to_msgpack}"...
|
195
|
+
|
196
|
+
|
197
|
+
Each event in the list will be sent to your output plugins as an individual
|
198
|
+
event.
|
199
|
+
|
200
|
+
## Performance
|
201
|
+
|
202
|
+
Comparison of in_http and in_http_ex.
|
203
|
+
send 10,000 messages.
|
204
|
+
|
205
|
+
|
206
|
+
machine spec
|
207
|
+
|
208
|
+
Mac OS X 10.8.2
|
209
|
+
1.8 GHz Intel Core i5
|
210
|
+
8 GB 1600 MHz DDR3
|
211
|
+
|
212
|
+
### in_http
|
213
|
+
|
214
|
+
json
|
215
|
+
|
216
|
+
$ time ruby examples/json.rb
|
217
|
+
|
218
|
+
real 2m27.480s
|
219
|
+
user 0m7.252s
|
220
|
+
sys 0m4.438s
|
221
|
+
|
222
|
+
msgpack
|
223
|
+
|
224
|
+
$ time ruby examples/msgpack.rb
|
225
|
+
|
226
|
+
real 2m36.408s
|
227
|
+
user 0m8.249s
|
228
|
+
sys 0m4.441s
|
229
|
+
|
230
|
+
### in_http_ex
|
231
|
+
|
232
|
+
json
|
233
|
+
|
234
|
+
$ time ruby examples/json.rb
|
235
|
+
|
236
|
+
real 2m30.639s
|
237
|
+
user 0m7.195s
|
238
|
+
sys 0m4.686s
|
239
|
+
|
240
|
+
msgpack
|
241
|
+
|
242
|
+
$ time ruby examples/msgpack.rb
|
243
|
+
|
244
|
+
real 2m28.442s
|
245
|
+
user 0m7.126s
|
246
|
+
sys 0m4.324s
|
247
|
+
|
248
|
+
json list
|
249
|
+
|
250
|
+
$ time ruby examples/json_list.rb
|
251
|
+
|
252
|
+
real 0m18.179s
|
253
|
+
user 0m0.872s
|
254
|
+
sys 0m0.477s
|
255
|
+
|
256
|
+
msgpack list
|
257
|
+
|
258
|
+
$ time ruby examples/msgpack_list.rb
|
259
|
+
|
260
|
+
real 0m13.787s
|
261
|
+
user 0m0.908s
|
262
|
+
sys 0m0.470s
|
263
|
+
|
264
|
+
msgpack chunked
|
265
|
+
|
266
|
+
$ time ruby examples/nc_chunked.rb
|
267
|
+
|
268
|
+
real 0m1.584s
|
269
|
+
user 0m0.244s
|
270
|
+
sys 0m0.107s
|
271
|
+
|
272
|
+
|
273
|
+
## Copyright
|
274
|
+
|
275
|
+
Copyright (c) 2013 hiro-su.
|
276
|
+
|
277
|
+
Based on the in_http plugin by FURUHASHI Sadayuki
|
278
|
+
|
279
|
+
Apache License, Version 2.0
|
data/Rakefile
ADDED
data/examples/chunked.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'stringio'
|
3
|
+
require 'msgpack'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
host, port = 'localhost', 8888
|
7
|
+
path = "/ms/test.tag"
|
8
|
+
http = Net::HTTP.new(host, port)
|
9
|
+
req = Net::HTTP::Post.new(path)
|
10
|
+
req[ "Content-Type" ] = 'application/x-msgpack'
|
11
|
+
req[ "Transfer-Encoding" ] = "chunked"
|
12
|
+
req[ "Connection" ] = "Keep-Alive"
|
13
|
+
|
14
|
+
io = StringIO.new
|
15
|
+
DATA.each do |line|
|
16
|
+
io << JSON.parse(line.chomp).to_msgpack
|
17
|
+
end
|
18
|
+
io.rewind
|
19
|
+
req.body_stream = io
|
20
|
+
http.request(req).body
|
21
|
+
|
22
|
+
__END__
|
23
|
+
{"key1":"value1"}
|
24
|
+
{"key2":"value2"}
|
25
|
+
{"key3":"value3"}
|
26
|
+
{"key4":"value4"}
|
27
|
+
{"key5":"value5"}
|
data/examples/json.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
# in_http
|
4
|
+
#host, port = 'localhost', 7777
|
5
|
+
#path = "/test.tag"
|
6
|
+
#http = Net::HTTP.new(host, port)
|
7
|
+
#req = Net::HTTP::Post.new(path)
|
8
|
+
#req["Content-Type"] = "application/json"
|
9
|
+
#1.upto(10000) do |i|
|
10
|
+
# req.body = "{\"key#{i}\":\"value#{i}\"}"
|
11
|
+
# http.request(req)
|
12
|
+
#end
|
13
|
+
|
14
|
+
# in_http_ex
|
15
|
+
host, port = 'localhost', 8888
|
16
|
+
path = "/j/test.tag"
|
17
|
+
http = Net::HTTP.new(host, port)
|
18
|
+
req = Net::HTTP::Post.new(path)
|
19
|
+
req["Content-Type"] = "application/json"
|
20
|
+
1.upto(10000) do |i|
|
21
|
+
req.body = "{\"key#{i}\":\"value#{i}\"}"
|
22
|
+
http.request(req)
|
23
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'msgpack'
|
3
|
+
|
4
|
+
host, port = 'localhost', 8888
|
5
|
+
path = "/js/test.tag"
|
6
|
+
http = Net::HTTP.new(host, port)
|
7
|
+
req = Net::HTTP::Post.new(path)
|
8
|
+
req["Content-Type"] = "application/json"
|
9
|
+
1.upto(1000) do |i|
|
10
|
+
req.body = "[{\"key#{i}\":\"value#{i}\"},{\"key#{i}\":\"value#{i}\"},{\"key#{i}\":\"value#{i}\"},{\"key#{i}\":\"value#{i}\"},{\"key#{i}\":\"value#{i}\"},{\"key#{i}\":\"value#{i}\"},{\"key#{i}\":\"value#{i}\"},{\"key#{i}\":\"value#{i}\"},{\"key#{i}\":\"value#{i}\"},{\"key#{i}\":\"value#{i}\"}]"
|
11
|
+
http.request(req)
|
12
|
+
end
|
data/examples/msgpack.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'msgpack'
|
3
|
+
|
4
|
+
# in_http
|
5
|
+
#host, port = 'localhost', 7777
|
6
|
+
#path = "/test.tag"
|
7
|
+
#http = Net::HTTP.new(host, port)
|
8
|
+
#1.upto(10000) do |i|
|
9
|
+
# record = URI.encode_www_form({"msgpack"=>{"key#{i}"=>"value#{i}"}.to_msgpack})
|
10
|
+
# http.post(path, record)
|
11
|
+
#end
|
12
|
+
|
13
|
+
# in_http_ex
|
14
|
+
host, port = 'localhost', 8888
|
15
|
+
path = "/m/test.tag"
|
16
|
+
http = Net::HTTP.new(host, port)
|
17
|
+
req = Net::HTTP::Post.new(path)
|
18
|
+
req["Content-Type"] = "application/x-msgpack"
|
19
|
+
1.upto(10000) do |i|
|
20
|
+
req.body = {"key#{i}"=>"value#{i}"}.to_msgpack
|
21
|
+
http.request(req)
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'msgpack'
|
3
|
+
|
4
|
+
host, port = 'localhost', 8888
|
5
|
+
path = "/ms/test.tag"
|
6
|
+
http = Net::HTTP.new(host, port)
|
7
|
+
req = Net::HTTP::Post.new(path)
|
8
|
+
req["Content-Type"] = "application/x-msgpack"
|
9
|
+
1.upto(1000) do |i|
|
10
|
+
req.body = [
|
11
|
+
{"key#{i}"=>"value#{i}"},
|
12
|
+
{"key#{i}"=>"value#{i}"},
|
13
|
+
{"key#{i}"=>"value#{i}"},
|
14
|
+
{"key#{i}"=>"value#{i}"},
|
15
|
+
{"key#{i}"=>"value#{i}"},
|
16
|
+
{"key#{i}"=>"value#{i}"},
|
17
|
+
{"key#{i}"=>"value#{i}"},
|
18
|
+
{"key#{i}"=>"value#{i}"},
|
19
|
+
{"key#{i}"=>"value#{i}"},
|
20
|
+
{"key#{i}"=>"value#{i}"},
|
21
|
+
].to_msgpack
|
22
|
+
http.request(req)
|
23
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'msgpack'
|
3
|
+
|
4
|
+
class ChunkTest
|
5
|
+
def initialize(host, port)
|
6
|
+
@host = host
|
7
|
+
@port = port
|
8
|
+
@cmd = "nc"
|
9
|
+
@term = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
Signal.trap(:INT){
|
14
|
+
@term = true
|
15
|
+
}
|
16
|
+
Open3.popen3("#{@cmd} #{@host} #{@port}") do |stdin, stdout, stderr, wait_thr|
|
17
|
+
begin
|
18
|
+
i = 0
|
19
|
+
loop do
|
20
|
+
break if @term
|
21
|
+
body = {"key#{i}"=>"value#{i}"}.to_msgpack
|
22
|
+
size = body.size
|
23
|
+
head = \
|
24
|
+
"POST /ms/test.tag HTTP/1.1\r\nUser-Agent: curl/7.28.0\r\nHost: #{@host}:#{@port}\r\nContent-type: application/x-msgpack\r\nTransfer-Encoding: chunked\r\nConnection: Keep-Alive\r\nExpect: 100-continue\r\n\r\n"
|
25
|
+
if i == 0
|
26
|
+
stdin << head
|
27
|
+
elsif i > 10000
|
28
|
+
stdin << "0\r\n\r\n"
|
29
|
+
break
|
30
|
+
else
|
31
|
+
chunk = "#{size.to_s(16)}\r\n#{body}\r\n"
|
32
|
+
stdin << chunk
|
33
|
+
end
|
34
|
+
i += 1
|
35
|
+
end
|
36
|
+
ensure
|
37
|
+
stdin.close
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
chunk = ChunkTest.new('localhost', 8888)
|
44
|
+
chunk.run
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "fluent-plugin-http-ex"
|
7
|
+
spec.version = "0.0.1"
|
8
|
+
spec.authors = ["hiro-su"]
|
9
|
+
spec.email = ["h.sugipon@gmail.com"]
|
10
|
+
spec.description = %q{fluent plugin to accept multiple json/msgpack events in HTTP request}
|
11
|
+
spec.summary = %q{fluent plugin to accept multiple json/msgpack events in HTTP request}
|
12
|
+
spec.homepage = "https://github.com/hiro-su/fluent-plugin-http-ex"
|
13
|
+
|
14
|
+
spec.files = `git ls-files`.split($/)
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "fluentd"
|
20
|
+
spec.add_runtime_dependency "fluentd"
|
21
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
module Fluent
|
2
|
+
|
3
|
+
class ExHttpInput < HttpInput
|
4
|
+
Plugin.register_input('http_ex', self)
|
5
|
+
|
6
|
+
config_param :port, :integer, :default => 8888
|
7
|
+
config_param :bind, :string, :default => '0.0.0.0'
|
8
|
+
config_param :body_size_limit, :size, :default => 32*1024*1024
|
9
|
+
config_param :keepalive_timeout, :time, :default => 30
|
10
|
+
|
11
|
+
class KeepaliveManager < Coolio::TimerWatcher
|
12
|
+
class TimerValue
|
13
|
+
def initialize
|
14
|
+
@value = 0
|
15
|
+
end
|
16
|
+
attr_accessor :value
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(timeout)
|
20
|
+
super(1, true)
|
21
|
+
@cons = {}
|
22
|
+
@timeout = timeout.to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(sock)
|
26
|
+
@cons[sock] = sock
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(sock)
|
30
|
+
@cons.delete(sock)
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_timer
|
34
|
+
@cons.each_pair {|sock,val|
|
35
|
+
if sock.step_idle > @timeout
|
36
|
+
sock.close unless @timeout == 0
|
37
|
+
end
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def start
|
43
|
+
$log.debug "listening http on #{@bind}:#{@port}"
|
44
|
+
lsock = TCPServer.new(@bind, @port)
|
45
|
+
|
46
|
+
detach_multi_process do
|
47
|
+
Input.new.start
|
48
|
+
@km = KeepaliveManager.new(@keepalive_timeout)
|
49
|
+
@lsock = Coolio::TCPServer.new(lsock, nil, ExHandler, @km, method(:on_request), @body_size_limit)
|
50
|
+
|
51
|
+
@loop = Coolio::Loop.new
|
52
|
+
@loop.attach(@km)
|
53
|
+
@loop.attach(@lsock)
|
54
|
+
|
55
|
+
@thread = Thread.new(&method(:run))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_request(path_info, params)
|
60
|
+
$log.debug "remote_addr: #{params["REMOTE_ADDR"]}, path_info: #{path_info}"
|
61
|
+
begin
|
62
|
+
path = path_info[1..-1] # remove /
|
63
|
+
resource, tag = path.split('/')
|
64
|
+
tag ||= resource
|
65
|
+
|
66
|
+
if chunk = params['chunked']
|
67
|
+
record = chunk
|
68
|
+
|
69
|
+
elsif js = params['json']
|
70
|
+
record = JSON.parse(js)
|
71
|
+
|
72
|
+
elsif ms = params['x-msgpack'] || ms = params['msgpack']
|
73
|
+
record = MessagePack::unpack(ms)
|
74
|
+
|
75
|
+
else
|
76
|
+
raise "'json' or 'msgpack' parameter is required"
|
77
|
+
end
|
78
|
+
|
79
|
+
time = params['time']
|
80
|
+
time = time.to_i
|
81
|
+
if time == 0
|
82
|
+
time = Engine.now
|
83
|
+
end
|
84
|
+
|
85
|
+
rescue
|
86
|
+
return ["400 Bad Request", {'Content-type'=>'text/plain'}, "400 Bad Request\n#{$!}\n"]
|
87
|
+
end
|
88
|
+
|
89
|
+
# TODO server error
|
90
|
+
begin
|
91
|
+
case params["resource"]
|
92
|
+
when :js
|
93
|
+
record.each do |v|
|
94
|
+
line = begin
|
95
|
+
JSON.parse(v)
|
96
|
+
rescue TypeError
|
97
|
+
v #hash
|
98
|
+
end
|
99
|
+
Engine.emit(tag, time, line)
|
100
|
+
end
|
101
|
+
|
102
|
+
when :ms
|
103
|
+
record.each {|line| Engine.emit(tag, time, line) }
|
104
|
+
|
105
|
+
else
|
106
|
+
Engine.emit(tag, time, record)
|
107
|
+
end
|
108
|
+
|
109
|
+
rescue
|
110
|
+
return ["500 Internal Server Error", {'Content-type'=>'text/plain'}, "500 Internal Server Error\n#{$!}\n"]
|
111
|
+
end
|
112
|
+
|
113
|
+
return ["200 OK", {'Content-type'=>'text/plain'}, ""]
|
114
|
+
end
|
115
|
+
|
116
|
+
class ExHandler < Handler
|
117
|
+
def on_close
|
118
|
+
$log.debug "close #{@remote_addr}:#{@remote_port}"
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
122
|
+
def on_headers_complete(headers)
|
123
|
+
expect = nil
|
124
|
+
size = nil
|
125
|
+
if @parser.http_version == [1, 1]
|
126
|
+
@keep_alive = true
|
127
|
+
else
|
128
|
+
@keep_alive = false
|
129
|
+
end
|
130
|
+
@env = {}
|
131
|
+
headers.each_pair {|k,v|
|
132
|
+
@env["HTTP_#{k.gsub('-','_').upcase}"] = v
|
133
|
+
case k
|
134
|
+
when /Expect/i
|
135
|
+
expect = v
|
136
|
+
when /Content-Length/i
|
137
|
+
size = v.to_i
|
138
|
+
when /Content-Type/i
|
139
|
+
@content_type = v
|
140
|
+
when /Connection/i
|
141
|
+
if v =~ /close/i
|
142
|
+
@keep_alive = false
|
143
|
+
elsif v =~ /Keep-alive/i
|
144
|
+
@keep_alive = true
|
145
|
+
end
|
146
|
+
when /Transfer-Encoding/i
|
147
|
+
if v =~ /chunked/i
|
148
|
+
@chunked = true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
}
|
152
|
+
if expect
|
153
|
+
if expect == '100-continue'
|
154
|
+
if !size || size < @body_size_limit
|
155
|
+
send_response_nobody("100 Continue", {})
|
156
|
+
else
|
157
|
+
send_response_and_close("413 Request Entity Too Large", {}, "Too large")
|
158
|
+
end
|
159
|
+
else
|
160
|
+
send_response_and_close("417 Expectation Failed", {}, "")
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def on_body(chunk)
|
166
|
+
if @chunked && @content_type =~ /application\/x-msgpack/i
|
167
|
+
m = method(:on_read_msgpack)
|
168
|
+
@u = MessagePack::Unpacker.new
|
169
|
+
(class << self; self; end).module_eval do
|
170
|
+
define_method(:on_body, m)
|
171
|
+
end
|
172
|
+
m.call(chunk)
|
173
|
+
else
|
174
|
+
if @body.bytesize + chunk.bytesize > @body_size_limit
|
175
|
+
unless closing?
|
176
|
+
send_response_and_close("413 Request Entity Too Large", {}, "Too large")
|
177
|
+
end
|
178
|
+
return
|
179
|
+
end
|
180
|
+
@body << chunk
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def on_read_msgpack(data)
|
185
|
+
params = WEBrick::HTTPUtils.parse_query(@parser.query_string)
|
186
|
+
path_info = @parser.request_path
|
187
|
+
@u.feed_each(data) do |obj|
|
188
|
+
params["chunked"] = obj
|
189
|
+
params["REMOTE_ADDR"] = @remote_addr
|
190
|
+
@callback.call(path_info, params)
|
191
|
+
end
|
192
|
+
rescue
|
193
|
+
$log.error "on_read_msgpack error: #{$!.to_s}"
|
194
|
+
$log.error_backtrace
|
195
|
+
close
|
196
|
+
end
|
197
|
+
|
198
|
+
def on_message_complete
|
199
|
+
return if closing?
|
200
|
+
|
201
|
+
@env['REMOTE_ADDR'] = @remote_addr
|
202
|
+
|
203
|
+
params = WEBrick::HTTPUtils.parse_query(@parser.query_string)
|
204
|
+
path_info = @parser.request_path
|
205
|
+
|
206
|
+
params = check_content_type(params, @content_type, @body, path_info)
|
207
|
+
params.merge!(@env)
|
208
|
+
@env.clear
|
209
|
+
|
210
|
+
unless @chunked
|
211
|
+
code, header, body = *@callback.call(path_info, params)
|
212
|
+
body = body.to_s
|
213
|
+
|
214
|
+
if @keep_alive
|
215
|
+
header['Connection'] = 'Keep-Alive'
|
216
|
+
send_response(code, header, body)
|
217
|
+
else
|
218
|
+
send_response_and_close(code, header, body)
|
219
|
+
end
|
220
|
+
else
|
221
|
+
send_response("200 OK", {'Content-type'=>'text/plain', 'Connection'=>'Keep-Alive'}, "")
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def check_content_type(params, content_type, body, path_info)
|
226
|
+
path = path_info[1..-1] # remove /
|
227
|
+
resource, _ = path.split('/')
|
228
|
+
case resource
|
229
|
+
when /^js$/
|
230
|
+
params["resource"] = :js
|
231
|
+
when /^ms$/
|
232
|
+
params["resource"] = :ms
|
233
|
+
end
|
234
|
+
|
235
|
+
if content_type =~ /^application\/x-www-form-urlencoded/
|
236
|
+
params.update WEBrick::HTTPUtils.parse_query(body)
|
237
|
+
elsif content_type =~ /^multipart\/form-data; boundary=(.+)/
|
238
|
+
boundary = WEBrick::HTTPUtils.dequote($1)
|
239
|
+
params.update WEBrick::HTTPUtils.parse_form_data(body, boundary)
|
240
|
+
elsif content_type =~ /^application\/(json|x-msgpack)/
|
241
|
+
params[$1] = body
|
242
|
+
end
|
243
|
+
|
244
|
+
params
|
245
|
+
rescue => ex
|
246
|
+
$log.error ex
|
247
|
+
$log.error ex.backtrace * "\n"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
require 'fluent/test'
|
7
|
+
require 'in_http_ex_test'
|
8
|
+
|
9
|
+
unless ENV.has_key?('VERBOSE')
|
10
|
+
nulllogger = Object.new
|
11
|
+
nulllogger.instance_eval {|obj|
|
12
|
+
def method_missing(method, *args)
|
13
|
+
# pass
|
14
|
+
end
|
15
|
+
}
|
16
|
+
$log = nulllogger
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'fluent/plugin/in_http'
|
20
|
+
require 'fluent/plugin/in_http_ex'
|
21
|
+
|
22
|
+
class Test::Unit::TestCase
|
23
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
#
|
2
|
+
# Fluent
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module Fluent
|
19
|
+
module Test
|
20
|
+
|
21
|
+
|
22
|
+
class ExHttpInputTestDriver < TestDriver
|
23
|
+
def initialize(klass, &block)
|
24
|
+
super(klass, &block)
|
25
|
+
@emit_streams = []
|
26
|
+
@expects = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def expect_emit(tag, time, record)
|
30
|
+
(@expects ||= []) << [tag, time, record]
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def expected_emits
|
35
|
+
@expects ||= []
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :emit_streams
|
39
|
+
|
40
|
+
def emits
|
41
|
+
all = []
|
42
|
+
@emit_streams.each {|tag,events|
|
43
|
+
events.each {|time,record|
|
44
|
+
all << [tag, time, record]
|
45
|
+
}
|
46
|
+
}
|
47
|
+
all
|
48
|
+
end
|
49
|
+
|
50
|
+
def events
|
51
|
+
all = []
|
52
|
+
@emit_streams.each {|tag,events|
|
53
|
+
all.concat events
|
54
|
+
}
|
55
|
+
all
|
56
|
+
end
|
57
|
+
|
58
|
+
def records
|
59
|
+
all = []
|
60
|
+
@emit_streams.each {|tag,events|
|
61
|
+
events.each {|time,record|
|
62
|
+
all << record
|
63
|
+
}
|
64
|
+
}
|
65
|
+
all
|
66
|
+
end
|
67
|
+
|
68
|
+
def run(&block)
|
69
|
+
m = method(:emit_stream)
|
70
|
+
super {
|
71
|
+
Engine.define_singleton_method(:emit_stream) {|tag,es|
|
72
|
+
m.call(tag, es)
|
73
|
+
}
|
74
|
+
|
75
|
+
block.call if block
|
76
|
+
|
77
|
+
if @expects && @emit_streams.size < 2
|
78
|
+
i = 0
|
79
|
+
@emit_streams.each {|tag,events|
|
80
|
+
events.each {|time,record|
|
81
|
+
assert_equal(@expects[i], [tag, time, record])
|
82
|
+
}
|
83
|
+
}
|
84
|
+
assert_equal @expects.length, i
|
85
|
+
elsif @expects
|
86
|
+
return [@expects, @emit_streams]
|
87
|
+
end
|
88
|
+
}
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def emit_stream(tag, es)
|
94
|
+
@emit_streams << [tag, es.to_a]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,347 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
class ExHttpInputTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Fluent::Test.setup
|
7
|
+
end
|
8
|
+
|
9
|
+
CONFIG = %[
|
10
|
+
port 9911
|
11
|
+
bind 127.0.0.1
|
12
|
+
body_size_limit 10m
|
13
|
+
keepalive_timeout 5
|
14
|
+
]
|
15
|
+
|
16
|
+
def create_driver(conf=CONFIG)
|
17
|
+
Fluent::Test::ExHttpInputTestDriver.new(Fluent::ExHttpInput).configure(conf)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_configure
|
21
|
+
d = create_driver
|
22
|
+
assert_equal 9911, d.instance.port
|
23
|
+
assert_equal '127.0.0.1', d.instance.bind
|
24
|
+
assert_equal 10*1024*1024, d.instance.body_size_limit
|
25
|
+
assert_equal 5, d.instance.keepalive_timeout
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_time
|
29
|
+
d = create_driver
|
30
|
+
|
31
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
32
|
+
Fluent::Engine.now = time
|
33
|
+
|
34
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
35
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
36
|
+
|
37
|
+
d.run do
|
38
|
+
d.expected_emits.each {|tag,time,record|
|
39
|
+
res = post("/#{tag}", {"json"=>record.to_json})
|
40
|
+
assert_equal "200", res.code
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_json
|
46
|
+
d = create_driver
|
47
|
+
|
48
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
49
|
+
|
50
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
51
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
52
|
+
|
53
|
+
d.run do
|
54
|
+
d.expected_emits.each {|tag,time,record|
|
55
|
+
res = post("/#{tag}", {"json"=>record.to_json, "time"=>time.to_s})
|
56
|
+
assert_equal "200", res.code
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_application_json
|
62
|
+
d = create_driver
|
63
|
+
|
64
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
65
|
+
|
66
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
67
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
68
|
+
|
69
|
+
d.run do
|
70
|
+
d.expected_emits.each {|tag,time,record|
|
71
|
+
http = Net::HTTP.new("127.0.0.1", 9911)
|
72
|
+
req = Net::HTTP::Post.new("/#{tag}?time=#{time.to_s}", {"content-type"=>"application/json; charset=utf-8"})
|
73
|
+
req.body = record.to_json
|
74
|
+
res = http.request(req)
|
75
|
+
assert_equal "200", res.code
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_resource_json
|
81
|
+
d = create_driver
|
82
|
+
|
83
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
84
|
+
|
85
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
86
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
87
|
+
|
88
|
+
d.run do
|
89
|
+
d.expected_emits.each {|tag,time,record|
|
90
|
+
res = post("/j/#{tag}", {"json"=>record.to_json, "time"=>time.to_s})
|
91
|
+
assert_equal "200", res.code
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_application_resource_json
|
97
|
+
d = create_driver
|
98
|
+
|
99
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
100
|
+
|
101
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
102
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
103
|
+
|
104
|
+
d.run do
|
105
|
+
d.expected_emits.each {|tag,time,record|
|
106
|
+
http = Net::HTTP.new("127.0.0.1", 9911)
|
107
|
+
req = Net::HTTP::Post.new("/j/#{tag}?time=#{time.to_s}", {"content-type"=>"application/json; charset=utf-8"})
|
108
|
+
req.body = record.to_json
|
109
|
+
res = http.request(req)
|
110
|
+
assert_equal "200", res.code
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_resource_json_list
|
116
|
+
d = create_driver
|
117
|
+
|
118
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
119
|
+
|
120
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
121
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
122
|
+
d.expect_emit "tag3", time, {"a"=>3}
|
123
|
+
|
124
|
+
rs = d.run do
|
125
|
+
d.expected_emits.each {|tag,time,record|
|
126
|
+
res = post("/js/#{tag}", {"json"=>"[#{record.to_json},#{record.to_json},#{record.to_json}]", "time"=>time.to_s})
|
127
|
+
assert_equal "200", res.code
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
expects, results = rs
|
132
|
+
i, c = 0, 0
|
133
|
+
results.each {|tag,es|
|
134
|
+
es.each {|time,record|
|
135
|
+
assert_equal(expects[i], [tag, time, record])
|
136
|
+
c += 1
|
137
|
+
i += 1 if c % 3 == 0
|
138
|
+
}
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_application_resource_json_list
|
143
|
+
d = create_driver
|
144
|
+
|
145
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
146
|
+
|
147
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
148
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
149
|
+
d.expect_emit "tag3", time, {"a"=>3}
|
150
|
+
|
151
|
+
rs = d.run do
|
152
|
+
d.expected_emits.each {|tag,time,record|
|
153
|
+
http = Net::HTTP.new("127.0.0.1", 9911)
|
154
|
+
req = Net::HTTP::Post.new("/js/#{tag}?time=#{time.to_s}", {"content-type"=>"application/json; charset=utf-8"})
|
155
|
+
req.body = "[#{record.to_json},#{record.to_json},#{record.to_json}]"
|
156
|
+
res = http.request(req)
|
157
|
+
assert_equal "200", res.code
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
expects, results = rs
|
162
|
+
i, c = 0, 0
|
163
|
+
results.each {|tag,es|
|
164
|
+
es.each {|time,record|
|
165
|
+
assert_equal(expects[i], [tag, time, record])
|
166
|
+
c += 1
|
167
|
+
i += 1 if c % 3 == 0
|
168
|
+
}
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_msgpack
|
173
|
+
d = create_driver
|
174
|
+
|
175
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
176
|
+
|
177
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
178
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
179
|
+
|
180
|
+
d.run do
|
181
|
+
d.expected_emits.each {|tag,time,record|
|
182
|
+
res = post("/#{tag}", {"msgpack"=>record.to_msgpack, "time"=>time.to_s})
|
183
|
+
assert_equal "200", res.code
|
184
|
+
}
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_application_msgpack
|
189
|
+
d = create_driver
|
190
|
+
|
191
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
192
|
+
|
193
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
194
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
195
|
+
|
196
|
+
d.run do
|
197
|
+
d.expected_emits.each {|tag,time,record|
|
198
|
+
http = Net::HTTP.new("127.0.0.1", 9911)
|
199
|
+
req = Net::HTTP::Post.new("/#{tag}?time=#{time.to_s}", {"content-type"=>"application/x-msgpack; charset=utf-8"})
|
200
|
+
req.body = record.to_msgpack
|
201
|
+
res = http.request(req)
|
202
|
+
assert_equal "200", res.code
|
203
|
+
}
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_resource_msgpack
|
208
|
+
d = create_driver
|
209
|
+
|
210
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
211
|
+
|
212
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
213
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
214
|
+
|
215
|
+
d.run do
|
216
|
+
d.expected_emits.each {|tag,time,record|
|
217
|
+
res = post("/m/#{tag}", {"msgpack"=>record.to_msgpack, "time"=>time.to_s})
|
218
|
+
assert_equal "200", res.code
|
219
|
+
}
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_application_resource_msgpack
|
224
|
+
d = create_driver
|
225
|
+
|
226
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
227
|
+
|
228
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
229
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
230
|
+
|
231
|
+
d.run do
|
232
|
+
d.expected_emits.each {|tag,time,record|
|
233
|
+
http = Net::HTTP.new("127.0.0.1", 9911)
|
234
|
+
req = Net::HTTP::Post.new("/m/#{tag}?time=#{time.to_s}", {"content-type"=>"application/x-msgpack; charset=utf-8"})
|
235
|
+
req.body = record.to_msgpack
|
236
|
+
res = http.request(req)
|
237
|
+
assert_equal "200", res.code
|
238
|
+
}
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_resource_msgpack_list
|
243
|
+
d = create_driver
|
244
|
+
|
245
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
246
|
+
|
247
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
248
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
249
|
+
d.expect_emit "tag3", time, {"a"=>3}
|
250
|
+
|
251
|
+
rs = d.run do
|
252
|
+
d.expected_emits.each {|tag,time,record|
|
253
|
+
res = post("/ms/#{tag}", {"msgpack"=>[record,record,record].to_msgpack, "time"=>time.to_s})
|
254
|
+
assert_equal "200", res.code
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
expects, results = rs
|
259
|
+
i, c = 0, 0
|
260
|
+
results.each {|tag,es|
|
261
|
+
es.each {|time,record|
|
262
|
+
assert_equal(expects[i], [tag, time, record])
|
263
|
+
c += 1
|
264
|
+
i += 1 if c % 3 == 0
|
265
|
+
}
|
266
|
+
}
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_application_resource_msgpack_list
|
270
|
+
d = create_driver
|
271
|
+
|
272
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
273
|
+
|
274
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
275
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
276
|
+
d.expect_emit "tag3", time, {"a"=>3}
|
277
|
+
|
278
|
+
rs = d.run do
|
279
|
+
d.expected_emits.each {|tag,time,record|
|
280
|
+
http = Net::HTTP.new("127.0.0.1", 9911)
|
281
|
+
req = Net::HTTP::Post.new("/ms/#{tag}?time=#{time.to_s}", {"content-type"=>"application/x-msgpack; charset=utf-8"})
|
282
|
+
req.body = [record,record,record].to_msgpack
|
283
|
+
res = http.request(req)
|
284
|
+
assert_equal "200", res.code
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
expects, results = rs
|
289
|
+
i, c = 0, 0
|
290
|
+
results.each {|tag,es|
|
291
|
+
es.each {|time,record|
|
292
|
+
assert_equal(expects[i], [tag, time, record])
|
293
|
+
c += 1
|
294
|
+
i += 1 if c % 3 == 0
|
295
|
+
}
|
296
|
+
}
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_msgpack_chunked
|
300
|
+
require 'stringio'
|
301
|
+
|
302
|
+
d = create_driver
|
303
|
+
|
304
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
305
|
+
|
306
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
307
|
+
d.expect_emit "tag1", time, {"a"=>2}
|
308
|
+
d.expect_emit "tag1", time, {"a"=>3}
|
309
|
+
|
310
|
+
http = Net::HTTP.new("127.0.0.1", 9911)
|
311
|
+
|
312
|
+
rs = d.run do
|
313
|
+
io = StringIO.new
|
314
|
+
req = ""
|
315
|
+
d.expected_emits.each {|tag,time,record|
|
316
|
+
req = Net::HTTP::Post.new("/ms/#{tag}?time=#{time.to_s}")
|
317
|
+
req["Content-Type"] = "application/x-msgpack"
|
318
|
+
req["Transfer-Encoding"] = "chunked"
|
319
|
+
io << record.to_msgpack
|
320
|
+
io << record.to_msgpack
|
321
|
+
io << record.to_msgpack
|
322
|
+
}
|
323
|
+
io.rewind
|
324
|
+
req.body_stream = io
|
325
|
+
res = http.request(req)
|
326
|
+
assert_equal "200", res.code
|
327
|
+
end
|
328
|
+
|
329
|
+
expects, results = rs
|
330
|
+
i, c = 0, 0
|
331
|
+
results.each {|tag,es|
|
332
|
+
es.each {|time,record|
|
333
|
+
assert_equal(expects[i], [tag, time, record])
|
334
|
+
c += 1
|
335
|
+
i += 1 if c % 3 == 0
|
336
|
+
}
|
337
|
+
}
|
338
|
+
end
|
339
|
+
|
340
|
+
def post(path, params)
|
341
|
+
http = Net::HTTP.new("127.0.0.1", 9911)
|
342
|
+
req = Net::HTTP::Post.new(path, {})
|
343
|
+
req.set_form_data(params)
|
344
|
+
http.request(req)
|
345
|
+
end
|
346
|
+
|
347
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-http-ex
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- hiro-su
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-08-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fluentd
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fluentd
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: fluent plugin to accept multiple json/msgpack events in HTTP request
|
42
|
+
email:
|
43
|
+
- h.sugipon@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- Gemfile
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- examples/chunked.rb
|
54
|
+
- examples/json.rb
|
55
|
+
- examples/json_list.rb
|
56
|
+
- examples/msgpack.rb
|
57
|
+
- examples/msgpack_list.rb
|
58
|
+
- examples/nc_chunked.rb
|
59
|
+
- examples/sample.conf
|
60
|
+
- fluent-plugin-http-ex.gemspec
|
61
|
+
- lib/fluent/plugin/in_http_ex.rb
|
62
|
+
- test/helper.rb
|
63
|
+
- test/in_http_ex_test.rb
|
64
|
+
- test/plugin/test_in_http_ex.rb
|
65
|
+
homepage: https://github.com/hiro-su/fluent-plugin-http-ex
|
66
|
+
licenses: []
|
67
|
+
metadata: {}
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 2.0.3
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: fluent plugin to accept multiple json/msgpack events in HTTP request
|
88
|
+
test_files:
|
89
|
+
- test/helper.rb
|
90
|
+
- test/in_http_ex_test.rb
|
91
|
+
- test/plugin/test_in_http_ex.rb
|