Skip to content

Commit 899ee77

Browse files
authored
Merge pull request #42 from saviorand/chore/client-uri-improvements
Improve uri handling
2 parents 6a39841 + 12dd28e commit 899ee77

File tree

5 files changed

+66
-48
lines changed

5 files changed

+66
-48
lines changed

README.md

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,18 @@ This is not production ready yet. We're aiming to keep up with new developments
3030

3131
Lightbug currently has the following features:
3232
- [x] Pure Mojo networking! No dependencies on Python by default
33-
- [x] Set up a server to listen on a given host/port
33+
- [x] TCP-based server and client implementation
3434
- [x] Assign your own custom handler to a route
3535
- [x] Craft HTTP requests and responses with built-in primitives
3636
- [x] Everything is fully typed, with no `def` functions used
3737

3838

3939
We're working on support for the following (contributors welcome!):
4040
- [ ] [SSL/HTTPS support](https://github.com/saviorand/lightbug_http/issues/20)
41+
- [ ] UDP support
4142
- [ ] [Better error handling](https://github.com/saviorand/lightbug_http/issues/3), [improved form/multipart and JSON support](https://github.com/saviorand/lightbug_http/issues/4)
4243
- [ ] [Multiple simultaneous connections](https://github.com/saviorand/lightbug_http/issues/5), [parallelization and performance optimizations](https://github.com/saviorand/lightbug_http/issues/6)
43-
- [ ] [WebSockets](https://github.com/saviorand/lightbug_http/issues/7), [HTTP 2.0 support](https://github.com/saviorand/lightbug_http/issues/8)
44+
- [ ] [WebSockets](https://github.com/saviorand/lightbug_http/issues/7), [HTTP 2.0/3.0 support](https://github.com/saviorand/lightbug_http/issues/8)
4445
- [ ] [ASGI spec conformance](https://github.com/saviorand/lightbug_http/issues/17)
4546

4647
The test coverage is also something we're working on.
@@ -63,11 +64,11 @@ Once you have Mojo set up locally,
6364
git clone https://github.com/saviorand/lightbug_http.git
6465
```
6566
2. Switch to the project directory:
66-
```bash
67+
```sh
6768
cd lightbug_http
6869
```
6970
then run:
70-
```bash
71+
```sh
7172
mojo lightbug.🔥
7273
```
7374

@@ -126,6 +127,37 @@ Once you have Mojo set up locally,
126127

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

130+
### Using the client
131+
132+
Create a file, e.g `client.mojo` with the following code:
133+
134+
```mojo
135+
from lightbug_http.http import HTTPRequest
136+
from lightbug_http.uri import URI
137+
from lightbug_http.sys.client import MojoClient
138+
139+
fn test_request(inout client: MojoClient) raises -> None:
140+
var uri = URI("http://httpbin.org/")
141+
var request = HTTPRequest(uri)
142+
var response = client.do(request)
143+
144+
# print status code
145+
print("Response:", response.header.status_code())
146+
147+
# print various parsed headers
148+
print("Header", response.header.content_length())
149+
150+
# print body
151+
print(String(response.get_body()))
152+
153+
154+
fn main() raises -> None:
155+
var client = MojoClient()
156+
test_request(client)
157+
```
158+
159+
Pure Mojo-based client is available by default. This client is also used internally for testing the server.
160+
129161
## Switching between pure Mojo and Python implementations
130162
By default, Lightbug uses the pure Mojo implementation for networking. To use Python's `socket` library instead, just import the `PythonServer` instead of the `SysServer` with the following line:
131163
```mojo

lightbug_http/http.mojo

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ fn OK(body: Bytes, content_type: String) -> HTTPResponse:
232232
)
233233

234234

235-
fn encode(req: HTTPRequest) raises -> Bytes:
235+
fn encode(req: HTTPRequest, uri: URI) raises -> Bytes:
236236
var res_str = String()
237237
var protocol = strHttp11
238238
var current_time = String()
@@ -241,7 +241,10 @@ fn encode(req: HTTPRequest) raises -> Bytes:
241241

242242
_ = builder.write(req.header.method())
243243
_ = builder.write_string(String(" "))
244-
_ = builder.write(req.header.request_uri())
244+
if len(uri.request_uri()) > 1:
245+
_ = builder.write_string(uri.request_uri())
246+
else:
247+
_ = builder.write_string("/")
245248
_ = builder.write_string(String(" "))
246249
_ = builder.write(protocol)
247250
_ = builder.write_string(String("\r\n"))

lightbug_http/sys/client.mojo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ struct MojoClient(Client):
9393

9494
var conn = create_connection(self.fd, host_str, port)
9595

96-
var req_encoded = encode(req)
96+
var req_encoded = encode(req, uri)
9797
var bytes_sent = conn.write(req_encoded)
9898
if bytes_sent == -1:
9999
raise Error("Failed to send message")

lightbug_http/sys/net.mojo

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ struct addrinfo_unix(AnAddrInfo):
358358
Returns:
359359
UInt32 - The IP address.
360360
"""
361-
var host_ptr = to_char_ptr(host)
361+
var host_ptr = to_char_ptr(String(host))
362362
var servinfo = Pointer[Self]().alloc(1)
363363
servinfo.store(Self())
364364

@@ -420,8 +420,6 @@ fn create_connection(sock: c_int, host: String, port: UInt16) raises -> SysConne
420420
_ = shutdown(sock, SHUT_RDWR)
421421
raise Error("Failed to connect to server")
422422

423-
print("Connected to server at " + host + ":" + port.__str__())
424-
425423
var laddr = TCPAddr()
426424
var raddr = TCPAddr(host, int(port))
427425
var conn = SysConnection(sock, laddr, raddr)

lightbug_http/uri.mojo

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -186,54 +186,39 @@ struct URI:
186186

187187
# Defaults to HTTP/1.1
188188
var proto_str = String(strHttp11)
189-
190-
# Parse requestURI
191-
var n = raw_uri.rfind(" ")
192-
if n < 0:
193-
n = len(raw_uri)
194-
elif n == 0:
195-
raise Error("Request URI cannot be empty")
196-
else:
197-
var proto = raw_uri[n + 1 :]
198-
if proto != strHttp11:
199-
proto_str = proto
200-
201-
var request_uri = raw_uri[:n]
202-
203-
# Parse host from requestURI
204-
n = request_uri.find("://")
205-
206189
var is_https = False
207190

208-
if n >= 0:
209-
var host_and_port = request_uri[n + 3 :]
210-
211-
if request_uri[:n] == https:
191+
# Parse the protocol
192+
var proto_end = raw_uri.find("://")
193+
var remainder_uri: String
194+
if proto_end >= 0:
195+
proto_str = raw_uri[:proto_end]
196+
if proto_str == https:
212197
is_https = True
213-
214-
n = host_and_port.find("/")
215-
if n >= 0:
216-
self.__host = host_and_port[:n]._buffer
217-
request_uri = request_uri[n + 3 :]
218-
else:
219-
self.__host = host_and_port._buffer
220-
request_uri = strSlash
198+
remainder_uri = raw_uri[proto_end + 3:]
221199
else:
222-
n = request_uri.find("/")
223-
if n >= 0:
224-
self.__host = request_uri[:n]._buffer
225-
request_uri = request_uri[n:]
226-
else:
227-
self.__host = request_uri._buffer
228-
request_uri = strSlash
200+
raise Error("Invalid URI: Missing protocol")
201+
202+
# Parse the host and optional port
203+
var path_start = remainder_uri.find("/")
204+
var host_and_port: String
205+
var request_uri: String
206+
if path_start >= 0:
207+
host_and_port = remainder_uri[:path_start]
208+
request_uri = remainder_uri[path_start:]
209+
self.__host = host_and_port[:path_start]._buffer
210+
else:
211+
host_and_port = remainder_uri
212+
request_uri = strSlash
213+
self.__host = host_and_port._buffer
229214

230215
if is_https:
231216
_ = self.set_scheme(https)
232217
else:
233218
_ = self.set_scheme(http)
234-
219+
235220
# Parse path
236-
n = request_uri.find("?")
221+
var n = request_uri.find("?")
237222
if n >= 0:
238223
self.__path_original = request_uri[:n]._buffer
239224
self.__query_string = request_uri[n + 1 :]._buffer

0 commit comments

Comments
 (0)