-- Copyright (c) Kong Inc. 2020

local deco = require "kong.plugins.grpc-web.deco"
local kong_meta = require "kong.meta"

local ngx = ngx
local kong = kong

local string_format = string.format

local ngx_arg = ngx.arg
local ngx_var = ngx.var

local kong_request_get_path = kong.request.get_path
local kong_request_get_header = kong.request.get_header
local kong_request_get_method = kong.request.get_method
local kong_request_get_raw_body = kong.request.get_raw_body
local kong_response_exit = kong.response.exit
local kong_response_set_header = kong.response.set_header
local kong_service_request_set_header = kong.service.request.set_header
local kong_service_request_set_raw_body = kong.service.request.set_raw_body


local grpc_web = {
  PRIORITY = 3,
  VERSION = kong_meta.version,
}


local CORS_HEADERS = {
  ["Content-Type"] = "application/grpc-web-text+proto",
  ["Access-Control-Allow-Origin"] = "*",
  ["Access-Control-Allow-Methods"] = "POST",
  ["Access-Control-Allow-Headers"] = "content-type,x-grpc-web,x-user-agent",
}

function grpc_web:access(conf)
  kong_response_set_header("Access-Control-Allow-Origin", conf.allow_origin_header)

  if kong_request_get_method() == "OPTIONS" then
    CORS_HEADERS["Access-Control-Allow-Origin"] = conf.allow_origin_header
    return kong_response_exit(200, "OK", CORS_HEADERS)
  end

  local uri
  if conf.pass_stripped_path then
    uri = ngx_var.upstream_uri
    ngx.req.set_uri(uri)
  else
    uri = kong_request_get_path()
  end

  local dec, err = deco.new(
    kong_request_get_header("Content-Type"),
    uri, conf.proto)

  if not dec then
    kong.log.err(err)
    return kong_response_exit(400, err)
  end

  kong.ctx.plugin.dec = dec

  kong_service_request_set_header("Content-Type", "application/grpc")
  kong_service_request_set_header("TE", "trailers")
  kong_service_request_set_raw_body(dec:upstream(kong_request_get_raw_body()))
end


function grpc_web:header_filter(conf)
  if kong_request_get_method() == "OPTIONS" then
    return
  end

  local dec = kong.ctx.plugin.dec
  if dec then
    kong_response_set_header("Content-Type", dec.mimetype)
  end
end


function grpc_web:body_filter(conf)
  if kong_request_get_method() ~= "POST" then
    return
  end
  local dec = kong.ctx.plugin.dec
  if not dec then
    return
  end

  local chunk, eof = ngx_arg[1], ngx_arg[2]

  chunk = dec:downstream(chunk)

  if eof and dec.framing == "grpc" then
    chunk = chunk .. dec:frame(0x80, string_format(
      "grpc-status:%s\r\ngrpc-message:%s\r\n",
      ngx_var["sent_trailer_grpc_status"] or "0",
      ngx_var["sent_trailer_grpc_message"] or ""))
  end

  ngx_arg[1] = chunk
end


return grpc_web
