···1010import gleam/int
1111import gleam/option.{type Option, None, Some}
1212import gleam/result
1313+import gleam/string
1314import gleam/yielder.{type Yielder}
1515+import logging
14161517import glisten
1618import glisten/socket
···2527} as http_
26282729// -----------------------------------------------------------------------------
2828-// TYPES
3030+// PUBLIC TYPES
2931// -----------------------------------------------------------------------------
30323131-// Control flow for the handler loop
3232-type Next {
3333- Continue(new_state: GlistenState)
3434- Stop
3535-}
3636-3333+// Custom message that can be sent to or received from the Glisten actor
3734pub type GlistenMessage {
3835 IdleTimeout
3936}
40373838+// State of the Glisten actor
4139pub type GlistenState {
4240 GlistenState(
4341 timer: Option(process.Timer),
···4644}
47454846// -----------------------------------------------------------------------------
4747+// INTERNAL TYPES
4848+// -----------------------------------------------------------------------------
4949+5050+// Control flow for the handler loop
5151+type Next {
5252+ Continue(new_state: GlistenState)
5353+ Stop
5454+}
5555+5656+// -----------------------------------------------------------------------------
4957// PUBLIC API
5058// -----------------------------------------------------------------------------
51596060+/// Initializes the Glisten actor's state and selector for custom messages
5261pub fn init(_) -> #(GlistenState, Option(process.Selector(GlistenMessage))) {
5362 let subject = process.new_subject()
5463 let selector =
···96105 _ -> 400
97106 }
98107108108+ logging.log(
109109+ logging.Error,
110110+ "Received invalid request: " <> string.inspect(reason),
111111+ )
112112+99113 let _ =
100114 response.new(status)
101115 |> response.set_body(bytes_tree.new())
···125139) -> Next {
126140 let resp =
127141 exception.rescue(fn() { handler(req) })
128128- |> result.map_error(fn(_e) {
129129- on_crash |> response.set_header("connection", "close")
142142+ |> result.map_error(fn(e) {
143143+ logging.log(logging.Error, string.inspect(e))
144144+145145+ response.set_header(on_crash, "connection", "close")
130146 })
131147 |> result.unwrap_both()
132148
+13-2
src/ewe/internal/http.gleam
···3636import ewe/internal/file
37373838// -----------------------------------------------------------------------------
3939-// TYPES
3939+// PUBLIC TYPES
4040// -----------------------------------------------------------------------------
41414242// HTTP response body types
···105105 MissingWebsocketKey
106106}
107107108108+// Possible results of consuming some amount of data from the request body
108109pub type Stream {
109110 Consumed(data: BitArray, next: fn(Int) -> Result(Stream, ParseError))
110111 Done
111112}
112113114114+// -----------------------------------------------------------------------------
115115+// INTERNAL TYPES
116116+// -----------------------------------------------------------------------------
117117+118118+// Function that reads `N` amount of bytes from the request body
113119type Consumer =
114120 fn(Int) -> Result(Stream, ParseError)
115121···120126 FinalChunk(rest: Buffer)
121127}
122128129129+// State of the chunked body parsing
123130type ChunkedStreamState {
124131 ChunkedStreamState(data: Buffer, chunk: Buffer, done: Bool)
125132}
···586593// BODY
587594// -----------------------------------------------------------------------------
588595596596+/// Creates a consumer function that reads `N` amount of bytes from the request
597597+/// body until it is fully consumed
589598fn do_stream_body(req: Request(Connection), buffer: Buffer) -> Consumer {
590599 fn(size: Int) {
591600 let buffer_size = bit_array.byte_size(buffer.data)
···701710 }
702711}
703712713713+/// Creates a consumer function that reads `N` amount of bytes from the chunked
714714+/// request body until it is fully consumed
704715fn do_stream_body_chunked(
705716 req: Request(Connection),
706717 chunked_stream_state: ChunkedStreamState,
···724735 }
725736}
726737738738+/// Reads data from socket until `N` amount of bytes are read
727739fn read_from_socket_until(
728740 transport transport: Transport,
729741 socket socket: Socket,
···854866// EXTERNAL FFI
855867// -----------------------------------------------------------------------------
856868857857-/// Splits binary data using external Erlang binary:split
858869@external(erlang, "binary", "split")
859870fn split(
860871 subject: BitArray,
+21-6
src/ewe/internal/websocket.gleam
···1111import gleam/option.{type Option, None, Some}
1212import gleam/otp/actor
1313import gleam/result
1414+import gleam/string
1515+import logging
14161517import glisten/socket.{type Socket, type SocketReason}
1618import glisten/socket/options.{ActiveMode, Once}
···153155 |> process.select_record(atom.create("ssl_closed"), 1, fn(_) { Close })
154156}
155157158158+/// Maps user selector to internal message
156159fn user_selector(
157160 selector: Option(Selector(user_message)),
158161) -> Option(Selector(InternalMessage(user_message))) {
···239242 deflate: Option(compression.Context),
240243 data: data,
241244) -> Result(Nil, SocketReason) {
242242- // use <- time_function("send_frame_total")
243243-244245 let frame =
245246 exception.rescue(fn() {
246247 encoder(data, deflate, option.None)
···249250250251 case frame {
251252 Ok(frame) -> frame
252252- Error(_) -> panic as non_owning_process
253253+ Error(reason) -> {
254254+ logging.log(
255255+ logging.Error,
256256+ "Frame should be sent from the WebSocket connection, but was sent from different process: "
257257+ <> string.inspect(reason),
258258+ )
259259+ panic as non_owning_process
260260+ }
253261 }
254262}
255263···295303 }
296304}
297305306306+/// Handles frames processing
298307fn handle_frames_processing(
299308 state: WebsocketState(user_state),
300309 conn: WebsocketConnection,
···303312 handler: Handler(user_state, user_message),
304313 on_close: OnClose(user_state),
305314) {
306306- // use <- time_function("handle_frames_processing")
307307-308315 let frames = list.append(state.awaiting_frames, frames)
309316310317 let #(data_frames, control_frames) = separate_frames(frames, [], [])
···379386 }
380387}
381388389389+/// Separates frames into data and control frames
382390fn separate_frames(
383391 frames: List(websocket.ParsedFrame),
384392 data_frames: List(websocket.ParsedFrame),
···449457 [websocket.Continuation(_, _), ..], Continue(_, _) -> {
450458 AbnormalStop("Unexpected continuation frame")
451459 }
460460+452461 // Data frames
453462 [frame, ..rest], Continue(user_state, selector) -> {
454463 let call =
···531540 on_close(conn, state.user_state)
532541533542 case abnormal_reason {
534534- Some(reason) -> actor.stop_abnormal(reason)
543543+ Some(reason) -> {
544544+ logging.log(
545545+ logging.Error,
546546+ "WebSocket connection closed abnormally: " <> reason,
547547+ )
548548+ actor.stop_abnormal(reason)
549549+ }
535550 None -> actor.stop()
536551 }
537552}