Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9c6b0de
Docs: Tutorials: HTTP Client: Chapter 1.
moigagoo Feb 6, 2026
c0c4e5c
Docs: Tutorials: HTTP Client: Chapter 2 (WIP).
moigagoo Feb 6, 2026
2bd39c0
Docs: Tutorials: HTTP Client: Chapter 2.
moigagoo Feb 7, 2026
7ecd27e
Docs: Uptimemon: Chapter 2: Replace status.im with mock.codes for rel…
moigagoo Feb 9, 2026
a429ab4
Docs: Tutorials: HTTP Client: Chapter 3.
moigagoo Feb 10, 2026
ff0899d
Docs: Tutorials: HTTP Client: Chapter 4 (WIP).
moigagoo Feb 11, 2026
3106871
Docs: Tutorials: HTTP Client: Chapter 4 (WIP).
moigagoo Feb 12, 2026
1591e5b
Docs: Tutorials: HTTP Client: Chapter 4.
moigagoo Feb 12, 2026
a8e520b
Docs: Tutorials: HTTP Client: Chapter 5 (WIP).
moigagoo Feb 13, 2026
a00a3a0
Docs: Tutorials: HTTP Client: Chapter 5.
moigagoo Feb 13, 2026
1d1c98a
Docs: Tutorials: HTTP Client: Chapter 6.
moigagoo Feb 13, 2026
b3bada4
Docs: Tutorials: HTTP Client: Add links to the source code.
moigagoo Feb 14, 2026
9003c05
Build API docs for modules in apps/http.
moigagoo Feb 14, 2026
0b5a75d
Fix exception printing.
moigagoo Feb 16, 2026
3ed384a
Docs: Tutorials: HTTP Client: Chapter 1: Add ref links.
moigagoo Feb 16, 2026
9d7e231
Docs: Tutorials: HTTP Client: Add ref links to all chapters.
moigagoo Feb 16, 2026
25dccf5
CI: Add shiftinclude.
moigagoo Mar 6, 2026
e801920
CI: Add shiftinclude.
moigagoo Mar 6, 2026
2ab3b67
Docs: Chapter 1: Replace hardcoded code snippets with includes.
moigagoo Mar 6, 2026
f0a5e9f
Docs: Uptimemon: Replace line numbers with anchors.
moigagoo Mar 7, 2026
a2312cb
Docs: Uptimemon: Chapter 1: Improve exception handling.
moigagoo Mar 7, 2026
ce9e273
Docs: Uptimemon: Chapter 2: Replace copypasta with includes.
moigagoo Mar 7, 2026
0d73475
Docs: Uptimemon: Chapter 3: Replace copypasta with includes.
moigagoo Mar 7, 2026
ddc599a
Docs: Uptimemon: Chapter 4: Replace copypasta with includes.
moigagoo Mar 9, 2026
927495b
Docs: Uptimemon: Chapter 5: Replace copypasta with includes.
moigagoo Mar 9, 2026
63b6d62
Docs: Uptimemon: Fix missing exceptions.
moigagoo Mar 9, 2026
5930532
Docs: Uptimemon: Chapter 6: Replace copypasta with includes.
moigagoo Mar 9, 2026
3dd9503
HTTP client tutorial: Rename directory to http_client.
moigagoo Mar 27, 2026
1e85d89
HTTP client tutorial: Switch from single files to Nimble projects in …
moigagoo Mar 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/workflows/doc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ jobs:
build:
timeout-minutes: 20

name: 'Generate & upload documentation'
runs-on: 'ubuntu-latest'
name: "Generate & upload documentation"
runs-on: "ubuntu-latest"
continue-on-error: true
steps:
- name: Checkout
Expand All @@ -24,14 +24,15 @@ jobs:

- name: Install mdBook and preprocessors
run: |
cargo binstall mdbook@0.4.36 \
cargo binstall mdbook@0.4.51 \
mdbook-toc@0.14.1 \
mdbook-open-on-gh@2.4.1 \
mdbook-admonish@1.14.0
mdbook-admonish@1.14.0 \
mdbook-shiftinclude@0.1.0

- uses: jiro4989/setup-nim-action@v1
with:
nim-version: '1.6.20'
nim-version: "1.6.20"

- name: Generate doc
run: |
Expand Down
13 changes: 12 additions & 1 deletion chronos.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ proc run(args, path: string) =
build args, path
exec "build/" & path.splitPath[1]

