fluentd 0.14.8 → 0.14.9

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CONTRIBUTING.md +6 -1
  4. data/ChangeLog +38 -0
  5. data/Rakefile +21 -0
  6. data/example/out_exec_filter.conf +42 -0
  7. data/lib/fluent/agent.rb +2 -2
  8. data/lib/fluent/command/binlog_reader.rb +1 -1
  9. data/lib/fluent/command/cat.rb +5 -2
  10. data/lib/fluent/compat/output.rb +7 -8
  11. data/lib/fluent/compat/parser.rb +139 -11
  12. data/lib/fluent/config/configure_proxy.rb +2 -11
  13. data/lib/fluent/config/section.rb +7 -0
  14. data/lib/fluent/configurable.rb +1 -3
  15. data/lib/fluent/log.rb +1 -1
  16. data/lib/fluent/plugin/base.rb +17 -0
  17. data/lib/fluent/plugin/filter_parser.rb +108 -0
  18. data/lib/fluent/plugin/filter_record_transformer.rb +4 -7
  19. data/lib/fluent/plugin/filter_stdout.rb +1 -1
  20. data/lib/fluent/plugin/formatter.rb +5 -0
  21. data/lib/fluent/plugin/formatter_msgpack.rb +4 -0
  22. data/lib/fluent/plugin/formatter_stdout.rb +3 -2
  23. data/lib/fluent/plugin/formatter_tsv.rb +34 -0
  24. data/lib/fluent/plugin/in_exec.rb +48 -93
  25. data/lib/fluent/plugin/in_forward.rb +25 -105
  26. data/lib/fluent/plugin/in_http.rb +68 -65
  27. data/lib/fluent/plugin/in_syslog.rb +29 -51
  28. data/lib/fluent/plugin/multi_output.rb +1 -3
  29. data/lib/fluent/plugin/out_exec.rb +58 -71
  30. data/lib/fluent/plugin/out_exec_filter.rb +199 -279
  31. data/lib/fluent/plugin/out_file.rb +155 -80
  32. data/lib/fluent/plugin/out_forward.rb +44 -47
  33. data/lib/fluent/plugin/out_stdout.rb +6 -21
  34. data/lib/fluent/plugin/output.rb +23 -17
  35. data/lib/fluent/plugin/parser.rb +121 -61
  36. data/lib/fluent/plugin/parser_csv.rb +9 -3
  37. data/lib/fluent/plugin/parser_json.rb +37 -35
  38. data/lib/fluent/plugin/parser_ltsv.rb +11 -19
  39. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  40. data/lib/fluent/plugin/parser_regexp.rb +15 -42
  41. data/lib/fluent/plugin/parser_tsv.rb +8 -3
  42. data/lib/fluent/plugin_helper.rb +8 -1
  43. data/lib/fluent/plugin_helper/child_process.rb +139 -73
  44. data/lib/fluent/plugin_helper/compat_parameters.rb +93 -4
  45. data/lib/fluent/plugin_helper/event_emitter.rb +14 -1
  46. data/lib/fluent/plugin_helper/extract.rb +16 -4
  47. data/lib/fluent/plugin_helper/formatter.rb +9 -11
  48. data/lib/fluent/plugin_helper/inject.rb +4 -0
  49. data/lib/fluent/plugin_helper/parser.rb +3 -3
  50. data/lib/fluent/root_agent.rb +1 -1
  51. data/lib/fluent/test/driver/base.rb +51 -37
  52. data/lib/fluent/test/driver/base_owner.rb +18 -8
  53. data/lib/fluent/test/driver/multi_output.rb +2 -1
  54. data/lib/fluent/test/driver/output.rb +29 -6
  55. data/lib/fluent/test/helpers.rb +3 -1
  56. data/lib/fluent/test/log.rb +4 -0
  57. data/lib/fluent/test/startup_shutdown.rb +13 -0
  58. data/lib/fluent/time.rb +14 -8
  59. data/lib/fluent/version.rb +1 -1
  60. data/test/command/test_binlog_reader.rb +5 -1
  61. data/test/config/test_configurable.rb +173 -0
  62. data/test/config/test_configure_proxy.rb +0 -43
  63. data/test/plugin/test_base.rb +16 -0
  64. data/test/plugin/test_filter_parser.rb +665 -0
  65. data/test/plugin/test_filter_record_transformer.rb +11 -3
  66. data/test/plugin/test_filter_stdout.rb +18 -27
  67. data/test/plugin/test_in_dummy.rb +1 -1
  68. data/test/plugin/test_in_exec.rb +206 -94
  69. data/test/plugin/test_in_forward.rb +310 -327
  70. data/test/plugin/test_in_http.rb +310 -186
  71. data/test/plugin/test_out_exec.rb +223 -68
  72. data/test/plugin/test_out_exec_filter.rb +520 -169
  73. data/test/plugin/test_out_file.rb +620 -177
  74. data/test/plugin/test_out_forward.rb +110 -132
  75. data/test/plugin/test_out_null.rb +1 -1
  76. data/test/plugin/test_out_secondary_file.rb +4 -2
  77. data/test/plugin/test_out_stdout.rb +14 -35
  78. data/test/plugin/test_parser.rb +359 -0
  79. data/test/plugin/test_parser_csv.rb +1 -2
  80. data/test/plugin/test_parser_json.rb +3 -4
  81. data/test/plugin/test_parser_labeled_tsv.rb +1 -2
  82. data/test/plugin/test_parser_none.rb +1 -2
  83. data/test/plugin/test_parser_regexp.rb +8 -4
  84. data/test/plugin/test_parser_tsv.rb +4 -3
  85. data/test/plugin_helper/test_child_process.rb +184 -0
  86. data/test/plugin_helper/test_compat_parameters.rb +88 -1
  87. data/test/plugin_helper/test_extract.rb +0 -1
  88. data/test/plugin_helper/test_formatter.rb +5 -2
  89. data/test/plugin_helper/test_parser.rb +6 -5
  90. data/test/test_output.rb +24 -2
  91. data/test/test_plugin_classes.rb +20 -0
  92. data/test/test_root_agent.rb +139 -0
  93. data/test/test_test_drivers.rb +132 -0
  94. metadata +12 -4
  95. data/test/plugin/test_parser_base.rb +0 -32
