Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change HTTPClient handler factory creation #106

Merged
merged 7 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .release-notes/106.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Change HTTPClient handler factory creation

The handler factory for creating handlers for new requests is now provided in the constructor of the client instead of the apply method. This makes it more clear, that the client will use the same handler for all requests.

The old version would look similar to this:

```pony
SeanTAllen marked this conversation as resolved.
Show resolved Hide resolved
let client = HTTPClient(auth)

// Later
let handler_factory = ...

client(payload, handler_factory)?

// Even later
client(other_payload, other_factory)
```

In the new version the handler factory needs to be supplied at the creation of the client:

```pony
let handler_factory = ...
let client = HTTPClient(auth, handler_factory)

client(payload)

// This will use the handler_factory
client(other_payload)

// To use a different handler factory, create a new client

let other_client = Client(auth, other_factory)
other_client(other_payload)
```
12 changes: 9 additions & 3 deletions examples/httpget/httpget.pony
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,19 @@ actor _GetWork
env.exitcode(1)
end

// The Client manages all links.
let client = HTTPClient(TCPConnectAuth(env.root), consume sslctx where keepalive_timeout_secs = timeout.u32())
// The Notify Factory will create HTTPHandlers as required. It is
// done this way because we do not know exactly when an HTTPSession
// is created - they can be re-used.
let dumpMaker = recover val NotifyFactory.create(this) end

// The Client manages all links.
let client = HTTPClient(
TCPConnectAuth(env.root),
dumpMaker,
consume sslctx
where keepalive_timeout_secs = timeout.u32()
)

try
// Start building a GET request.
let req = Payload.request("GET", url)
Expand All @@ -119,7 +125,7 @@ actor _GetWork
end

// Submit the request
let sentreq = client(consume req, dumpMaker)?
let sentreq = client(consume req)?

// Could send body data via `sentreq`, if it was a POST
else
Expand Down
4 changes: 2 additions & 2 deletions http/_test.pony
Original file line number Diff line number Diff line change
Expand Up @@ -435,13 +435,13 @@ class \nodoc\ iso _HTTPConnTest is UnitTest
let url = URL.build(us)?
h.log("url.string()=" + url.string())
let hf = _HTTPConnTestHandlerFactory(h)
client = recover iso HTTPClient(TCPConnectAuth(h.env.root)) end
client = recover iso HTTPClient(TCPConnectAuth(h.env.root), hf) end

