Skip to content

Commit 43289ee

Browse files
committed
update client
1 parent 557e252 commit 43289ee

File tree

6 files changed

+234
-158
lines changed

6 files changed

+234
-158
lines changed

lightbug_http/header.mojo

Lines changed: 92 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -244,20 +244,18 @@ struct RequestHeader:
244244
return String(self.raw_headers)
245245

246246
fn parse_raw(inout self, inout r: Reader) raises -> Int:
247-
var n = 1
248-
# while True:
249-
var first_byte = r.peek(n)
247+
var first_byte = r.peek(1)
250248
if len(first_byte) == 0:
251-
raise Error("Failed to read first byte from header")
249+
raise Error("Failed to read first byte from request header")
252250

253251
var buf: Bytes
254252
var e: Error
255253

256254
buf, e = r.peek(r.buffered())
257255
if e:
258-
raise Error("Failed to read header: " + e.__str__())
256+
raise Error("Failed to read request header: " + e.__str__())
259257
if len(buf) == 0:
260-
raise Error("Failed to read header")
258+
raise Error("Failed to read request header, empty buffer")
261259

262260
var end_of_first_line = self.parse_first_line(buf)
263261

@@ -278,14 +276,14 @@ struct RequestHeader:
278276

279277
var first_whitespace = index_byte(b, bytes(whitespace, pop=False)[0])
280278
if first_whitespace <= 0:
281-
raise Error("Could not find HTTP request method in the request: " + String(b))
279+
raise Error("Could not find HTTP request method in request line: " + String(b))
282280

283281
_ = self.set_method_bytes(b[:first_whitespace])
284282

285283
var last_whitespace = last_index_byte(b, bytes(whitespace, pop=False)[0]) + 1
286284

287285
if last_whitespace < 0:
288-
raise Error("Could not find last whitespace in request line: " + String(b))
286+
raise Error("Could not find request target or HTTP version in request line: " + String(b))
289287
elif last_whitespace == 0:
290288
raise Error("Request URI is empty: " + String(b))
291289

@@ -629,47 +627,78 @@ struct ResponseHeader:
629627
fn headers(self) -> String:
630628
return String(self.raw_headers)
631629

632-
fn parse_first_line(inout self, first_line: String) raises -> None:
633-
var n = first_line.find(" ")
634-
635-
var proto = first_line[:n + 1]
630+
# fn parse_from_list(inout self, headers: List[String], first_line: String) raises -> None:
631+
# _ = self.parse_first_line(first_line)
632+
633+
# for header in headers:
634+
# var header_str = header[]
635+
# var separator = header_str.find(":")
636+
# if separator == -1:
637+
# raise Error("Invalid header")
636638

637-
_ = self.set_protocol(proto)
639+
# var key = String(header_str)[:separator]
640+
# var value = String(header_str)[separator + 1 :]
638641

639-
var rest_of_response_line = first_line[n + 1 :]
642+
# if len(key) > 0:
643+
# self.parse_header(key, value)
640644

641-
var status_code = atol(rest_of_response_line[:3])
642-
_ = self.set_status_code(status_code)
643-
644-
var message = rest_of_response_line[4:]
645-
if len(message) > 1:
646-
_ = self.set_status_message(bytes((message), pop=False))
645+
fn parse_raw(inout self, inout r: Reader) raises -> Int:
646+
var first_byte = r.peek(1)
647+
if len(first_byte) == 0:
648+
raise Error("Failed to read first byte from response header")
647649

648-
_ = self.set_content_length(-2)
650+
var buf: Bytes
651+
var e: Error
652+
653+
buf, e = r.peek(r.buffered())
654+
if e:
655+
raise Error("Failed to read response header: " + e.__str__())
656+
if len(buf) == 0:
657+
raise Error("Failed to read response header, empty buffer")
649658

650-
fn parse_from_list(inout self, headers: List[String], first_line: String) raises -> None:
651-
_ = self.parse_first_line(first_line)
659+
var end_of_first_line = self.parse_first_line(buf)
660+
661+
var header_len = self.read_raw_headers(buf[end_of_first_line:])
652662

