Skip to content

Commit

Permalink
Various http.post fixes (#2643)
Browse files Browse the repository at this point in the history
* Fix `http.{post,put}` data type.
* Rename `http.post.multipart_form_data` into `http.multipart_form_data`
* Add `http.put.file`
* Test, fix curl headers logic.
  • Loading branch information
toots committed Sep 25, 2022
1 parent 6e03efa commit 8aa9e85
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 35 deletions.
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

New:
* Added `string.char`, `string.getter.flush` and `string.getter.concat`.
* Added `http.post.multipart_form_data` and `http.post.file`.
* Added `http.multipart_form_data` and `http.{post,put}.file`.

Changed:
* Allow sub-second values in `sleep()` (#2610)
Expand Down
70 changes: 45 additions & 25 deletions libs/http.liq
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ end
# @category Interaction
# @param ~boundary Specify boundary to use for multipart/form-data.
# @param data data to insert
def http.post.multipart_form_data(~boundary=null(), data)
def http.multipart_form_data(~boundary=null(), data)
def default_boundary()
range = [...string.char.ascii.alphabet, ...string.char.ascii.number]
l = list.init(12, fun(_) -> string.char.ascii.random(range))
Expand Down Expand Up @@ -269,23 +269,11 @@ def http.post.multipart_form_data(~boundary=null(), data)
{ contents = string.getter.concat(contents), boundary = boundary }
end

# @flag hidden
stdlib_file = file

# Send a file via POST request encoded in multipart/form-data. The contents can
# either be directly specified (with the `contents` argument) or taken from a
# file (with the `file` argument).
# @category Interaction
# @param ~name Name of the field field
# @param ~content_type Content-type (mime) for the file.
# @param ~headers Additional headers.
# @param ~boundary Specify boundary to use for multipart/form-data.
# @param ~filename File name sent in the request.
# @param ~file File whose contents is to be sent in the request.
# @param ~contents Contents of the file sent in the request.
# @param ~timeout Timeout in ms.
# @param ~redirect Follow reidrections.
# @param url URL to post to.
def http.post.file(~name="file", ~content_type=null(), ~headers=[], ~boundary=null(), ~filename=null(), ~file=null(), ~contents=null(), ~timeout=null(), ~redirect=true, url)
# @flag hidden
upload_file_fn = fun (~name, ~content_type, ~headers, ~boundary, ~filename, ~file, ~contents, ~timeout, ~redirect, url, fn) -> begin
if not null.defined(filename) and not null.defined(file) then
error.raise(error.http, "At least one of: `file` or `filename` must be defined!")
end
Expand All @@ -300,21 +288,53 @@ def http.post.file(~name="file", ~content_type=null(), ~headers=[], ~boundary=nu

# Create query

content_type =
if null.defined(content_type) then
null.get(content_type)
else
mime = stdlib_file.mime_default(filename)
mime == "" ? "application/octet-stream" : mime
end
content_type = content_type ?? "application/octet-stream"

data = http.post.multipart_form_data(boundary=boundary, [{
data = http.multipart_form_data(boundary=boundary, [{
name=name,
attributes=[("filename", filename)],
headers=[("Content-Type",content_type)],
contents=contents
}])
headers = ("Content-Type", "multipart/form-data; boundary=#{data.boundary}")::headers

http.post(headers=headers, timeout_ms=timeout, redirect=redirect, data=data.contents, url)
fn(headers=headers, timeout_ms=timeout, redirect=redirect, data=data.contents, url)
end

# Send a file via POST request encoded in multipart/form-data. The contents can
# either be directly specified (with the `contents` argument) or taken from a
# file (with the `file` argument).
# @category Interaction
# @param ~name Name of the field field
# @param ~content_type Content-type (mime) for the file.
# @param ~headers Additional headers.
# @param ~boundary Specify boundary to use for multipart/form-data.
# @param ~filename File name sent in the request.
# @param ~file File whose contents is to be sent in the request.
# @param ~contents Contents of the file sent in the request.
# @param ~timeout Timeout in ms.
# @param ~redirect Follow reidrections.
# @param url URL to post to.
def http.post.file(~name="file", ~content_type=null(), ~headers=[], ~boundary=null(), ~filename=null(), ~file=null(), ~contents=null(), ~timeout=null(), ~redirect=true, url)
upload_file_fn(name=name, content_type=content_type, headers=headers, boundary=boundary, filename=filename,
file=file, contents=contents, timeout=timeout, redirect=redirect, url, http.post)
end

# Send a file via PUT request encoded in multipart/form-data. The contents can
# either be directly specified (with the `contents` argument) or taken from a
# file (with the `file` argument).
# @category Interaction
# @param ~name Name of the field field
# @param ~content_type Content-type (mime) for the file.
# @param ~headers Additional headers.
# @param ~boundary Specify boundary to use for multipart/form-data.
# @param ~filename File name sent in the request.
# @param ~file File whose contents is to be sent in the request.
# @param ~contents Contents of the file sent in the request.
# @param ~timeout Timeout in ms.
# @param ~redirect Follow reidrections.
# @param url URL to put to.
def http.put.file(~name="file", ~content_type=null(), ~headers=[], ~boundary=null(), ~filename=null(), ~file=null(), ~contents=null(), ~timeout=null(), ~redirect=true, url)
upload_file_fn(name=name, content_type=content_type, headers=headers, boundary=boundary, filename=filename,
file=file, contents=contents, timeout=timeout, redirect=redirect, url, http.put)
end
6 changes: 3 additions & 3 deletions src/builtins/builtins_http.ml
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ let add_http_request ~stream_body ~descr ~request name =
else
[
( "data",
Lang.getter_t (Lang.nullable_t Lang.string_t),
Lang.getter_t Lang.string_t,
Some (Lang.string ""),
Some
"POST data. Use a `string?` getter to stream data and return \
`null` when all data has been passed." );
"POST data. Use a `string` getter to stream data and return `\"\"` \
when all data has been passed." );
]
in
let params =
Expand Down
12 changes: 6 additions & 6 deletions src/tools/liqcurl.ml
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,6 @@ let rec http_request ?headers ?http_version ~follow_redirect ~timeout ~url
connection#set_writefunction (fun s ->
on_body_data s;
String.length s);
ignore
(Option.map
(fun headers ->
connection#set_httpheader
(List.map (fun (k, v) -> Printf.sprintf "%s: %s" k v) headers))
headers);
connection#set_httpversion
(match http_version with
| None -> Curl.HTTP_VERSION_NONE
Expand All @@ -176,6 +170,12 @@ let rec http_request ?headers ?http_version ~follow_redirect ~timeout ~url
connection#set_readfunction get_data
| `Head -> connection#set_nobody true
| `Delete -> connection#set_customrequest "DELETE");
ignore
(Option.map
(fun headers ->
connection#set_httpheader
(List.map (fun (k, v) -> Printf.sprintf "%s: %s" k v) headers))
headers);
let response_headers = Buffer.create 1024 in
connection#set_headerfunction (fun s ->
Buffer.add_string response_headers s;
Expand Down

0 comments on commit 8aa9e85

Please sign in to comment.