@@ -14,23 +14,26 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
- require 'fcntl'
18
17
 
19
- require 'cool.io'
18
+ require 'fluent/plugin/input'
19
+ require 'fluent/msgpack_factory'
20
20
  require 'yajl'
21
+ require 'digest'
21
22
 
22
- require 'fluent/input'
23
+ require 'fluent/plugin/socket_util'
24
+ require 'fcntl'
25
+ require 'cool.io'
23
26
 
24
- module Fluent
27
+ module Fluent::Plugin
25
28
  class ForwardInput < Input
26
- Plugin.register_input('forward', self)
29
+ Fluent::Plugin.register_input('forward', self)
27
30
 
28
- LISTEN_PORT = 24224
31
+ # See the wiki page below for protocol specification
32
+ # https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1
29
33
 
30
- def initialize
31
- super
32
- require 'fluent/plugin/socket_util'
33
- end
34
+ helpers :event_loop
35
+
36
+ LISTEN_PORT = 24224
34
37
 
35
38
  desc 'The port to listen to.'
36
39
  config_param :port, :integer, default: LISTEN_PORT
@@ -125,13 +128,12 @@ module Fluent
125
128
  })
126
129
  end
127
130
  end
131
+ @lsock = @usock = nil
128
132
  end
129
133
 
130
134
  def start
131
135
  super
132
136
 
133
- @loop = Coolio::Loop.new
134
-
135
137
  socket_manager_path = ENV['SERVERENGINE_SOCKETMANAGER_PATH']
136
138
  if Fluent.windows?
137
139
  socket_manager_path = socket_manager_path.to_i
@@ -139,32 +141,17 @@ module Fluent
139
141
  client = ServerEngine::SocketManager::Client.new(socket_manager_path)
140
142
 
141
143
  @lsock = listen(client)
142
- @loop.attach(@lsock)
144
+ event_loop_attach(@lsock)
143
145
 
144
146
  @usock = client.listen_udp(@bind, @port)
145
147
  @usock.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
146
148
  @hbr = HeartbeatRequestHandler.new(@usock, method(:on_heartbeat_request))
