recovered webclient branch; needs a little clean up#2771
Conversation
jurgenvinju
commented
May 12, 2026
- added initial webclient API that mirrors the Webserver API and uses the same Response and Request encodings
- cleanup and added POST method
- added the other methods
- added progress bar
- fixed post
…he same Response and Request encodings
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2771 +/- ##
========================================
- Coverage 46% 46% -1%
+ Complexity 6718 6717 -1
========================================
Files 794 795 +1
Lines 65937 66210 +273
Branches 9889 9918 +29
========================================
- Hits 30760 30759 -1
- Misses 32797 33072 +275
+ Partials 2380 2379 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
DavyLandman
left a comment
There was a problem hiding this comment.
I think this is a nice idea but not ready yet, I've written down my concerns.
I think this needs tests.
|
|
||
| Thread writer = new Thread(() -> { | ||
| try (OutputStream os = out; Writer w = new OutputStreamWriter(out)) { | ||
| JsonWriter jsonWriter = new JsonWriter(w); |
There was a problem hiding this comment.
not all POST request have JSON bodies, we should think of a design that doesn't enforce json, but allows the user to specify the body format.
There was a problem hiding this comment.
Yes that's possible. The mirror design in response body's is that we have text with arbitrary mime types, files with mime types decided by file extension and JSON values as arbitrary serialized rascal values. That seems to be general enough.
| PipedOutputStream out = new PipedOutputStream(); | ||
| PipedInputStream in = new PipedInputStream(out, 64 * 1024); | ||
|
|
||
| Thread writer = new Thread(() -> { |
There was a problem hiding this comment.
I don't really like this pattern of starting a separate thread just to invert the stream direction. Looking at the BodyPublishers, it should be okayish to write a class that wraps a ByteBuffer around a buffer that's getting flushed.
Here is such an implementation:
public class OutputStreamBodySupplier extends BufferedOutputStream implements BodyPublisher {
private final List<Subscriber<? super ByteBuffer>> subscribers;
public OutputStreamBodySupplier() {
super(new PublishingStream());
this.subscribers = ((PublishingStream)super.out).subscribers;
}
/**
* The buffed outputstream will take care to collect the bytes untill there's a decent chunk to forward to the consumers
*/
private static class PublishingStream extends OutputStream {
private final List<Subscriber<? super ByteBuffer>> subscribers = new CopyOnWriteArrayList<>();
@Override
public void write(int b) throws IOException {
for (var sub: subscribers) {
sub.onNext(ByteBuffer.wrap(new byte[] { (byte)(b & 0xFF) }));
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
for (var sub: subscribers) {
sub.onNext(ByteBuffer.wrap(b, off, len).asReadOnlyBuffer());
}
}
@Override
public void close() throws IOException {
for (var sub: subscribers) {
sub.onComplete();
}
}
}
@Override
public void subscribe(Subscriber<? super ByteBuffer> subscriber) {
this.subscribers.add(subscriber);
}
@Override
public long contentLength() {
return -1;
}
}There was a problem hiding this comment.
Me too. Thanks for the code!
| PipedInputStream in = new PipedInputStream(out, 64 * 1024); | ||
|
|
||
| Thread writer = new Thread(() -> { | ||
| try (OutputStream os = out; Writer w = new OutputStreamWriter(out)) { |
There was a problem hiding this comment.
the encoding should be set for the writer, and the same encoding should also be set in the http headers.
nit: the writer will close os/out no need to add that to the try with resources.
| | unsupportedMediaType() | ||
| ; | ||
|
|
||
| data Request( |
There was a problem hiding this comment.
I'm missing the option to set headers in the request, quite some API's require this.
There was a problem hiding this comment.
That's provided in the Content module. Here you see only the things we need extra to make the client api work. The goal is to make Request and Response be shared as much as possible between server and client API.
There was a problem hiding this comment.
okay, but I'm also missing that mapping in the java side. that's why I remarked that here.
There was a problem hiding this comment.
ok cool. will fix that
|
|
||
| @synopsis{Short-hand for construction of JSON post bodies} | ||
| Request jsonPost(str path, &T content, loc host=|http://www.example.com|, BodyExpectation body = textBody()) | ||
| = post(path, &T (type[&T] _expected) { return content; }, host=host, body=body); |
There was a problem hiding this comment.
Shouldn't this also change the content type to application/json?
There was a problem hiding this comment.
Yes and also there are missing parameters for the headers, query, etc.
| .build(); | ||
| } | ||
|
|
||
| private HttpRequest makePutRequest(IConstructor input) { |
There was a problem hiding this comment.
That should be one of the header settings in the keyword field of this constructor. I'll check.
| }); | ||
|
|
||
| writer.start(); | ||
|
|
|
Great review @DavyLandman I'll try and improve this PR and let's have a look together later again. I've tested this client so far with a couple of web APIs and some very large test downloads. It seems very natural apart from the host/path/query separation. I think some shorthands where a full URL can be used which is split up internally would be nice. Also there is no support for fragments yet. |
… body in a POST and PUT ina Request, the same way as on the Response side. So support for text, json and file content. HTML is for later.
|


