Skip to content

Commit 9478f49

Browse files
committed
remove extra null termination
1 parent 75f39a0 commit 9478f49

File tree

14 files changed

+183
-157
lines changed

14 files changed

+183
-157
lines changed

README.md

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -154,31 +154,28 @@ struct Welcome(HTTPService):
154154

155155
### Using the client
156156

157-
Create a file, e.g `client.mojo` with the following code:
157+
Create a file, e.g `client.mojo` with the following code. Run `mojo client.mojo` to execute the request to a given URL.
158158

159159
```mojo
160-
from lightbug_http.http import HTTPRequest
161-
from lightbug_http.uri import URI
162-
from lightbug_http.sys.client import MojoClient
163-
164160
fn test_request(inout client: MojoClient) raises -> None:
165-
var uri = URI("http://httpbin.org/")
161+
var uri = URI("http://httpbin.org/status/404")
166162
var request = HTTPRequest(uri)
167163
var response = client.do(request)
168164
169165
# print status code
170166
print("Response:", response.header.status_code())
171167
172-
# print various parsed headers
173-
print("Header", response.header.content_length())
168+
# print raw headers
169+
# print("Headers:", response.header.headers())
170+
171+
# print parsed headers (only some are parsed for now)
172+
print("Content-Type:", String(response.header.content_type()))
173+
print("Content-Length", response.header.content_length())
174+
print("Connection:", response.header.connection_close())
175+
print("Server:", String(response.header.server()))
174176
175177
# print body
176178
print(String(response.get_body()))
177-
178-
179-
fn main() raises -> None:
180-
var client = MojoClient()
181-
test_request(client)
182179
```
183180

184181
Pure Mojo-based client is available by default. This client is also used internally for testing the server.

client.mojo

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from lightbug_http.http import HTTPRequest, encode
2+
from lightbug_http.header import RequestHeader
3+
from lightbug_http.uri import URI
4+
from lightbug_http.sys.client import MojoClient
5+
6+
fn test_request(inout client: MojoClient) raises -> None:
7+
var uri = URI("http://httpbin.org/status/404")
8+
var request = HTTPRequest(uri, RequestHeader())
9+
var response = client.do(request)
10+
11+
# print status code
12+
print("Response:", response.header.status_code())
13+
14+
# print raw headers
15+
# print("Headers:", response.header.headers())
16+
17+
# print parsed headers (only some are parsed for now)
18+
print("Content-Type:", String(response.header.content_type()))
19+
print("Content-Length", response.header.content_length())
20+
print("Connection:", response.header.connection_close())
21+
print("Server:", String(response.header.server()))
22+
23+
# print body
24+
print(String(response.get_body()))
25+
26+
27+
fn main() raises -> None:
28+
var client = MojoClient()
29+
test_request(client)

lightbug.🔥

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from lightbug_http import *
2-
from sys import is_defined
32

43
fn main() raises:
54
var server = SysServer()

lightbug_http/error.mojo

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,4 @@ from lightbug_http.header import ResponseHeader
66
@value
77
struct ErrorHandler:
88
fn Error(self) -> HTTPResponse:
9-
return HTTPResponse(ResponseHeader(), String("TODO").as_bytes())
10-
11-
12-
alias errNeedMore = Error("need more data: cannot find trailing lf")
13-
alias errInvalidName = Error("invalid header name")
14-
alias errSmallBuffer = Error("small read buffer. Increase ReadBufferSize")
9+
return HTTPResponse(ResponseHeader(), String("TODO")._buffer)

lightbug_http/header.mojo

Lines changed: 32 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ from lightbug_http.strings import (
88
nChar,
99
)
1010
from lightbug_http.io.bytes import Bytes, bytes_equal
11-
from lightbug_http.error import errNeedMore, errInvalidName
1211

1312
alias statusOK = 200
1413

@@ -105,7 +104,7 @@ struct RequestHeader:
105104
self.__trailer = trailer
106105

107106
fn set_content_type(inout self, content_type: String) -> Self:
108-
self.__content_type = content_type.as_bytes()
107+
self.__content_type = content_type._buffer
109108
return self
110109

111110
fn set_content_type_bytes(inout self, content_type: Bytes) -> Self:
@@ -116,7 +115,7 @@ struct RequestHeader:
116115
return self.__content_type
117116

