Skip to content

Commit

Permalink
Keep persistent connections in client
Browse files Browse the repository at this point in the history
  • Loading branch information
bgreni committed Oct 24, 2024
1 parent e7710b9 commit 5fac875
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 54 deletions.
58 changes: 46 additions & 12 deletions lightbug_http/client.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from lightbug_http.libc import (
from .libc import (
c_int,
AF_INET,
SOCK_STREAM,
Expand All @@ -9,29 +9,43 @@ from lightbug_http.libc import (
close,
)
from lightbug_http.strings import to_string
from lightbug_http.io.bytes import Bytes
from lightbug_http.utils import ByteReader
from lightbug_http.net import create_connection, default_buffer_size
from lightbug_http.net import default_buffer_size
from lightbug_http.http import HTTPRequest, HTTPResponse, encode
from lightbug_http.header import Headers, HeaderKey
from lightbug_http.net import create_connection, SysConnection
from lightbug_http.io.bytes import Bytes
from lightbug_http.utils import ByteReader
from collections import Dict


struct Client:
var host: StringLiteral
var port: Int
var name: String

var _connections: Dict[String, SysConnection]

fn __init__(inout self) raises:
self.host = "127.0.0.1"
self.port = 8888
self.name = "lightbug_http_client"
self._connections = Dict[String, SysConnection]()

fn __init__(inout self, host: StringLiteral, port: Int) raises:
self.host = host
self.port = port
self.name = "lightbug_http_client"
self._connections = Dict[String, SysConnection]()

fn do(self, owned req: HTTPRequest) raises -> HTTPResponse:
fn __del__(owned self):
for conn in self._connections.values():
try:
conn[].close()
except:
# TODO: Add an optional debug log entry here
pass

fn do(inout self, owned req: HTTPRequest) raises -> HTTPResponse:
"""
The `do` method is responsible for sending an HTTP request to a server and receiving the corresponding response.
Expand Down Expand Up @@ -83,31 +97,47 @@ struct Client:
else:
port = 80

# TODO: Actually handle persistent connections
var conn = create_connection(socket(AF_INET, SOCK_STREAM, 0), host_str, port)
var conn: SysConnection
var cached_connection = False
if host_str in self._connections:
conn = self._connections[host_str]
cached_connection = True
else:
conn = create_connection(socket(AF_INET, SOCK_STREAM, 0), host_str, port)
self._connections[host_str] = conn

var bytes_sent = conn.write(encode(req))
if bytes_sent == -1:
# Maybe peer reset ungracefully, so try a fresh connection
self._close_conn(host_str)
if cached_connection:
return self.do(req^)
raise Error("Failed to send message")

var new_buf = Bytes(capacity=default_buffer_size)
var bytes_recv = conn.read(new_buf)

if bytes_recv == 0:
conn.close()
self._close_conn(host_str)
if cached_connection:
return self.do(req^)
raise Error("No response received")
try:
var res = HTTPResponse.from_bytes(new_buf^)
if res.is_redirect():
conn.close()
self._close_conn(host_str)
return self._handle_redirect(req^, res^)
if res.connection_close():
self._close_conn(host_str)
return res
except e:
conn.close()
self._close_conn(host_str)
raise e

return HTTPResponse(Bytes())

fn _handle_redirect(
self, owned original_req: HTTPRequest, owned original_response: HTTPResponse
inout self, owned original_req: HTTPRequest, owned original_response: HTTPResponse
) raises -> HTTPResponse:
var new_uri: URI
var new_location = original_response.headers[HeaderKey.LOCATION]
Expand All @@ -119,3 +149,7 @@ struct Client:
new_uri.path = new_location
original_req.uri = new_uri
return self.do(original_req^)

fn _close_conn(inout self, host: String) raises:
self._connections[host].close()
_ = self._connections.pop(host)
1 change: 1 addition & 0 deletions lightbug_http/header.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct HeaderKey:
alias DATE = "date"
alias LOCATION = "location"
alias HOST = "host"
alias SERVER = "server"


@value
Expand Down
1 change: 1 addition & 0 deletions lightbug_http/http/request.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ from lightbug_http.strings import (
to_string,
)


@value
struct HTTPRequest(Formattable, Stringable):
var headers: Headers
Expand Down
14 changes: 4 additions & 10 deletions lightbug_http/http/response.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,10 @@ struct HTTPResponse(Formattable, Stringable):
self.set_content_length(len(self.body_raw))

fn format_to(self, inout writer: Formatter):
writer.write(
self.protocol,
whitespace,
self.status_code,
whitespace,
self.status_text,
lineBreak,
"server: lightbug_http",
lineBreak,
)
writer.write(self.protocol, whitespace, self.status_code, whitespace, self.status_text, lineBreak)

if HeaderKey.SERVER not in self.headers:
writer.write("server: lightbug_http", lineBreak)

self.headers.format_to(writer)

Expand Down
30 changes: 0 additions & 30 deletions magic.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.8.30-hbcca054_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda
- conda: https://repo.prefix.dev/mojo-community/linux-64/gojo-0.1.9-hb0f4dca_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.5.0-hd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda
Expand Down Expand Up @@ -71,7 +70,6 @@ environments:
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.8.30-hf0a4a13_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda
- conda: https://repo.prefix.dev/mojo-community/osx-arm64/gojo-0.1.9-h60d57d3_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.5.0-hd8ed1ab_0.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.2-pyhd8ed1ab_0.conda
Expand Down Expand Up @@ -216,34 +214,6 @@ packages:
license_family: BSD
size: 84437
timestamp: 1692311973840
- kind: conda
name: gojo
version: 0.1.9
build: h60d57d3_0
subdir: osx-arm64
url: https://repo.prefix.dev/mojo-community/osx-arm64/gojo-0.1.9-h60d57d3_0.conda
sha256: 4c268d0d8d5f1b78a547e78a3db9e8037143918cd1d696f5adb5db55942cef5e
depends:
- max >=24.5.0,<24.6.0
arch: arm64
platform: osx
license: MIT
size: 1009999
timestamp: 1726268309700
- kind: conda
name: gojo
version: 0.1.9
build: hb0f4dca_0
subdir: linux-64
url: https://repo.prefix.dev/mojo-community/linux-64/gojo-0.1.9-hb0f4dca_0.conda
sha256: 9a49e21b4269368a6d906769bd041b8b91b99da3375d7944f7d8ddd73392c2f0
depends:
- max >=24.5.0,<24.6.0
arch: x86_64
platform: linux
license: MIT
size: 1011206
timestamp: 1726268249824
- kind: conda
name: importlib-metadata
version: 8.5.0
Expand Down
1 change: 0 additions & 1 deletion mojoproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ format = { cmd = "magic run mojo format -l 120 lightbug_http" }

[dependencies]
max = ">=24.5.0,<25"
gojo = "0.1.9"
small_time = "0.1.3"
1 change: 0 additions & 1 deletion recipes/recipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ build:
requirements:
run:
- max >=24.5.0
- gojo == 0.1.9
- small_time == 0.1.3

about:
Expand Down

0 comments on commit 5fac875

Please sign in to comment.