···4040}
41414242pub fn ip_address_to_string(address: IpAddress) -> String {
4343- unsafe_to_internal_ip_address(address)
4343+ to_internal_ip_address(address)
4444 |> glisten.ip_address_to_string
4545}
46464747-// IpAddress and glisten.IpAddress are structurally identical.
4848-@external(erlang, "gleam_stdlib", "identity")
4949-fn unsafe_to_internal_ip_address(address: IpAddress) -> glisten.IpAddress
4747+fn to_internal_ip_address(address: IpAddress) -> glisten.IpAddress {
4848+ case address {
4949+ IpV4(a, b, c, d) -> glisten.IpV4(a, b, c, d)
5050+ IpV6(a, b, c, d, e, f, g, h) -> glisten.IpV6(a, b, c, d, e, f, g, h)
5151+ }
5252+}
50535151-// IpAddress and options.IpAddress are structurally identical.
5252-@external(erlang, "gleam_stdlib", "identity")
5353-fn unsafe_from_internal_options_ip_address(
5454- address: options.IpAddress,
5555-) -> IpAddress
5454+fn from_internal_options_ip_address(address: options.IpAddress) -> IpAddress {
5555+ case address {
5656+ options.IpV4(a, b, c, d) -> IpV4(a, b, c, d)
5757+ options.IpV6(a, b, c, d, e, f, g, h) -> IpV6(a, b, c, d, e, f, g, h)
5858+ }
5959+}
56605757-// glisten.IpAddress and IpAddress are structurally identical.
5858-@external(erlang, "gleam_stdlib", "identity")
5959-fn unsafe_from_internal_ip_address(address: glisten.IpAddress) -> IpAddress
6161+fn from_internal_ip_address(address: glisten.IpAddress) -> IpAddress {
6262+ case address {
6363+ glisten.IpV4(a, b, c, d) -> IpV4(a, b, c, d)
6464+ glisten.IpV6(a, b, c, d, e, f, g, h) -> IpV6(a, b, c, d, e, f, g, h)
6565+ }
6666+}
60676168/// The address a socket is bound to, or the address of a connected peer.
6269pub type SocketAddress {
···6875fn convert_socket_address(address: glisten.SocketAddress) -> SocketAddress {
6976 case address {
7077 glisten.TcpSocketAddress(port:, ip_address:) ->
7171- TcpSocketAddress(
7272- ip_address: unsafe_from_internal_ip_address(ip_address),
7373- port:,
7474- )
7878+ TcpSocketAddress(ip_address: from_internal_ip_address(ip_address), port:)
7579 glisten.UnixSocketAddress(path:) -> UnixSocketAddress(path:)
7680 }
7781}
···8084/// the socket information is unavailable.
8185pub fn get_client_info(connection: Connection) -> Result(SocketAddress, Nil) {
8286 case connection {
8383- connection.Http1(transport:, socket:, ..) -> {
8484- let peername = transport.peername(transport, socket)
8787+ connection.Http1(connection) -> {
8888+ let peername = transport.peername(connection.transport, connection.socket)
8589 use info <- result.map(over: peername)
86908791 case info {
8892 socket.TcpSockName(ip_address:, port:) ->
8989- unsafe_from_internal_options_ip_address(ip_address)
9393+ from_internal_options_ip_address(ip_address)
9094 |> TcpSocketAddress(port:)
9195 socket.UnixSockName(path:) -> UnixSocketAddress(path:)
9296 }
···128132 PrivateKeyInfo
129133}
130134131131-// TlsKeyType and options.TlsKeyType are structurally identical.
132132-@external(erlang, "gleam_stdlib", "identity")
133133-fn unsafe_to_internal_tls_key_type(key_type: TlsKeyType) -> options.TlsKeyType
135135+fn to_internal_tls_key_type(key_type: TlsKeyType) -> options.TlsKeyType {
136136+ case key_type {
137137+ RsaPrivateKey -> options.RsaPrivateKey
138138+ EcPrivateKey -> options.EcPrivateKey
139139+ DsaPrivateKey -> options.DsaPrivateKey
140140+ PrivateKeyInfo -> options.PrivateKeyInfo
141141+ }
142142+}
134143135144/// Contains all server configurations, can be adjusted by different builder
136145/// functions.
···283292}
284293285294// Body and connection.Body are structurally identical.
286286-@external(erlang, "gleam_stdlib", "identity")
295295+@external(erlang, "ewe_ffi", "identity")
287296fn unsafe_to_internal_response(
288297 response: response.Response(Body),
289298) -> response.Response(connection.Body)
···313322 glisten.with_tls_der(
314323 pool,
315324 cert:,
316316- key_type: unsafe_to_internal_tls_key_type(key_type),
325325+ key_type: to_internal_tls_key_type(key_type),
317326 key:,
318327 )
319328 None -> pool
···363372 InvalidLimit
364373}
365374366366-// FileError and file.FileError are structurally identical.
367367-@external(erlang, "gleam_stdlib", "identity")
368368-fn unsafe_from_internal_file_error(error: file.FileError) -> FileError
375375+fn from_internal_file_error(error: file.FileError) -> FileError {
376376+ case error {
377377+ file.NotFound -> NotFound
378378+ file.IsDirectory -> IsDirectory
379379+ file.AccessDenied -> AccessDenied
380380+ file.UnknownError -> UnknownError
381381+ file.InvalidOffset -> InvalidOffset
382382+ file.InvalidLimit -> InvalidLimit
383383+ }
384384+}
369385370386/// Prepares a file to be streamed as a response body. `offset` and `limit` in
371387/// bytes let you serve a byte range from the file. leave either as `None` to
···377393) -> Result(Body, FileError) {
378394 case file.resolve(path, offset, limit) {
379395 Ok(file) -> Ok(File(file))
380380- Error(error) -> Error(unsafe_from_internal_file_error(error))
396396+ Error(error) -> Error(from_internal_file_error(error))
381397 }
382398}
383399···389405 InvalidBody
390406}
391407392392-// BodyError and http1.BodyError are structurally identical.
393393-@external(erlang, "gleam_stdlib", "identity")
394394-fn unsafe_from_internal_body_error(error: http1.BodyError) -> BodyError
408408+fn from_internal_http1_body_error(error: http1.BodyError) -> BodyError {
409409+ case error {
410410+ http1.BodyTooLarge -> BodyTooLarge
411411+ http1.InvalidBody -> InvalidBody
412412+ }
413413+}
395414396415/// Reads the entire request body into memory, up to `limit` bytes. For a
397416/// chunked request, any trailer fields are appended to the returned request's
···401420 limit limit: Int,
402421) -> Result(request.Request(BitArray), BodyError) {
403422 case req.body {
404404- connection.Http1(..) -> {
423423+ connection.Http1(connection) -> {
405424 use #(body, trailers) <- result.try(
406406- http1.read_body(http1.unsafe_to_http1_connection(req.body), limit)
407407- |> result.map_error(unsafe_from_internal_body_error),
425425+ http1.read_body(connection, limit)
426426+ |> result.map_error(from_internal_http1_body_error),
408427 )
409428410429 request.Request(..req, headers: list.append(req.headers, trailers), body:)
···414433 }
415434}
416435417417-// http1.Connection and connection.Connection are structurally identical.
418418-@external(erlang, "gleam_stdlib", "identity")
419419-fn unsafe_from_http1_connection(conn: http1.Connection) -> Connection
420420-421436/// The result of one `read_body_chunk` call.
422437pub type ReadEvent {
423438 /// Up to `max_chunk_bytes` of body data. Feed `request` into the next call.
···436451 limit limit: Int,
437452) -> Result(ReadEvent, BodyError) {
438453 case req.body {
439439- connection.Http1(..) -> {
440440- let conn = http1.unsafe_to_http1_connection(req.body)
441441- case http1.read_body_chunk(conn, max_chunk_bytes:, limit:) {
454454+ connection.Http1(connection) -> {
455455+ case http1.read_body_chunk(connection, max_chunk_bytes:, limit:) {
442456 Ok(http1.Chunk(data, connection)) -> {
443443- let body = unsafe_from_http1_connection(connection)
457457+ let body = connection.Http1(connection)
444458 Ok(Chunk(data, request.set_body(req, body)))
445459 }
446460 Ok(http1.Done(trailers)) -> {
447461 let headers = list.append(req.headers, trailers)
448462 Ok(Done(request.Request(..req, headers:, body: Nil)))
449463 }
450450- Error(error) -> Error(unsafe_from_internal_body_error(error))
464464+ Error(error) -> Error(from_internal_http1_body_error(error))
451465 }
452466 }
453467 connection.Http2 -> todo as "HTTP/2 is not implemented yet!"
···459473pub type ResponseWriter =
460474 connection.ResponseWriter
461475462462-// http1.ResponseWriter and ResponseWriter are structurally identical.
463463-@external(erlang, "gleam_stdlib", "identity")
464464-fn unsafe_from_http1_writer(writer: http1.ResponseWriter) -> ResponseWriter
465465-466476/// Starts a streamed response. `handler` must end by calling `finish_chunk` or
467477/// `finish_response` on it, since that's what closes the stream.
468478pub fn stream_response(
···477487/// stream in the same round trip.
478488pub fn send_chunk(writer: ResponseWriter, chunk: BitArray) -> ResponseWriter {
479489 case writer {
480480- connection.Http1Writer(..) ->
481481- http1.send_chunk(http1.unsafe_to_http1_writer(writer), chunk)
482482- |> unsafe_from_http1_writer
490490+ connection.Http1Writer(writer) ->
491491+ connection.Http1Writer(http1.send_chunk(writer, chunk))
483492 connection.Http2Writer -> todo as "HTTP/2 is not implemented yet!"
484493 }
485494}
···487496/// Sends `chunk` as the final response body chunk and closes the stream.
488497pub fn finish_chunk(writer: ResponseWriter, chunk: BitArray) -> Nil {
489498 case writer {
490490- connection.Http1Writer(..) ->
491491- http1.finish_chunk(http1.unsafe_to_http1_writer(writer), chunk)
499499+ connection.Http1Writer(writer) -> http1.finish_chunk(writer, chunk)
492500 connection.Http2Writer -> todo as "HTTP/2 is not implemented yet!"
493501 }
494502}
···497505/// there's one last chunk to send.
498506pub fn finish_response(writer: ResponseWriter) -> Nil {
499507 case writer {
500500- connection.Http1Writer(..) ->
501501- http1.finish_response(http1.unsafe_to_http1_writer(writer))
508508+ connection.Http1Writer(writer) -> http1.finish_response(writer)
502509 connection.Http2Writer -> todo as "HTTP/2 is not implemented yet!"
503510 }
504511}
+26-19
src/ewe/internal/connection.gleam
···44import glisten/transport
5566pub type Connection {
77- Http1(
77+ Http1(Http1Connection)
88+ Http2
99+}
1010+1111+pub type Http1Connection {
1212+ Http1Connection(
813 transport: transport.Transport,
914 socket: socket.Socket,
1015 self: process.Subject(Http1Signal),
···1217 framing: Framing,
1318 // Body bytes delivered to the caller so far, via `read_body_chunk`.
1419 read: Int,
1515- // Bytes left in the chunked-encoding chunk currently being delivered;
2020+ // Bytes left in the chunked-encoding chunk currently being delivered.
1621 // 0 means the next pull starts at a chunk boundary. Unused for `Fixed`
1722 // and `NoBody`.
1823 chunk_remaining: Int,
1924 )
2020- Http2
2125}
22262327/// How to find the end of a request body on the wire, derived once from
···4549 Timeout
4650}
47514848-/// Sent by `http1.gleam` to a request's own private subject, drained
4949-/// synchronously within the same `http1.handle_message` call
5252+/// Sent to a http1 request's own subject, drained synchronously within the same
5353+/// `http1.handle_message` call
5054pub type Http1Signal {
5151- /// Sent by `http1.read_body` and `http1.read_body_chunk` to themselves
5252- /// once the request body has been fully consumed, carrying whatever bytes
5353- /// came after it.
5555+ /// Sent by `http1.read_body` and `http1.read_body_chunk` to themselves once
5656+ /// the request body has been fully consumed, carrying whatever bytes came
5757+ /// after it.
5458 BodyDrained(leftover: BitArray)
5555- /// Sent by `http1.read_body` and `http1.read_body_chunk` to themselves
5656- /// when they gave up on the body part-way through, leaving the connection
5757- /// in an unknown position.
5959+ /// Sent by `http1.read_body` and `http1.read_body_chunk` to themselves when
6060+ /// they gave up on the body part way through, leaving the connection in an
6161+ /// unknown position.
5862 BodyAbandoned
5959- /// Sent by `http1.read_body_chunk` to itself after every chunk it
6060- /// delivers, so that if the caller stops reading before `BodyDrained`,
6161- /// the connection can still resume draining from here instead of from the
6262- /// start of the body.
6363+ /// Sent by `http1.read_body_chunk` to itself after every chunk it delivers,
6464+ /// so that if the caller stops reading before `BodyDrained`, the connection
6565+ /// can still resume draining from here instead of from the start of the body.
6366 BodyProgress(buffer: BitArray, read: Int, chunk_remaining: Int)
6464- /// Sent by `http1.finish_chunk` and `http1.finish_response` to themselves once
6565- /// a streamed response's terminator has been written.
6767+ /// Sent by `http1.finish_chunk` and `http1.finish_response` to themselves
6868+ /// once a streamed response's terminator has been written.
6669 StreamFinished(keep_alive: Bool)
6770}
68716972/// How a streamed response writes its body chunks to the wire.
7073pub type ResponseWriter {
7171- Http1Writer(
7474+ Http1Writer(Http1ResponseWriter)
7575+ Http2Writer
7676+}
7777+7878+pub type Http1ResponseWriter {
7979+ Http1ResponseWriter(
7280 transport: transport.Transport,
7381 socket: socket.Socket,
7482 self: process.Subject(Http1Signal),
···8290 // `False` when `chunked` is `False`.
8391 keep_alive: Bool,
8492 )
8585- Http2Writer
8693}