118117
fn set_host(inout self, host: String) -> Self:
119-
self.__host = host.as_bytes()
118+
self.__host = host._buffer
120119
return self
121120

122121
fn set_host_bytes(inout self, host: Bytes) -> Self:
@@ -127,7 +126,7 @@ struct RequestHeader:
127126
return self.__host
128127

129128
fn set_user_agent(inout self, user_agent: String) -> Self:
130-
self.__user_agent = user_agent.as_bytes()
129+
self.__user_agent = user_agent._buffer
131130
return self
132131

133132
fn set_user_agent_bytes(inout self, user_agent: Bytes) -> Self:
@@ -138,7 +137,7 @@ struct RequestHeader:
138137
return self.__user_agent
139138

140139
fn set_method(inout self, method: String) -> Self:
141-
self.__method = method.as_bytes()
140+
self.__method = method._buffer
142141
return self
143142

144143
fn set_method_bytes(inout self, method: Bytes) -> Self:
@@ -151,8 +150,8 @@ struct RequestHeader:
151150
return self.__method
152151

153152
fn set_protocol(inout self, method: String) -> Self:
154-
self.no_http_1_1 = bytes_equal(method.as_bytes(), strHttp11)
155-
self.proto = method.as_bytes()
153+
self.no_http_1_1 = bytes_equal(method._buffer, strHttp11)
154+
self.proto = method._buffer
156155
return self
157156

158157
fn set_protocol_bytes(inout self, method: Bytes) -> Self:
@@ -190,7 +189,7 @@ struct RequestHeader:
190189
return self.__request_uri
191190

192191
fn set_trailer(inout self, trailer: String) -> Self:
193-
self.__trailer = trailer.as_bytes()
192+
self.__trailer = trailer._buffer
194193
return self
195194

196195
fn set_trailer_bytes(inout self, trailer: Bytes) -> Self:
@@ -276,7 +275,7 @@ struct RequestHeader:
276275
if self.content_length() != -1:
277276
var content_length = s.value
278277
_ = self.set_content_length(atol(content_length))
279-
_ = self.set_content_length_bytes(content_length.as_bytes())
278+
_ = self.set_content_length_bytes(content_length._buffer)
280279
continue
281280
if s.key.lower() == "connection":
282281
if s.value == "close":
@@ -461,7 +460,7 @@ struct ResponseHeader:
461460
return self.__content_type
462461

463462
fn set_content_type(inout self, content_type: String) -> Self:
464-
self.__content_type = content_type.as_bytes()
463+
self.__content_type = content_type._buffer
465464
return self
466465

467466
fn set_content_type_bytes(inout self, content_type: Bytes) -> Self:
@@ -472,7 +471,7 @@ struct ResponseHeader:
472471
return self.__content_encoding
473472

474473
fn set_content_encoding(inout self, content_encoding: String) -> Self:
475-
self.__content_encoding = content_encoding.as_bytes()
474+
self.__content_encoding = content_encoding._buffer
476475
return self
477476

478477
fn set_content_encoding_bytes(inout self, content_encoding: Bytes) -> Self:
@@ -494,7 +493,7 @@ struct ResponseHeader:
494493
return self.__server
495494

496495
fn set_server(inout self, server: String) -> Self:
497-
self.__server = server.as_bytes()
496+
self.__server = server._buffer
498497
return self
499498

500499
fn set_server_bytes(inout self, server: Bytes) -> Self:
@@ -511,7 +510,7 @@ struct ResponseHeader:
511510
return self.__protocol
512511

513512
fn set_trailer(inout self, trailer: String) -> Self:
514-
self.__trailer = trailer.as_bytes()
513+
self.__trailer = trailer._buffer
515514
return self
516515

517516
fn set_trailer_bytes(inout self, trailer: Bytes) -> Self:
@@ -532,34 +531,27 @@ struct ResponseHeader:
532531
fn connection_close(self) -> Bool:
533532
return self.__connection_close
534533

535-
fn parse(inout self, request_line: String) raises -> None:
536-
var headers = self.raw_headers
537-
538-
var n = request_line.find(" ")
539-
if n <= 0:
540-
raise Error("Cannot find HTTP request method in the request")
534+
fn headers(self) -> String:
535+
return String(self.raw_headers)
541536

542-
var method = request_line[:n]
543-
var rest_of_request_line = request_line[n + 1 :]
537+
fn parse(inout self, first_line: String) raises -> None:
538+
var headers = self.raw_headers
544539

