Module: Jimmu::BodyParser

Defined in:
lib/jimmu.rb

Overview

Parses application/x-www-form-urlencoded and multipart/form-data request bodies, and application/json bodies. All parsing is done byte-safe (ASCII-8BIT) throughout and only the final text values are tagged back to UTF-8, so binary upload data is never at risk of being mangled or raising encoding errors partway through.

Class Method Summary collapse

Class Method Details

.extract_boundary(content_type) ⇒ Object



487
488
489
490
# File 'lib/jimmu.rb', line 487

def extract_boundary(content_type)
  return nil unless content_type
  content_type[/boundary="?([^";]+)"?/, 1]
end

.parse_json(raw_body) ⇒ Object



432
433
434
435
436
437
438
# File 'lib/jimmu.rb', line 432

def parse_json(raw_body)
  return {} if raw_body.nil? || raw_body.empty?
  data = JSON.parse(raw_body.dup.force_encoding(Encoding::UTF_8), symbolize_names: true)
  data.is_a?(Hash) ? data : { _json: data }
rescue JSON::ParserError
  {}
end

.parse_multipart(raw_body, boundary) ⇒ Object

Returns [fields_hash, files_hash], both keyed by Symbol.



441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# File 'lib/jimmu.rb', line 441

def parse_multipart(raw_body, boundary)
  fields = {}
  files = {}
  return [fields, files] if raw_body.nil? || boundary.nil? || boundary.empty?

  body = raw_body.dup.force_encoding(Encoding::BINARY)
  delim = "--#{boundary}".b

  body.split(delim).each do |part|
    next if part.nil? || part.empty?
    next if part.start_with?('--'.b)

    part = part.sub(/\A\r\n/n, ''.b)
    part = part.sub(/\r\n\z/n, ''.b)
    next if part.empty?

    header_section, sep, body_section = part.partition("\r\n\r\n".b)
    next if sep.empty?

    headers = {}
    header_section.split("\r\n".b).each do |line|
      k, v = line.split(':'.b, 2)
      next unless k && v
      headers[k.strip.downcase] = v.strip
    end

    disposition = headers['content-disposition'.b]
    next unless disposition

    name = disposition[/name="([^"]*)"/n, 1] || disposition[/name=([^;]*)/n, 1]
    next unless name

    name_sym = name.dup.force_encoding(Encoding::UTF_8).to_sym
    filename = disposition[/filename="([^"]*)"/n, 1]

    if filename
      content_type = (headers['content-type'.b] || 'application/octet-stream').dup.force_encoding(Encoding::UTF_8)
      files[name_sym] = UploadedFile.new(filename.dup.force_encoding(Encoding::UTF_8), content_type, body_section)
    else
      fields[name_sym] = body_section.dup.force_encoding(Encoding::UTF_8)
    end
  end

  [fields, files]
end

.parse_urlencoded(raw_body) ⇒ Object



421
422
423
424
425
426
427
428
429
430
# File 'lib/jimmu.rb', line 421

def parse_urlencoded(raw_body)
  str = raw_body.to_s.dup.force_encoding(Encoding::UTF_8)
  result = {}
  URI.decode_www_form(str, Encoding::UTF_8).each do |k, v|
    result[k.to_sym] = v
  end
  result
rescue ArgumentError
  {}
end