147
- @loop.attach(@hbr)
148
-
149
- @thread = Thread.new(&method(:run))
149
+ event_loop_attach(@hbr)
150
150
  end
151
151
 
152
- def shutdown
153
- # In test cases it occasionally appeared that when detaching a watcher, another watcher is also detached.
154
- # In the case in the iteration of watchers, a watcher that has been already detached is intended to be detached
155
- # and therfore RuntimeError occurs saying that it is not attached to a loop.
156
- # It occurs only when testing for sending responses to ForwardOutput.
157
- # Sending responses needs to write the socket that is previously used only to read
158
- # and a handler has 2 watchers that is used to read and to write.
159
- # This problem occurs possibly because those watchers are thought to be related to each other
160
- # and when detaching one of them the other is also detached for some reasons.
161
- # As a workaround, check if watchers are attached before detaching them.
162
- @loop.watchers.each {|w| w.detach if w.attached? }
163
- @loop.stop
164
- @usock.close
165
- @thread.join
166
- @lsock.close
167
-
152
+ def close
153
+ @lsock.close if @lsock
154
+ @usock.close if @usock
168
155
  super
169
156
  end
170
157
 
@@ -176,23 +163,6 @@ module Fluent
176
163
  s
177
164
  end
178
165
 
179
- #config_param :path, :string, :default => DEFAULT_SOCKET_PATH
180
- #def listen
181
- # if File.exist?(@path)
182
- # File.unlink(@path)
183
- # end
184
- # FileUtils.mkdir_p File.dirname(@path)
185
- # log.debug "listening fluent socket on #{@path}"
186
- # Coolio::UNIXServer.new(@path, Handler, method(:on_message))
187
- #end
188
-
189
- def run
190
- @loop.run(@blocking_timeout)
191
- rescue => e
192
- log.error "unexpected error", error: e
193
- log.error_backtrace
194
- end
195
-
196
166
  private
197
167
 
198
168
  def handle_connection(conn)
@@ -288,29 +258,6 @@ module Fluent
288
258
  nil
289
259
  end
290
260
 
291
- # message Entry {
292
- # 1: long time
293
- # 2: object record
294
- # }
295
- #
296
- # message Forward {
297
- # 1: string tag
298
- # 2: list<Entry> entries
299
- # 3: object option (optional)
300
- # }
301
- #
302
- # message PackedForward {
303
- # 1: string tag
304
- # 2: raw entries # msgpack stream of Entry
305
- # 3: object option (optional)
306
- # }
307
- #
308
- # message Message {
309
- # 1: string tag
310
- # 2: long? time
311
- # 3: object record
312
- # 4: object option (optional)
313
- # }
314
261
  def on_message(msg, chunk_size, peeraddr)
315
262
  if msg.nil?
316
263
  # for future TCP heartbeat_request
@@ -338,7 +285,7 @@ module Fluent
338
285
  # PackedForward
339
286
  option = msg[2]
340
287
  size = (option && option['size']) || 0
341
- es_class = (option && option['compressed'] == 'gzip') ? CompressedMessagePackEventStream : MessagePackEventStream
288
+ es_class = (option && option['compressed'] == 'gzip') ? Fluent::CompressedMessagePackEventStream : Fluent::MessagePackEventStream
342
289
  es = es_class.new(entries, nil, size.to_i)
343
290
  es = check_and_skip_invalid_event(tag, es, peeraddr) if @skip_invalid_event
344
291
  es = add_source_host(es, peeraddr[2]) if @source_hostname_key
@@ -349,12 +296,12 @@ module Fluent
349
296
  es = if @skip_invalid_event
350
297
  check_and_skip_invalid_event(tag, entries, peeraddr)
351
298
  else
352
- es = MultiEventStream.new
299
+ es = Fluent::MultiEventStream.new
353
300
  entries.each { |e|
354
301
  record = e[1]
355
302
  next if record.nil?
356
303
  time = e[0]
357
- time = (now ||= Engine.now) if time.to_i == 0
304
+ time = Fluent::Engine.now if time.nil? || time.to_i == 0 # `to_i == 0` for empty EventTime
358
305
  es.add(time, record)
359
306
  }
