Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ Lightbug currently has the following features:
- [x] Pure Mojo HTTP 1.1 server with no Python dependencies. Everything is fully typed, with no `def` functions used
- [x] Cookie support

### Check Out These Mojo Libraries:
### Check Out These Mojo Libraries:

- Logging - [@toasty/stump](https://github.com/thatstoasty/stump)
- CLI and Terminal - [@toasty/prism](https://github.com/thatstoasty/prism), [@toasty/mog](https://github.com/thatstoasty/mog)
- Date/Time - [@mojoto/morrow](https://github.com/mojoto/morrow.mojo) and [@toasty/small_time](https://github.com/thatstoasty/small_time)
- HTTP Client - [@thatstoasty/floki](https://github.com/thatstoasty/floki)
- CLI and Terminal - [@thatstoasty/prism](https://github.com/thatstoasty/prism), [@thatstoasty/mog](https://github.com/thatstoasty/mog)
- Date/Time - [@mojoto/morrow](https://github.com/mojoto/morrow.mojo) and [@thatstoasty/small_time](https://github.com/thatstoasty/small_time)

<p align="right">(<a href="#readme-top">back to top</a>)</p>

Expand Down Expand Up @@ -176,6 +176,11 @@ struct Welcome(HTTPService):
return NotFound(req.uri.path)
```

<!-- Examples -->
## Examples

Check out the examples directory for more example services built with Lightbug, including an echo server and client implementation!

<!-- ROADMAP -->
## Roadmap

Expand Down
30 changes: 0 additions & 30 deletions client.mojo

This file was deleted.

15 changes: 15 additions & 0 deletions examples/echo_server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Echo Server

A simple echo server example using `lightbug_http` for the server and `floki` for the client. The server listens for incoming HTTP requests and responds with the same body and content type.

To run the server, execute the following command in the terminal:

```bash
pixi run server
```

In a separate terminal, you can run the client to send a request to the server:

```bash
pixi run client
```
17 changes: 17 additions & 0 deletions examples/echo_server/client.mojo
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import floki


fn main() raises:
var response = floki.post(
"localhost:8080",
headers={"Content-Type": "text/plain"},
data="Hello, Echo Server!".as_bytes()
)
print(response.body.as_string_slice())

response = floki.post(
"localhost:8080",
headers={"Content-Type": "application/json"},
data={"message": "Hello, Echo Server!"}
)
print(response.body.as_json())
1,680 changes: 1,680 additions & 0 deletions examples/echo_server/pixi.lock

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions examples/echo_server/pixi.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[workspace]
authors = ["Mikhail Tavarez <miktavarez@gmail.com>"]
channels = ["https://conda.modular.com/max", "https://repo.prefix.dev/modular-community", "https://repo.prefix.dev/mojo-community", "conda-forge"]
name = "echo_server"
platforms = ["osx-arm64", "linux-64", "linux-aarch64"]
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

platforms declares linux targets, but the committed examples/echo_server/pixi.lock only contains an osx-arm64 section. Either regenerate the lock so it includes linux-64 and linux-aarch64 entries as well, or restrict platforms to the platform(s) actually locked to avoid broken installs on Linux.

Suggested change
platforms = ["osx-arm64", "linux-64", "linux-aarch64"]
platforms = ["osx-arm64"]

Copilot uses AI. Check for mistakes.
version = "0.1.0"
preview = ["pixi-build"]

[tasks]
server = "mojo run server.mojo"
client = "mojo run client.mojo"

[dependencies]
mojo = ">=0.26.1.0,<0.26.2.0"
curl_wrapper = { git = "https://github.com/thatstoasty/mojo-curl.git", branch = "main", subdirectory = "shim" }
floki = ">=0.1.0,<0.2"
lightbug_http = ">=0.26.1.2,<0.26.2.0"
14 changes: 14 additions & 0 deletions examples/echo_server/server.mojo
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from lightbug_http import OK, HTTPRequest, HTTPResponse, HTTPService, Server


@fieldwise_init
struct EchoService(HTTPService):
fn func(mut self, req: HTTPRequest) raises -> HTTPResponse:
var content_type = req.headers.get("content-type")
return OK(req.body_raw, content_type.or_else("text/plain; charset=utf-8"))


fn main() raises:
var server = Server()
var handler = EchoService()
server.listen_and_serve("localhost:8080", handler)
5 changes: 0 additions & 5 deletions lightbug_http/address.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -426,8 +426,6 @@ fn is_ipv6(network: NetworkType) -> Bool:
return network in (NetworkType.tcp6, NetworkType.udp6, NetworkType.ip6)




@fieldwise_init
@register_passable("trivial")
struct ParseEmptyAddressError(CustomError):
Expand Down Expand Up @@ -548,8 +546,6 @@ struct ParseIPProtocolPortError(CustomError):
return Self.message




@fieldwise_init
@register_passable("trivial")
struct GetaddrinfoNullAddrError(CustomError):
Expand All @@ -574,7 +570,6 @@ struct GetaddrinfoError(CustomError):
return Self.message



@fieldwise_init
struct GetIPAddressError(Movable, Stringable, Writable):
"""Typed error variant for get_ip_address() function."""
Expand Down
2 changes: 1 addition & 1 deletion lightbug_http/connection.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ struct TCPConnection[network: NetworkType = NetworkType.tcp4]:
"""
var total_sent: UInt = 0
while total_sent < UInt(len(buf)):
var sent = self.socket.send(buf[Int(total_sent):])
var sent = self.socket.send(buf[Int(total_sent) :])
total_sent += sent
return total_sent

Expand Down
4 changes: 3 additions & 1 deletion lightbug_http/header.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,9 @@ fn encode_latin1_header_value(value: String) -> List[UInt8]:
var b4 = utf8[i + 3]
if b2 >= 0x80 and b2 <= 0xBF and b3 >= 0x80 and b3 <= 0xBF and b4 >= 0x80 and b4 <= 0xBF:
seq_len = 4
codepoint = ((Int(b) & 0x07) << 18) | ((Int(b2) & 0x3F) << 12) | ((Int(b3) & 0x3F) << 6) | (Int(b4) & 0x3F)
codepoint = (
((Int(b) & 0x07) << 18) | ((Int(b2) & 0x3F) << 12) | ((Int(b3) & 0x3F) << 6) | (Int(b4) & 0x3F)
)

if seq_len > 0 and codepoint <= 0xFF:
out.append(UInt8(codepoint))
Expand Down
3 changes: 1 addition & 2 deletions lightbug_http/socket.mojo
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from sys.ffi import c_uint
from sys.info import CompilationTarget

from lightbug_http.c.aliases import c_void

from lightbug_http.address import (
Addr,
NetworkType,
Expand All @@ -13,6 +11,7 @@ from lightbug_http.address import (
get_ip_address,
)
from lightbug_http.c.address import AddressFamily, AddressLength
from lightbug_http.c.aliases import c_void
from lightbug_http.c.network import InetNtopError, InetPtonError, SocketAddress, inet_pton
from lightbug_http.c.socket import (
SOL_SOCKET,
Expand Down
Loading
Loading