proc tryExec(cmd: string) =
try:
exec cmd
except Exception as e:
echo e.msg

task examples, "Build examples":
# Build book examples
for file in listFiles("docs/examples"):
Expand Down Expand Up @@ -82,4 +88,9 @@ task test_libbacktrace, "test with libbacktrace":

task docs, "Generate API documentation":
exec "mdbook build docs"
exec nimc & " doc " & "--git.url:https://github.com/status-im/nim-chronos --git.commit:master --outdir:docs/book/api --project chronos"
tryExec nimc & " doc " & "--git.url:https://github.com/status-im/nim-chronos --git.commit:master --outdir:docs/book/api --project chronos"

# Build the docs for modules that aren't part of the main module.
for item in walkDir("chronos/apps/http"):
if item.kind == pcFile and item.path.splitFile().ext == ".nim":
tryExec nimc & " doc " & "--git.url:https://github.com/status-im/nim-chronos --git.commit:master --outdir:docs/book/api/chronos/apps/http " & item.path
12 changes: 9 additions & 3 deletions docs/book.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[book]
authors = ["Jacek Sieka"]
authors = ["Jacek Sieka", "Constantine Molchanov"]
language = "en"
multilingual = false
src = "src"
Expand All @@ -13,8 +13,14 @@ max-level = 2
[preprocessor.open-on-gh]
command = "mdbook-open-on-gh"
renderer = ["html"]
git-branch = "master"

[preprocessor.admonish]
command = "mdbook-admonish"
assets_version = "3.1.0" # do not edit: managed by `mdbook-admonish install`

[preprocessor.shiftinclude]

[output.html]
git-repository-url = "https://github.com/status-im/nim-chronos/"
git-branch = "master"
additional-css = ["open-in.css"]
additional-css = ["open-in.css", "mdbook-admonish.css"]
19 changes: 19 additions & 0 deletions docs/examples/uptimemon/chapter1.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import chronos/apps/http/httpclient

proc check(uri: string) {.async.} =
let session = HttpSessionRef.new()

try:
let response = await session.fetch(parseUri(uri))

if response.status == 200:
echo "[OK] " & uri
else:
echo "[NOK] " & uri & ": " & $response.status
except CatchableError:
Comment thread
moigagoo marked this conversation as resolved.
Outdated
echo "[ERR] " & uri & ": " & getCurrentExceptionMsg()
finally:
await noCancel(session.closeWait())

when isMainModule:
waitFor check("https://google.com")
29 changes: 29 additions & 0 deletions docs/examples/uptimemon/chapter2.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import chronos/apps/http/httpclient

const uris = @[
"https://duckduckgo.com/?q=chronos", "https://mock.codes/403", "http://123.456.78.90"
]

proc check(session: HttpSessionRef, uri: string) {.async.} =
try:
let response = await session.fetch(parseUri(uri))

if response.status == 200:
echo "[OK] " & uri
else:
echo "[NOK] " & uri & ": " & $response.status
except CatchableError:
echo "[ERR] " & uri & ": " & getCurrentExceptionMsg()

proc check(uris: seq[string]) {.async.} =
let session = HttpSessionRef.new()
var futures: seq[Future[void]]

for uri in uris:
futures.add(session.check(uri))

await allFutures(futures)
await noCancel(session.closeWait())

when isMainModule:
waitFor check(uris)
35 changes: 35 additions & 0 deletions docs/examples/uptimemon/chapter3.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import chronos/apps/http/httpclient

const uris = @[
"https://duckduckgo.com/?q=chronos", "https://mock.codes/403", "http://123.456.78.90",
"http://10.255.255.1",
]

proc check(session: HttpSessionRef, uri: string) {.async.} =
try:
let responseFuture = session.fetch(parseUri(uri))

if await responseFuture.withTimeout(5.seconds):
let response = responseFuture.read()

if response.status == 200:
echo "[OK] " & uri
else:
echo "[NOK] " & uri & ": " & $response.status
else:
raise newException(AsyncTimeoutError, "Connection timed out")
except CatchableError:
echo "[ERR] " & uri & ": " & getCurrentExceptionMsg()

proc check(uris: seq[string]) {.async.} =
let session = HttpSessionRef.new()
var futures: seq[Future[void]]

for uri in uris:
futures.add(session.check(uri))

await allFutures(futures)
await noCancel(session.closeWait())

