Skip to content

Commit

Permalink
feat: add cookie parser (#152)
Browse files Browse the repository at this point in the history
  • Loading branch information
gorillamoe authored Aug 17, 2024
1 parent e75cbd0 commit 67bac82
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 4 deletions.
32 changes: 32 additions & 0 deletions docs/docs/usage/request-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,38 @@ You have two reference part choices of the `response` or `request`: `body` and `

For `body` part, you can use JSONPath and XPath to extract specific property or attribute.

### Special case for cookies

The response cookies can be referenced by `{{REQUEST_NAME.response.cookies.CookieName.(value|domain|flag|path|secure|expires)}}`.

```http
# @name REQUEST_GH
GET https://github.com HTTP/1.1
###
POST https://httpbin.org/post HTTP/1.1
Accept: application/json
Content-Type: application/json
{
"logged_into_gh": "{{REQUEST_GH.response.cookies.logged_in.value}}"
}
```

:::tip

If you don't want Kulala to create a cookie jar for a specific request,
you can add the meta-tag `@no-cookie-jar` to the request.

```http
# @no-cookie-jar
GET https://github.com HTTP/1.1
```

:::

## Example

if a JSON response returns `body` `{"id": "mock"}`, you can set the JSONPath part to `$.id` to reference the `id`.
Expand Down
3 changes: 2 additions & 1 deletion lua/kulala/globals/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ local FS = require("kulala.utils.fs")

local M = {}

M.VERSION = "3.3.0"
M.VERSION = "3.4.0"
M.UI_ID = "kulala://ui"
M.SCRATCHPAD_ID = "kulala://scratchpad"
M.HEADERS_FILE = FS.get_plugin_tmp_dir() .. "/headers.txt"
M.BODY_FILE = FS.get_plugin_tmp_dir() .. "/body.txt"
M.COOKIES_JAR_FILE = FS.get_plugin_tmp_dir() .. "/cookies.txt"

return M
43 changes: 43 additions & 0 deletions lua/kulala/internal_processing/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,48 @@ local get_headers_as_table = function()
return headers_table
end

local get_cookies_as_table = function()
local cookies_file = FS.read_file(GLOBALS.COOKIES_JAR_FILE)
if cookies_file == nil then
return {}
end
cookies_file = cookies_file:gsub("\r\n", "\n")
local lines = vim.split(cookies_file, "\n")
local cookies = {}
for _, line in ipairs(lines) do
-- Trim leading and trailing whitespace
line = line:gsub("^%s+", ""):gsub("%s+$", "")

-- Skip empty lines or comment lines (except #HttpOnly_)
if line ~= "" and (line:sub(1, 1) ~= "#" or line:find("^#HttpOnly_")) then
-- If it's a #HttpOnly_ line, remove the #HttpOnly_ part
if line:find("^#HttpOnly_") then
line = line:gsub("^#HttpOnly_", "")
end

-- Split the line into fields based on tabs
local fields = {}
for field in line:gmatch("[^\t]+") do
table.insert(fields, field)
end

-- The field before the last one is the key
local key = fields[#fields - 1]

-- Store the key-value pair in the cookies table
cookies[key] = {
domain = fields[1],
flag = fields[2],
path = fields[3],
secure = fields[4],
expires = fields[5],
value = fields[7],
}
end
end
return cookies
end

local get_lower_headers_as_table = function()
local headers = get_headers_as_table()
local headers_table = {}
Expand All @@ -60,6 +102,7 @@ M.set_env_for_named_request = function(name, body)
response = {
headers = get_headers_as_table(),
body = body,
cookies = get_cookies_as_table(),
},
request = {
headers = DB.data.current_request.headers,
Expand Down
11 changes: 9 additions & 2 deletions lua/kulala/parser/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -420,12 +420,19 @@ function M.parse()
end
table.insert(res.cmd, "-A")
table.insert(res.cmd, "kulala.nvim/" .. GLOBALS.VERSION)
-- if the user has not specified the no-cookie meta tag,
-- then use the cookies jar file
if PARSER_UTILS.contains_meta_tag(req, "no-cookie-jar") == false then
table.insert(res.cmd, "--cookie-jar")
table.insert(res.cmd, GLOBALS.COOKIES_JAR_FILE)
end
for _, additional_curl_option in pairs(CONFIG.get().additional_curl_options) do
table.insert(res.cmd, additional_curl_option)
end
table.insert(res.cmd, res.url)
FS.delete_file(PLUGIN_TMP_DIR .. "/headers.txt")
FS.delete_file(PLUGIN_TMP_DIR .. "/body.txt")
FS.delete_file(GLOBALS.HEADERS_FILE)
FS.delete_file(GLOBALS.BODY_FILE)
FS.delete_file(GLOBALS.COOKIES_JAR_FILE)
if CONFIG.get().debug then
FS.write_file(PLUGIN_TMP_DIR .. "/request.txt", table.concat(res.cmd, " "))
end
Expand Down
2 changes: 1 addition & 1 deletion lua/kulala/parser/jsonpath.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
local M = {}

M.parse = function(body, path)
subpath = string.gsub(path, "^%$%.", "")
local subpath = string.gsub(path, "^%$%.", "")

local result = vim.fn.json_decode(body)

Expand Down
33 changes: 33 additions & 0 deletions lua/kulala/parser/request_variables.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ local function get_match(str)
"^([%w_]+)%.(request)%.(headers)(.*)",
"^([%w_]+)%.(request)%.(body)%.(.*)",
"^([%w_]+)%.(response)%.(headers)(.*)",
"^([%w_]+)%.(response)%.(cookies)(.*)",
"^([%w_]+)%.(response)%.(body)%.(.*)",
}
for _, p in ipairs(patterns) do
Expand Down Expand Up @@ -108,6 +109,36 @@ local function get_header_value_from_path(name, method, subpath)
return result
end

local function get_cookies_value_from_path(name, subpath)
local base_table = DB.data.env[name]
if not base_table then
return nil
end
if not base_table.response then
return nil
end
if not base_table.response.cookies then
return nil
end
local result = base_table.response.cookies
local path_parts = {}

-- Split the path into parts
for part in string.gmatch(subpath, "[^%.%[%]\"']+") do
table.insert(path_parts, part)
end

for _, key in ipairs(path_parts) do
if result[key] then
result = result[key]
else
return nil -- Return nil if any part of the path is not found
end
end

return result
end

M.parse = function(path)
local path_name, path_method, path_type, path_subpath = get_match(path)

Expand All @@ -117,6 +148,8 @@ M.parse = function(path)

if path_type == "headers" then
return get_header_value_from_path(path_name, path_method, path_subpath)
elseif path_type == "cookies" then
return get_cookies_value_from_path(path_name, path_subpath)
elseif path_type == "body" then
return get_body_value_from_path(path_name, path_method, path_subpath)
end
Expand Down

0 comments on commit 67bac82

Please sign in to comment.