360
307
  es
@@ -372,7 +319,7 @@ module Fluent
372
319
  return msg[3] # retry never succeeded so return ack and drop incoming event.
373
320
  end
374
321
  return if record.nil?
375
- time = Engine.now if time.to_i == 0
322
+ time = Fluent::Engine.now if time.to_i == 0
376
323
  record[@source_hostname_key] = peeraddr[2] if @source_hostname_key
377
324
  router.emit(tag, time, record)
378
325
  option = msg[3]
@@ -387,7 +334,7 @@ module Fluent
387
334
  end
388
335
 
389
336
  def check_and_skip_invalid_event(tag, es, peeraddr)
390
- new_es = MultiEventStream.new
337
+ new_es = Fluent::MultiEventStream.new
391
338
  es.each { |time, record|
392
339
  if invalid_event?(tag, time, record)
393
340
  log.warn "skip invalid event:", source: source_message(peeraddr), tag: tag, time: time, record: record
@@ -399,7 +346,7 @@ module Fluent
399
346
  end
400
347
 
401
348
  def add_source_host(es, host)
402
- new_es = MultiEventStream.new
349
+ new_es = Fluent::MultiEventStream.new
403
350
  es.each { |time, record|
404
351
  record[@source_hostname_key] = host
405
352
  new_es.add(time, record)
@@ -430,33 +377,6 @@ module Fluent
430
377
  ['HELO', {'nonce' => nonce, 'auth' => (@security ? user_auth_salt : ''), 'keepalive' => !@deny_keepalive}]
431
378
  end
432
379
 
433
- ##### Authentication Handshake
434
- #
435
- # 1. (client) connect to server
436
- # * Socket handshake, checks certificate and its significate (in client, if using SSL)
437
- # 2. (server)
438
- # * check network/domain acl (if enabled)
439
- # * disconnect when failed
440
- # 3. (server) send HELO
441
- # * ['HELO', options(hash)]
442
- # * options:
443
- # * nonce: string (required)
444
- # * auth: string or blank_string (string: authentication required, and its salt is this value)
445
- # 4. (client) send PING
446
- # * ['PING', selfhostname, sharedkey_salt, sha512_hex(sharedkey_salt + selfhostname + nonce + sharedkey), username || '', sha512_hex(auth_salt + username + password) || '']
447
- # 5. (server) check PING
448
- # * check sharedkey
449
- # * check username / password (if required)
450
- # * send PONG FAILURE if failed
451
- # * ['PONG', false, 'reason of authentication failure', '', '']
452
- # 6. (server) send PONG
453
- # * ['PONG', bool(authentication result), 'reason if authentication failed', selfhostname, sha512_hex(salt + selfhostname + nonce + sharedkey)]
454
- # 7. (client) check PONG
455
- # * check sharedkey
456
- # * disconnect when failed
457
- # 8. connection established
458
- # * send data from client
459
-
460
380
  def check_ping(message, remote_addr, user_auth_salt, nonce)
461
381
  log.debug "checking ping"
462
382
  # ['PING', self_hostname, shared_key_salt, sha512_hex(shared_key_salt + self_hostname + nonce + shared_key), username || '', sha512_hex(auth_salt + username + password) || '']
@@ -14,28 +14,29 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
+ require 'fluent/plugin/input'
18
+ require 'fluent/plugin/parser'
19
+ require 'fluent/event'
20
+
21
+ require 'http/parser'
22
+ require 'webrick/httputils'
17
23
  require 'uri'
18
24
  require 'socket'
19
25
  require 'json'
20
26
 
21
- require 'cool.io'
22
-
23
- require 'fluent/input'
24
- require 'fluent/event'
25
- require 'fluent/process'
27
+ module Fluent::Plugin
28
+ class InHttpParser < Parser
29
+ Fluent::Plugin.register_parser('in_http', self)
30
+ def parse(text)
31
+ # this plugin is dummy implementation not to raise error
32
+ yield nil, nil
33
+ end
34
+ end
26
35
 
27
- module Fluent
28
36
  class HttpInput < Input
29
- Plugin.register_input('http', self)
30
-
31
- include DetachMultiProcessMixin
32
-
33
- require 'http/parser'
37
+ Fluent::Plugin.register_input('http', self)
34
38
 
35
- def initialize
36
- require 'webrick/httputils'
37
- super
38
- end
39
+ helpers :parser, :compat_parameters, :event_loop
39
40
 
40
41
  EMPTY_GIF_IMAGE = "GIF89a\u0001\u0000\u0001\u0000\x80\xFF\u0000\xFF\xFF\xFF\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;".force_encoding("UTF-8")
41
42
 
@@ -52,25 +53,36 @@ module Fluent
52
53
  config_param :add_http_headers, :bool, default: false
53
54
  desc 'Add REMOTE_ADDR header to the record.'
54
55
  config_param :add_remote_addr, :bool, default: false
55
- desc 'The format of the HTTP body.'
56
- config_param :format, :string, default: 'default'
57
56
  config_param :blocking_timeout, :time, default: 0.5
58
57
  desc 'Set a white list of domains that can do CORS (Cross-Origin Resource Sharing)'
59
58
  config_param :cors_allow_origins, :array, default: nil
60
59
  desc 'Respond with empty gif image of 1x1 pixel.'
61
60
  config_param :respond_with_empty_img, :bool, default: false
62
61
 
62
+ config_section :parse do
63
+ config_set_default :@type, 'in_http'
64
+ end
65
+
66
+ EVENT_RECORD_PARAMETER = '_event_record'
67
+
63
68
  def configure(conf)
69
+ compat_parameters_convert(conf, :parser)
70
+
64
71
  super
65
72
 
66
- m = if @format == 'default'
73
+ m = if @parser_configs.first['@type'] == 'in_http'
74
+ @parser_msgpack = parser_create(usage: 'parser_in_http_msgpack', type: 'msgpack')
75
+ @parser_msgpack.estimate_current_event = false
76
+ @parser_json = parser_create(usage: 'parser_in_http_json', type: 'json')
77
+ @parser_json.estimate_current_event = false
78
+ @format_name = 'default'
67
79
  method(:parse_params_default)
68
80
  else
69
- @parser = Plugin.new_parser(@format)
70
- @parser.configure(conf)
81
+ @parser = parser_create
82
+ @format_name = @parser_configs.first['@type']
71
83
  method(:parse_params_with_parser)
72
84
  end
73
- (class << self; self; end).module_eval do
85
+ self.singleton_class.module_eval do
74
86
  define_method(:parse_params, m)
75
87
  end
76
88
  end
@@ -100,7 +112,11 @@ module Fluent
100
112
  end
101
113
 
102
114
  def start
103
- log.debug "listening http on #{@bind}:#{@port}"
115
+ @_event_loop_run_timeout = @blocking_timeout
116
+
117
+ super
118
+
119
+ log.debug "listening http", bind: @bind, port: @port
104
120
 
105
121
  socket_manager_path = ENV['SERVERENGINE_SOCKETMANAGER_PATH']
106
122
  if Fluent.windows?
@@ -109,38 +125,24 @@ module Fluent
109
125
  client = ServerEngine::SocketManager::Client.new(socket_manager_path)
110
126
  lsock = client.listen_tcp(@bind, @port)
111
127
 
112
- detach_multi_process do
113
- super
114
- @km = KeepaliveManager.new(@keepalive_timeout)
115
- @lsock = Coolio::TCPServer.new(lsock, nil, Handler, @km, method(:on_request),
116
- @body_size_limit, @format, log,
117
- @cors_allow_origins)
118
- @lsock.listen(@backlog) unless @backlog.nil?
119
-
120
- @loop = Coolio::Loop.new
121
- @loop.attach(@km)
122
- @loop.attach(@lsock)
123
-
124
- @thread = Thread.new(&method(:run))
125
- end
128
+ @km = KeepaliveManager.new(@keepalive_timeout)
129
+ @lsock = Coolio::TCPServer.new(
130
+ lsock, nil, Handler, @km, method(:on_request),
131
+ @body_size_limit, @format_name, log,
132
+ @cors_allow_origins
133
+ )
134
+ @lsock.listen(@backlog) unless @backlog.nil?
135
+ event_loop_attach(@km)
136
+ event_loop_attach(@lsock)
137
+
138
+ @float_time_parser = Fluent::NumericTimeParser.new(:float)
126
139
  end
127
140
 
128
- def shutdown
129
- @loop.watchers.each {|w| w.detach }
130
- @loop.stop
141
+ def close
131
142
  @lsock.close
132
- @thread.join
133
-
134
143
  super
135
144
  end
136
145
 
137
- def run
138
- @loop.run(@blocking_timeout)
139
- rescue
140
- log.error "unexpected error", error: $!.to_s
141
- log.error_backtrace
142
- end
143
-
144
146
  def on_request(path_info, params)
145
147
  begin
146
148
  path = path_info[1..-1] # remove /
@@ -170,9 +172,9 @@ module Fluent
170
172
  end
171
173
  time = if param_time = params['time']
172
174
  param_time = param_time.to_f
173
- param_time.zero? ? Engine.now : Fluent::EventTime.from_time(Time.at(param_time))
175
+ param_time.zero? ? Fluent::Engine.now : @float_time_parser.parse(param_time)
174
176
  else
175
- record_time.nil? ? Engine.now : record_time
177
+ record_time.nil? ? Fluent::Engine.now : record_time
176
178
  end
177
179
  rescue
178
180
  return ["400 Bad Request", {'Content-Type'=>'text/plain'}, "400 Bad Request\n#{$!}\n"]
@@ -182,7 +184,7 @@ module Fluent
182
184
  begin
183
185
  # Support batched requests
184
186
  if record.is_a?(Array)
185
- mes = MultiEventStream.new
187
+ mes = Fluent::MultiEventStream.new
186
188
  record.each do |single_record|
187
189
  if @add_http_headers
188
190
  params.each_pair { |k,v|
@@ -215,22 +217,23 @@ module Fluent
215
217
  private
216
218
 
217
219
  def parse_params_default(params)
218
- record = if msgpack = params['msgpack']
219
- Engine.msgpack_factory.unpacker.feed(msgpack).read
220
- elsif js = params['json']
221
- JSON.parse(js)
222
- else
223
- raise "'json' or 'msgpack' parameter is required"
224
- end
225
- return nil, record
220
+ if msgpack = params['msgpack']
221
+ @parser_msgpack.parse(msgpack) do |_time, record|
222
+ return nil, record
223
+ end
224
+ elsif js = params['json']
225
+ @parser_json.parse(js) do |_time, record|
226
+ return nil, record
227
+ end
228
+ else
229
+ raise "'json' or 'msgpack' parameter is required"
230
+ end
226
231
  end
227
232
 
228
- EVENT_RECORD_PARAMETER = '_event_record'
229
-
230
233
  def parse_params_with_parser(params)
231
234
  if content = params[EVENT_RECORD_PARAMETER]
232
235
  @parser.parse(content) { |time, record|
233
- raise "Received event is not #{@format}: #{content}" if record.nil?
236
+ raise "Received event is not #{@format_name}: #{content}" if record.nil?
234
237
  return time, record
235
238
  }
236
239
  else
@@ -241,13 +244,13 @@ module Fluent
241
244
  class Handler < Coolio::Socket
242
245
  attr_reader :content_type
243
246
 
244
- def initialize(io, km, callback, body_size_limit, format, log, cors_allow_origins)
247
+ def initialize(io, km, callback, body_size_limit, format_name, log, cors_allow_origins)
245
248
  super(io)
246
249
  @km = km
247
250
  @callback = callback
248
251
  @body_size_limit = body_size_limit
249
252
  @next_close = false
250
- @format = format
253
+ @format_name = format_name
251
254
  @log = log
252
255
  @cors_allow_origins = cors_allow_origins
253
256
  @idle = 0
@@ -355,7 +358,7 @@ module Fluent
355
358
  uri = URI.parse(@parser.request_url)
356
359
  params = WEBrick::HTTPUtils.parse_query(uri.query)
357
360
 
358
- if @format != 'default'
361
+ if @format_name != 'default'
359
362
  params[EVENT_RECORD_PARAMETER] = @body
360
363
  elsif @content_type =~ /^application\/x-www-form-urlencoded/
361
364
  params.update WEBrick::HTTPUtils.parse_query(@body)