Current section
Files
Jump to
Current section
Files
src/dream@http@request.erl
-module(dream@http@request).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/dream/http/request.gleam").
-export([body_as_string/1, method_to_string/1, parse_method/1, get_query_param/2, has_content_type/2, is_method/2, set_params/2, get_param/2, get_int_param/2, get_string_param/2]).
-export_type([method/0, protocol/0, version/0, request/0, path_param/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" HTTP request types and utilities\n"
"\n"
" Core request types and functions for working with HTTP requests in Dream.\n"
" Includes request methods, path parameters, query parameters, and request inspection.\n"
"\n"
" ## Quick Example\n"
"\n"
" ```gleam\n"
" import dream/http/request.{type Request}\n"
"\n"
" pub fn show_user(request: Request, context, services) {\n"
" let result = {\n"
" use id <- result.try(get_int_param(request, \"id\"))\n"
" let db = services.database.connection\n"
" user_operations.get_user(db, id)\n"
" }\n"
" \n"
" case result {\n"
" Ok(user) -> json_response(status.ok, user_to_json(user))\n"
" Error(err) -> handle_error(err)\n"
" }\n"
" }\n"
" ```\n"
).
-type method() :: post | get | put | delete | patch | options | head.
-type protocol() :: http | https.
-type version() :: http1 | http2 | http3.
-type request() :: {request,
method(),
protocol(),
version(),
binary(),
binary(),
list({binary(), binary()}),
gleam@option:option(binary()),
gleam@option:option(integer()),
gleam@option:option(binary()),
binary(),
gleam@option:option(gleam@yielder:yielder(bitstring())),
list(dream@http@header:header()),
list(dream@http@cookie:cookie()),
gleam@option:option(binary()),
gleam@option:option(integer())}.
-type path_param() :: {path_param,
binary(),
binary(),
gleam@option:option(binary()),
{ok, integer()} | {error, nil},
{ok, float()} | {error, nil}}.
-file("src/dream/http/request.gleam", 178).
?DOC(
" Get the request body as a string\n"
"\n"
" Helper that handles both buffered (String) and streaming (Yielder) bodies.\n"
" If the body is streaming, it consumes the stream and converts it to a string.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" case body_as_string(request) {\n"
" Ok(body) -> // Use body string\n"
" Error(_) -> // Handle UTF-8 error\n"
" }\n"
" ```\n"
).
-spec body_as_string(request()) -> {ok, binary()} | {error, nil}.
body_as_string(Request) ->
case erlang:element(12, Request) of
{some, Stream} ->
_pipe = Stream,
_pipe@1 = gleam@yielder:fold(
_pipe,
<<>>,
fun gleam@bit_array:append/2
),
gleam@bit_array:to_string(_pipe@1);
none ->
{ok, erlang:element(11, Request)}
end.
-file("src/dream/http/request.gleam", 210).
?DOC(" Convert Method to its string representation\n").
-spec method_to_string(method()) -> binary().
method_to_string(Method) ->
case Method of
get ->
<<"GET"/utf8>>;
post ->
<<"POST"/utf8>>;
put ->
<<"PUT"/utf8>>;
delete ->
<<"DELETE"/utf8>>;
patch ->
<<"PATCH"/utf8>>;
options ->
<<"OPTIONS"/utf8>>;
head ->
<<"HEAD"/utf8>>
end.
-file("src/dream/http/request.gleam", 223).
?DOC(" Parse a string to Method (case-insensitive)\n").
-spec parse_method(binary()) -> gleam@option:option(method()).
parse_method(Str) ->
case string:lowercase(Str) of
<<"get"/utf8>> ->
{some, get};
<<"post"/utf8>> ->
{some, post};
<<"put"/utf8>> ->
{some, put};
<<"delete"/utf8>> ->
{some, delete};
<<"patch"/utf8>> ->
{some, patch};
<<"options"/utf8>> ->
{some, options};
<<"head"/utf8>> ->
{some, head};
_ ->
none
end.
-file("src/dream/http/request.gleam", 279).
-spec match_query_key(binary(), binary(), binary()) -> gleam@option:option(binary()).
match_query_key(Key, Name, Value) ->
case Key =:= Name of
true ->
{some, Value};
false ->
none
end.
-file("src/dream/http/request.gleam", 294).
?DOC(
" Decode a URL-encoded component (key or value)\n"
" \n"
" Handles percent-encoded sequences (e.g., %20, %26) and plus signs (+ → space)\n"
" Falls back to original string if decoding fails\n"
).
-spec decode_url_component(binary()) -> binary().
decode_url_component(Component) ->
With_spaces = gleam@string:replace(Component, <<"+"/utf8>>, <<" "/utf8>>),
case gleam_stdlib:percent_decode(With_spaces) of
{ok, Decoded} ->
Decoded;
{error, _} ->
With_spaces
end.
-file("src/dream/http/request.gleam", 262).
-spec parse_query_pair(binary(), binary()) -> gleam@option:option(binary()).
parse_query_pair(Param, Name) ->
case gleam@string:split(Param, <<"="/utf8>>) of
[Key, Value] ->
Decoded_key = decode_url_component(Key),
Decoded_value = decode_url_component(Value),
match_query_key(Decoded_key, Name, Decoded_value);
[Key@1] ->
Decoded_key@1 = decode_url_component(Key@1),
match_query_key(Decoded_key@1, Name, <<""/utf8>>);
_ ->
none
end.
-file("src/dream/http/request.gleam", 246).
-spec get_query_param_recursive(list(binary()), binary()) -> gleam@option:option(binary()).
get_query_param_recursive(Params, Name) ->
case Params of
[] ->
none;
[Param | Rest] ->
Result = parse_query_pair(Param, Name),
case Result of
{some, _} ->
Result;
none ->
get_query_param_recursive(Rest, Name)
end
end.
-file("src/dream/http/request.gleam", 242).
?DOC(
" Get a query parameter value from the raw query string\n"
" \n"
" Properly decodes URL-encoded values (e.g., %20 → space, %26 → &)\n"
" Returns None if the parameter is not found.\n"
).
-spec get_query_param(binary(), binary()) -> gleam@option:option(binary()).
get_query_param(Query, Name) ->
get_query_param_recursive(gleam@string:split(Query, <<"&"/utf8>>), Name).
-file("src/dream/http/request.gleam", 306).
?DOC(" Check if request has a specific content type\n").
-spec has_content_type(request(), binary()) -> boolean().
has_content_type(Request, Content_type) ->
case erlang:element(15, Request) of
{some, Actual_content_type} ->
gleam_stdlib:contains_string(Actual_content_type, Content_type);
none ->
false
end.
-file("src/dream/http/request.gleam", 315).
?DOC(" Check if request method matches\n").
-spec is_method(request(), method()) -> boolean().
is_method(Request, Method) ->
erlang:element(2, Request) =:= Method.
-file("src/dream/http/request.gleam", 320).
?DOC(" Create a new request with updated params\n").
-spec set_params(request(), list({binary(), binary()})) -> request().
set_params(Request, New_params) ->
{request,
erlang:element(2, Request),
erlang:element(3, Request),
erlang:element(4, Request),
erlang:element(5, Request),
erlang:element(6, Request),
New_params,
erlang:element(8, Request),
erlang:element(9, Request),
erlang:element(10, Request),
erlang:element(11, Request),
erlang:element(12, Request),
erlang:element(13, Request),
erlang:element(14, Request),
erlang:element(15, Request),
erlang:element(16, Request)}.
-file("src/dream/http/request.gleam", 344).
-spec extract_format(list(binary()), binary()) -> {binary(),
gleam@option:option(binary())}.
extract_format(Parts, Raw) ->
case Parts of
[Val, Ext] ->
{Val, {some, Ext}};
_ ->
{Raw, none}
end.
-file("src/dream/http/request.gleam", 330).
?DOC(" Parse a path parameter string into PathParam with format detection\n").
-spec parse_path_param(binary()) -> path_param().
parse_path_param(Raw) ->
Parts = gleam@string:split(Raw, <<"."/utf8>>),
{Value, Format} = extract_format(Parts, Raw),
{path_param,
Raw,
Value,
Format,
gleam_stdlib:parse_int(Value),
gleam_stdlib:parse_float(Value)}.
-file("src/dream/http/request.gleam", 397).
?DOC(
" Extract a path parameter by name\n"
"\n"
" Returns a `PathParam` with automatic type conversion and format detection.\n"
" If the parameter doesn't exist, returns an error message.\n"
"\n"
" For common cases, use `get_int_param()` or `get_string_param()` instead,\n"
" which return `Result(Int, String)` or `Result(String, String)` with\n"
" custom error messages.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" // Route: /users/:id\n"
" // Request: /users/123\n"
" case get_param(request, \"id\") {\n"
" Ok(param) -> {\n"
" param.value // \"123\"\n"
" param.as_int // Ok(123)\n"
" }\n"
" Error(msg) -> // handle error\n"
" }\n"
" ```\n"
"\n"
" ```gleam\n"
" // Route: /users/:id\n"
" // Request: /users/123.json\n"
" case get_param(request, \"id\") {\n"
" Ok(param) -> {\n"
" param.value // \"123\"\n"
" param.format // Some(\"json\")\n"
" param.as_int // Ok(123)\n"
" }\n"
" Error(msg) -> // handle error\n"
" }\n"
" ```\n"
"\n"
" ```gleam\n"
" // For simple integer extraction, use get_int_param:\n"
" case get_int_param(request, \"id\") {\n"
" Ok(id) -> show_user(id)\n"
" Error(msg) -> json_response(status.bad_request, error_json(msg))\n"
" }\n"
" ```\n"
).
-spec get_param(request(), binary()) -> {ok, path_param()} | {error, binary()}.
get_param(Request, Name) ->
case gleam@list:key_find(erlang:element(7, Request), Name) of
{ok, Value} ->
{ok, parse_path_param(Value)};
{error, _} ->
{error, <<"Missing required path parameter: "/utf8, Name/binary>>}
end.
-file("src/dream/http/request.gleam", 426).
-spec param_to_int(path_param(), binary()) -> {ok, integer()} |
{error, binary()}.
param_to_int(Param, Name) ->
case erlang:element(5, Param) of
{ok, Id} ->
{ok, Id};
{error, _} ->
{error, <<Name/binary, " must be an integer"/utf8>>}
end.
-file("src/dream/http/request.gleam", 419).
?DOC(
" Extract a path parameter as an integer\n"
"\n"
" Returns a Result with a custom error message if the parameter is missing\n"
" or cannot be converted to an integer.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" // Route: /users/:id\n"
" // Request: /users/123\n"
" case get_int_param(request, \"id\") {\n"
" Ok(id) -> show_user(id)\n"
" Error(msg) -> json_response(status.bad_request, error_json(msg))\n"
" }\n"
" ```\n"
).
-spec get_int_param(request(), binary()) -> {ok, integer()} | {error, binary()}.
get_int_param(Request, Name) ->
case get_param(Request, Name) of
{ok, Param} ->
param_to_int(Param, Name);
{error, _} ->
{error,
<<<<"Missing "/utf8, Name/binary>>/binary, " parameter"/utf8>>}
end.
-file("src/dream/http/request.gleam", 457).
-spec param_to_string(path_param(), binary()) -> {ok, binary()} |
{error, binary()}.
param_to_string(Param, _) ->
{ok, erlang:element(3, Param)}.
-file("src/dream/http/request.gleam", 447).
?DOC(
" Extract a path parameter as a string\n"
"\n"
" Returns a Result with a custom error message if the parameter is missing.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" // Route: /users/:name\n"
" // Request: /users/john\n"
" case get_string_param(request, \"name\") {\n"
" Ok(name) -> show_user_by_name(name)\n"
" Error(msg) -> json_response(status.bad_request, error_json(msg))\n"
" }\n"
" ```\n"
).
-spec get_string_param(request(), binary()) -> {ok, binary()} |
{error, binary()}.
get_string_param(Request, Name) ->
case get_param(Request, Name) of
{ok, Param} ->
param_to_string(Param, Name);
{error, _} ->
{error,
<<<<"Missing "/utf8, Name/binary>>/binary, " parameter"/utf8>>}
end.