XU XUv0.8.8
EN

文档 / 教程

11 · HTTP 自己路由

自己 listen / accept,按 path 分支。静态目录见 12;Handler 上下文见 13。

监听 http://127.0.0.1:18080:GET / /hello /health,POST /echo。每连接 handle(fd)。

源码

@main

#net
#net/http

// 自己 listen / accept / 按路径处理。
//   GET  /        HTML
//   GET  /hello   text
//   GET  /health  ok
//   POST /echo    回显 body
// 静态目录见 12file_server;Handler 上下文见 13http_context。

fn path_only(p string) string {
    mut i := 0
    while i < p.len {
        if (p[i] & 255) == 63 {
            << p[0:i]
        }
        i = i + 1
    }
    << p + ""
}

fn write_http(fd int, status int, ctype string, body string) int {
    mut reason := "OK"
    if status == 404 {
        reason = "Not Found"
    }
    if status == 400 {
        reason = "Bad Request"
    }
    if status == 405 {
        reason = "Method Not Allowed"
    }
    mut out := "HTTP/1.1 " + str(status) + " " + reason + "\r\n"
    out = out + "Content-Type: " + ctype + "\r\n"
    out = out + "Content-Length: " + str(body.len) + "\r\n"
    out = out + "Connection: close\r\n"
    out = out + "\r\n"
    out = out + body
    << net.conn_write(fd, &out) or (err) { >> err }
}

fn index_page() string {
    << "<!doctype html><html><head><meta charset=\"utf-8\"><title>Xu HTTP</title>" +
        "<style>body{font-family:ui-sans-serif,system-ui,sans-serif;max-width:40rem;" +
        "margin:3rem auto;padding:0 1.5rem;background:#0f1419;color:#e7ecf1}" +
        "h1{font-weight:650;letter-spacing:-.03em}a{color:#7dd3fc}" +
        "code{background:#1b2430;padding:.15rem .4rem;border-radius:4px}" +
        "ul{line-height:1.8}</style></head><body>" +
        "<h1>Xu HTTP</h1>" +
        "<p>自己 <code>listen</code> / <code>accept</code> / 按 path 分支。</p>" +
        "<ul>" +
        "<li><a href=\"/hello\">/hello</a></li>" +
        "<li><a href=\"/health\">/health</a></li>" +
        "<li>POST <code>/echo</code></li>" +
        "</ul></body></html>"
}

fn handle(fd int) {
    mut req := http.new_request()
    req.read(fd) or (err) {
        msg := err.msg()
        write_http(fd, 400, "text/plain; charset=utf-8", msg) or (e2) { << }
        net.conn_close(fd) or (e3) { << }
        <<
    }
    method := req.method()
    raw_path := req.path()
    path := path_only(raw_path)
    if path == "/" || path == "/index.html" {
        page := index_page()
        write_http(fd, 200, "text/html; charset=utf-8", page) or (err) { << }
        net.conn_close(fd) or (err) { << }
        <<
    }
    if path == "/hello" {
        body := "hello, xu\n"
        write_http(fd, 200, "text/plain; charset=utf-8", body) or (err) { << }
        net.conn_close(fd) or (err) { << }
        <<
    }
    if path == "/health" {
        body := "ok\n"
        write_http(fd, 200, "text/plain; charset=utf-8", body) or (err) { << }
        net.conn_close(fd) or (err) { << }
        <<
    }
    if path == "/echo" {
        if method != "POST" && method != "PUT" {
            body := "method not allowed\n"
            write_http(fd, 405, "text/plain; charset=utf-8", body) or (err) { << }
            net.conn_close(fd) or (err) { << }
            <<
        }
        echo := req.body()
        write_http(fd, 200, "text/plain; charset=utf-8", echo) or (err) { << }
        net.conn_close(fd) or (err) { << }
        <<
    }
    miss := "not found: " + path + "\n"
    write_http(fd, 404, "text/plain; charset=utf-8", miss) or (err) { << }
    net.conn_close(fd) or (err) { << }
}

fn main() int {
    mut s := http.new_server()
    s.listen("127.0.0.1:18080") or (err) {
        println(err.msg())
        << 1
    }
    println("http://127.0.0.1:18080")
    mut running := true
    while running {
        fd := s.accept() or (err) {
            ->
        }
        handle(fd)
    }
    << 0
}

在独立的 examples 仓库根执行;源码位于 examples/。