Skip to content

Commit c6c5e1d

Browse files
author
Ryan Miville
committed
http handler
1 parent d481d60 commit c6c5e1d

File tree

4 files changed

+132
-6
lines changed

4 files changed

+132
-6
lines changed

gleam.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ target = "javascript"
1717
[dependencies]
1818
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
1919
gleam_javascript = ">= 0.13.0 and < 1.0.0"
20+
gleam_http = ">= 3.7.0 and < 4.0.0"
2021

2122
[dev-dependencies]
2223
gleeunit = ">= 1.0.0 and < 2.0.0"

manifest.toml

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ packages = [
1111
{ name = "gleam_community_ansi", version = "1.4.1", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "4CD513FC62523053E62ED7BAC2F36136EC17D6A8942728250A9A00A15E340E4B" },
1212
{ name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" },
1313
{ name = "gleam_erlang", version = "0.27.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "DE468F676D71B313C6C8C5334425CFCF827837333F8AB47B64D8A6D7AA40185D" },
14+
{ name = "gleam_http", version = "3.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "EA66440C2269F7CED0F6845E5BD0DB68095775D627FA709A841CA78A398D6D56" },
1415
{ name = "gleam_javascript", version = "0.13.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "F98328FCF573DA6F3A35D7F6CB3F9FF19FD5224CCBA9151FCBEAA0B983AF2F58" },
1516
{ name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" },
1617
{ name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" },
@@ -26,6 +27,7 @@ packages = [
2627

2728
[requirements]
2829
birdie = { version = ">= 1.2.3 and < 2.0.0" }
30+
gleam_http = { version = ">= 3.7.0 and < 4.0.0" }
2931
gleam_javascript = { version = ">= 0.13.0 and < 1.0.0" }
3032
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
3133
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }

src/glambda.gleam

+113-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1+
import gleam/bit_array
2+
import gleam/bool
13
import gleam/dict.{type Dict}
24
import gleam/dynamic.{type Dynamic}
5+
import gleam/http
6+
import gleam/http/request.{type Request, Request}
7+
import gleam/http/response.{type Response}
8+
import gleam/io
39
import gleam/javascript/promise.{type Promise}
4-
import gleam/option.{type Option}
10+
import gleam/list
11+
import gleam/option.{type Option, None, Some}
12+
import gleam/regex
13+
import gleam/result
14+
import gleam/string
515

616
pub type JsEvent
717

@@ -182,6 +192,108 @@ pub fn to_handler(
182192
}
183193
}
184194

195+
pub fn http_handler(
196+
handler: fn(Request(BitArray), Context) -> Promise(Response(BitArray)),
197+
) -> JsHandler {
198+
api_gateway_proxy_v2_handler(fn(event, ctx) {
199+
event
200+
|> create_request
201+
|> handler(ctx)
202+
|> promise.map(create_response)
203+
})
204+
}
205+
206+
fn create_request(event: ApiGatewayProxyEventV2) -> Request(BitArray) {
207+
io.debug(event.headers)
208+
let method =
209+
event.request_context.http.method
210+
|> http.parse_method
211+
|> result.unwrap(http.Get)
212+
let headers = case event.cookies {
213+
Some([_] as cookies) -> {
214+
event.headers
215+
|> dict.insert("cookie", string.join(cookies, "; "))
216+
|> dict.to_list
217+
}
218+
_ -> dict.to_list(event.headers)
219+
}
220+
let body =
221+
event.body
222+
|> body_bit_array(event.is_base64_encoded)
223+
|> result.unwrap(<<>>)
224+
let host = event.request_context.domain_name
225+
let path = event.raw_path
226+
let query = string.to_option(event.raw_query_string)
227+
228+
Request(
229+
method:,
230+
headers:,
231+
body:,
232+
scheme: http.Https,
233+
host:,
234+
port: None,
235+
path:,
236+
query:,
237+
)
238+
}
239+
240+
fn body_bit_array(
241+
body: Option(String),
242+
is_base64_encoded: Bool,
243+
) -> Result(BitArray, Nil) {
244+
use body <- result.try(option.to_result(body, Nil))
245+
use <- bool.guard(!is_base64_encoded, Ok(bit_array.from_string(body)))
246+
use body <- result.try(bit_array.base64_decode(body))
247+
Ok(body)
248+
}
249+
250+
fn create_response(response: Response(BitArray)) -> ApiGatewayProxyResultV2 {
251+
let is_content_type_binary =
252+
response.get_header(response, "content-type")
253+
|> result.map(is_content_type_binary)
254+
255+
let is_base64_encoded = case is_content_type_binary {
256+
Ok(True) -> True
257+
_ -> {
258+
response.get_header(response, "content-encoding")
259+
|> result.map(is_content_encoding_binary)
260+
|> result.unwrap(False)
261+
}
262+
}
263+
264+
let body = case is_base64_encoded {
265+
True -> bit_array.base64_encode(response.body, False)
266+
False -> bit_array.to_string(response.body) |> result.unwrap("")
267+
}
268+
let cookies = get_cookies(response)
269+
270+
ApiGatewayProxyResultV2(
271+
status_code: response.status,
272+
headers: dict.from_list(response.headers),
273+
body: string.to_option(body),
274+
is_base64_encoded:,
275+
cookies:,
276+
)
277+
}
278+
279+
fn get_cookies(response: Response(a)) -> List(String) {
280+
response.get_cookies(response)
281+
|> list.map(fn(cookie) { cookie.0 <> "=" <> cookie.1 })
282+
}
283+
284+
fn is_content_type_binary(content_type: String) -> Bool {
285+
let assert Ok(re) =
286+
regex.from_string(
287+
"!/^(text\\/(plain|html|css|javascript|csv).*|application\\/(.*json|.*xml).*|image\\/svg\\+xml.*)$/",
288+
)
289+
regex.check(re, content_type)
290+
}
291+
292+
fn is_content_encoding_binary(content_encoding: String) -> Bool {
293+
let assert Ok(re) = regex.from_string("/^(gzip|deflate|compress|br)/")
294+
regex.check(re, content_encoding)
295+
}
296+
185297
pub fn api_gateway_proxy_v2_handler(
186298
handler: Handler(ApiGatewayProxyEventV2, ApiGatewayProxyResultV2),
187299
) -> JsHandler {

src/glambda_ffi.mjs

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { List } from "./gleam.mjs";
22
import { Some, None, unwrap } from "../gleam_stdlib/gleam/option.mjs";
3+
import * as $dict from "../gleam_stdlib/gleam/dict.mjs";
34
import {
45
ApiGatewayProxyEventV2,
56
ApiGatewayRequestContextV2,
@@ -26,10 +27,10 @@ export function to_api_gateway_proxy_event_v2(event) {
2627
event.rawPath,
2728
event.rawQueryString,
2829
maybeList(event.cookies),
29-
event.headers,
30-
maybe(event.queryStringParameters),
31-
maybe(event.pathParameters),
32-
maybe(event.stageVariables),
30+
toDict(event.headers),
31+
maybeDict(event.queryStringParameters),
32+
maybeDict(event.pathParameters),
33+
maybeDict(event.stageVariables),
3334
to_request_context(event.requestContext),
3435
maybe(event.body),
3536
event.isBase64Encoded,
@@ -134,6 +135,13 @@ function maybeList(a) {
134135
return new None();
135136
}
136137

138+
function maybeDict(a) {
139+
if (a) {
140+
return new Some(toDict(a));
141+
}
142+
return new None();
143+
}
144+
137145
export function from_api_gateway_proxy_result_v2(result) {
138146
return {
139147
statusCode: result.status_code,
@@ -163,7 +171,6 @@ function to_cognito_identity(identity) {
163171
if (!identity) {
164172
return undefined;
165173
}
166-
console.log(identity);
167174
return new CognitoIdentity(
168175
identity.cognitoIdentityId,
169176
identity.cognitoIdentityPoolId,
@@ -200,3 +207,7 @@ function to_client_context_env(env) {
200207
env.timezone,
201208
);
202209
}
210+
211+
function toDict(obj) {
212+
return $dict.from_list(List.fromArray(Object.entries(obj)));
213+
}

0 commit comments

Comments
 (0)