Skip to content

Commit 973c883

Browse files
committed
follow redirects
Signed-off-by: Brian Grenier <grenierb96@gmail.com>
1 parent ae1c2ba commit 973c883

File tree

14 files changed

+238
-188
lines changed

14 files changed

+238
-188
lines changed

lightbug_http/header.mojo

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ struct HeaderKey:
1212
alias CONTENT_LENGTH = "content-length"
1313
alias CONTENT_ENCODING = "content-encoding"
1414
alias DATE = "date"
15+
alias LOCATION = "location"
16+
alias HOST = "host"
1517

1618

1719
@value
@@ -70,16 +72,12 @@ struct Headers(Formattable, Stringable):
7072
self._inner[key.lower()] = value
7173

7274
fn content_length(self) -> Int:
73-
if HeaderKey.CONTENT_LENGTH not in self:
74-
return 0
7575
try:
7676
return int(self[HeaderKey.CONTENT_LENGTH])
7777
except:
7878
return 0
7979

80-
fn parse_raw(
81-
inout self, inout r: ByteReader
82-
) raises -> (String, String, String):
80+
fn parse_raw(inout self, inout r: ByteReader) raises -> (String, String, String):
8381
var first_byte = r.peek()
8482
if not first_byte:
8583
raise Error("Failed to read first byte from response header")

lightbug_http/http.mojo

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ fn encode(owned res: HTTPResponse) -> Bytes:
3535
return res._encoded()
3636

3737

38+
struct StatusCode:
39+
alias OK = 200
40+
alias MOVED_PERMANENTLY = 301
41+
alias FOUND = 302
42+
alias TEMPORARY_REDIRECT = 307
43+
alias PERMANENT_REDIRECT = 308
44+
alias NOT_FOUND = 404
45+
46+
3847
@value
3948
struct HTTPRequest(Formattable, Stringable):
4049
var headers: Headers
@@ -48,9 +57,7 @@ struct HTTPRequest(Formattable, Stringable):
4857
var timeout: Duration
4958

5059
@staticmethod
51-
fn from_bytes(
52-
addr: String, max_body_size: Int, owned b: Bytes
53-
) raises -> HTTPRequest:
60+
fn from_bytes(addr: String, max_body_size: Int, owned b: Bytes) raises -> HTTPRequest:
5461
var reader = ByteReader(b^)
5562
var headers = Headers()
5663
var method: String
@@ -65,16 +72,10 @@ struct HTTPRequest(Formattable, Stringable):
6572

6673
var content_length = headers.content_length()
6774

68-
if (
69-
content_length > 0
70-
and max_body_size > 0
71-
and content_length > max_body_size
72-
):
75+
if content_length > 0 and max_body_size > 0 and content_length > max_body_size:
7376
raise Error("Request body too large")
7477

75-
var request = HTTPRequest(
76-
uri, headers=headers, method=method, protocol=protocol
77-
)
78+
var request = HTTPRequest(uri, headers=headers, method=method, protocol=protocol)
7879

7980
try:
8081
request.read_body(reader, content_length, max_body_size)
@@ -103,6 +104,8 @@ struct HTTPRequest(Formattable, Stringable):
103104
self.set_content_length(len(body))
104105
if HeaderKey.CONNECTION not in self.headers:
105106
self.set_connection_close()
107+
if HeaderKey.HOST not in self.headers:
108+
self.headers[HeaderKey.HOST] = uri.host
106109

107110
fn set_connection_close(inout self):
108111
self.headers[HeaderKey.CONNECTION] = "close"
@@ -114,20 +117,22 @@ struct HTTPRequest(Formattable, Stringable):
114117
return self.headers[HeaderKey.CONNECTION] == "close"
115118

116119
@always_inline
117-
fn read_body(
118-
inout self, inout r: ByteReader, content_length: Int, max_body_size: Int
119-
) raises -> None:
120+
fn read_body(inout self, inout r: ByteReader, content_length: Int, max_body_size: Int) raises -> None:
120121
if content_length > max_body_size:
121122
raise Error("Request body too large")
122123

123-
r.consume(self.body_raw)
124+
r.consume(self.body_raw, content_length)
124125
self.set_content_length(content_length)
125126

