Re: HttpClient API usage feedback/questions

2018-06-06 Thread James Roper
On Wed, 6 Jun 2018 at 21:03, Chris Hegarty  wrote:

>
> > On 4 Jun 2018, at 03:48, James Roper  wrote:
> >
> > ...
> >
> > +1 on variants of getContentType that give you either the media type or
> the charset parsed from the content type header. Without them, we're going
> to end up with a lot of regexps doing ad hoc parsing to get the information
> needed out of the Content-Type header. For example, if you want to know if
> the incoming request is application/json, text/plain, or application/xml,
> in order to parse that correctly, you'll need to do things like
> .matches("text/plain(?;.*)?"). Whereas, if you had a getMediaType() you
> could just do a .equals("text/plain").
>
> I agree that it can be a little cumbersome, but still possible, to get
> to the meat of specific header values like Content-Type. There is a
> mini-design space around this; should a single method be exposed to
> return the Charset, or a higher-level interface like ContentType that
> exposes the media-type and the list of parameters, exceptions/defaults,
> etc?
>

Completely agreed that this is an important discussion to have. Personally
I don't have a strong opinion, I've had experience maintaining both styles
of API and for me there's no clear winner in which approach is best. Here's
a few additional considerations:

* Scope is always an issue with these types of features. Why stop at just
content type? Why not also add high level modelling around accept headers?
And so on... I think we need to carefully decide what the criteria is for
making a header a first class concept in the API. Some questions to ask
there (which I'm guessing you already have a good understanding of, but I
don't) is how much we expect this API to be used directly by end users, and
how much we expect it to be used underneath a higher level API, either as a
third party open source library, or perhaps we expect users to create their
own use case specific abstractions on top? The lower level it will sit (on
average), the less demand for higher level concepts in the API.
* If we model it using a higher level interface like ContentType, and maybe
we decide to model a few other headers using a higher level interface too,
then why not model all headers using a higher level interface, and use some
sort of strongly typed map API to get the header values? For example:

interface HeaderKey {
  String getHeaderName();
  V parse(String value);
}

class ContentType {
  ...
}

class ContentTypeKey implements HeaderKey {
  String getHeaderName() {
return "Content-Type";
  }
  ContentType parse(String value) {
// ...
  }
}

class HttpHeaders {
   V getHeader(HeaderKey key) {
String value = getHeader(key.getHeaderName());
if (value != null) {
  return key.parse(value);
} else {
  return null;
}
  }
  ...
}

The above uses a parse on the fly approach (which would result in multiple
parses on each access), other approaches might involve registering header
keys when creating the client (and this can be taken advantage of to do
caching of parsed values, particularly with an HTTP2 underlying impl) etc.
Anyway, the above obviously means introducing a lot more API, and wouldn't
be worth it if the only thing we're interested in adding a convenience for
is the content type, but may be more useful if we want to add convenience
methods for more headers, since it means other libraries can provide their
own convenience methods using the same mechanism for parsing and accessing
headers.

I filed the following enhancement request to track this:
>   https://bugs.openjdk.java.net/browse/JDK-8204470
>
> Since this request is more of a convenience API, and is additive, it may
> not happen in time for 11. There are a number of other higher priority
> issues that need to be resolved before JDK 11 RDP 1 [1] . Some of these
> have minor impact on the existing API, and as such need to be resolved
> in 11.
>
> -Chris.
>
> [1] http://openjdk.java.net/projects/jdk/11/
>
>
>
>

-- 
*James Roper*
*Senior Developer, Office of the CTO*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: HttpClient API usage feedback/questions

2018-06-03 Thread James Roper
On Sat, 2 Jun 2018 at 16:52, Jaikiran Pai  wrote:

> 2. Would it be a good idea to add APIs (perhaps in HttpHeaders class) to
> return values of higher level constructs represented by the headers.
> Imagine certain well-known headers in the response, like Content-Length
> and Content-Type. Would it be a good idea to expose higher level APIs of
> the form getContentLength(), getContentCharset() and such? It probably
> doesn't add much value to Content-Length header but there's a particular
> mechanism the charset is supposed to be parsed out of the "Content-Type"
> header and having it done with the API's implementation would prevent
> all the parsing/logic in every other application.
>

-1 on getContentLength, it's a detail of handling the message, it often
won't be there (eg chunked encoding in HTTP 1.1 or regular content length
less in HTTP 2), and so it should only be relevant to the BodyHandler (or
whatever it's called now, I forget which APIs have been renamed to what). I
don't think that's an important enough use case, nor do I think it adds
enough value, to warrant support in the API.

+1 on variants of getContentType that give you either the media type or the
charset parsed from the content type header. Without them, we're going to
end up with a lot of regexps doing ad hoc parsing to get the information
needed out of the Content-Type header. For example, if you want to know if
the incoming request is application/json, text/plain, or application/xml,
in order to parse that correctly, you'll need to do things like
.matches("text/plain(?;.*)?"). Whereas, if you had a getMediaType() you
could just do a .equals("text/plain").

-- 
*James Roper*
*Senior Developer, Office of the CTO*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: OT? reactive streams

2018-05-16 Thread James Roper
Hi Simon,

This is the right place to talk about the new HTTP client.

I wrote a blog post which has some hypothetical examples of integrating the
HTTP client with other APIs (such as the servlet API) and their
hypothetical future adoption of Reactive Streams:

https://developer.lightbend.com/blog/2018-02-06-reactive-streams-ee4j/index.html

Not sure if that will be useful to you or not.

You're right that there are no pre built processors for doing
map/flatmap/filter. This is something that I (and my company, Lightbend) am
looking to address, independent of the HTTP client. The current way to do
these transformations is using a third party Reactive Streams library, such
as Akka Streams, RxJava or Reactor. In MicroProfile, we are creating a
generic library for doing these transformations, and after some incubation
in there, we are hoping that the JDK maintainers will pull this library in
the JDK (we've seen some support from JDK maintainers for this path in
private channels). Here are some resources relevant to this effort
(everything is under active development, so things are moving fairly
quickly at the moment):

https://github.com/eclipse/microprofile-reactive/pull/3/files
https://github.com/eclipse/microprofile-reactive/tree/master/streams
http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-March/051799.html

Cheers,

James

On Thu, 17 May 2018 at 07:32, Simon Roberts <si...@dancingcloudservices.com>
wrote:

> Hi, apologies for the rather off topic inquiry, but I'm trying to find out
> more about the reactive streams features that are used inside the new
> httpClient. I tried to subscribe to what I think is the appropriate list
> but appeared to hit a "bug in mailman" (three times, so not, apparently,
> transient).
>
> I'm hoping to discover if there are samples, examples, whatever for the
> streams features (I was successful in implementing a crude BodyHandler /
> BodySubscriber, so the basics make sense.
>
> In particular, this feels like there might be Processors that perform
> map/flatmap/filter operations, prebuilt for ease of use
>
> Also, it feels like there might be examples or tools imteracting with GUI
> comonents, or perhaps performing infinite series math, like sieve of
> Erastothanes (sp!?) or the Pi approximation series.
>
> Can anyone tell me if they know of any such resources for
> learning/tinkering?
>
> Thanks!
>
> --
> Simon Roberts
> (303) 249 3613
>
>

-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: java.net.Socket should report the attempted address and port

2018-04-22 Thread James Roper
This would be especially useful in asynchronous applications - since in
those cases the exception rarely maps back to a place in user code that
would indicate what is being connected to. As someone who has spent a lot
of time supporting developers who use asynchronous libraries and post
exceptions of this nature (supporting both in open source, eg on stack
overflow, as well as providing commercial support), where I don't have
access to their code base so I can't do the necessary investigations
directly myself, having the attempted address and port in the error message
would save a lot of time, and probably even prevent a lot of people from
requiring support in the first place.

On 22 April 2018 at 20:59, Péter Gergely Horváth <
peter.gergely.horv...@gmail.com> wrote:

> Hi All,
>
> I am wondering if it would be possible to make a minor improvement to the
> way *java.net.Socket* reports connectivity errors and incorporate the
> attempted address, port and the timeout used into the exception message.
>
> The current implementation emits a generic error message, which is not
> that helpful when one is operating a _large_ application. (Consider e.g.
> Big Data or complex legacy systems written by someone else).
>
> java.net.ConnectException: Connection refused (Connection refused)
> at java.net.PlainSocketImpl.socketConnect(Native Method)
> at java.net.AbstractPlainSocketImpl.doConnect(
> AbstractPlainSocketImpl.java:350)
> at java.net.AbstractPlainSocketImpl.connectToAddress(
> AbstractPlainSocketImpl.java:206)
> at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:
> 188)
> at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
> at java.net.Socket.connect(Socket.java:589)
> at java.net.Socket.connect(Socket.java:538)
> at java.net.Socket.(Socket.java:434)
> at java.net.Socket.(Socket.java:211)
> at Sample.main(Sample.java:9)
>
>
> I have looked into the JDK code base and implemented a patch that reports
> the address, port and timeout used in the error message without touching
> any native parts (see attached patch file). I have tested this (created my
> own build of the JDK and run a sample application against it) and it seems
> to work properly.
>
> Would it be possible to incorporate this change into the official JDK code
> base? I do believe it would help a lot of people out there.
>
> Based on my understanding, once I have signed the OCA, I should simply
> write an email to the group and request a sponsor to pick up this issue.
> Could someone help me with this?
>
> Thank you,
> Peter
>
>
>
>
>
>
>
>


-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: RFR [11] 8197564: HTTP Client implementation - JEP 321

2018-04-02 Thread James Roper
parameter, which would be better for future maintenance in case other
> information needs to be provided.
> The simple case that comes to mind is the HTTP version of the
> response, which may be different from that of the request and
> indicates server capabilities: this is currently not provided in the
> BodyHandler.apply() signature.
>
> So perhaps:
>
> public Publisher apply(HttpResponse response, Publisher
> responseContent);
>
> This change would also simplify the maintenance since BodySubscriber
> will be removed.
>
> My concern is that driving users of the JDK APIs outside of the
> familiar RS APIs offered by Reactor and RxJava2 where only Publishers
> are relevant, by forcing them to implement Subscribers and Processors
> (heck, I even have an IntelliJ warning telling me that I should not do
> that!), will cause confusion and be potential source of bugs - we all
> know non-blocking/async programming is hard.
>
> Thanks !
>
> --
> Simone Bordet
> ---
> Finally, no matter how good the architecture and design are,
> to deliver bug-free software with optimal performance and reliability,
> the implementation technique must be flawless.   Victoria Livschitz
>



-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: RFR [11] 8197564: HTTP Client implementation - JEP 321

2018-03-28 Thread James Roper
 the rate
produced by the remote end, potentially buffering and running out of
memory, or having to fail.

So, as you can see, WebSockets don't need an explicit backpressure
mechanism in the protocol, they can piggy back off TCP. And Reactive
Streams can use this to ensure a consumer of a WebSocket stream on one end
can push back on the producer at the other end, and have that push back
propagated all the way through, albeit with a certain amount of buffering
delaying the push back from happening immediately.


> Thanks.
>
>
>> > with the understanding that such a helper will limit the capabilities
>>>> and performance when its used - eg, the range of options for closing, range
>>>> of options for how errors are handled, and perhaps introduce some small
>>>> performance penalties such as additional allocations and/or buffer copies.
>>>>
>>>> That is a whole set of comprises and decisions that would need to
>>>> be considered.
>>>>
>>>> > The use case here is how well this API will interact with other APIs.
>>>> WebSockets are rarely used in isolation from other technologies. Most non
>>>> trivial usages of WebSockets will involve plumbing the WebSocket API to
>>>> some other source or sink of streaming data - for example, a message
>>>> broker, a database, perhaps a gRPC stream to another service.
>>>>
>>>> I could well image a set of vertical-market specific libraries being
>>>> built
>>>> on top of the WebSocket API, and that is how it should be. In this
>>>> specific area, providing the lowest level API is an enabler for others.
>>>>
>>>> > The question is, how difficult will it be to connect those two APIs?
>>>>
>>>> I imagine some work may be required, but certainly much less than
>>>> without the WebSocket API.
>>>>
>>>> > Will their backpressure mechanisms map to each other easily?
>>>>
>>>> I see no reason that it should not. Are you aware of any?
>>>>
>>>> >  Will their failure handling and error handling semantics map to each
>>>> other easily?
>>>>
>>>> I see no reason that they should not. In fact, most higher-level APIs
>>>> provide more coarse grain error handling.
>>>>
>>>> > How many lines of boilerplate will it take to hook them up?
>>>>
>>>> I guess that depends on the particular library's functionality, will it
>>>> support/handle/expose partial message delivery, buffering, etc. I
>>>> don’t see this as boilerplate though.
>>>>
>>>> > If the WebSocket provides an option to use Reactive Streams, then for
>>>> any other sources/sink of streamed data that support Reactive Streams,
>>>> application developers will have an option where the answers are guaranteed
>>>> to be yes without the developer having to do any mapping themselves, and
>>>> the lines of code to do that will be one.
>>>>
>>>> Sure, if one has a higher-level library that works with Reactive
>>>> Streams,
>>>> and one wants to use WebSocket as a transport, then it would be little
>>>> work if Java SE provided a Reactive Streams interface to WebSocket.
>>>> There are also vertical-markets using WebSocket, but not using Reactive
>>>> Streams.
>>>>
>>>> At this point in JEP 321, I do not want to increase the scope of the
>>>> project
>>>> to include a Reactive Streams interface for WebSocket. This is something
>>>> that can, and should, be considered separate to JEP 321. It could follow
>>>> in a future release if deemed appropriate, or not, but adding it at
>>>> this point
>>>> will add risk to JEP 321, which I am not willing to do.
>>>>
>>>> -Chris.
>>>>
>>>>
>>>
>>
>>
>> --
>> *James Roper*
>> *Senior Octonaut*
>>
>> Lightbend <https://www.lightbend.com/> – Build reactive apps!
>> Twitter: @jroper <https://twitter.com/jroper>
>>
>
>


-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: RFR [11] 8197564: HTTP Client implementation - JEP 321

2018-03-21 Thread James Roper
Hi Chris,

I'm still really keen to see an option provided out of the box for using
Reactive Streams with the WebSocket API. Note that I'm not proposing that
anything be changed about the existing API, I understand fully that you
want to provide a full powered API that offers the full range of WebSocket
features with very high performance, and mapping that to the Reactive
Streams API goes against that. Rather, I think that there should be a built
in helper that allows Reactive Streams to be added on top, with the
understanding that such a helper will limit the capabilities and
performance when its used - eg, the range of options for closing, range of
options for how errors are handled, and perhaps introduce some small
performance penalties such as additional allocations and/or buffer copies.

The use case here is how well this API will interact with other APIs.
WebSockets are rarely used in isolation from other technologies. Most non
trivial usages of WebSockets will involve plumbing the WebSocket API to
some other source or sink of streaming data - for example, a message
broker, a database, perhaps a gRPC stream to another service. The question
is, how difficult will it be to connect those two APIs? Will their
backpressure mechanisms map to each other easily? Will their failure
handling and error handling semantics map to each other easily? How many
lines of boilerplate will it take to hook them up? If the WebSocket
provides an option to use Reactive Streams, then for any other sources/sink
of streamed data that support Reactive Streams, application developers will
have an option where the answers are guaranteed to be yes without the
developer having to do any mapping themselves, and the lines of code to do
that will be one.

Regards,

James

On 22 March 2018 at 07:33, Chris Hegarty <chris.hega...@oracle.com> wrote:

> Hi,
>
> This is a request for review for the HTTP Client implementation - JEP 321.
>
> The javadoc link has been refreshed, based on feedback received from
> the CSR [1] ( and a follow on supplementary CSR [2] ):
> http://cr.openjdk.java.net/~chegar/httpclient/02/javadoc/
> api/java.net.http/java/net/http/package-summary.html
>
> The webrev link has been refreshed to include implementation changes
> to support the latest API, additional test code coverage and scenarios:
> http://cr.openjdk.java.net/~chegar/httpclient/02/webrev/
>
> > On 9 Feb 2018, at 14:33, Chris Hegarty <chris.hega...@oracle.com> wrote:
> > Development of the JDK HTTP Client, incubated in JDK 9 and re-incubated
> > in JDK 10, has continued. It will soon be in a position to be proposed
> > for inclusion in Java 11, through JEP 321. This will proceed, as usual,
> > through the JEP 2.0 process.
> >
> > JEP 321 has recently been updated with the proposed package and module
> > name, `java.net.http`. The code in the sandbox has been updated to
> > reflect this.
> >
> > Javadoc based on the latest code in the sandbox:
> > http://cr.openjdk.java.net/~chegar/httpclient/00/javadoc/
> api/java.net.http-summary.html
> >
> > Webrev based on the latest code in the sandbox:
> >  http://cr.openjdk.java.net/~chegar/httpclient/00/webrev/
> >
> > ( much of the changes in the webrev are mechanical due to renaming, but
> >  there are stability fixes and the API documentation has been
> >  reorganized to improve readability )
> >
> > More information about the JDK HTTP Client can be found on the
> > networking groups page.
> >  http://openjdk.java.net/groups/net/httpclient/
>
>
> -Chris.
>
> [1] https://bugs.openjdk.java.net/browse/JDK-8197565
> [2] https://bugs.openjdk.java.net/browse/JDK-8199938




-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: RFE: Add PATCH to the list of the supported HTTP methods

2018-03-06 Thread James Roper
I don't have any opinion on the matter, but a datapoint to the popularity
of PATCH is its use in the GitHub API:

https://developer.github.com/v3/

GitHub is often looked to as a gold standard for how to write a REST API,
so if they do it, it's likely that many others do too.

On 7 March 2018 at 05:40, Chris Hegarty <chris.hega...@oracle.com> wrote:

> Hi Andrej,
>
> On 02/03/18 08:28, Andrej Golovnin wrote:
>
>> Hi all,
>>
>> it would be nice if could add PATCH
>> (https://tools.ietf.org/html/rfc5789) to the list of the supported
>> ...
>> - and one for the HTTP Client branch in the JDK sandbox repository
>> which adds PATCH to the HttpRequest builder:
>>java_net_http_HttpRequest_PATCH_method.diff
>>
>
> The JDK HTTP Client has:
>   `HttpRequest.Builder::method(String method, BodyPublisher publisher)`
>
>  ,so it is currently possible to use the `PATCH` method.
>
> Is `PATCH` sufficiently popular to warrant its own self-named
> method in the request builder?
>
> -Chris.
>



-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: websockets

2018-03-04 Thread James Roper
Hi Chuck,

I'm definitely not saying that the Java client is for browsers. I'm saying
that the WebSocket protocol was designed to be used by the API provided by
web browsers. No protocol is ever implemented on an island, it is
implemented with an intended use case. To best understand a protocol, to
understand why it is the way it is, to understand what would be a good way
to use it, and what might be a bad way to use it, and therefore to
understand what an API for it should look like, it is good to understand
what use case the protocol was designed for. And that use case was to be
used from browsers. So when you create a WebSocket client, or server, in
any language, it is very valuable to look at the browser provided APIs to
understand how the protocol was intended to be used, and therefore what
features you should expose to users.

To put in perspective, most server implementations of WebSockets are
designed to be connected to from browsers, non browser clients are still a
relatively niche use case for WebSockets. And so servers are going to look
to the browser for what features that should give priority to. If a browser
doesn't support X, then it's likely that a server implemention either won't
support X, or won't provide very good or easy to use support for X.
Consequently, it's of dubious value to provide a new client where support
for X complicates the API, since servers aren't going to support that well
anyway, it's providing a feature that you won't be able to use at the cost
of making other features harder to use.

Regards,

James

On 4 March 2018 at 05:49, Chuck Davis <cjgun...@gmail.com> wrote:

> James, this does not make any sense to me.  Last I heard browsers no
> longer run java.  Why would JSE give a hang about designing a
> communications mechanism for browsers on the client side?  That is the job
> of the browser developers.  On  the other hand, JSE WebSocket is the best
> thing to come along for java clients since RMI which was released I think
> in 1.1.  Java WebSocket is NOT for browsersit's for Java clients
>   And I'm quite pleased with the progress I've made refactoring to use
> the jdk9 WebSocket api together with wildfly11 on the server (and still
> blazingly FAST -- seems to be snappier than jdk8 implementation).  Now if
> the IDEs would get in gear to fully implement jdk9 I'd be in fat city.
>
> And my thanks to everyone who has participated in this particular
> conversation on this list and all the expertise that has been shared.
>
> >Yes, there's no formal definition of high/low level, but in the case of
> WebSockets, we can draw some lines based roughly on what web browsers
> (which is the target application developer experience that >WebSockets were
> designed for) support as being high level, and what the WebSocket protocol
> allows beyond this as low level.
>
>>
>> Regards,
>>
>> James
>>
>>
>


-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: websockets

2018-03-01 Thread James Roper
Hi Simone,

Sorry, I think there's been some missed information here. I am in no way
suggesting that the API as provided currently should be changed or
replaced. What I'm suggesting is that a high level adapter be provided out
of the box that provides Reactive Streams support on top of this API. Yes,
there's no formal definition of high/low level, but in the case of
WebSockets, we can draw some lines based roughly on what web browsers
(which is the target application developer experience that WebSockets were
designed for) support as being high level, and what the WebSocket protocol
allows beyond this as low level. I think a Reactive Streams adapter on top
of the existing low level API that has feature parity with the JavaScript
API for WebSockets offered by browsers is a worthwhile thing to aim for.
Would you agree with that?

So, for example, the browser APIs offer one error callback for handling
errors from both directions. All WebSocket messages handled by the browser
APIs are discrete, fixed length in memory, any chunking is handled
transparently underneath. Developers in the browser don't have to
explicitly send or implement any sending or receiving of the close
messages, they just handle the high level concept of the socket being
closed, and the browser implements the handshake underneath transparently.

I'd say WebSocket proxies would be completely out of scope for a high level
API, they definitely would want to deal with splitting messages, custom
close handling, very precise error semantics etc, and such a use case would
be well served by the current low level API.

Regards,

James

On 26 February 2018 at 22:15, Simone Bordet <simone.bor...@gmail.com> wrote:

> James,
>
> On Mon, Feb 26, 2018 at 4:37 AM, James Roper <ja...@lightbend.com> wrote:
> > On the topic of error handling. A high level API doesn't need to report
> each
> > individual error with sending.
>
> Depends on what you mean by "high level APIs"... I guess there is no
> formal definition of that, as one can even think that the Java socket
> APIs are high level.
>
> > So firstly, it is impossible to report *all*
> > errors with sending, since it's impossible to know, once you send a
> message
> > across the network, whether it got there or not. So if an application
> > developer has a requirement to handle errors in sending (and
> realistically,
> > there is never a business require to handle just the errors that can be
> > detected in sending, it's always about handling all errors), do we expect
> > them to implement that logic in two places, both on the sending side to
> > handle errors that can be detected on the sending side, and then
> > additionally write logic to handle errors that can't be detected on the
> > sending side (such as, for example, having the remote side send ACKs, and
> > track the ACKs on the receiving end)? I doubt it, having to handle logic
> on
> > both sides is going to mean the application developer has to spend more
> time
> > implementing boiler plate that could otherwise be spent solving their
> actual
> > business problem.
>
> I think you are oversimplifying this.
>
> If my business is to write a WebSocket Proxy, I may totally want to
> care about writing code that handles errors differently whether they
> happened on the read side or on the write side, if just for logging
> reasons.
>

> If my business is to write a protocol on top of WebSocket, I may want
> to use custom WebSocket error codes (depending on errors) when I close
> the connection.
> I may want to send a WS message the moment I receive a WebSocket
> "close" event from a peer.
>
> Sure, maybe in 80% of the cases read and write errors are handled the
> same way, but as an application writer I would like to have the choice
> between using an API that is "lower" level and allows me full
> flexibility with respect to the WebSocket protocol, and an opinionated
> (correctly opinionated in the majority of the cases) framework that
> for some case reduces the API surface at the cost of something else
> like flexibility, increased allocation, ecc.
> Both have their use cases.
>
> I believe the JDK net group always stated they went for the first choice.
> The lack of the second choice is under discussion, and as I have
> already stated may be a matter of resources.
>
> > If an application developer needs to handle errors in
> > sending messages, it would be much simpler for them to treat both types
> of
> > errors in the same way. And hence, a high level API should expose
> detectable
> > errors in sending on the receiving end, by cancelling upstream,
> terminating
> > the WebSocket with an error, and then emitting the error in the
> downstream

Re: websockets

2018-02-25 Thread James Roper
the API exposes a "read" Publisher, a way to send
> with a "write" Publisher and some Handler/Listener that returns
> Publisher (or CompletionStage) for when handling is complete. The
> "read" Publisher would complete when the server closes. The Publisher
> from the Handler/Listener would complete when all is handled.
>
>
>
>> Sure one may recycle buffers transparently. But only if the
>> buffer type supports it. io.netty.buffer.PooledByteBufAllocator provides
>> instances of io.netty.buffer.ByteBuf. Not java.nio.ByteBuffer. The latter
>> one
>> does not have any release/free/dispose/etc. methods and semantics.
>>
>
> ​If the API exposed some Consumer callback to be applied to
> buffers that have been written, then such a pool could also be implemented
> externally.
> ​​
>
>> P.S. How may purely RS WebSocket APIs are there? If there are some, we
>> can probably
>> learn from their experience and make things a bit better. Otherwise I
>> don't see
>> why we should create one more of them. Would you rather have something
>> low-level, you could then implement your high-level API that is most
>> relevant to
>> your case?
>>
>
> You can see the reactive WebSocket API facade (abstracting different
> clients) in the Spring Framework:
> https://docs.spring.io/spring/docs/current/javadoc-api/org/
> springframework/web/reactive/socket/package-summary.html
>
>
> This is an example of just one way that WebSocket could be modelled.
> It appears reasonably straight forward, but restrictive in terms of the
> underlying protocol, not exposing partial messages for example,
> requiring an additional allocation of a message type for each message
> sent / received. I would expect that such a higher-level API could be
> implemented on top of the Java SE API without too much work.
>
> -Chris.
>
> [1] https://github.com/spring-projects/spring-framework/
> blob/master/spring-webflux/src/main/java/org/springframework/web/reactive/
> socket/WebSocketMessage.java
>
>


-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: websockets

2018-02-19 Thread James Roper
If it's a question of resources for building a separate Flow based API on
top of the existing API provided, then perhaps we could help? I could
certainly implement it outside the JDK in the coming months, would the HTTP
client team be willing to consider folding it in as a utility alongside the
WebSocket API in the JDK? I know that's not a zero resource thing, but
assuming I can produce something reasonable, reviewing it should be cheaper
than implementing it. Also I don't know if there's any work being done on a
TCK for this, and whether inclusion of this in the JDK would require a TCK
to verify it.

On 20 February 2018 at 08:41, Simone Bordet <simone.bor...@gmail.com> wrote:

> James (hi! :)
>
> On Fri, Feb 16, 2018 at 12:55 AM, James Roper <ja...@lightbend.com> wrote:
> > The question for me then is whether there is value in introducing an
> > additional higher level API (ie, a reactive streams based API) for the
> > purposes of integration with other libraries that provide RS support.
>
> I have mixed feeling about this.
>
> I first tried to push the WebSocket APIs to be Flow-based when they
> were slated for JDK 9 official inclusion.
> As the WebSocket APIs were moved to incubation and are now slated for
> JDK 11, it may be worth considering again whether it's the case to
> provide a Flow-based APIs.
>
> However, Pavel's point about the limits of the Flow APIs still stand,
> so a Flow API may be useful, but less generic.
>
> Another point that makes me feeling uncertain is the good comment by
> Konrad Malawski about the design of Akka Streams versus e.g. Spring's
> Flowable, where the former does not implement RS or Flow interfaces,
> while the latter does.
> Now that we have the dichotomy of RS and JDK's Flow, it is easy for
> Akka Stream implement methods such as asRSPublisher() and
> asFlowPublisher() so that they work in both environments (with - yuck
> - multi-release jars).
> It would be harder for Spring's Flowable to provide support for both
> RS and Flow.
>
> So at the end I would like to see a *separate* Flow API on top of the
> current WebSocket API, but who's going to implement it ?
> Would be great to standardize a community effort, but even better if
> the JDK provided it.
>
> > Introducing another API, regardless of what that API is, obviously has
> > drawbacks as it increases the surface area of the API, and thus
> complexity,
> > and also potentially introduces confusion for application developers as
> they
> > now have two different ways of achieving things, and they need to know
> which
> > to use when. So, the value of that API must be high enough to outweigh
> those
> > drawbacks. I strongly believe that a Reactive Streams based API does add
> > enough value to do that - and the post that I shared in my previous email
> > demonstrates how an ecosystem where reactive streams is embraced can
> provide
> > a lot of value to application developers. So I'm happy to talk more here
> > about use cases and code samples etc if you don't agree here.
>
> I personally don't question the value of a RS or Flow API.
> It's just a matter of resources - we can ask Oracle to do it, but if
> they don't have resources to do it, then the road ahead is
> complicated.
> I see better a Flow wrapper of the JDK WebSocket APIs under the
> Reactive Extension (https://github.com/Reactive-Extensions) or
> Reactive Streams (https://github.com/Reactive-Streams) umbrella.
> That would perhaps have less resource issues and still give a unique
> library rather than a gazillion similar wrappers.
>
> > For signalling errors upstream, in all the cases that I've worked with,
> > we've never found a reason that sending the error downstream, and just
> > cancelling upstream, is a problem. At the end of the day, there is only
> one
> > thing that can fail, the TCP connection, and if it fails, both upstream
> and
> > downstream will fail - there is no such thing as a half failure in TCP.
>
> HttpServlet.service() allows to throw exceptions that the Servlet
> container must handle by running the error page algorithm, which is
> user code.
> A Subscriber (provided by a library) encounters an exception while
> performing its job.
> Since it comes from a library, the library itself does not know how to
> handle the error because it does not know in what environment it is
> being used.
> The common solution I saw in RS libraries for this is to provide
> library specific APIs that registers a callback that is invoked in
> case of errors.
> Another common solution is to have an inverse RS that relays errors in
> the opposite direction.
> With this library specific APIs, an app running in a Servlet Container
> can tri

Re: WebSocket

2018-02-18 Thread James Roper
Hi Chuck,

Let's just imagine that for a moment that there existed a reactive streams
equivalent to an ObjectReader (there isn't, and there's good reason why
there isn't, but I'll get to that later). Then, the code would be something
like this (the Source API there is an Akka Streams like API):

HttpRequest.create(URI.create("..."))
  .GET()
  .response((status, headers) ->
  BodySubscriber.from(
Source.asPublisher()
  .via(ReactiveStreamsObjectReader.create())
  .forEach(object -> {
 // do what you want with each object as its passed here
  })
  )
  );

Now that actually looks like less, and simpler, code than your
implementation - the above is the full code for the example, you only
included code for handling the body. You can also do a lot more than that,
you declaratively define how you process your objects, create complex
graphs feeding them to other locations, etc. I'd suggest you read the docs
on Reactive Streams implementations, because one of the goals of the JDK9
client is to be asynchronous, and being asynchronous means you do have to
turn your processing on its heaḋ, you don't pull (ie, you don't invoke
readObject and get something back), rather, you are pushed things, so you
define stages in your processing, and provide callbacks when you want to do
custom things. Here's the docs for Akka streams to get acquainted with an
asynchronous streaming view of the world:

https://doc.akka.io/docs/akka/2.5/stream/index.html

Now, as I said, no reactive streams implementation (that I'm aware of)
provides Java serialization support, and for good reason. It's 2018, Java
serialization has been shown, time and time again, to have major security
flaws in it. If you make an HTTP request on another application, and parse
what it gives back to you using ObjectInputStream, you are opening a quite
trivial way for that application to execute code on your application. Even
if you trust the remote system, it goes against the security best practice
of bulkheading - ensuring that if one application is compromised, the
entire system isn't compromised. No one wants to provide support these days
for such insecure practices.

Here's a good summary of why you should never use Java serialization:

https://www.christian-schneider.net/JavaDeserializationSecurityFAQ.html

Regards,

James

On 18 February 2018 at 20:12, Chuck Davis <cjgun...@gmail.com> wrote:

> There is a great hush here regarding strategy for putting the pieces
> back together so here goes my tirade.
>
> Following is based on assumption of a 100k payload.
>
> With jdk8:
> The decoder received a ByteBuffer
> bais = new ByteArrayInputStream(ByteBuffer.array());  // wrap the
> buffer -- optional method but implemented in Oracle JVM
> ois = new ObjectInputStream(bais)  // wrap the input stream
> MyDTO = ois.readObject();   // deserialize the object (which may
> contain hundreds or thousands of  objects)
>
> DONE!  Simple, clean and blazingly fast.
>
> With jdk9:
> The Listener receives a ByteBuffer (I read someplace the default size
> is potentially going to be 256 bytes)
> If it's the first part (or whole) create a byte[] of 10k (or whatever)
> Start copying bytes from the buffer to the array
> Check each byte to be sure there is room in the array for another byte
> if room, copy the byte
> if no room, copy the array to a larger array (current size + 10k)
> and then copy the byte
> repeat until all messages are processed (i.e. each byte will have
> been copied somewhere between 1 and 10 times)
> bais = new ByteArrayInputStrea(byte[]);
> ois = new ObjectInputStream(bais);
> MyDTO = ois.readObject();
>
> DONE!  More complicated, messy and very slow.  (i.e. lots of wasted cpu
> cycles)
>
> RESULT:  The formerly fabulous WebSocket has been rendered relatively
> useless as a platform for building responsive, bidirectional
> client/server applications.  Am I the only person who sees this as a
> HUGE regression of functionality??  I am ALARMED by this turn of
> events.
>
> OPINION:
>
> I'm pretty naive about the world of midrange and mainframe except that
> they can throw a lot of bytes in a short time.  But I imagine the
> choke-point of any desktop application is going to be the network.
> Unless somebody is running a Pentium I doubt any relatively modern
> desktop is going to have a difficult time keeping up with networking
> speeds.  It seems to me, therefore, that backpressure in WebSocket is
> a solution looking for a problem.  If backpressure is somehow
> essential in WebSocket include the possibility but please don't
> destroy the best client/server communication in the process.  If
> necessary, create two implementations:  WebSocketFast and
> WebSocketSlow.
>
> I'll go back to my cave now and pout about what has

Re: websockets

2018-02-15 Thread James Roper
. If one side decides to restart, the otherside must
restart too. They are already coupled, and cannot be decoupled. Introducing
coupling in the error handling simplifies the handling, because it exposes
what is already true.

The creation of additional objects for representing message types is of
course an overhead, but one that users of the high level API would be happy
to have given the advantages of not having to implement their own
integration code. The JVM is highly optimised to handle the creation and
garbage collection of short lived objects, and this allows high level APIs
like Reactive Streams to exploit this, allowing simpler programming models
without having to forgo a huge amount of performance. Of course, that
doesn't mean we needlessly create objects for no reason, but in this case I
believe the benefits of being able to seamlessly integrate with other
streaming libraries outweigh the overhead of an allocation or two per
message. Having one stream per message type would cause problems - for
example, the socket.io protocol encodes binary messages by first sending a
text message containing metadata, which is then followed by one or more
binary messages. Sending this in multiple different streams would make
correlation of those binary messages with the text header very difficult if
not impossible.

Regards,

James



On 12 February 2018 at 16:33, Pavel Rappo <pavel.ra...@oracle.com> wrote:

> Hello James,
>
> Thanks for the comprehensive reply to Chuck's email. Now regarding your
> own email.
>
> > On 10 Feb 2018, at 07:38, James Roper <ja...@lightbend.com> wrote:
> >
> > 
> >
> > But that brings me to a problem that I'd like to give as feedback to the
> implementers - this API is not Reactive Streams, and so therefore can't
> take advantage of Reactive Streams implementations, and more problematic,
> can't interop with other Reactive Streams sinks/sources. If I wanted to
> stream a WebSocket into a message broker that supports Reactive Streams, I
> can't. I would definitely hope that Reactive Streams support could be added
> to this API, at a minimum as a wrapper, so that application developers can
> easily focus on their business problems, plumbing and transforming messages
> from one place to another, rather than having to deal with implementing
> concurrent code to pass messages.
> >
>
> I totally understand your concern over the lack of a Reactive Streams
> interface
> to this low-level API. All I can say is that it's not that we haven't
> tried. As
> usual the devil is in the detail. There were at least a couple of major
> issues
> we couldn't find a satisfactory solution to.
>
> The first one is how to communicate errors back to the user's code that
> publishes messages to WebSocket. This issue has been extensively discussed
> here [1].
> The only signal the publisher can receive from a subscriber in the case of
> a
> failure is a cancellation signal. After this signal the subscription is
> considered cancelled. Now, there are different solutions to this problem,
> but
> none of the ones we looked at seemed comprehensive. For example, such
> errors
> can be propagated by the WebSocket to the user's subscriber receiving
> messages.
> In which case WebSocket input and output will become tied and the WebSocket
> client will seem to require both the publisher and the subscriber to be
> present
> at the same time.
>
> The second issue is how to communicate completion signals from processing
> individual messages. That might be handy in the case the implementation or
> the
> application decide to recycle buffers they use. With a naive RS interface
> to
> WebSocket all the user's publisher can receive from the WebSocket's
> subscriber
> is a request for a number of extra messages. Using only this number the
> publisher cannot deduce which of the previously published messages have
> been
> actually sent. This problem also has a number of solutions. One of which
> would
> be to use more streams. An extra publisher WebSocket would use to emit
> outcomes
> of sending messages and an extra subscriber WebSocket would use to receive
> signals from the user once a message has been received. To be honest it
> looks
> cumbersome.
>
> There are also a dozen of smaller issues that have to be resolved before
> creating a well-defined RS interface to WebSocket.
>
> > It may well require wrapping messages in a high level object - text,
> binary, ping, pong, etc, to differentiate between the message types.
> >
>
> We went great lengths in supporting different kinds of requirements in
> this API.
> One of the such requirements that arose from a number of discussions on
> this
> mailing list was not to force unnecessary allocations, garbage 

Re: websockets

2018-02-13 Thread James Roper
On 12 February 2018 at 18:56, Chris Hegarty <chris.hega...@oracle.com>
wrote:

> James,
>
> On 10/02/18 07:38, James Roper wrote:
>
>> ...
>>
>> https://developer.lightbend.com/blog/2018-02-06-reactive-str
>> eams-ee4j/index.html
>>
>
> Regarding: https://github.com/jroper/reactive-streams-servlet/blob/7a2a
> 651b706bb0612f6d11311e442f82ce307ed2/reactive-streams-servle
> t/src/main/java/org/reactivestreams/servlet/RequestPublisher.java
>
> If I'm not mistaken, this appears to be mainly an adapter
> from Publisher of InputStream to Publisher of ByteBuffer.
> Did you overlook HttpRequest.BodyPublisher
> fromInputStream​(Supplier streamSupplier)?
> Or did you run into some issue with it?


Hi Chris,

Not quite. RequestPublisher is an adapter of ServletInputStream to
Publisher, and the big difference here is that ServletInputStream has a
mechanism (introduced in Servlet 3.1) for doing non blocking IO, which is
handled by registering a ReadListener, which is not part of the InputStream
interface, but is part of the ServletInputStream interface.  Using this,
you use ServletInputStream.isReady to see if the stream is ready to return
data from ServletInputStream.read without blocking. If it is, you invoke
read, otherwise, you wait until ReadListener.onDataAvailable() is invoked,
and then you can invoke ServletInputStream.read without blocking again.

So the HttpRequest.BodyPublisher.fromInputStream, I presume, does blocking
IO on the InputStream (since InputStream only supports blocking IO), using
the blocking InputStream.read call, while RequestPublisher does non
blocking IO on ServletInputStream.

Cheers,

James


>
> -Chris.
>



-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: websockets

2018-02-11 Thread James Roper
; }
> };
>
> The next message will be requested after the previous one has been echoed
> back
> to the server. In this example there is zero copying from the API user's
> perspective.
>
> Practically speaking, could it be any more asynchronous?
>
> Mind you, going one-by-one (i.e. request(1) after consumption) is not the
> only
> possibility. Back-pressure provides quite flexible control over
> consumption.
> Have a look at java.util.concurrent.Flow. The spec for this type provides
> a very
> nice example (SampleSubscriber) of buffering.
>
> Chuck, if you still think this API is missing some crucial capability, I
> will
> kindly ask you to provide an example of an application that is possible to
> implement in a non-back-pressured model and is not possible in the proposed
> WebSocket API.
>
> Thanks,
> -Pavel
>
> ---
> [1] https://github.com/reactive-streams/reactive-streams-jvm/blob/
> 3716ba57f17e55ec414aed7ccb1530251e193b61/README.md#goals-design-and-scope
> [2] One does not have to signal the message has been processed in order to
> receive the next message, if there are any. The signal is about
> handing over
> the message contents, not about the back-pressure.
>
>


-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: websockets

2018-02-09 Thread James Roper
Hi Chuck,

Presumably this API is similar in intention to the request method used in
reactive streams (aka java.util.concurrent.Flow), that is, request is the
means by which backpressure is propagated. One major problem with JDK8
WebSockets is there's no way to asynchronously propagate backpressure, you
have to accept every message that comes as it comes, you can't tell the
other end to back off, which means if it's producing messages faster than
you can consume them, your only two options are to fail fast, or risk
running out of memory.  Reactive Streams solves this by requiring consumers
to signal demand for data/messages before they receive any - an invocation
of the request method is just saying how many more elements the consumer is
currently ready to receive, and can be invoked many times, as the consumer
processes messages and is ready to receive more. Generally, for Reactive
Streams, application developers are not expected to implement or invoke
these APIs directly, instead, they are expected to use reactive streams
implementations like Akka Streams, rxJava or Reactor, which efficiently
manage buffering and keeping the buffer at an appropriate level for the
application developer, so the application developer can just focus on their
business concerns.

But that brings me to a problem that I'd like to give as feedback to the
implementers - this API is not Reactive Streams, and so therefore can't
take advantage of Reactive Streams implementations, and more problematic,
can't interop with other Reactive Streams sinks/sources. If I wanted to
stream a WebSocket into a message broker that supports Reactive Streams, I
can't. I would definitely hope that Reactive Streams support could be added
to this API, at a minimum as a wrapper, so that application developers can
easily focus on their business problems, plumbing and transforming messages
from one place to another, rather than having to deal with implementing
concurrent code to pass messages. It may well require wrapping messages in
a high level object - text, binary, ping, pong, etc, to differentiate
between the message types.

For a broader context of how this would fit into the broader Reactive
Streams ecosystem, I've published a blog post of what it would look like if
Java EE/EE4J were to adopt Reactive Streams everywhere, as it happens this
also includes proposals for using Reactive Streams in the JSR 356 WebSocket
spec:

https://developer.lightbend.com/blog/2018-02-06-reactive-streams-ee4j/index.html

Regards,

James

On 9 February 2018 at 19:16, Chuck Davis <cjgun...@gmail.com> wrote:

> I've been using jdk8 websockets to develop my desktop java
> applications.  Now that jdk9 is on my machine I started looking at
> websockets and I'm not at all sure I like what I see.  Can someone
> familiar with this feature please explain the rationale for what is
> happening?
>
> I'm concerned, at this initial stage, primarily by
> WebSocket.request(long).  This "feature" seems to have at least two
> very negative impacts:
>
> 1)  It appears to destroy the asynchronous nature of websockets;
> 2)  It appears to place programmers in the impossible position of
> guessing how many messages the server side might send.
>
> 1)  If everything has to stop while the client asks for more messages
> asynchronous communication is nullified.  The jdk8 implementation is,
> therefore, much more functional.
>
> 2)  It would appear the only logical use of WebSocket.request() would
> be to use a long value of Long.MAX_VALUE since there is no way to know
> how many messages may be received.  And what if the programmer asks
> for 1 message and the next message has 3 parts.  We're screwed.
> Additionally, the documentation specifically states that the
> WebSocket.Listener does not distinguish between partial and whole
> messages.  The jdk8 implementation decoders/encoders accumulate
> messages and assemble them until the message is complete and then pass
> it to the Endpoint -- much more satisfactory arrangement.
>
> I cannot fathom the meaning or improvement of this new wrinkle.
>
> Thanks for any insight
>