when isMainModule:
waitFor check(uris)
62 changes: 62 additions & 0 deletions docs/examples/uptimemon/chapter4.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import chronos/apps/http/httpclient

const uris = @[
"https://duckduckgo.com/?q=chronos", "https://mock.codes/403", "http://123.456.78.90",
"http://10.255.255.1", "https://html.spec.whatwg.org/", "https://mock.codes/200",
]

proc findMarker(response: HttpClientResponseRef): Future[bool] {.async.} =
let bodyReader = response.getBodyReader()

var
buffer = newSeq[byte](1024)
fetchedBytes: seq[byte]

while not result and len(fetchedBytes) <= 10 * 1024:
let bytesRead = await bodyReader.readOnce(addr(buffer[0]), len(buffer))

if bytesRead == 0:
break

fetchedBytes &= buffer

result = "<html" in bytesToString(fetchedBytes)

proc check(session: HttpSessionRef, uri: string) {.async.} =
try:
let request = HttpClientRequestRef.new(session, uri)

if request.isErr:
raise newException(HttpRequestError, request.error)

let responseFuture = request.value.send()

if await responseFuture.withTimeout(5.seconds):
let response = responseFuture.read()

if response.status == 200:
let markerFound = await findMarker(response)

if markerFound:
echo "[OK] " & uri
else:
echo "[NOK] " & uri & ": Not valid HTML"
else:
echo "[NOK] " & uri & ": " & $response.status
else:
raise newException(AsyncTimeoutError, "Connection timed out")
except CatchableError:
echo "[ERR] " & uri & ": " & getCurrentExceptionMsg()

proc check(uris: seq[string]) {.async.} =
let session = HttpSessionRef.new()
var futures: seq[Future[void]]

for uri in uris:
futures.add(session.check(uri))

await allFutures(futures)
await noCancel(session.closeWait())

when isMainModule:
waitFor check(uris)
92 changes: 92 additions & 0 deletions docs/examples/uptimemon/chapter5.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import chronos/apps/http/httpclient

const
ntfyTopic = "X3JIaLZSrFqBJXfJ"
uris = @[
"https://duckduckgo.com/?q=chronos", "https://mock.codes/403",
"http://123.456.78.90", "http://10.255.255.1", "https://html.spec.whatwg.org/",
"https://mock.codes/200",
]

proc sendAlert(
session: HttpSessionRef, message: string, priority = 3
) {.async.} =
let
headers = {"Title": "Chronos Uptime Monitor", "Priority": $priority}
body = message.stringToBytes()
request = HttpClientRequestRef.new(
session,
"https://ntfy.sh/" & ntfyTopic,
meth = MethodPost,
headers = headers,
body = body,
)

if request.isOk:
try:
let response = await request.get.send()
await response.closeWait()
except CatchableError:
echo "[WRN] Failed to send alert: " & getCurrentExceptionMsg()

proc findMarker(response: HttpClientResponseRef): Future[bool] {.async.} =
let bodyReader = response.getBodyReader()

var
buffer = newSeq[byte](1024)
fetchedBytes: seq[byte]

while not result and len(fetchedBytes) <= 10 * 1024:
let bytesRead = await bodyReader.readOnce(addr(buffer[0]), len(buffer))

if bytesRead == 0:
break

fetchedBytes &= buffer

result = "<html" in bytesToString(fetchedBytes)

proc check(session: HttpSessionRef, uri: string) {.async.} =
try:
let request = HttpClientRequestRef.new(session, uri)

if request.isErr:
raise newException(HttpRequestError, request.error)

let responseFuture = request.get.send()

if await responseFuture.withTimeout(5.seconds):
let response = responseFuture.read()

if response.status == 200:
let markerFound = await findMarker(response)

if markerFound:
echo "[OK] " & uri
else:
let message = "[NOK] " & uri & ": Not valid HTML"
echo message
await session.sendAlert(message)
else:
let message = "[NOK] " & uri & ": " & $response.status
echo message
await session.sendAlert(message)
else:
raise newException(AsyncTimeoutError, "Connection timed out")
except CatchableError:
let message = "[ERR] " & uri & ": " & getCurrentExceptionMsg()
echo message
await session.sendAlert(message, 4)

proc check(uris: seq[string]) {.async.} =
let session = HttpSessionRef.new()
var futures: seq[Future[void]]

for uri in uris:
futures.add(session.check(uri))

await allFutures(futures)
await noCancel(session.closeWait())

when isMainModule:
waitFor check(uris)
Loading