class Fluent::Plugin::HttpInput::Handler

def close

def close
  @io.close
end

def closing?

def closing?
  @next_close
end

def handle_options_request

to check if cross-origin requests are supported.
Web browsers can send an OPTIONS request before performing POST
def handle_options_request
  # Is CORS enabled in the first place?
  if @cors_allow_origins.nil?
    return send_response_and_close("403 Forbidden", {}, "")
  end
  # in_http does not support HTTP methods except POST
  if @access_control_request_method != 'POST'
    return send_response_and_close("403 Forbidden", {}, "")
  end
  header = {
    "Access-Control-Allow-Methods" => "POST",
    "Access-Control-Allow-Headers" => @access_control_request_headers || "",
  }
  # Check the origin and send back a CORS response
  if @cors_allow_origins.include?('*')
    header["Access-Control-Allow-Origin"] = "*"
    send_response_and_close("200 OK", header, "")
  elsif include_cors_allow_origin
    header["Access-Control-Allow-Origin"] = @origin
    send_response_and_close("200 OK", header, "")
  else
    send_response_and_close("403 Forbidden", {}, "")
  end
end

def include_cors_allow_origin

def include_cors_allow_origin
  if @cors_allow_origins.include?(@origin)
    return true
  end
  filtered_cors_allow_origins = @cors_allow_origins.select {|origin| origin != ""}
  return filtered_cors_allow_origins.find do |origin|
    (start_str,end_str) = origin.split("*",2)
    @origin.start_with?(start_str) and @origin.end_with?(end_str)
  end != nil
end

def initialize(io, km, callback, body_size_limit, format_name, log, cors_allow_origins)

def initialize(io, km, callback, body_size_limit, format_name, log, cors_allow_origins)
  @io = io
  @km = km
  @callback = callback
  @body_size_limit = body_size_limit
  @next_close = false
  @format_name = format_name
  @log = log
  @cors_allow_origins = cors_allow_origins
  @idle = 0
  @km.add(self)
  @remote_port, @remote_addr = io.remote_port, io.remote_addr
  @parser = Http::Parser.new(self)
end

def on_body(chunk)

def on_body(chunk)
  if @body.bytesize + chunk.bytesize > @body_size_limit
    unless closing?
      send_response_and_close("413 Request Entity Too Large", {}, "Too large")
    end
    return
  end
  @body << chunk
end

def on_close

def on_close
  @km.delete(self)
end

def on_headers_complete(headers)

def on_headers_complete(headers)
  expect = nil
  size = nil
  if @parser.http_version == [1, 1]
    @keep_alive = true
  else
    @keep_alive = false
  end
  @env = {}
  @content_type = ""
  @content_encoding = ""
  headers.each_pair {|k,v|
    @env["HTTP_#{k.gsub('-','_').upcase}"] = v
    case k
    when /Expect/i
      expect = v
    when /Content-Length/i
      size = v.to_i
    when /Content-Type/i
      @content_type = v
    when /Content-Encoding/i
      @content_encoding = v
    when /Connection/i
      if v =~ /close/i
        @keep_alive = false
      elsif v =~ /Keep-alive/i
        @keep_alive = true
      end
    when /Origin/i
      @origin  = v
    when /X-Forwarded-For/i
      # For multiple X-Forwarded-For headers. Use first header value.
      v = v.first if v.is_a?(Array)
      @remote_addr = v.split(",").first
    when /Access-Control-Request-Method/i
      @access_control_request_method = v
    when /Access-Control-Request-Headers/i
      @access_control_request_headers = v
    end
  }
  if expect
    if expect == '100-continue'
      if !size || size < @body_size_limit
        send_response_nobody("100 Continue", {})
      else
        send_response_and_close("413 Request Entity Too Large", {}, "Too large")
      end
    else
      send_response_and_close("417 Expectation Failed", {}, "")
    end
  end
end

def on_message_begin

def on_message_begin
  @body = ''
end

def on_message_complete

def on_message_complete
  return if closing?
  if @parser.http_method == 'OPTIONS'
    return handle_options_request()
  end
  # CORS check
  # ==========
  # For every incoming request, we check if we have some CORS
  # restrictions and white listed origins through @cors_allow_origins.
  unless @cors_allow_origins.nil?
    unless @cors_allow_origins.include?('*') or include_cors_allow_origin
      send_response_and_close("403 Forbidden", {'Connection' => 'close'}, "")
      return
    end
  end
  # Content Encoding
  # =================
  # Decode payload according to the "Content-Encoding" header.
  # For now, we only support 'gzip' and 'deflate'.
  begin
    if @content_encoding == 'gzip'
      @body = Zlib::GzipReader.new(StringIO.new(@body)).read
    elsif @content_encoding == 'deflate'
      @body = Zlib::Inflate.inflate(@body)
    end
  rescue
    @log.warn 'fails to decode payload', error: $!.to_s
    send_response_and_close("400 Bad Request", {}, "")
    return
  end
  @env['REMOTE_ADDR'] = @remote_addr if @remote_addr
  uri = URI.parse(@parser.request_url)
  params = WEBrick::HTTPUtils.parse_query(uri.query)
  if @format_name != 'default'
    params[EVENT_RECORD_PARAMETER] = @body
  elsif @content_type =~ /^application\/x-www-form-urlencoded/
    params.update WEBrick::HTTPUtils.parse_query(@body)
  elsif @content_type =~ /^multipart\/form-data; boundary=(.+)/
    boundary = WEBrick::HTTPUtils.dequote($1)
    params.update WEBrick::HTTPUtils.parse_form_data(@body, boundary)
  elsif @content_type =~ /^application\/json/
    params['json'] = @body
  elsif @content_type =~ /^application\/msgpack/
    params['msgpack'] = @body
  end
  path_info = uri.path
  params.merge!(@env)
  @env.clear
  code, header, body = *@callback.call(path_info, params)
  body = body.to_s
  unless @cors_allow_origins.nil?
    if @cors_allow_origins.include?('*')
      header['Access-Control-Allow-Origin'] = '*'
    elsif include_cors_allow_origin
      header['Access-Control-Allow-Origin'] = @origin
    end
  end
  if @keep_alive
    header['Connection'] = 'Keep-Alive'
    send_response(code, header, body)
  else
    send_response_and_close(code, header, body)
  end
end

def on_read(data)

def on_read(data)
  @idle = 0
  @parser << data
rescue
  @log.warn "unexpected error", error: $!.to_s
  @log.warn_backtrace
  @io.close
end

def on_write_complete

def on_write_complete
  @io.close if @next_close
end

def send_response(code, header, body)

def send_response(code, header, body)
  header['Content-Length'] ||= body.bytesize
  header['Content-Type'] ||= 'text/plain'
  data = %[HTTP/1.1 #{code}\r\n]
  header.each_pair {|k,v|
    data << "#{k}: #{v}\r\n"
  }
  data << "\r\n"
  @io.write(data)
  @io.write(body)
end

def send_response_and_close(code, header, body)

def send_response_and_close(code, header, body)
  send_response(code, header, body)
  @next_close = true
end

def send_response_nobody(code, header)

def send_response_nobody(code, header)
  data = %[HTTP/1.1 #{code}\r\n]
  header.each_pair {|k,v|
    data << "#{k}: #{v}\r\n"
  }
  data << "\r\n"
  @io.write(data)
end

def step_idle

def step_idle
  @idle += 1
end