gleam HTTP server. because it glistens on a web
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

you know it

+360 -83
+2
examples/complete/gleam.toml
··· 15 15 [dependencies] 16 16 gleam_stdlib = "~> 0.34 or ~> 1.0" 17 17 mist = { path = "../.." } 18 + glisten = { path = "../../../glisten" } 18 19 gleam_erlang = "~> 0.24" 19 20 gleam_http = "~> 3.5" 20 21 gleam_otp = "~> 0.9" 22 + logging = "~> 1.0" 21 23 22 24 [dev-dependencies] 23 25 gleeunit = "~> 1.0"
+7 -4
examples/complete/manifest.toml
··· 2 2 # You typically do not need to edit this file 3 3 4 4 packages = [ 5 - { name = "birl", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "0757CFE97DA52F19BC3262AC3DD284D9DAD2718D4C1830888DE483FB147477D4" }, 5 + { name = "birl", version = "1.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "976CFF85D34D50F7775896615A71745FBE0C325E50399787088F941B539A0497" }, 6 6 { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, 7 7 { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, 8 8 { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, 9 9 { name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" }, 10 - { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, 11 - { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" }, 10 + { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, 11 + { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], source = "local", path = "../../../glisten" }, 12 12 { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, 13 13 { name = "logging", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "82C112ED9B6C30C1772A6FE2613B94B13F62EA35F5869A2630D13948D297BD39" }, 14 14 { name = "mist", version = "1.0.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "hpack_erl", "logging"], source = "local", path = "../.." }, 15 - { name = "ranger", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "28E615AE7590ED922AF1510DDF606A2ECBBC2A9609AF36D412EDC925F06DFD20" }, 15 + { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, 16 + { name = "telemetry", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "DAD9CE9D8EFFC621708F99EAC538EF1CBE05D6A874DD741DE2E689C47FEAFED5" }, 16 17 ] 17 18 18 19 [requirements] ··· 21 22 gleam_otp = { version = "~> 0.9" } 22 23 gleam_stdlib = { version = "~> 0.34 or ~> 1.0" } 23 24 gleeunit = { version = "~> 1.0" } 25 + glisten = { path = "../../../glisten" } 26 + logging = { version = "~> 1.0" } 24 27 mist = { path = "../.." }
+5
examples/complete/src/complete.gleam
··· 10 10 import gleam/otp/actor 11 11 import gleam/result 12 12 import gleam/string 13 + import glisten 13 14 import mist.{type Connection, type ResponseData} 15 + import logging 14 16 15 17 @external(erlang, "logger", "update_primary_config") 16 18 fn logger_update_primary_config(config: Dict(Atom, Atom)) -> Result(Nil, any) 17 19 18 20 pub fn main() { 21 + logging.configure() 19 22 logger_update_primary_config( 20 23 dict.from_list([ 21 24 #(atom.create_from_string("level"), atom.create_from_string("debug")), 22 25 ]), 23 26 ) 27 + // glisten.configure_logger() 28 + // mist.configure_logger() 24 29 // These values are for the Websocket process initialized below 25 30 let selector = process.new_selector() 26 31 let state = Nil
+2 -1
gleam.toml
··· 15 15 hpack_erl = "~> 0.3" 16 16 birl = "~> 1.3" 17 17 logging = "~> 1.0" 18 - glisten = "~> 2.0" 18 + # glisten = "~> 2.0" 19 + glisten = { path = "../glisten" } 19 20 20 21 [dev-dependencies] 21 22 gleeunit = "~> 1.0"
+3 -2
manifest.toml
··· 10 10 { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, 11 11 { name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" }, 12 12 { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, 13 - { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" }, 13 + { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], source = "local", path = "../glisten" }, 14 14 { name = "hackney", version = "1.20.1", build_tools = ["rebar3"], requirements = ["certifi", "idna", "metrics", "mimerl", "parse_trans", "ssl_verify_fun", "unicode_util_compat"], otp_app = "hackney", source = "hex", outer_checksum = "FE9094E5F1A2A2C0A7D10918FEE36BFEC0EC2A979994CFF8CFE8058CD9AF38E3" }, 15 15 { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, 16 16 { name = "idna", version = "6.1.1", build_tools = ["rebar3"], requirements = ["unicode_util_compat"], otp_app = "idna", source = "hex", outer_checksum = "92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA" }, ··· 20 20 { name = "parse_trans", version = "3.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "parse_trans", source = "hex", outer_checksum = "620A406CE75DADA827B82E453C19CF06776BE266F5A67CFF34E1EF2CBB60E49A" }, 21 21 { name = "ranger", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "28E615AE7590ED922AF1510DDF606A2ECBBC2A9609AF36D412EDC925F06DFD20" }, 22 22 { name = "ssl_verify_fun", version = "1.1.7", build_tools = ["mix", "rebar3", "make"], requirements = [], otp_app = "ssl_verify_fun", source = "hex", outer_checksum = "FE4C190E8F37401D30167C8C405EDA19469F34577987C76DDE613E838BBC67F8" }, 23 + { name = "telemetry", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "DAD9CE9D8EFFC621708F99EAC538EF1CBE05D6A874DD741DE2E689C47FEAFED5" }, 23 24 { name = "unicode_util_compat", version = "0.7.0", build_tools = ["rebar3"], requirements = [], otp_app = "unicode_util_compat", source = "hex", outer_checksum = "25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521" }, 24 25 ] 25 26 ··· 31 32 gleam_otp = { version = "~> 0.9" } 32 33 gleam_stdlib = { version = "~> 0.35 or ~> 1.0" } 33 34 gleeunit = { version = "~> 1.0" } 34 - glisten = { version = "~> 2.0"} 35 + glisten = { path = "../glisten" } 35 36 hpack_erl = { version = "~> 0.3" } 36 37 logging = { version = "~> 1.0" }
+5
src/mist.gleam
··· 28 28 Chunked as InternalChunked, File as InternalFile, 29 29 ServerSentEvents as InternalServerSentEvents, Websocket as InternalWebsocket, 30 30 } 31 + import mist/internal/telemetry 31 32 import mist/internal/websocket.{ 32 33 type HandlerMessage, type WebsocketConnection as InternalWebsocketConnection, 33 34 BinaryFrame, Data, Internal, TextFrame, User, ··· 681 682 |> result.replace(Nil) 682 683 |> result.nil_error 683 684 } 685 + 686 + pub fn configure_logger() { 687 + telemetry.attach_many("mist-logger", telemetry.events, telemetry.log) 688 + }
+3
src/mist/internal/file.gleam
··· 76 76 77 77 @external(erlang, "filelib", "file_size") 78 78 fn size(path: BitArray) -> Int 79 + 80 + @external(erlang, "mist_ffi", "file_close") 81 + pub fn close(file: FileDescriptor) -> Result(Nil, FileError)
+24 -4
src/mist/internal/handler.gleam
··· 1 + import gleam/dict 1 2 import gleam/erlang/process.{type Selector, type Subject} 2 3 import gleam/function 3 4 import gleam/http/response ··· 14 15 import mist/internal/http/handler as http_handler 15 16 import mist/internal/http2/handler.{type Message, Send} as http2_handler 16 17 import mist/internal/http2 18 + import mist/internal/telemetry 17 19 import logging 18 20 19 21 pub type HandlerError { ··· 89 91 Some(t) -> process.cancel_timer(t) 90 92 _ -> process.TimerNotFound 91 93 } 92 - msg 93 - |> http.parse_request(conn) 94 + 95 + let res = { 96 + use <- telemetry.span( 97 + [telemetry.Mist, telemetry.ParseRequest], 98 + dict.new(), 99 + ) 100 + http.parse_request(msg, conn) 101 + } 102 + 103 + res 94 104 |> result.map_error(fn(err) { 95 105 case err { 96 106 DiscardPacket -> process.Normal ··· 103 113 }) 104 114 |> result.then(fn(req) { 105 115 case req { 106 - http.Http1Request(req) -> 116 + http.Http1Request(req) -> { 117 + use <- telemetry.span( 118 + [telemetry.Mist, telemetry.Http1Handler], 119 + dict.new(), 120 + ) 107 121 http_handler.call(req, handler, conn, sender) 108 122 |> result.map(fn(new_state) { 109 123 Http1(state: new_state, self: self) 110 124 }) 111 - http.Upgrade(data) -> 125 + } 126 + http.Upgrade(data) -> { 127 + use <- telemetry.span( 128 + [telemetry.Mist, telemetry.HttpUpgrade], 129 + dict.new(), 130 + ) 112 131 http2_handler.upgrade(data, conn, self) 113 132 |> result.map(Http2) 133 + } 114 134 } 115 135 }) 116 136 }
+100 -50
src/mist/internal/http.gleam
··· 1 - // import birl 2 1 import gleam/bit_array 3 2 import gleam/bool 4 3 import gleam/bytes_builder.{type BytesBuilder} ··· 17 16 import gleam/pair 18 17 import gleam/result 19 18 import gleam/string 20 - import gleam/uri 21 19 import glisten.{type ClientIp, type Socket} 22 20 import glisten/transport.{type Transport} 23 21 import mist/internal/buffer.{type Buffer, Buffer} 24 22 import mist/internal/clock 25 23 import mist/internal/encoder 26 24 import mist/internal/file 25 + import mist/internal/telemetry 27 26 28 27 pub type ResponseData { 29 28 Websocket(Selector(ProcessDown)) ··· 99 98 transport: Transport, 100 99 headers: Dict(String, String), 101 100 ) -> Result(#(Dict(String, String), BitArray), DecodeError) { 102 - case decode_packet(HttphBin, bs, []) { 101 + let packet = { 102 + use <- telemetry.span([telemetry.Mist, telemetry.DecodePacket], dict.new()) 103 + decode_packet(HttphBin, bs, []) 104 + } 105 + case packet { 103 106 Ok(BinaryData(HttpHeader(_, _field, field, value), rest)) -> { 104 107 let field = from_header(field) 105 108 let assert Ok(value) = bit_array.to_string(value) ··· 128 131 buffer: Buffer, 129 132 error: DecodeError, 130 133 ) -> Result(BitArray, DecodeError) { 134 + use <- telemetry.span([telemetry.Mist, telemetry.ReadData], dict.new()) 131 135 // TODO: don't hard-code these, probably 132 136 let to_read = int.min(buffer.remaining, 1_000_000) 133 137 let timeout = 15_000 ··· 249 253 Upgrade(BitArray) 250 254 } 251 255 256 + import gleam/io 257 + 252 258 /// Turns the TCP message into an HTTP request 253 259 pub fn parse_request( 254 260 bs: BitArray, 255 261 conn: Connection, 256 262 ) -> Result(ParsedRequest, DecodeError) { 257 - case decode_packet(HttpBin, bs, []) { 263 + use <- telemetry.span([telemetry.Mist, telemetry.ParseRequest2], dict.new()) 264 + let packet = { 265 + use <- telemetry.span([telemetry.Mist, telemetry.DecodePacket], dict.new()) 266 + decode_packet(HttpBin, bs, []) 267 + } 268 + case packet { 258 269 Ok(BinaryData(HttpRequest(http_method, AbsPath(path), _version), rest)) -> { 259 - use method <- result.then( 260 - http_method 261 - |> atom.from_dynamic 262 - |> result.map(atom.to_string) 263 - |> result.or(dynamic.string(http_method)) 264 - |> result.nil_error 265 - |> result.then(http.parse_method) 266 - |> result.replace_error(UnknownMethod), 267 - ) 268 - use #(headers, rest) <- result.then(parse_headers( 269 - rest, 270 - conn.socket, 271 - conn.transport, 272 - dict.new(), 273 - )) 270 + use method <- 271 + { 272 + use <- telemetry.span( 273 + [telemetry.Mist, telemetry.ParseMethod], 274 + dict.new(), 275 + ) 276 + result.then( 277 + http_method 278 + |> atom.from_dynamic 279 + |> result.map(atom.to_string) 280 + |> result.or(dynamic.string(http_method)) 281 + |> result.nil_error 282 + |> result.then(http.parse_method) 283 + |> result.replace_error(UnknownMethod), 284 + _, 285 + ) 286 + } 287 + use #(headers, rest) <- 288 + { 289 + use <- telemetry.span( 290 + [telemetry.Mist, telemetry.ParseHeaders], 291 + dict.new(), 292 + ) 293 + result.then( 294 + parse_headers(rest, conn.socket, conn.transport, dict.new()), 295 + _, 296 + ) 297 + } 298 + use <- telemetry.span([telemetry.Mist, telemetry.ParseRest], dict.new()) 274 299 use path <- result.then( 275 300 path 276 301 |> bit_array.to_string 277 302 |> result.replace_error(InvalidPath), 278 303 ) 279 - use parsed <- result.then( 280 - uri.parse(path) 281 - |> result.replace_error(InvalidPath), 282 - ) 283 - let #(path, query) = #(parsed.path, parsed.query) 304 + use #(path, query) <- 305 + { 306 + use <- telemetry.span( 307 + [telemetry.Mist, telemetry.ParsePath], 308 + dict.new(), 309 + ) 310 + result.try( 311 + get_path_and_query(path) 312 + |> result.replace_error(InvalidPath), 313 + _, 314 + ) 315 + } 284 316 let scheme = case conn.transport { 285 317 transport.Ssl(..) -> http.Https 286 318 transport.Tcp(..) -> http.Http 287 319 } 288 - use host_header <- result.then( 289 - dict.get(headers, "host") 290 - |> result.replace_error(NoHostHeader), 320 + use host_header <- result.then({ 321 + use <- telemetry.span([telemetry.Mist, telemetry.ParseHost], dict.new()) 322 + use host_header <- result.then( 323 + dict.get(headers, "host") 324 + |> result.replace_error(NoHostHeader), 325 + ) 326 + Ok(host_header) 327 + }) 328 + let #(hostname, port) = { 329 + use <- telemetry.span([telemetry.Mist, telemetry.ParsePort], dict.new()) 330 + let #(hostname, port) = 331 + host_header 332 + |> string.split_once(":") 333 + |> result.unwrap(#(host_header, "")) 334 + let port = 335 + int.parse(port) 336 + |> result.map_error(fn(_err) { 337 + case scheme { 338 + http.Https -> 443 339 + http.Http -> 80 340 + } 341 + }) 342 + |> result.unwrap_both 343 + #(hostname, port) 344 + } 345 + use <- telemetry.span( 346 + [telemetry.Mist, telemetry.BuildRequest], 347 + dict.new(), 291 348 ) 292 - let #(hostname, port) = 293 - host_header 294 - |> string.split_once(":") 295 - |> result.unwrap(#(host_header, "")) 296 - let port = 297 - int.parse(port) 298 - |> result.map_error(fn(_err) { 299 - case scheme { 300 - http.Https -> 443 301 - http.Http -> 80 302 - } 303 - }) 304 - |> result.unwrap_both 305 349 let req = 306 - request.new() 307 - |> request.set_scheme(scheme) 308 - |> request.set_host(hostname) 309 - |> request.set_port(port) 310 - |> request.set_body(Connection(..conn, body: Initial(rest))) 311 - |> request.set_method(method) 312 - |> request.set_path(path) 313 - Ok(Http1Request( 314 - request.Request(..req, query: query, headers: dict.to_list(headers)), 315 - )) 350 + request.Request( 351 + body: Connection(..conn, body: Initial(rest)), 352 + headers: dict.to_list(headers), 353 + host: hostname, 354 + method: method, 355 + path: path, 356 + port: option.Some(port), 357 + query: option.from_result(query), 358 + scheme: scheme, 359 + ) 360 + Ok(Http1Request(req)) 316 361 } 317 362 // "\r\nSM\r\n\r\n" 318 363 Ok(Http2Upgrade(<< ··· 555 600 556 601 @external(erlang, "binary", "split") 557 602 fn binary_split(source: BitArray, pattern: BitArray) -> List(BitArray) 603 + 604 + @external(erlang, "mist_ffi", "get_path_and_query") 605 + fn get_path_and_query( 606 + str: String, 607 + ) -> Result(#(String, Result(String, Nil)), #(value, term))
+37 -21
src/mist/internal/http/handler.gleam
··· 133 133 conn: Connection, 134 134 ) -> Result(Nil, SocketReason) { 135 135 let assert File(file_descriptor, offset, length) = body 136 - resp 137 - |> response.prepend_header("content-length", int.to_string(length - offset)) 138 - |> response.set_body(bytes_builder.new()) 139 - |> fn(r: response.Response(BytesBuilder)) { 140 - encoder.response_builder(resp.status, r.headers) 141 - } 142 - |> transport.send(conn.transport, conn.socket, _) 143 - |> result.then(fn(_) { 144 - file.sendfile( 145 - conn.transport, 146 - file_descriptor, 147 - conn.socket, 148 - offset, 149 - length, 150 - [], 151 - ) 152 - |> result.map_error(fn(err) { 153 - logging.log(logging.Error, "Failed to send file: " <> string.inspect(err)) 154 - Badarg 136 + let return = 137 + resp 138 + |> response.prepend_header("content-length", int.to_string(length - offset)) 139 + |> response.set_body(bytes_builder.new()) 140 + |> fn(r: response.Response(BytesBuilder)) { 141 + encoder.response_builder(resp.status, r.headers) 142 + } 143 + |> transport.send(conn.transport, conn.socket, _) 144 + |> result.then(fn(_) { 145 + file.sendfile( 146 + conn.transport, 147 + file_descriptor, 148 + conn.socket, 149 + offset, 150 + length, 151 + [], 152 + ) 153 + |> result.map_error(fn(err) { 154 + logging.log( 155 + logging.Error, 156 + "Failed to send file: " <> string.inspect(err), 157 + ) 158 + Badarg 159 + }) 155 160 }) 156 - }) 157 - |> result.replace(Nil) 161 + |> result.replace(Nil) 162 + 163 + case file.close(file_descriptor) { 164 + Ok(_nil) -> Nil 165 + Error(reason) -> { 166 + logging.log( 167 + logging.Error, 168 + "Failed to close file: " <> string.inspect(reason), 169 + ) 170 + } 171 + } 172 + 173 + return 158 174 } 159 175 160 176 fn handle_bytes_builder_body(
+141
src/mist/internal/telemetry.gleam
··· 1 + import gleam/dict.{type Dict} 2 + import gleam/dynamic.{type Dynamic} 3 + import gleam/erlang/atom.{type Atom} 4 + import gleam/int 5 + import gleam/result 6 + import gleam/string 7 + import logging 8 + 9 + pub type Event { 10 + Start 11 + Stop 12 + 13 + Mist 14 + 15 + ParseRequest 16 + ParseRequest2 17 + 18 + DecodePacket 19 + ConvertPath 20 + ParseMethod 21 + ParseHeaders 22 + ParseRest 23 + ParsePath 24 + ParseTransport 25 + ParseHost 26 + ParsePort 27 + BuildRequest 28 + ReadData 29 + 30 + Http1Handler 31 + HttpUpgrade 32 + Http2Handler 33 + } 34 + 35 + pub const events = [ 36 + [Mist, ParseRequest, Stop], 37 + [Mist, ParseRequest2, Stop], 38 + [Mist, Http1Handler, Stop], 39 + [Mist, HttpUpgrade, Stop], 40 + [Mist, Http2Handler, Stop], 41 + [Mist, DecodePacket, Stop], 42 + [Mist, ConvertPath, Stop], 43 + [Mist, ParseMethod, Stop], 44 + [Mist, ParseHeaders, Stop], 45 + [Mist, ParseRest, Stop], 46 + [Mist, ParsePath, Stop], 47 + [Mist, ParseTransport, Stop], 48 + [Mist, ParseHost, Stop], 49 + [Mist, ParsePort, Stop], 50 + [Mist, BuildRequest, Stop], 51 + [Mist, ReadData, Stop], 52 + ] 53 + 54 + type TimeUnit { 55 + Native 56 + Microsecond 57 + } 58 + 59 + @external(erlang, "erlang", "convert_time_unit") 60 + fn convert_time_unit( 61 + time: Int, 62 + from from_unit: TimeUnit, 63 + to to_unit: TimeUnit, 64 + ) -> Int 65 + 66 + pub fn log( 67 + path: List(Event), 68 + measurements: Dict(Atom, Dynamic), 69 + _metadata: Dict(String, Dynamic), 70 + _config: List(config), 71 + ) -> Nil { 72 + let duration_string = 73 + dict.get(measurements, atom.create_from_string("duration")) 74 + |> result.then(fn(val) { result.nil_error(dynamic.int(val)) }) 75 + |> result.map(convert_time_unit(_, Native, Microsecond)) 76 + |> result.map(fn(time) { " duration: " <> int.to_string(time) <> "μs, " }) 77 + |> result.unwrap("") 78 + 79 + logging.log(logging.Debug, string.inspect(path) <> duration_string) 80 + // <> " metadata: " 81 + // <> string.inspect(metadata), 82 + } 83 + 84 + pub fn span( 85 + path: List(Event), 86 + metadata: Dict(String, Dynamic), 87 + wrapping: fn() -> return, 88 + ) -> return { 89 + use <- do_span(path, metadata) 90 + let res = wrapping() 91 + #(res, dict.new()) 92 + } 93 + 94 + @external(erlang, "telemetry", "span") 95 + fn do_span( 96 + path: List(Event), 97 + metadata: Dict(String, Dynamic), 98 + wrapping: fn() -> #(return, Dict(String, Dynamic)), 99 + ) -> return 100 + 101 + pub fn attach_many( 102 + id: String, 103 + path: List(List(Event)), 104 + handler: fn( 105 + List(Event), 106 + Dict(Atom, Dynamic), 107 + Dict(String, Dynamic), 108 + List(config), 109 + ) -> 110 + Nil, 111 + ) -> Nil { 112 + do_attach_many(id, path, handler, Nil) 113 + } 114 + 115 + @external(erlang, "telemetry", "attach_many") 116 + fn do_attach_many( 117 + id: String, 118 + path: List(List(Event)), 119 + handler: fn( 120 + List(Event), 121 + Dict(Atom, Dynamic), 122 + Dict(String, Dynamic), 123 + List(config), 124 + ) -> 125 + Nil, 126 + config: Nil, 127 + ) -> Nil 128 + 129 + @external(erlang, "telemetry", "attach") 130 + pub fn attach( 131 + id: String, 132 + event: List(Event), 133 + handler: fn( 134 + List(Event), 135 + Dict(Atom, Dynamic), 136 + Dict(String, Dynamic), 137 + List(config), 138 + ) -> 139 + Nil, 140 + config: Nil, 141 + ) -> Nil
+31 -1
src/mist_ffi.erl
··· 1 1 -module(mist_ffi). 2 2 3 3 -export([binary_match/2, decode_packet/3, file_open/1, string_to_int/2, hpack_decode/2, 4 - hpack_encode/2, hpack_new_max_table_size/2, ets_lookup_element/3]). 4 + hpack_encode/2, hpack_new_max_table_size/2, ets_lookup_element/3, get_path_and_query/1, 5 + file_close/1]). 5 6 6 7 decode_packet(Type, Packet, Opts) -> 7 8 case erlang:decode_packet(Type, Packet, Opts) of ··· 49 50 {error, unknown_file_error} 50 51 end. 51 52 53 + file_close(File) -> 54 + case file:close(File) of 55 + ok -> 56 + {ok, nil}; 57 + {error, enoent} -> 58 + {error, no_entry}; 59 + {error, eacces} -> 60 + {error, no_access}; 61 + {error, eisdir} -> 62 + {error, is_dir}; 63 + _ -> 64 + {error, unknown_file_error} 65 + end. 66 + 52 67 hpack_decode(Context, Bin) -> 53 68 case hpack:decode(Bin, Context) of 54 69 {ok, {Headers, NewContext}} -> ··· 72 87 error:badarg -> 73 88 {error, nil} 74 89 end. 90 + 91 + get_path_and_query(String) -> 92 + case uri_string:parse(String) of 93 + {error, Value, Term} -> 94 + {error, {Value, Term}}; 95 + UriMap -> 96 + Query = 97 + case maps:find(query, UriMap) of 98 + {ok, Value} -> 99 + {ok, Value}; 100 + error -> 101 + {error, nil} 102 + end, 103 + {ok, {maps:get(path, UriMap), Query}} 104 + end.