Reactor's `DependencyUtils` has been renamed to `Converters` and
all the `from` converter methods have been disambiguated to
`fromPublisher`, `toPublisher`.
This commit adds support for handling an empty request body with both
HttpEntity where the body is not required and with @RequestBody where
the body is required depending on the annotation's required flag.
If the body is an explicit type (e.g. String, HttpEntity<String>) and
the body is required an exception is raised before the method is even
invoked or otherwise the body is passed in as null.
If the body is declared as an async type (e.g. Mono<String>,
HttpEntity<Mono<String>>) and is required, the error will flow through
the async type. If not required, the async type will be passed with no
values (i.e. empty).
A notable exception is rx.Single which can only have one value or one
error and cannot be empty. As a result currently the use of rx.Single
to represent the request body in any form effectively implies the body
is required.
Before this change decodeToMono always created a StringBuilder to
aggregate resulting in an "" (empty string) rather than an empty
Mono for an empty input stream.
Now we aggregate in the DataBuffer instead and then decode to String.
The RequestBodyArgumentResolver has been refactored to have a shared
base class and tests with the new HttpEntityMethodArgumentResolver.
An HttpEntity argument is not expected to have an async wrapper because
the request headers are available immediately. The body however can be
asynchronous, e.g. HttpEntity<Flux<String>>.
This commit changes the AbstractResponseBodySubscriber into a
AbstractResponseBodyProcessor<DataBuffer, Void>, so that the processor
can be used as a return value for writeWith.
Additional, this commit no longer closes the response after an eror
occurred.
This fixes#59.
When the thread is writing to the response in
RECEIVED.onWritePossible(), the execution may stop because isReady()
returned false. In this case the buffer is partially written.
When there is partially written case:
1. The state will be changed from WRITING to RECEIVED
2. A check for "write possible" will be performed:
- If onWritePossible event has been already called by the web
container while in WRITING state then this check will trigger
RECEIVED.onWritePossible() because isReady() will be true and the
writing will continue.
- Otherwise the writing will be resumed when the web container sends
onWritePossible event.
Before this commit, it was not possible to set the status code of an
HTTP response without throwing an exception if it was already
committed. The consequence was a lot of errors in the logs for long
lived HTTP exchanges like Server-Sent Events for example.
After this commit, ServerHttpResponse#setStatusCode() returns
true if the operation succeeded and false if the status code has not
been set because the response has already been committed.
In term of implementation, that makes status code managed
consistently with headers and cookies:
AbstractServerHttpResponse#setStatusCode() stores the status
code that will be effectively set later in the response lifecycle by
the HTTP server via AbstractServerHttpResponse#writeStatusCode()
when the response will be committed.
When using the ConversionService to check and bridge to and from
reactive types we now generallly provide the full type information
available from method signatures. However that full type information
is not always necessary such as when we perform additional checks on
the generics of the reactive type (e.g. Mono<ResponseEntity>).
This allows us to switch to use DefaultFormattingConversionService
instead of GenericConversionService while also ensuring that the
CollectionToObjectConverter doesn't think it can convert List<?> to
any reactive type.
The ObjectToObjectConverter can also interfere because it is smart
enough to find the "from(Publisher<?>)" method on Flux and Mono.
To make up for that on the response side we now check if a type
is assignable to Publisher first in which case it is a simple cast.
In turn that means we don't need a PublisherToFluxConverter which can
be problematic in its own right because it can convert from Mono to
Flux which technically doesn't lose data but switches stream semantics.
Issue: #124, #128
HandlerAdapter's should always be able to provide a MethodParameter
which in turn ensures that HandlerResultHandler's have full type
information from method declarations.
This commit also introduces ResolvableMethod for use in tests to make
it easy to obtain MethodParameter return types.
Issue: #128
This commit adds a Decoder#decodeOne() method in order
to handle correctly the streaming versus one value
deserialization based on the type provided by the user.
For example, if a List parameter is provided in a controller
method, Jackson will be called once, while if the user provides
a Flux or an Observable parameter, Jackson will be called for
each element.
This commit replaces Reactive Streams converters for RxJava1 and
CompletableFuture with Reactor specific ones. The results in conversion
that preserves stream semantics, i.e. Mono vs Flux.
For example this is allowed:
Flux -> Observable
Mono -> Single
Mono -> CompletableFuture
This is not allowed:
Flux -> Single
Mono -> Observable
Flux -> CompletableFuture
As a result it is now possible to check through the ConversionService
if a target type to convert to is a stream of many or of one which is
useful for decoding purposes.
The commit also adds PublisherToFluxConverter to allow conversion from
raw Publisher to Flux. The reverse is not necessary since Flux is a
Publisher and it's a no-op conversion.
The DefaultWebSessionManager now uses Mono.defer to protect the call
to getSession from parsing session cookies immediately. This allows
pre-initializing the Mono<WebSession> upfront vs using a lock.
When in state DATA_AVAILABLE if there are simultaneous invocations of
AbstractRequestBodyPublisher.RequestBodySubscription.request and
ReadListener.onDataAvailable, the first one will process the available
data, the second one should not throw an exception because thus it will
signal to web container that there are problems while there are not.
Refactored Undertow support to register a response listener only when
the body is written to, as opposed to registering it at startup. The
reason for this is that getting the response channel from the
HttpServerExchange commits the status and response, making it impossible
to change them after the fact.
Fixed issue #119.
When there are simultaneous invocations of onWritePossible, only the
first one should succeed. This can happens when
AbstractResponseBodySubscriber.onNext and
WriteListener.onWritePossible() are called respectively by the
application and the web container.