126127
fn format_to(self, inout writer: Formatter):
128+
writer.write(self.method, whitespace)
129+
path = self.uri.path if len(self.uri.path) > 1 else strSlash
130+
if len(self.uri.query_string) > 0:
131+
path += "?" + self.uri.query_string
132+
133+
writer.write(path)
134+
127135
writer.write(
128-
self.method,
129-
whitespace,
130-
self.uri.path if len(self.uri.path) > 1 else strSlash,
131136
whitespace,
132137
self.protocol,
133138
lineBreak,
@@ -147,6 +152,8 @@ struct HTTPRequest(Formattable, Stringable):
147152
writer.write(self.method)
148153
writer.write(whitespace)
149154
var path = self.uri.path if len(self.uri.path) > 1 else strSlash
155+
if len(self.uri.query_string) > 0:
156+
path += "?" + self.uri.query_string
150157
writer.write(path)
151158
writer.write(whitespace)
152159
writer.write(self.protocol)
@@ -215,8 +222,16 @@ struct HTTPResponse(Formattable, Stringable):
215222
self.status_text = status_text
216223
self.protocol = protocol
217224
self.body_raw = body_bytes
218-
self.set_connection_keep_alive()
219-
self.set_content_length(len(body_bytes))
225+
if HeaderKey.CONNECTION not in self.headers:
226+
self.set_connection_keep_alive()
227+
if HeaderKey.CONTENT_LENGTH not in self.headers:
228+
self.set_content_length(len(body_bytes))
229+
if HeaderKey.DATE not in self.headers:
230+
try:
231+
var current_time = now(utc=True).__str__()
232+
self.headers[HeaderKey.DATE] = current_time
233+
except:
234+
pass
220235

221236
fn get_body_bytes(self) -> Bytes:
222237
return self.body_raw
@@ -236,9 +251,25 @@ struct HTTPResponse(Formattable, Stringable):
236251
fn set_content_length(inout self, l: Int):
237252
self.headers[HeaderKey.CONTENT_LENGTH] = str(l)
238253

254+
@always_inline
255+
fn content_length(inout self) -> Int:
256+
try:
257+
return int(self.headers[HeaderKey.CONTENT_LENGTH])
258+
except:
259+
return 0
260+
261+
fn is_redirect(self) -> Bool:
262+
return (
263+
self.status_code == StatusCode.MOVED_PERMANENTLY
264+
or self.status_code == StatusCode.FOUND
265+
or self.status_code == StatusCode.TEMPORARY_REDIRECT
266+
or self.status_code == StatusCode.PERMANENT_REDIRECT
267+
)
268+
239269
@always_inline
240270
fn read_body(inout self, inout r: ByteReader) raises -> None:
241-
r.consume(self.body_raw)
271+
r.consume(self.body_raw, self.content_length())
272+
self.set_content_length(len(self.body_raw))
242273

243274
fn format_to(self, inout writer: Formatter):
244275
writer.write(
@@ -252,13 +283,6 @@ struct HTTPResponse(Formattable, Stringable):
252283
lineBreak,
253284
)
254285

255-
if HeaderKey.DATE not in self.headers:
256-
try:
257-
var current_time = now(utc=True).__str__()
258-
write_header(writer, HeaderKey.DATE, current_time)
259-
except:
260-
pass
261-
262286
self.headers.format_to(writer)
263287

264288
writer.write(lineBreak)
@@ -326,9 +350,7 @@ fn OK(body: Bytes, content_type: String) -> HTTPResponse:
326350
)
327351

328352