653-
for header in headers:
654-
var header_str = header[]
655-
var separator = header_str.find(":")
656-
if separator == -1:
657-
raise Error("Invalid header")
663+
self.parse_headers(buf[end_of_first_line:])
664+
665+
return end_of_first_line + header_len
666+
667+
fn parse_first_line(inout self, buf: Bytes) raises -> Int:
668+
var b_next = buf
669+
var b = Bytes()
670+
while len(b) == 0:
671+
try:
672+
b, b_next = next_line(b_next)
673+
except e:
674+
raise Error("Failed to read first line from response, " + e.__str__())
675+
676+
var first_whitespace = index_byte(b, bytes(whitespace, pop=False)[0])
677+
if first_whitespace <= 0:
678+
raise Error("Could not find HTTP version in response line: " + String(b))
658679

659-
var key = String(header_str)[:separator]
660-
var value = String(header_str)[separator + 1 :]
680+
_ = self.set_protocol(b[:first_whitespace])
661681

662-
if len(key) > 0:
663-
self.parse_header(key, value)
682+
var last_whitespace = last_index_byte(b, bytes(whitespace, pop=False)[0]) + 1
664683

665-
fn parse_raw(inout self, first_line: String) raises -> None:
666-
var headers = self.raw_headers
667-
668-
_ = self.parse_first_line(first_line)
684+
if last_whitespace < 0:
685+
raise Error("Could not find status code or in response line: " + String(b))
686+
elif last_whitespace == 0:
687+
raise Error("Response URI is empty: " + String(b))
669688

689+
var status_text = b[last_whitespace :]
690+
if len(status_text) > 1:
691+
_ = self.set_status_message(status_text)
692+
693+
var status_code = atol(b[first_whitespace+1:last_whitespace])
694+
_ = self.set_status_code(status_code)
695+
696+
return len(buf) - len(b_next)
697+
698+
fn parse_headers(inout self, buf: Bytes) raises -> None:
699+
_ = self.set_content_length(-2)
670700
var s = headerScanner()
671-
s.set_b(headers)
672-
s.disable_normalization = self.disable_normalization
701+
s.set_b(buf)
673702

674703
while s.next():
675704
if len(s.key()) > 0:
@@ -710,6 +739,31 @@ struct ResponseHeader:
710739
return
711740
if key.lower() == "trailer":
712741
_ = self.set_trailer_bytes(bytes(value, pop=False))
742+
743+
fn read_raw_headers(inout self, buf: Bytes) raises -> Int:
744+
var n = index_byte(buf, bytes(nChar, pop=False)[0])
745+
746+
if n == -1:
747+
self.raw_headers = self.raw_headers[:0]
748+
raise Error("Failed to find a newline in headers")
749+
750+
if n == 0 or (n == 1 and (buf[0] == bytes(rChar, pop=False)[0])):
751+
# empty line -> end of headers
752+
return n + 1
753+
754+
n += 1
755+
var b = buf
756+
var m = n
757+
while True:
758+
b = b[m:]
759+
m = index_byte(b, bytes(nChar, pop=False)[0])
760+
if m == -1:
761+
raise Error("Failed to find a newline in headers")
762+
m += 1
763+
n += m
764+
if m == 2 and (b[0] == bytes(rChar, pop=False)[0]) or m == 1:
765+
self.raw_headers = self.raw_headers + buf[:n]
766+
return n
713767

714768
struct headerScanner:
715769
var __b: Bytes

lightbug_http/http.mojo

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ struct HTTPResponse(Response):
211211
fn get_body_bytes(self) -> BytesView:
212212
return BytesView(unsafe_ptr=self.body_raw.unsafe_ptr(), len=self.body_raw.size)
213213

214+
fn set_body_bytes(inout self, body: Bytes) -> Self:
215+
self.body_raw = body
216+
return self
217+
214218
fn set_status_code(inout self, status_code: Int) -> Self:
215219
_ = self.header.set_status_code(status_code)
216220
return self
@@ -224,6 +228,14 @@ struct HTTPResponse(Response):
224228

225229
fn connection_close(self) -> Bool:
226230
return self.header.connection_close()
231+
232+
fn read_body(inout self, inout r: Reader, header_len: Int) raises -> None:
233+
_ = r.discard(header_len)
234+
235+
var body_buf: Bytes
236+
body_buf, _ = r.peek(r.buffered())
237+
238+
_ = self.set_body_bytes(bytes(body_buf))
227239