545540
# Defaults to HTTP/1.1
546541
var proto_str = String(strHttp11)
547542

548-
# Parse requestURI
549-
n = rest_of_request_line.rfind(" ")
550-
if n < 0:
551-
n = len(rest_of_request_line)
552-
proto_str = strHttp10
553-
elif n == 0:
554-
raise Error("Request URI cannot be empty")
555-
else:
556-
var proto = rest_of_request_line[n + 1 :]
557-
if proto != strHttp11:
558-
proto_str = proto
543+
var n = first_line.find(" ")
544+
var proto = first_line[:n]
545+
if proto != strHttp11:
546+
proto_str = proto
559547

560-
var request_uri = rest_of_request_line[:n]
548+
var rest_of_response_line = first_line[n + 1 :]
549+
var status_code = atol(rest_of_response_line[:3])
550+
var message = rest_of_response_line[4:]
561551

562552
_ = self.set_protocol(proto_str._buffer)
553+
_ = self.set_status_code(status_code)
554+
_ = self.set_status_message(message._buffer)
563555
_ = self.set_content_length(-2)
564556

565557
var s = headerScanner()
@@ -568,8 +560,7 @@ struct ResponseHeader:
568560

569561
while s.next():
570562
if len(s.key) > 0:
571-
# Spaces between the header key and colon are not allowed.
572-
# See RFC 7230, Section 3.2.4.
563+
# Spaces between header key and colon not allowed (RFC 7230, 3.2.4)
573564
if s.key.find(" ") != -1 or s.key.find("\t") != -1:
574565
raise Error("Invalid header key")
575566
elif s.key[0] == "c" or s.key[0] == "C":
@@ -583,25 +574,22 @@ struct ResponseHeader:
583574
if self.content_length() != -1:
584575
var content_length = s.value
585576
_ = self.set_content_length(atol(content_length))
586-
_ = self.set_content_length_bytes(content_length.as_bytes())
577+
_ = self.set_content_length_bytes(content_length._buffer)
587578
continue
588579
if s.key.lower() == "connection":
589580
if s.value == "close":
590581
_ = self.set_connection_close()
591582
else:
592583
_ = self.reset_connection_close()
593-
# _ = self.appendargbytes(s.key, s.value)
594584
continue
595585
elif s.key[0] == "s" or s.key[0] == "S":
596586
if s.key.lower() == "server":
597587
_ = self.set_server(s.value)
598588
continue
599-
# TODO: set cookie
600589
elif s.key[0] == "t" or s.key[0] == "T":
601590
if s.key.lower() == "transfer-encoding":
602591
if s.value != "identity":
603592
_ = self.set_content_length(-1)
604-
# _ = self.setargbytes(s.key, strChunked)
605593
continue
606594
if s.key.lower() == "trailer":
607595
_ = self.set_trailer(s.value)
@@ -633,20 +621,17 @@ struct headerScanner:
633621
if not self.initialized:
634622
self.initialized = True
635623

636-
if self.b.startswith('\n\n'):
624+
if self.b.startswith('\r\n\r\n'):
637625
self.b = self.b[2:]
638-
print("Error: Double newline")
639626
return False
640627

641-
if self.b.startswith('\n'):
628+
if self.b.startswith('\r\n'):
642629
self.b = self.b[1:]
643-
print("Error: Newline at start")
644630
return False
645631

646632
var n = self.b.find(':')
647-
var x = self.b.find('\n')
633+
var x = self.b.find('\r\n')
648634
if x != -1 and x < n:
649-
print("Error: Newline before colon")
650635
return False
651636

652637
if n == -1:
@@ -656,17 +641,15 @@ struct headerScanner:
656641
self.key = self.b[:n].strip()
657642
self.b = self.b[n+1:].strip()
658643

659-
x = self.b.find('\n')
644+
x = self.b.find('\r\n')
660645
if x == -1:
661-
# If we don't find a newline, assume we have reached the end
662646
if len(self.b) == 0:
663-
print("Error: No ending newline and no data after colon")
664647
return False
665648
self.value = self.b.strip()
666649
self.b = ''
667650
else:
668651
self.value = self.b[:x].strip()
669652
self.b = self.b[x+1:]
670-
653+
671654
return True
672655

0 commit comments

Comments
 (0)