de6cdc548d44df7d835386a49116364e4ce8df8729801ca85dc975000e66af7f.tri (4809B)
1 !import base as B 2 !import http as HTTP 3 !import conversions as Convert 4 !import arboricx_server as Store 5 !import handlers as H 6 !import io_v2 as V2 7 !import io_v2_storage as Storage 8 !import handler_log as Log 9 !import handler_artifacts as Artifacts 10 !export request 11 !export packageLookupV2 12 !export stdoutLogV2 13 !export writeLogV2 14 !export routerV2 15 16 -- HTTP policy is separate from storage, logging, and the socket lifecycle. 17 -- Outcomes carry a status beside the complete response so logging does not 18 -- parse response bytes. Only completed application routes emit access events; 19 -- parser/protocol rejections remain owned by the scoped HTTP transport. 20 reply status bytes = H.pure [status bytes] 21 error status message = reply status (HTTP.errorResponse status message) 22 23 load digest = do H.bind 24 result <- Artifacts.load digest 25 case result of 26 ((t t), (bytes, t)) = reply 200 (HTTP.response 200 Store.bundleContentType bytes) 27 (t, (["invalid-digest" message], t)) = error 400 message 28 _ = error 404 "Artifact unavailable\n" 29 30 publish bytes = do H.bind 31 result <- Artifacts.publish bytes 32 case result of 33 ((t t), (digest, t)) = reply 201 (HTTP.createdResponse digest) 34 _ = error 400 "Upload failed\n" 35 36 -- Package storage is an explicit captured service: its identity is stable for 37 -- this server. Contextual artifact overrides need no package-path authority. 38 -- The HTTP parser supplies canonical target bytes, as in the original server. 39 40 package lookup target = do H.bind 41 result <- lookup target 42 case result of 43 ((t t), (bytes, t)) = reply 200 (HTTP.response 200 "application/vnd.arboricx.package-release" bytes) 44 (t, (["invalid-package" message], t)) = error 400 message 45 _ = error 404 "Package release not found" 46 47 get lookup target = case Store.stripPrefix Store.packagePrefix target of 48 (t suffix) = package lookup suffix 49 _ = case Store.stripPrefix Store.bundleHashPrefix target of 50 (t digest) = load digest 51 _ = reply 404 HTTP.notFoundResponse 52 53 route _ "GET" "/" _ _ = reply 200 (HTTP.htmlResponse Store.htmlLandingPage) 54 route _ "GET" "/_arboricx/health" _ _ = reply 200 (HTTP.okResponse "OK\n") 55 route lookup "GET" target _ _ = get lookup target 56 route _ "POST" "/_arboricx/bundle" _ body = publish body 57 route _ _ _ _ _ = reply 404 HTTP.notFoundResponse 58 59 request lookup method target headers body = H.label "registry-request" <| do H.bind 60 outcome <- route lookup method target headers body 61 case outcome of 62 [status response] = do H.bind 63 logged <- Log.info status 64 case logged of 65 ((t t), (t, t)) = H.pure response 66 (t, (message, t)) = H.fail "application-failure" "logging" "emit" message 67 _ = H.fail "application-failure" "logging" "emit" "Invalid logger reply" 68 _ = H.fail "application-failure" "registry" "route" "Invalid route outcome" 69 70 -- The captured package service remains independent of contextual artifact routes. 71 packageLookupV2 root limit target = case Store.packageTarget target of 72 (t (name, version)) = H.liftIO (Storage.readBytes 73 (Store.pathJoin (Store.pathJoin (Store.pathJoin root "packages") name) version) limit) 74 _ = H.pure (B.err ["invalid-package" "Invalid package name/version"] t) 75 76 -- Status-only ASCII logging. Ordinary write failures remain Results; publication 77 -- precedes logging and remains visible if the logger fails. 78 writeLogV2 bytes = do V2.onOk 79 writer <- V2.stdout 80 V2.writeAll writer bytes 81 V2.pure (B.ok t t) 82 stdoutLogV2 ["info" fields status] = case B.checkContract B.nat? status of 83 ((t t), _) = H.liftIO (writeLogV2 84 (B.append "registry status=" (B.append (Convert.showNumber status) "\n"))) 85 _ = H.pure (B.err "Invalid access-log status" t) 86 stdoutLogV2 _ = H.pure (B.err "Invalid access-log event" t) 87 88 -- A supervised child contains request interpretation/logger failure without 89 -- weakening native fail-fast semantics or retaining completed task tickets. 90 -- Cancellation remains cancellation; a failure becomes a redacted HTTP response. 91 -- The listener owns sockets and framing. 92 routerV2 root limit sink method target headers body = V2.scope (_ : do V2.require 93 spawned <- V2.spawn 1 1 [] (_ : H.runIOV2 94 (request (packageLookupV2 root limit) method target headers body 95 |> Log.withContext [["method" method] ["target" target]] 96 |> Artifacts.withFilesystemV2 root limit 97 |> Log.withSink sink)) 98 case spawned of 99 [task ticket] = do V2.require 100 outcome <- V2.join ticket 101 case outcome of 102 ["completed" response] = V2.pure response 103 ["failed" _ _] = do V2.bind 104 writeLogV2 "registry request failed\n" 105 V2.pure (HTTP.errorResponse 500 "Internal Server Error\n") 106 ["cancelled" reason _] = V2.cancelled reason 107 _ = V2.fail "Invalid registry task outcome" 108 _ = V2.fail "Invalid registry spawn reply")