228240
fn OK(body: StringLiteral) -> HTTPResponse:
229241
return HTTPResponse(

lightbug_http/sys/client.mojo

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
from lightbug_http.client import Client
2-
from lightbug_http.http import HTTPRequest, HTTPResponse, encode, split_http_string
3-
from lightbug_http.header import ResponseHeader
4-
from lightbug_http.sys.net import create_connection
5-
from lightbug_http.io.bytes import Bytes
1+
from external.gojo.bufio import Reader, Scanner, scan_words, scan_bytes
2+
from external.gojo.bytes import buffer
63
from external.libc import (
74
c_int,
85
AF_INET,
@@ -13,6 +10,12 @@ from external.libc import (
1310
recv,
1411
close,
1512
)
13+
from lightbug_http.client import Client
14+
from lightbug_http.net import default_buffer_size
15+
from lightbug_http.http import HTTPRequest, HTTPResponse, encode, split_http_string
16+
from lightbug_http.header import ResponseHeader
17+
from lightbug_http.sys.net import create_connection
18+
from lightbug_http.io.bytes import Bytes
1619

1720

1821
struct MojoClient(Client):
@@ -92,46 +95,49 @@ struct MojoClient(Client):
9295

9396
var conn = create_connection(self.fd, host_str, port)
9497

95-
var req_encoded = encode(req, uri)
98+
var req_encoded = encode(req)
9699

97100
var bytes_sent = conn.write(req_encoded)
98101
if bytes_sent == -1:
99102
raise Error("Failed to send message")
100103

101-
var new_buf = Bytes()
102-
104+
var new_buf = Bytes(capacity=default_buffer_size)
103105
var bytes_recv = conn.read(new_buf)
104106
if bytes_recv == 0:
105107
conn.close()
106108

107-
var response_first_line: String
108-
var response_headers: String
109-
var response_body: String
109+
var buf = buffer.new_buffer(new_buf^)
110+
var reader = Reader(buf^)
110111

111-
response_first_line, response_headers, response_body = split_http_string(new_buf)
112+
var error = Error()
112113

113-
# Ugly hack for now in case the default buffer is too large and we read additional responses from the server
114-
var newline_in_body = response_body.find("\r\n")
115-
if newline_in_body != -1:
116-
response_body = response_body[:newline_in_body]
117-
118-
var header = ResponseHeader(response_headers.as_bytes())
114+
# # Ugly hack for now in case the default buffer is too large and we read additional responses from the server
115+
# var newline_in_body = response_body.find("\r\n")
116+
# if newline_in_body != -1:
117+
# response_body = response_body[:newline_in_body]
119118

119+
var header = ResponseHeader()
120+
var first_line_and_headers_len = 0
120121
try:
121-
header.parse_raw(response_first_line)
122+
first_line_and_headers_len = header.parse_raw(reader)
122123
except e:
123124
conn.close()
124-
raise Error("Failed to parse response header: " + e.__str__())
125+
error = Error("Failed to parse response headers: " + e.__str__())
125126

126-
var total_recv = bytes_recv
127+
var response = HTTPResponse(header, Bytes())
127128

128-
while header.content_length() > total_recv:
129-
if header.content_length() != 0 and header.content_length() != -2:
130-
var remaining_body = Bytes()
131-
var read_len = conn.read(remaining_body)
132-
response_body += remaining_body
133-
total_recv += read_len
129+
try:
130+
response.read_body(reader, first_line_and_headers_len,)
131+
except e:
132+
error = Error("Failed to read request body: " + e.__str__())
133+
# var total_recv = bytes_recv
134+
# while header.content_length() > total_recv:
135+
# if header.content_length() != 0 and header.content_length() != -2:
136+
# var remaining_body = Bytes()
137+
# var read_len = conn.read(remaining_body)
138+
# response_body += remaining_body
139+
# total_recv += read_len
134140

135141
conn.close()
136142

137-
return HTTPResponse(header, response_body.as_bytes_slice())
143+
return response

lightbug_http/sys/server.mojo

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,6 @@ struct SysServer:
191191
buf = buffer.new_buffer(b^)
192192
reader = Reader(buf^)
193193

194-
195-
var first_byte = reader.peek(1)
196-
if len(first_byte) == 0:
197-
error = Error("Failed to read first byte from connection")
198-
199194
var header = RequestHeader()
200195
var first_line_and_headers_len = 0
201196
try:

0 commit comments

Comments
 (0)