Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

exchange.nct

see ./index.nct

see ./streaming.nct

use std/http.{Client, Request, ResponseHead, Server}
use std/net.{IpAddress, Ipv4Address, SocketAddress}
use std/string.String
use std/task
use std/time.Duration
use std/url.Url

func loopback(): SocketAddress {
    return SocketAddress.new(IpAddress.from_ipv4(Ipv4Address.loopback()), 0)
}

async func serve_once(server: Server, timeout: Duration): void! {
    var owner = move server
    let connection = await owner.accept_with_timeout(timeout)?
    var request = await connection.read_request_with_timeout(timeout)?
    if request.request_method().text() != "POST"
    || request.target().path() != "/echo" {
        return error.new("async-http.request", "local request changed during HTTP decoding")
    }
    let request_body = await collect_text_before(&+request, timeout)?
    if request_body != "ping" {
        return error.new("async-http.request", "local request body changed during HTTP decoding")
    }
    let responder = await request.finish_body_with_timeout(timeout)?
    var head = ResponseHead.ok()
    head.append_header_text("X-Example", "local")?
    var writer = await responder.begin_chunked_with_timeout(move head, timeout)?
    await writer.write_with_timeout("po".bytes(), timeout)?
    await writer.write_with_timeout("ng".bytes(), timeout)?
    let _next = await writer.finish_with_timeout(timeout)?
    return
}

async func run(): i32! {
    let timeout = Duration.from_seconds(1)
    let server = await Server.bind(loopback())?
    let address = server.local_address()?
    let port = address.port().to_string()
    let url_text = String.concat("http://127.0.0.1:", &port, "/echo")

    var request = Request.post(Url.parse(&url_text)?)?
    request.append_header_text("X-Example", "client")?
    request.set_text_body("ping")
    let client = Client.new()
    let response = client.send_with_timeout(move request, timeout)
    let exchanged = await task.join(
        move response,
        serve_once(move server, timeout),
    )
    let response_result = move exchanged.0
    let server_result = move exchanged.1
    var received = move response_result?
    move server_result?
    if received.status().code() != 200 {
        return error.new("async-http.status", "local server returned an unexpected status")
    }
    let _header = received.headers().first("x-example") otherwise {
        return error.new("async-http.header", "local response header was absent")
    }
    let body = await collect_text_before(&+received, timeout)?
    if body != "pong" {
        return error.new("async-http.body", "local server returned an unexpected body")
    }
    return 0
}