use of io.helidon.common.reactive.Single in project helidon by oracle.
the class RestApiBase method requestBytesPayload.
/**
* Create a supplier for a response with publisher request.
* Defaults to "{@code () -> clientRequest.submit(publisher)}".
* Also configures content type and accept headers.
*
* @param path path requested
* @param request API request
* @param method HTTP method
* @param requestId ID of this request
* @param requestBuilder {@link io.helidon.webclient.WebClient} request builder
* @param publisher publisher to be used as request entity
*
* @return supplier of a web client response
*/
protected Supplier<Single<WebClientResponse>> requestBytesPayload(String path, ApiRequest<?> request, Http.RequestMethod method, String requestId, WebClientRequestBuilder requestBuilder, Flow.Publisher<DataChunk> publisher) {
requestBuilder.accept(request.responseMediaType().orElse(MediaType.APPLICATION_JSON));
requestBuilder.contentType(request.requestMediaType().orElse(MediaType.APPLICATION_OCTET_STREAM));
AtomicBoolean updated = new AtomicBoolean();
return () -> {
if (updated.compareAndSet(false, true)) {
return updateRequestBuilderBytesPayload(requestBuilder, path, request, method, requestId).flatMapSingle(it -> it.submit(publisher));
} else {
return requestBuilder.submit(publisher);
}
};
}
use of io.helidon.common.reactive.Single in project helidon by oracle.
the class RestApiBase method errorResponse.
/**
* Create an error response.
* This method attempts to read the response entity as a string, parse it into a JsonObject and
* depending on result, calls methods to create a proper exception.
*
* @param path requested path
* @param request original request
* @param method HTTP method
* @param requestId ID of the request
* @param response actual response where we do not expect an entity
* @param <T> type of the response
*
* @return future with error
*/
protected <T extends ApiResponse> Single<T> errorResponse(String path, ApiRequest<?> request, Http.RequestMethod method, String requestId, WebClientResponse response) {
if (response.headers().contentLength().orElse(-1L) == 0) {
// explicitly no content
return Single.error(readError(path, request, method, requestId, response));
} else {
AtomicBoolean processedError = new AtomicBoolean();
return response.content().as(String.class).flatMapSingle(string -> {
try {
JsonObject json = jsonReaderFactory.createReader(new StringReader(string)).readObject();
Single<T> error = Single.error(readError(path, request, method, requestId, response, json));
processedError.set(true);
return error;
} catch (Exception e) {
Single<T> error = Single.error(readError(path, request, method, requestId, response, string));
processedError.set(true);
return error;
}
}).onErrorResumeWithSingle(it -> {
if (processedError.get()) {
return Single.error(it);
}
return Single.error(readErrorFailedEntity(path, request, method, requestId, response, it));
});
}
}
use of io.helidon.common.reactive.Single in project helidon by oracle.
the class OciRestApi method requestJsonPayload.
@Override
protected Supplier<Single<WebClientResponse>> requestJsonPayload(String path, ApiRequest<?> request, Http.RequestMethod method, String requestId, WebClientRequestBuilder requestBuilder, JsonObject jsonObject) {
requestBuilder.accept(request.responseMediaType().orElse(MediaType.APPLICATION_JSON));
requestBuilder.contentType(request.requestMediaType().orElse(MediaType.APPLICATION_JSON));
// common stuff
updateRequestBuilder(requestBuilder, request, requestId);
// signature requirement
// this requires a content length and hash
ByteArrayOutputStream baos = new ByteArrayOutputStream();
jsonWriterFactory().createWriter(baos).write(jsonObject);
byte[] requestBytes = baos.toByteArray();
String sha256 = HASH_DIGEST.digest(Base64Value.create(requestBytes)).toBase64();
requestBuilder.headers(headers -> {
headers.contentLength(requestBytes.length);
headers.add("x-content-sha256", sha256);
return headers;
}).contentType(request.requestMediaType().orElse(MediaType.APPLICATION_JSON));
return () -> requestBuilder.submit(requestBytes);
}
use of io.helidon.common.reactive.Single in project helidon by oracle.
the class OciConfigInstancePrincipal method refresh.
@Override
public Single<OciSignatureData> refresh() {
// we must use future, as there may be multiple singles created from it
CompletableFuture<OciSignatureData> nextFuture = new CompletableFuture<>();
CompletionStage<OciSignatureData> inProgress = refreshFuture.compareAndExchange(null, nextFuture);
if (inProgress != null) {
LOGGER.fine("Refresh already in progress.");
// I do not own this refresh
return Single.create(inProgress);
}
Instant instant = lastSuccessfulRefresh.get();
if (instant != null && instant.plus(1, ChronoUnit.MINUTES).isAfter(Instant.now())) {
LOGGER.fine("Refresh requested within one minute of last successful refresh, ignoring");
refreshFuture.set(null);
return Single.just(currentSignatureData.get());
}
LOGGER.fine("Refresh of signature data initialized");
nextFuture.handle((it, throwable) -> {
if (throwable != null) {
LOGGER.fine("Finished refresh with exception: " + throwable.getMessage() + ", stack trace available in FINEST");
LOGGER.log(Level.FINEST, "Exception", throwable);
} else {
lastSuccessfulRefresh.set(Instant.now());
LOGGER.fine("Finished refresh successfully. New kid: " + it.keyId());
}
// now we can open for next refresh command
refreshFuture.set(null);
return null;
});
// we may have parallel requests to refresh under heavy load
KeyPair keyPair = sessionKeys.refresh();
// trigger read in parallel
Single<PrivateKey> pkSingle = privateKeySupplier.get();
Single<X509Certificate> certSingle = certificateSupplier.get();
Single<X509Certificate> intermediateSingle = intermediateCertificateSupplier.get();
pkSingle.flatMapSingle(privateKey -> certSingle.flatMapSingle(certificate -> intermediateSingle.flatMapSingle(intermediateCert -> refresh(keyPair, privateKey, certificate, intermediateCert)))).forSingle(nextFuture::complete).exceptionallyAccept(nextFuture::completeExceptionally);
return Single.create(nextFuture);
}
use of io.helidon.common.reactive.Single in project helidon by oracle.
the class OciConfigInstancePrincipal method getData.
// only get the new signature data
private Single<OciSignatureData> getData(KeyPair keyPair, X509Certificate leafCertificate, X509Certificate intermediateCert) {
LOGGER.fine("Getting signature data");
PublicKey publicKey = keyPair.getPublic();
String publicKeyPem = toPem(publicKey);
String leafCertificatePem = toPem(leafCertificate);
String intermediatePem = toPem(intermediateCert);
String purpose = "DEFAULT";
String fingerprintAlgorithm = "SHA256";
JsonObject jsonRequest = JSON.createObjectBuilder().add("publicKey", publicKeyPem).add("certificate", leafCertificatePem).add("intermediateCertificates", JSON.createArrayBuilder().add(intermediatePem)).add("purpose", purpose).add("fingerprintAlgorithm", fingerprintAlgorithm).build();
// this requires a content length and hash
ByteArrayOutputStream baos = new ByteArrayOutputStream();
JSON_WRITER_FACTORY.createWriter(baos).write(jsonRequest);
byte[] requestBytes = baos.toByteArray();
String sha256 = HASH_DIGEST.digest(Base64Value.create(requestBytes)).toBase64();
return federationClient.post().path("/v1/x509").contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).headers(headers -> {
headers.contentLength(requestBytes.length);
headers.add("x-content-sha256", sha256);
return headers;
}).submit(requestBytes).flatMapSingle(it -> {
if (it.status().family() != Http.ResponseStatus.Family.SUCCESSFUL) {
return readError(it);
}
return it.content().as(JsonObject.class);
}).map(it -> it.getString("token")).map(newToken -> toData(newToken, keyPair));
}
Aggregations