for _ in Range(0, loops) do
let payload: Payload iso = Payload.request("GET", url)
payload.set_length(0)
try
(client as HTTPClient iso)(consume payload, hf)?
(client as HTTPClient iso)(consume payload)?
end
end
else
Expand Down
6 changes: 2 additions & 4 deletions http/_test_client.pony
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,14 @@ class \nodoc\ iso _ClientStreamTransferTest is UnitTest
try
let client = HTTPClient(
TCPConnectAuth(_h.env.root),
_StreamTransferHandlerFactory(_h),
None
where keepalive_timeout_secs = U32(2)
)
(let host, let port) = listen.local_address().name()?
_h.log("connecting to server at " + host + ":" + port)
let req = Payload.request("GET", URL.build("http://" + host + ":" + port + "/bla")?)
client(
consume req,
_StreamTransferHandlerFactory(_h)
)?
client(consume req)?
else
_h.fail("request building failed")
end
Expand Down
15 changes: 6 additions & 9 deletions http/_test_client_error_handling.pony
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,15 @@ class \nodoc\ iso _ConnectionClosedTest is UnitTest
try
let client = HTTPClient(
TCPConnectAuth(_h.env.root),
_ConnectionClosedHandlerFactory(_h),
None
where keepalive_timeout_secs = U32(2)
)
(let host, let port) = listen.local_address().name()?
let req = Payload.request("GET",
URL.build("http://" + host + ":" + port + "/bla")?)
req.add_chunk("CHUNK")
client(
consume req,
_ConnectionClosedHandlerFactory(_h)
)?
client(consume req)?
else
_h.fail("request building failed")
end
Expand Down Expand Up @@ -123,13 +121,14 @@ actor \nodoc\ _Connecter
try
let client = HTTPClient(
TCPConnectAuth(_h.env.root),
_ConnectFailedHandlerFactory(_h),
None
where keepalive_timeout_secs = U32(2)
)
let req = Payload.request("GET",
URL.build("http://" + host + ":" + port' + "/bla")?)
req.add_chunk("CHUNK")
client(consume req, _ConnectFailedHandlerFactory(_h))?
client(consume req)?
else
_h.fail("request building failed")
end
Expand Down Expand Up @@ -303,17 +302,15 @@ class \nodoc\ iso _SSLAuthFailedTest is UnitTest
end
let client = HTTPClient(
TCPConnectAuth(_h.env.root),
_SSLAuthFailedHandlerFactory(_h),
ssl_ctx
where keepalive_timeout_secs = U32(2)
)
let req = Payload.request(
"GET",
URL.build("https://" + host + ":" + port + "/bla")?)
req.add_chunk("CHUNK")
client(
consume req,
_SSLAuthFailedHandlerFactory(_h)
)?
client(consume req)?
else
_h.fail("request building failed")
end
Expand Down
28 changes: 14 additions & 14 deletions http/http_client.pony
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,24 @@ class HTTPClient
let _pipeline: Bool
let _keepalive_timeout_secs: U32
let _sessions: Map[_HostService, _ClientConnection] = _sessions.create()
let _handlermaker: HandlerFactory val

new create(
auth: TCPConnectAuth,
handlermaker: HandlerFactory val,
sslctx: (SSLContext | None) = None,
pipeline: Bool = true,
keepalive_timeout_secs: U32 = 0)
=>
"""
Create the context in which all HTTP sessions will originate.
Create the context in which all HTTP sessions will originate. The `handlermaker`
is used to create the `HTTPHandler` that is applied with each received
payload after making a request. All requests made with one client are created
using the same handler factory, if you need different handlers for different
requests, you need to create different clients.

Parameters:

- keepalive_timeout_secs: Use TCP Keepalive and check if the other side is down
every `keepalive_timeout_secs` seconds.
"""
Expand All @@ -40,12 +47,9 @@ class HTTPClient

_pipeline = pipeline
_keepalive_timeout_secs = keepalive_timeout_secs
_handlermaker = handlermaker

fun ref apply(
request: Payload trn,
handlermaker: HandlerFactory val)
: Payload val ?
=>
fun ref apply(request: Payload trn) : Payload val ? =>
"""
Schedule a request on an HTTP session. If a new connection is created,
a new instance of the application's Receive Handler will be created
Expand All @@ -54,7 +58,7 @@ class HTTPClient
This is useful in Stream and Chunked transfer modes, so that the
application can follow up with calls to `Client.send_body`.
"""
let session = _get_session(request.url, handlermaker)?
let session = _get_session(request.url)?
let mode = request.transfer_mode
request.session = session
let valrequest: Payload val = consume request
Expand All @@ -80,11 +84,7 @@ class HTTPClient
end
*/

fun ref _get_session(
url: URL,
handlermaker: HandlerFactory val)
: _ClientConnection ?
=>
fun ref _get_session(url: URL) : _ClientConnection ? =>
"""
Gets or creates an HTTP Session for the given URL. If a new session
is created, a new Receive Handler instance is created too.
Expand All @@ -100,10 +100,10 @@ class HTTPClient
match url.scheme
| "http" =>
_ClientConnection(_auth, hs.host, hs.service,
None, _pipeline, _keepalive_timeout_secs, handlermaker)
None, _pipeline, _keepalive_timeout_secs, _handlermaker)
| "https" =>
_ClientConnection(_auth, hs.host, hs.service,
_sslctx, _pipeline, _keepalive_timeout_secs, handlermaker)
_sslctx, _pipeline, _keepalive_timeout_secs, _handlermaker)
else
error
end
Expand Down
4 changes: 2 additions & 2 deletions http/test_netssl_105_regression.pony
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ class \nodoc\ iso _NetSSL105RegressionTest is UnitTest
try
let url = URL.build("https://echo.sacovo.ch")?
let auth = TCPConnectAuth(h.env.root)
let client = HTTPClient(auth)
let client = HTTPClient(auth, _NetSSL105RegressionHandlerFactory(h))
let payload = Payload.request("GET", url)
client.apply(consume payload, _NetSSL105RegressionHandlerFactory(h))?
client.apply(consume payload)?
else
h.fail("Unable to setup test")
end
Loading