11from collections import Dict
2+ from utils import StringSlice
23from memory import UnsafePointer
3- from lightbug_http.libc import (
4- c_int,
5- AF_INET ,
6- SOCK_STREAM ,
7- socket,
8- connect,
9- send,
10- recv,
11- close,
12- )
13- from lightbug_http.strings import to_string
144from lightbug_http.net import default_buffer_size
155from lightbug_http.http import HTTPRequest, HTTPResponse, encode
166from lightbug_http.header import Headers, HeaderKey
177from lightbug_http.net import create_connection, TCPConnection
18- from lightbug_http.io.bytes import Bytes
19- from lightbug_http.utils import ByteReader, logger
20- from lightbug_http.pool_manager import PoolManager
8+ from lightbug_http.io.bytes import Bytes, ByteReader
9+ from lightbug_http._logger import logger
10+ from lightbug_http.pool_manager import PoolManager, PoolKey
11+ from lightbug_http.uri import URI , Scheme
2112
2213
2314struct Client :
@@ -41,7 +32,7 @@ struct Client:
4132 self .allow_redirects = allow_redirects
4233 self ._connections = PoolManager[TCPConnection](cached_connections)
4334
44- fn do (mut self , owned req : HTTPRequest) raises -> HTTPResponse:
35+ fn do (mut self , owned request : HTTPRequest) raises -> HTTPResponse:
4536 """ The `do` method is responsible for sending an HTTP request to a server and receiving the corresponding response.
4637
4738 It performs the following steps:
@@ -54,81 +45,77 @@ struct Client:
5445 Note: The code assumes that the `HTTPRequest` object passed as an argument has a valid URI with a host and port specified.
5546
5647 Args:
57- req : An `HTTPRequest` object representing the request to be sent.
48+ request : An `HTTPRequest` object representing the request to be sent.
5849
5950 Returns:
6051 The received response.
6152
6253 Raises:
6354 Error: If there is a failure in sending or receiving the message.
6455 """
65- if req.uri.host == " " :
66- raise Error(" Client.do: Request failed because the host field is empty." )
67- var is_tls = False
56+ if request.uri.host == " " :
57+ raise Error(" Client.do: Host must not be empty." )
6858
69- if req.uri.is_https():
59+ var is_tls = False
60+ var scheme = Scheme.HTTP
61+ if request.uri.is_https():
7062 is_tls = True
63+ scheme = Scheme.HTTPS
7164
72- var host_str : String
73- var port : Int
74- if " :" in req.uri.host:
75- var host_port : List[String]
76- try :
77- host_port = req.uri.host.split(" :" )
78- except :
79- raise Error(" Client.do: Failed to split host and port." )
80- host_str = host_port[0 ]
81- port = atol(host_port[1 ])
65+ var port : UInt16
66+ if request.uri.port:
67+ port = request.uri.port.value()
8268 else :
83- host_str = req.uri.host
84- if is_tls:
69+ if request.uri.scheme == Scheme.HTTP .value:
70+ port = 80
71+ elif request.uri.scheme == Scheme.HTTPS .value:
8572 port = 443
8673 else :
87- port = 80
74+ raise Error( " Client.do: Invalid scheme received in the URI. " )
8875
76+ var pool_key = PoolKey(request.uri.host, port, scheme)
8977 var cached_connection = False
9078 var conn : TCPConnection
9179 try :
92- conn = self ._connections.take(host_str )
80+ conn = self ._connections.take(pool_key )
9381 cached_connection = True
9482 except e:
9583 if str (e) == " PoolManager.take: Key not found." :
96- conn = create_connection(host_str , port)
84+ conn = create_connection(request.uri.host , port)
9785 else :
9886 logger.error(e)
9987 raise Error(" Client.do: Failed to create a connection to host." )
10088
10189 var bytes_sent : Int
10290 try :
103- bytes_sent = conn.write(encode(req ))
91+ bytes_sent = conn.write(encode(request ))
10492 except e:
10593 # Maybe peer reset ungracefully, so try a fresh connection
10694 if str (e) == " SendError: Connection reset by peer." :
10795 logger.debug(" Client.do: Connection reset by peer. Trying a fresh connection." )
10896 conn.teardown()
10997 if cached_connection:
110- return self .do(req ^ )
98+ return self .do(request ^ )
11199 logger.error(" Client.do: Failed to send message." )
112100 raise e
113101
114102 # TODO : What if the response is too large for the buffer? We should read until the end of the response. (@thatstoasty)
115103 var new_buf = Bytes(capacity = default_buffer_size)
116-
117104 try :
118105 _ = conn.read(new_buf)
119106 except e:
120107 if str (e) == " EOF" :
121108 conn.teardown()
122109 if cached_connection:
123- return self .do(req ^ )
110+ return self .do(request ^ )
124111 raise Error(" Client.do: No response received from the server." )
125112 else :
126113 logger.error(e)
127114 raise Error(" Client.do: Failed to read response from peer." )
128115
129- var res : HTTPResponse
116+ var response : HTTPResponse
130117 try :
131- res = HTTPResponse.from_bytes(new_buf, conn)
118+ response = HTTPResponse.from_bytes(new_buf, conn)
132119 except e:
133120 logger.error(" Failed to parse a response..." )
134121 try :
@@ -138,19 +125,19 @@ struct Client:
138125 raise e
139126
140127 # Redirects should not keep the connection alive, as redirects can send the client to a different server.
141- if self .allow_redirects and res .is_redirect():
128+ if self .allow_redirects and response .is_redirect():
142129 conn.teardown()
143- return self ._handle_redirect(req ^ , res ^ )
130+ return self ._handle_redirect(request ^ , response ^ )
144131 # Server told the client to close the connection, we can assume the server closed their side after sending the response.
145- elif res .connection_close():
132+ elif response .connection_close():
146133 conn.teardown()
147134 # Otherwise, persist the connection by giving it back to the pool manager.
148135 else :
149- self ._connections.give(host_str , conn^ )
150- return res
136+ self ._connections.give(pool_key , conn^ )
137+ return response
151138
152139 fn _handle_redirect (
153- mut self , owned original_req : HTTPRequest, owned original_response : HTTPResponse
140+ mut self , owned original_request : HTTPRequest, owned original_response : HTTPResponse
154141 ) raises -> HTTPResponse:
155142 var new_uri : URI
156143 var new_location : String
@@ -164,9 +151,9 @@ struct Client:
164151 new_uri = URI .parse(new_location)
165152 except e:
166153 raise Error(" Client._handle_redirect: Failed to parse the new URI: " + str (e))
167- original_req .headers[HeaderKey.HOST ] = new_uri.host
154+ original_request .headers[HeaderKey.HOST ] = new_uri.host
168155 else :
169- new_uri = original_req .uri
156+ new_uri = original_request .uri
170157 new_uri.path = new_location
171- original_req .uri = new_uri
172- return self .do(original_req ^ )
158+ original_request .uri = new_uri
159+ return self .do(original_request ^ )
0 commit comments