-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>


Re: JEP 321: HTTP Client - Use of Flow.Subscriber and Flow.Publisher

2017-12-10 Thread James Roper
Hi Chris,

This looks like a straight forward way to solve the problem with minimal
disruption from the existing API. Can I make a few suggestions though?

We could add a contentLength parameter to fromPublisher, to allow
Flow.Publishers where the content length is known to be easily converted to
BodyPublisher:

static BodyPublisher fromPublisher(Flow.Publisher publisher,
int contentLength) {
...
}

This would mean if you were receiving a servlet request body and publishing
it to another location, then you could do something like this (this uses a
reactive streams implementation on top of the servlet API that I wrote):

HttpServletRequest request = ...
long contentLength = -1;
if (request.getHeader("Content-Length") != null) {
  contentLength = Long.parseLong(request.getHeader("Content-Length"));
}
Publisher publisher = new RequestPublisher(request.startAsync(),
8192);

HttpRequest clientRequest = HttpRequest.newBuilder(target)
  .POST(BodyPublisher.fromPublisher(publisher, contentLength))
  .build()

Perhaps the method could be overloaded for both supplying and not supplying
a content length.

Similarly, I think a fromSubscriber API that accepted a CompletionStage
would be a little more fluent than having to supply it externally:

public static  BodyHandler fromSubscriber(Subscriber> subscriber, CompletionStage bodyFuture) {
  ...
}

