aboutsummaryrefslogtreecommitdiff
path: root/examples/http-server
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-09-06 14:47:45 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-09-06 14:47:45 -0400
commita8316252db95e3d77f9f0e9beb89cfcb4573d5b1 (patch)
treee5905bce9611e35ccb2f84481232fca0e657ff42 /examples/http-server
parenta0ac652cd1eebdc42425b34f1685f8cb20cb4eea (diff)
parent73246764f88f6f652316ee0c138a990d836698a7 (diff)
Merge branch 'main' into simplified-quotes
Diffstat (limited to 'examples/http-server')
-rw-r--r--examples/http-server/CHANGES.md5
-rw-r--r--examples/http-server/README.md8
-rw-r--r--examples/http-server/connection-queue.tm25
-rw-r--r--examples/http-server/http-server.tm149
-rw-r--r--examples/http-server/modules.ini8
-rw-r--r--examples/http-server/sample-site/foo.html6
-rw-r--r--examples/http-server/sample-site/hello.txt1
-rw-r--r--examples/http-server/sample-site/index.html16
-rwxr-xr-xexamples/http-server/sample-site/random.tm17
-rw-r--r--examples/http-server/sample-site/styles.css11
10 files changed, 0 insertions, 246 deletions
diff --git a/examples/http-server/CHANGES.md b/examples/http-server/CHANGES.md
deleted file mode 100644
index 42ae752c..00000000
--- a/examples/http-server/CHANGES.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Version History
-
-## v1.0
-
-Initial version
diff --git a/examples/http-server/README.md b/examples/http-server/README.md
deleted file mode 100644
index 78c8d793..00000000
--- a/examples/http-server/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# HTTP Server
-
-This is a simple multithreaded Tomo HTTP server that can be run like this:
-
-```
-tomo -e http-server.tm
-./http-server ./sample-site
-```
diff --git a/examples/http-server/connection-queue.tm b/examples/http-server/connection-queue.tm
deleted file mode 100644
index c56069e1..00000000
--- a/examples/http-server/connection-queue.tm
+++ /dev/null
@@ -1,25 +0,0 @@
-use pthreads
-
-func _assert_success(name:Text, val:Int32; inline)
- fail("$name() failed!") if val < 0
-
-struct ConnectionQueue(_connections:@[Int32]=@[], _mutex=pthread_mutex_t.new(), _cond=pthread_cond_t.new())
- func enqueue(queue:ConnectionQueue, connection:Int32)
- queue._mutex.lock()
- queue._connections.insert(connection)
- queue._mutex.unlock()
- queue._cond.signal()
-
-
- func dequeue(queue:ConnectionQueue -> Int32)
- conn : Int32?
-
- queue._mutex.lock()
-
- while queue._connections.length == 0
- queue._cond.wait(queue._mutex)
-
- conn = queue._connections.pop(1)
- queue._mutex.unlock()
- queue._cond.signal()
- return conn!
diff --git a/examples/http-server/http-server.tm b/examples/http-server/http-server.tm
deleted file mode 100644
index dbe57805..00000000
--- a/examples/http-server/http-server.tm
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/bin/env tomo
-
-# This file provides an HTTP server module and standalone executable
-
-use <stdio.h>
-use <stdlib.h>
-use <string.h>
-use <unistd.h>
-use <arpa/inet.h>
-use <err.h>
-
-use commands
-use pthreads
-use patterns
-
-use ./connection-queue.tm
-
-func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_threads=16)
- connections := ConnectionQueue()
- workers : &[@pthread_t]
- for i in num_threads
- workers.insert(pthread_t.new(func()
- repeat
- connection := connections.dequeue()
- request_text := C_code:Text```
- Text_t request = EMPTY_TEXT;
- char buf[1024] = {};
- for (ssize_t n; (n = read(@connection, buf, sizeof(buf) - 1)) > 0; ) {
- buf[n] = 0;
- request = Text$concat(request, Text$from_strn(buf, n));
- if (request.length > 1000000 || strstr(buf, "\r\n\r\n"))
- break;
- }
- request
- ```
-
- request := HTTPRequest.from_text(request_text) or skip
- response := handler(request).bytes()
- C_code `
- if (@response.stride != 1)
- List$compact(&@response, 1);
- write(@connection, @response.data, @response.length);
- close(@connection);
- `
- ))
-
-
- sock := C_code:Int32 `
- int s = socket(AF_INET, SOCK_STREAM, 0);
- if (s < 0) err(1, "Couldn't connect to socket!");
-
- int opt = 1;
- if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
- err(1, "Couldn't set socket option");
-
- struct sockaddr_in addr = {AF_INET, htons(@port), INADDR_ANY};
- if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0)
- err(1, "Couldn't bind to socket");
- if (listen(s, 8) < 0)
- err(1, "Couldn't listen on socket");
-
- s
- `
-
- repeat
- conn := C_code:Int32`accept(@sock, NULL, NULL)`
- stop if conn < 0
- connections.enqueue(conn)
-
- say("Shutting down...")
- for w in workers
- w.cancel()
-
-struct HTTPRequest(method:Text, path:Text, version:Text, headers:[Text], body:Text)
- func from_text(text:Text -> HTTPRequest?)
- m := text.pattern_captures($Pat'{word} {..} HTTP/{..}{crlf}{..}') or return none
- method := m[1]
- path := m[2].replace_pattern($Pat'{2+ /}', '/')
- version := m[3]
- rest := m[-1].pattern_captures($Pat'{..}{2 crlf}{0+ ..}') or return none
- headers := rest[1].split_pattern($Pat'{crlf}')
- body := rest[-1]
- return HTTPRequest(method, path, version, headers, body)
-
-struct HTTPResponse(body:Text, status=200, content_type="text/plain", headers:{Text=Text}={})
- func bytes(r:HTTPResponse -> [Byte])
- body_bytes := r.body.bytes()
- extra_headers := (++: "$k: $v\r\n" for k,v in r.headers) or ""
- return "
- HTTP/1.1 $(r.status) OK\r
- Content-Length: $(body_bytes.length + 2)\r
- Content-Type: $(r.content_type)\r
- Connection: close\r
- $extra_headers
- \r\n
- ".bytes() ++ body_bytes
-
-func _content_type(file:Path -> Text)
- when file.extension() is "html" return "text/html"
- is "tm" return "text/html"
- is "js" return "text/javascript"
- is "css" return "text/css"
- else return "text/plain"
-
-enum RouteEntry(ServeFile(file:Path), Redirect(destination:Text))
- func respond(entry:RouteEntry, request:HTTPRequest -> HTTPResponse)
- when entry is ServeFile(file)
- body := if file.can_execute()
- Command(Text(file)).get_output()!
- else
- file.read()!
- return HTTPResponse(body, content_type=_content_type(file))
- is Redirect(destination)
- return HTTPResponse("Found", 302, headers={"Location"=destination})
-
-func load_routes(directory:Path -> {Text=RouteEntry})
- routes : &{Text=RouteEntry}
- for file in (directory ++ (./*)).glob()
- skip unless file.is_file()
- contents := file.read() or skip
- server_path := "/" ++ "/".join(file.relative_to(directory).components)
- if file.base_name() == "index.html"
- canonical := server_path.without_suffix("index.html")
- routes[server_path] = Redirect(canonical)
- routes[canonical] = ServeFile(file)
- else if file.extension() == "html"
- canonical := server_path.without_suffix(".html")
- routes[server_path] = Redirect(canonical)
- routes[canonical] = ServeFile(file)
- else if file.extension() == "tm"
- canonical := server_path.without_suffix(".tm")
- routes[server_path] = Redirect(canonical)
- routes[canonical] = ServeFile(file)
- else
- routes[server_path] = ServeFile(file)
- return routes[]
-
-func main(directory:Path, port=Int32(8080))
- say("Serving on port $port")
- routes := load_routes(directory)
- say("Hosting: $routes")
-
- serve(port, func(request:HTTPRequest)
- if handler := routes[request.path]
- return handler.respond(request)
- else
- return HTTPResponse("Not found!", 404)
- )
-
diff --git a/examples/http-server/modules.ini b/examples/http-server/modules.ini
deleted file mode 100644
index 171e11d2..00000000
--- a/examples/http-server/modules.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[pthreads]
-version=v1.0
-
-[patterns]
-version=v1.0
-
-[commands]
-version=v1.0
diff --git a/examples/http-server/sample-site/foo.html b/examples/http-server/sample-site/foo.html
deleted file mode 100644
index 162a7146..00000000
--- a/examples/http-server/sample-site/foo.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE HTML>
-<html>
- <body>
- This is the <b>foo</b> page.
- </body>
-</html>
diff --git a/examples/http-server/sample-site/hello.txt b/examples/http-server/sample-site/hello.txt
deleted file mode 100644
index e965047a..00000000
--- a/examples/http-server/sample-site/hello.txt
+++ /dev/null
@@ -1 +0,0 @@
-Hello
diff --git a/examples/http-server/sample-site/index.html b/examples/http-server/sample-site/index.html
deleted file mode 100644
index 8e1573bb..00000000
--- a/examples/http-server/sample-site/index.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE HTML>
-<html>
- <head>
- <title>HTTP Example</title>
- <link rel="stylesheet" href="styles.css">
- </head>
- <body>
- <p>
- Hello <b>world!</b>
- </p>
-
- <p>
- Try going to <a href="/random">/random</a> or <a href="/foo">/foo</a> or <a href="/hello.txt">/hello.txt</a>
- </p>
- </body>
-</html>
diff --git a/examples/http-server/sample-site/random.tm b/examples/http-server/sample-site/random.tm
deleted file mode 100755
index 153ac2af..00000000
--- a/examples/http-server/sample-site/random.tm
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/env tomo
-use random
-
-func main()
- say("
- <!DOCTYPE HTML>
- <html>
- <head>
- <title>Random Number</title>
- <link rel="stylesheet" href="styles.css">
- </head>
- <body>
- <h1>Random Number</h1>
- Your random number is: $(random.int(1,100))
- </body>
- </html>
- ")
diff --git a/examples/http-server/sample-site/styles.css b/examples/http-server/sample-site/styles.css
deleted file mode 100644
index f15d25de..00000000
--- a/examples/http-server/sample-site/styles.css
+++ /dev/null
@@ -1,11 +0,0 @@
-body{
- margin:40px auto;
- max-width:650px;
- line-height:1.6;
- font-size:18px;
- color:#444;
- padding:0 10px;
-}
-h1,h2,h3{
- line-height:1.2
-}