329-
fn OK(
330-
body: Bytes, content_type: String, content_encoding: String
331-
) -> HTTPResponse:
353+
fn OK(body: Bytes, content_type: String, content_encoding: String) -> HTTPResponse:
332354
return HTTPResponse(
333355
headers=Headers(
334356
Header(HeaderKey.CONTENT_TYPE, content_type),

lightbug_http/libc.mojo

Lines changed: 15 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -459,9 +459,7 @@ fn inet_ntop(
459459
](af, src, dst, size)
460460

461461

462-
fn inet_pton(
463-
af: c_int, src: UnsafePointer[c_char], dst: UnsafePointer[c_void]
464-
) -> c_int:
462+
fn inet_pton(af: c_int, src: UnsafePointer[c_char], dst: UnsafePointer[c_void]) -> c_int:
465463
"""Libc POSIX `inet_pton` function
466464
Reference: https://man7.org/linux/man-pages/man3/inet_ntop.3p.html
467465
Fn signature: int inet_pton(int af, const char *restrict src, void *restrict dst).
@@ -512,9 +510,7 @@ fn socket(domain: c_int, type: c_int, protocol: c_int) -> c_int:
512510
protocol: The protocol to use.
513511
Returns: A File Descriptor or -1 in case of failure.
514512
"""
515-
return external_call[
516-
"socket", c_int, c_int, c_int, c_int # FnName, RetType # Args
517-
](domain, type, protocol)
513+
return external_call["socket", c_int, c_int, c_int, c_int](domain, type, protocol) # FnName, RetType # Args
518514

519515

520516
fn setsockopt(
@@ -592,16 +588,12 @@ fn getpeername(
592588
](sockfd, addr, address_len)
593589

594590

595-
fn bind(
596-
socket: c_int, address: UnsafePointer[sockaddr], address_len: socklen_t
597-
) -> c_int:
591+
fn bind(socket: c_int, address: UnsafePointer[sockaddr], address_len: socklen_t) -> c_int:
598592
"""Libc POSIX `bind` function
599593
Reference: https://man7.org/linux/man-pages/man3/bind.3p.html
600594
Fn signature: int bind(int socket, const struct sockaddr *address, socklen_t address_len).
601595
"""
602-
return external_call[
603-
"bind", c_int, c_int, UnsafePointer[sockaddr], socklen_t
604-
](socket, address, address_len)
596+
return external_call["bind", c_int, c_int, UnsafePointer[sockaddr], socklen_t](socket, address, address_len)
605597

606598

607599
fn listen(socket: c_int, backlog: c_int) -> c_int:
@@ -639,9 +631,7 @@ fn accept(
639631
](socket, address, address_len)
640632

641633

642-
fn connect(
643-
socket: c_int, address: Reference[sockaddr], address_len: socklen_t
644-
) -> c_int:
634+
fn connect(socket: c_int, address: Reference[sockaddr], address_len: socklen_t) -> c_int:
645635
"""Libc POSIX `connect` function
646636
Reference: https://man7.org/linux/man-pages/man3/connect.3p.html
647637
Fn signature: int connect(int socket, const struct sockaddr *address, socklen_t address_len).
@@ -674,9 +664,7 @@ fn recv(
674664
](socket, buffer, length, flags)
675665

676666

677-
fn send(
678-
socket: c_int, buffer: UnsafePointer[c_void], length: c_size_t, flags: c_int
679-
) -> c_ssize_t:
667+
fn send(socket: c_int, buffer: UnsafePointer[c_void], length: c_size_t, flags: c_int) -> c_ssize_t:
680668
"""Libc POSIX `send` function
681669
Reference: https://man7.org/linux/man-pages/man3/send.3p.html
682670
Fn signature: ssize_t send(int socket, const void *buffer, size_t length, int flags).
@@ -699,11 +687,7 @@ fn shutdown(socket: c_int, how: c_int) -> c_int:
699687
how: How to shutdown the socket.
700688
Returns: 0 on success, -1 on error.
701689
"""
702-
return external_call[
703-
"shutdown", c_int, c_int, c_int
704-
]( # FnName, RetType # Args
705-
socket, how
706-
)
690+
return external_call["shutdown", c_int, c_int, c_int](socket, how) # FnName, RetType # Args
707691

708692

709693
fn getaddrinfo(
@@ -734,9 +718,7 @@ fn gai_strerror(ecode: c_int) -> UnsafePointer[c_char]:
734718
Args: ecode: The error code.
735719
Returns: A UnsafePointer to a string describing the error.
736720
"""
737-
return external_call[
738-
"gai_strerror", UnsafePointer[c_char], c_int # FnName, RetType # Args
739-
](ecode)
721+
return external_call["gai_strerror", UnsafePointer[c_char], c_int](ecode) # FnName, RetType # Args
740722

741723

742724
fn inet_pton(address_family: Int, address: String) -> Int:
@@ -745,9 +727,7 @@ fn inet_pton(address_family: Int, address: String) -> Int:
745727
ip_buf_size = 16
746728

747729
var ip_buf = UnsafePointer[c_void].alloc(ip_buf_size)
748-
var conv_status = inet_pton(
749-
rebind[c_int](address_family), to_char_ptr(address), ip_buf
750-
)
730+
var conv_status = inet_pton(rebind[c_int](address_family), to_char_ptr(address), ip_buf)
751731
return int(ip_buf.bitcast[c_uint]())
752732

753733

@@ -772,9 +752,7 @@ fn close(fildes: c_int) -> c_int:
772752
return external_call["close", c_int, c_int](fildes)
773753

774754

775-
fn open[
776-
*T: AnyType
777-
](path: UnsafePointer[c_char], oflag: c_int, *args: *T) -> c_int:
755+
fn open[*T: AnyType](path: UnsafePointer[c_char], oflag: c_int, *args: *T) -> c_int:
778756
"""Libc POSIX `open` function
779757
Reference: https://man7.org/linux/man-pages/man3/open.3p.html
780758
Fn signature: int open(const char *path, int oflag, ...).
@@ -814,9 +792,7 @@ fn read(fildes: c_int, buf: UnsafePointer[c_void], nbyte: c_size_t) -> c_int:
814792
nbyte: The number of bytes to read.
815793
Returns: The number of bytes read or -1 in case of failure.
816794
"""
817-
return external_call[
818-
"read", c_ssize_t, c_int, UnsafePointer[c_void], c_size_t
819-
](fildes, buf, nbyte)
795+
return external_call["read", c_ssize_t, c_int, UnsafePointer[c_void], c_size_t](fildes, buf, nbyte)
820796

821797

822798
fn write(fildes: c_int, buf: UnsafePointer[c_void], nbyte: c_size_t) -> c_int:
@@ -829,9 +805,7 @@ fn write(fildes: c_int, buf: UnsafePointer[c_void], nbyte: c_size_t) -> c_int:
829805
nbyte: The number of bytes to write.
830806
Returns: The number of bytes written or -1 in case of failure.
831807
"""
832-
return external_call[
833-
"write", c_ssize_t, c_int, UnsafePointer[c_void], c_size_t
834-
](fildes, buf, nbyte)
808+
return external_call["write", c_ssize_t, c_int, UnsafePointer[c_void], c_size_t](fildes, buf, nbyte)
835809

836810

837811
fn __test_getaddrinfo__():
@@ -853,8 +827,8 @@ fn __test_getaddrinfo__():
853827
UnsafePointer.address_of(servinfo),
854828
)
855829
var msg_ptr = gai_strerror(c_int(status))
856-
_ = external_call[
857-
"printf", c_int, UnsafePointer[c_char], UnsafePointer[c_char]
858-
](to_char_ptr("gai_strerror: %s"), msg_ptr)
830+
_ = external_call["printf", c_int, UnsafePointer[c_char], UnsafePointer[c_char]](
831+
to_char_ptr("gai_strerror: %s"), msg_ptr
832+
)
859833
var msg = c_charptr_to_string(msg_ptr)
860834
print("getaddrinfo satus: " + msg)

lightbug_http/net.mojo

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,7 @@ struct TCPAddr(Addr):
120120

121121
fn string(self) -> String:
122122
if self.zone != "":
123-
return join_host_port(
124-
self.ip + "%" + self.zone, self.port.__str__()
125-
)
123+
return join_host_port(self.ip + "%" + self.zone, self.port.__str__())
126124
return join_host_port(self.ip, self.port.__str__())
127125

128126

@@ -143,11 +141,7 @@ fn resolve_internet_addr(network: String, address: String) raises -> TCPAddr:
143141
host = host_port.host
144142
port = host_port.port
145143
portnum = atol(port.__str__())
146-
elif (
147-
network == NetworkType.ip.value
148-
or network == NetworkType.ip4.value
149-
or network == NetworkType.ip6.value
150-
):
144+
elif network == NetworkType.ip.value or network == NetworkType.ip4.value or network == NetworkType.ip6.value:
151145
if address != "":
152146
host = address
153147
elif network == NetworkType.unix.value:
@@ -221,9 +215,7 @@ fn convert_binary_port_to_int(port: UInt16) -> Int:
221215
return int(ntohs(port))
222216

223217

224-
fn convert_binary_ip_to_string(
225-
owned ip_address: UInt32, address_family: Int32, address_length: UInt32
226-
) -> String:
218+
fn convert_binary_ip_to_string(owned ip_address: UInt32, address_family: Int32, address_length: UInt32) -> String:
227219
"""Convert a binary IP address to a string by calling inet_ntop.
228220
229221
Args:

lightbug_http/server.mojo

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ trait ServerTrait:
1717
fn get_concurrency(self) -> Int:
1818
...
1919

20-
fn listen_and_serve(
21-
self, address: String, handler: HTTPService
22-
) raises -> None:
20+
fn listen_and_serve(self, address: String, handler: HTTPService) raises -> None:
2321
...
2422

2523
fn serve(self, ln: Listener, handler: HTTPService) raises -> None:

lightbug_http/service.mojo

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ struct Printer(HTTPService):
1818
var header = req.headers
1919
print("Request protocol: ", req.protocol)
2020
print("Request method: ", req.method)
21-
print(
22-
"Request Content-Type: ", to_string(header[HeaderKey.CONTENT_TYPE])
23-
)
21+
print("Request Content-Type: ", to_string(header[HeaderKey.CONTENT_TYPE]))
2422

2523
var body = req.body_raw
2624
print("Request Body: ", to_string(body))

0 commit comments

Comments
 (0)