Then you could have something like this:

TextSubscriber subscriber = ...;  // accumulates bytes and transforms them
into a CompletionStage.
CompletionStage result = subscriber.getTextResult();

CompletableFuture cf =  client
  .sendAsync(request, BodyHandler.fromSubscriber(subscriber, result));
String text = cf.join();

Likewise, this could be an overload of fromSubscriber if we want the option
of not specifying a body future.

One thing I think needs to be carefully specified is, if the method doesn't
accept a CompletionStage, when/how the CompletionStage returned from send
is redeemed.

Regards,

James

On 9 December 2017 at 04:31, Chris Hegarty <chris.hega...@oracle.com> wrote:

> James,
>
> Thanks for taking the time to look at this, and sending your thoughts.
>
> On 08/12/17 00:30, James Roper wrote:
> > Hi all,
> >
> > I wanted to start a discussion about the use of Flow.Subscriber and
> > Flow.Publisher in JEP 321 (HTTP Client API).
> >
> > It seems that users are required to implement their own publishers and
> > subscribers, that is, they can't take a Flow.Publisher or
> > Flow.Subscriber provided by another reactive streams implementation, and
> > pass it on to the HttpClient API. The reason for this is that the
> > HttpClient API doesn't accept Flow.Publisher/Flow.Subscriber, rather it
> > extends them in HttpRequest.BodyPublisher and
> > HttpResponse.BodySubscriber, and then requires the user to return
> > instances of those sub interfaces from their BodyHandlers. ...
>
> Great point. I think we can address this with straight forward adapters.
> For example:
>
>   public interface BodyPublisher extends Flow.Publisher {
>
>  /**
>  * Returns a request body publisher whose body is retrieved from the
>  * given {@code Flow.Publisher}. The returned request body publisher
>  * has an unknown content length.
>  *
>  * @apiNote This method can be used as an adapter between {@code
>  * BodyPublisher} and {@code Flow.Publisher}.
>  *
>  * @param publisher the publisher responsible for publishing the body
>  * @return a BodyPublisher
>  */
> static BodyPublisher fromPublisher(Flow.Publisher
> publisher) {
> ...
> }
>
> ...
>
>public BodySubscriber apply(int statusCode, HttpHeaders
> responseHeaders);
>
>  /**
>   * Returns a response body handler that returns a {@link
> BodySubscriber
>   * BodySubscriber}{@code } obtained from {@link
>   * BodySubscriber#fromSubscriber(Subscriber)}.
>   *
>   * @apiNote This method can be used as an adapter between {@code
>   * BodySubscriber} and {@code Flow.Subscriber}.
>   *
>   *  For example:
>   *  {@code
>   * TextSubscriber subscriber = ...;  // accumulates bytes and
> transforms them into a String.
>   * Supplier result = subscriber::getTextResult;
>   *
>   * CompletableFuture cf =  client
>   * .sendAsync(request, BodyHandler.fromSubscriber(sub
> scriber))
>   * .thenApply((response -> result.get()));
>   * String text = cf.join();
>   * }
>   *
>   * @param subscriber the subscriber
>   * @return a response body handler
>   */
>  public static BodyHandler fromSubscriber(Subscriber List> subscriber) {
>  ...
>  }
>
>  // Add an equiv

JEP 321: HTTP Client - Use of Flow.Subscriber and Flow.Publisher

2017-12-07 Thread James Roper
Hi all,

I wanted to start a discussion about the use of Flow.Subscriber and
Flow.Publisher in JEP 321 (HTTP Client API).

It seems that users are required to implement their own publishers and
subscribers, that is, they can't take a Flow.Publisher or Flow.Subscriber
provided by another reactive streams implementation, and pass it on to the
HttpClient API. The reason for this is that the HttpClient API doesn't
accept Flow.Publisher/Flow.Subscriber, rather it extends them in
HttpRequest.BodyPublisher and HttpResponse.BodySubscriber, and then
requires the user to return instances of those sub interfaces from their
BodyHandlers. Let's say I have a database driver that produces a
Flow.Subscriber for consuming a stream to be stored in the database, and I
want to plumb a response body into that database subscriber. I can't return
this from an HttpResponse.BodyHandler it requires me to return a
HttpResponse.BodySubscriber, not a Flow.Subscriber. Of course, users can
implement their own HttpResponse.BodySubscriber that delegates
onSubscribe/onNext/onComplete/onError to the databases Flow.Subscriber, but
needing to do this every time does not provide a great developer experience.

In order for reactive streams implementations to integrate with each other,
Flow.Subscriber and Flow.Publisher should only be extended if the
implementation is providing its own implementation of that interface and
doesn't expect end users to implement it. For cases where an implementation
expects users either to pass that sub interface, or return it from a method
to that they implement as is the case for BodyHandler, then it has to be a
Flow.Subscriber or Flow.Publisher, not a sub interface.

So perhaps HttpResponse.BodySubscriber should be modified to, rather than
extending Flow.Subscriber, have a getSubscriber() method that returns a
Flow.Subscriber.

Alternatively, the API could be inverted, for example,
HttpResponse.BodyHandler.apply could be modified to take three parameters,
the status code, the HttpHeaders, and a Flow.Publisher, and the return
value could be CompletionStage. This approach would actually maximise
interoperability, since a lot of reactive streams implementations are
publisher centric. For example, to my knowledge RxJava only provides
limited support for creating and transforming data using a Flow.Subscriber,
whereas if you give RxJava a Flow.Publisher, you can then do the full range
of operations (eg map) that RxJava supports on that stream (someone with a
better understanding of RxJava correct me if I'm wrong).

There's probably many other solutions, these are just two that I've thought
of.

There is also a broader discussion that needs to be had in the reactive
streams community over whether there should be a bias towards everything
returning/accepting publishers, or if implementations should evenly support
both. Supporting one or the other arbitrarily will have interoperability
implications, and while it's definitely beyond the scope of JEP 321 to
decide on that, it's something that we should keep in mind.

Regards,

James

-- 
*James Roper*
*Senior Octonaut*

Lightbend <https://www.lightbend.com/> – Build reactive apps!
Twitter: @jroper <https://twitter.com/jroper>