use of io.micronaut.http.client.exceptions.HttpClientException in project micronaut-core by micronaut-projects.
the class DefaultNettyHttpClientRegistry method resolveSocketChannelFactory.
private ChannelFactory resolveSocketChannelFactory(HttpClientConfiguration configuration, BeanContext beanContext) {
final String eventLoopGroup = configuration.getEventLoopGroup();
final EventLoopGroupConfiguration eventLoopGroupConfiguration = beanContext.findBean(EventLoopGroupConfiguration.class, Qualifiers.byName(eventLoopGroup)).orElseGet(() -> {
if (EventLoopGroupConfiguration.DEFAULT.equals(eventLoopGroup)) {
return new DefaultEventLoopGroupConfiguration();
} else {
throw new HttpClientException("Specified event loop group is not defined: " + eventLoopGroup);
}
});
return () -> eventLoopGroupFactory.clientSocketChannelInstance(eventLoopGroupConfiguration);
}
use of io.micronaut.http.client.exceptions.HttpClientException in project micronaut-core by micronaut-projects.
the class DefaultNettyHttpClientRegistry method resolveEventLoopGroup.
private EventLoopGroup resolveEventLoopGroup(HttpClientConfiguration configuration, BeanContext beanContext) {
final String eventLoopGroupName = configuration.getEventLoopGroup();
EventLoopGroup eventLoopGroup;
if (EventLoopGroupConfiguration.DEFAULT.equals(eventLoopGroupName)) {
eventLoopGroup = eventLoopGroupRegistry.getDefaultEventLoopGroup();
} else {
eventLoopGroup = beanContext.findBean(EventLoopGroup.class, Qualifiers.byName(eventLoopGroupName)).orElseThrow(() -> new HttpClientException("Specified event loop group is not defined: " + eventLoopGroupName));
}
return eventLoopGroup;
}
use of io.micronaut.http.client.exceptions.HttpClientException in project micronaut-core by micronaut-projects.
the class DefaultNettyHttpClientRegistry method getClient.
private DefaultHttpClient getClient(ClientKey key, BeanContext beanContext, AnnotationMetadata annotationMetadata) {
return clients.computeIfAbsent(key, clientKey -> {
DefaultHttpClient clientBean = null;
final String clientId = clientKey.clientId;
final Class<?> configurationClass = clientKey.configurationClass;
if (clientId != null) {
clientBean = (DefaultHttpClient) this.beanContext.findBean(HttpClient.class, Qualifiers.byName(clientId)).orElse(null);
}
if (configurationClass != null && !HttpClientConfiguration.class.isAssignableFrom(configurationClass)) {
throw new IllegalStateException("Referenced HTTP client configuration class must be an instance of HttpClientConfiguration for injection point: " + configurationClass);
}
final List<String> filterAnnotations = clientKey.filterAnnotations;
final String path = clientKey.path;
if (clientBean != null && path == null && configurationClass == null && filterAnnotations.isEmpty()) {
return clientBean;
}
LoadBalancer loadBalancer = null;
List<String> clientIdentifiers = null;
final HttpClientConfiguration configuration;
if (configurationClass != null) {
configuration = (HttpClientConfiguration) this.beanContext.getBean(configurationClass);
} else if (clientId != null) {
configuration = this.beanContext.findBean(HttpClientConfiguration.class, Qualifiers.byName(clientId)).orElse(defaultHttpClientConfiguration);
} else {
configuration = defaultHttpClientConfiguration;
}
if (clientId != null) {
loadBalancer = loadBalancerResolver.resolve(clientId).orElseThrow(() -> new HttpClientException("Invalid service reference [" + clientId + "] specified to @Client"));
clientIdentifiers = Collections.singletonList(clientId);
}
String contextPath = null;
if (StringUtils.isNotEmpty(path)) {
contextPath = path;
} else if (StringUtils.isNotEmpty(clientId) && clientId.startsWith("/")) {
contextPath = clientId;
} else {
if (loadBalancer != null) {
contextPath = loadBalancer.getContextPath().orElse(null);
}
}
final DefaultHttpClient client = buildClient(loadBalancer, clientKey.httpVersion, configuration, clientIdentifiers, contextPath, beanContext, annotationMetadata);
final JsonFeatures jsonFeatures = clientKey.jsonFeatures;
if (jsonFeatures != null) {
List<MediaTypeCodec> codecs = new ArrayList<>(2);
MediaTypeCodecRegistry codecRegistry = client.getMediaTypeCodecRegistry();
for (MediaTypeCodec codec : codecRegistry.getCodecs()) {
if (codec instanceof MapperMediaTypeCodec) {
codecs.add(((MapperMediaTypeCodec) codec).cloneWithFeatures(jsonFeatures));
} else {
codecs.add(codec);
}
}
if (!codecRegistry.findCodec(MediaType.APPLICATION_JSON_TYPE).isPresent()) {
codecs.add(createNewJsonCodec(this.beanContext, jsonFeatures));
}
client.setMediaTypeCodecRegistry(MediaTypeCodecRegistry.of(codecs));
}
return client;
});
}
use of io.micronaut.http.client.exceptions.HttpClientException in project micronaut-core by micronaut-projects.
the class DefaultHttpClient method connectWebSocket.
private <T> Flux<T> connectWebSocket(URI uri, MutableHttpRequest<?> request, Class<T> clientEndpointType, WebSocketBean<T> webSocketBean) {
Bootstrap bootstrap = this.bootstrap.clone();
if (webSocketBean == null) {
webSocketBean = webSocketRegistry.getWebSocket(clientEndpointType);
}
WebSocketBean<T> finalWebSocketBean = webSocketBean;
return Flux.create(emitter -> {
SslContext sslContext = buildSslContext(uri);
WebSocketVersion protocolVersion = finalWebSocketBean.getBeanDefinition().enumValue(ClientWebSocket.class, "version", WebSocketVersion.class).orElse(WebSocketVersion.V13);
int maxFramePayloadLength = finalWebSocketBean.messageMethod().map(m -> m.intValue(OnMessage.class, "maxPayloadLength").orElse(65536)).orElse(65536);
String subprotocol = finalWebSocketBean.getBeanDefinition().stringValue(ClientWebSocket.class, "subprotocol").orElse(StringUtils.EMPTY_STRING);
RequestKey requestKey;
try {
requestKey = new RequestKey(uri);
} catch (HttpClientException e) {
emitter.error(e);
return;
}
bootstrap.remoteAddress(requestKey.getHost(), requestKey.getPort());
initBootstrapForProxy(bootstrap, sslContext != null, requestKey.getHost(), requestKey.getPort());
bootstrap.handler(new HttpClientInitializer(sslContext, requestKey.getHost(), requestKey.getPort(), false, false, false, null) {
@Override
protected void addFinalHandler(ChannelPipeline pipeline) {
pipeline.remove(ChannelPipelineCustomizer.HANDLER_HTTP_DECODER);
ReadTimeoutHandler readTimeoutHandler = pipeline.get(ReadTimeoutHandler.class);
if (readTimeoutHandler != null) {
pipeline.remove(readTimeoutHandler);
}
Optional<Duration> readIdleTime = configuration.getReadIdleTimeout();
if (readIdleTime.isPresent()) {
Duration duration = readIdleTime.get();
if (!duration.isNegative()) {
pipeline.addLast(ChannelPipelineCustomizer.HANDLER_IDLE_STATE, new IdleStateHandler(duration.toMillis(), duration.toMillis(), duration.toMillis(), TimeUnit.MILLISECONDS));
}
}
final NettyWebSocketClientHandler webSocketHandler;
try {
String scheme = (sslContext == null) ? "ws" : "wss";
URI webSocketURL = UriBuilder.of(uri).scheme(scheme).host(host).port(port).build();
MutableHttpHeaders headers = request.getHeaders();
HttpHeaders customHeaders = EmptyHttpHeaders.INSTANCE;
if (headers instanceof NettyHttpHeaders) {
customHeaders = ((NettyHttpHeaders) headers).getNettyHeaders();
}
if (StringUtils.isNotEmpty(subprotocol)) {
customHeaders.add("Sec-WebSocket-Protocol", subprotocol);
}
webSocketHandler = new NettyWebSocketClientHandler<>(request, finalWebSocketBean, WebSocketClientHandshakerFactory.newHandshaker(webSocketURL, protocolVersion, subprotocol, true, customHeaders, maxFramePayloadLength), requestBinderRegistry, mediaTypeCodecRegistry, emitter);
pipeline.addLast(WebSocketClientCompressionHandler.INSTANCE);
pipeline.addLast(ChannelPipelineCustomizer.HANDLER_MICRONAUT_WEBSOCKET_CLIENT, webSocketHandler);
} catch (Throwable e) {
emitter.error(new WebSocketSessionException("Error opening WebSocket client session: " + e.getMessage(), e));
}
}
});
addInstrumentedListener(bootstrap.connect(), future -> {
if (!future.isSuccess()) {
emitter.error(future.cause());
}
});
}, FluxSink.OverflowStrategy.ERROR);
}
use of io.micronaut.http.client.exceptions.HttpClientException in project micronaut-core by micronaut-projects.
the class DefaultHttpClient method addFullHttpResponseHandler.
@SuppressWarnings("MagicNumber")
private <O, E> void addFullHttpResponseHandler(io.micronaut.http.HttpRequest<?> request, Channel channel, boolean secure, ChannelPool channelPool, FluxSink<io.micronaut.http.HttpResponse<O>> emitter, Argument<O> bodyType, Argument<E> errorType) {
ChannelPipeline pipeline = channel.pipeline();
final SimpleChannelInboundHandler<FullHttpResponse> newHandler = new SimpleChannelInboundHandlerInstrumented<FullHttpResponse>(false) {
AtomicBoolean complete = new AtomicBoolean(false);
boolean keepAlive = true;
@Override
public boolean acceptInboundMessage(Object msg) {
return msg instanceof FullHttpResponse && (secure || !discardH2cStream((HttpMessage) msg));
}
@Override
protected void channelReadInstrumented(ChannelHandlerContext channelHandlerContext, FullHttpResponse fullResponse) {
try {
HttpResponseStatus status = fullResponse.status();
int statusCode = status.code();
HttpStatus httpStatus;
try {
httpStatus = HttpStatus.valueOf(statusCode);
} catch (IllegalArgumentException e) {
if (complete.compareAndSet(false, true)) {
if (!emitter.isCancelled()) {
emitter.error(e);
}
} else if (log.isWarnEnabled()) {
log.warn("Unsupported http status after handler completed: " + e.getMessage(), e);
}
return;
}
try {
HttpHeaders headers = fullResponse.headers();
if (log.isDebugEnabled()) {
log.debug("Received response {} from {}", status.code(), request.getUri());
}
if (log.isTraceEnabled()) {
traceHeaders(headers);
traceBody("Response", fullResponse.content());
}
// it is a redirect
if (statusCode > 300 && statusCode < 400 && configuration.isFollowRedirects() && headers.contains(HttpHeaderNames.LOCATION)) {
String location = headers.get(HttpHeaderNames.LOCATION);
final MutableHttpRequest<Object> redirectRequest;
if (statusCode == 307) {
redirectRequest = io.micronaut.http.HttpRequest.create(request.getMethod(), location);
request.getBody().ifPresent(redirectRequest::body);
} else {
redirectRequest = io.micronaut.http.HttpRequest.GET(location);
}
setRedirectHeaders(request, redirectRequest);
Flux<io.micronaut.http.HttpResponse<O>> redirectExchange = Flux.from(resolveRedirectURI(request, redirectRequest)).switchMap(buildExchangePublisher(request, redirectRequest, bodyType, errorType));
redirectExchange.defaultIfEmpty(io.micronaut.http.HttpResponse.notFound()).subscribe(oHttpResponse -> {
if (bodyType == null || !bodyType.isVoid()) {
emitter.next(oHttpResponse);
}
emitter.complete();
}, throwable -> {
if (!emitter.isCancelled()) {
emitter.error(throwable);
}
});
return;
}
if (statusCode == HttpStatus.NO_CONTENT.getCode()) {
// normalize the NO_CONTENT header, since http content aggregator adds it even if not present in the response
headers.remove(HttpHeaderNames.CONTENT_LENGTH);
}
boolean convertBodyWithBodyType = statusCode < 400 || (!DefaultHttpClient.this.configuration.isExceptionOnErrorStatus() && bodyType.equalsType(errorType));
FullNettyClientHttpResponse<O> response = new FullNettyClientHttpResponse<>(fullResponse, httpStatus, mediaTypeCodecRegistry, byteBufferFactory, bodyType, convertBodyWithBodyType);
if (complete.compareAndSet(false, true)) {
if (convertBodyWithBodyType) {
if (bodyType == null || !bodyType.isVoid()) {
emitter.next(response);
}
response.onComplete();
emitter.complete();
} else {
// error flow
try {
HttpClientResponseException clientError;
if (errorType != null && errorType != HttpClient.DEFAULT_ERROR_TYPE) {
clientError = new HttpClientResponseException(status.reasonPhrase(), null, response, new HttpClientErrorDecoder() {
@Override
public Argument<?> getErrorType(MediaType mediaType) {
return errorType;
}
});
} else {
clientError = new HttpClientResponseException(status.reasonPhrase(), response);
}
try {
if (!emitter.isCancelled()) {
emitter.error(clientError);
}
} finally {
response.onComplete();
}
} catch (Throwable t) {
if (t instanceof HttpClientResponseException) {
try {
if (!emitter.isCancelled()) {
emitter.error(t);
}
} finally {
response.onComplete();
}
} else {
response.onComplete();
FullNettyClientHttpResponse<Object> errorResponse = new FullNettyClientHttpResponse<>(fullResponse, httpStatus, mediaTypeCodecRegistry, byteBufferFactory, null, false);
errorResponse.onComplete();
HttpClientResponseException clientResponseError = new HttpClientResponseException("Error decoding HTTP error response body: " + t.getMessage(), t, errorResponse, null);
if (!emitter.isCancelled()) {
emitter.error(clientResponseError);
}
}
}
}
}
} catch (Throwable t) {
if (complete.compareAndSet(false, true)) {
if (t instanceof HttpClientResponseException) {
if (!emitter.isCancelled()) {
emitter.error(t);
}
} else {
FullNettyClientHttpResponse<Object> response = new FullNettyClientHttpResponse<>(fullResponse, httpStatus, mediaTypeCodecRegistry, byteBufferFactory, null, false);
HttpClientResponseException clientResponseError = new HttpClientResponseException("Error decoding HTTP response body: " + t.getMessage(), t, response, new HttpClientErrorDecoder() {
@Override
public Argument<?> getErrorType(MediaType mediaType) {
return errorType;
}
});
try {
if (!emitter.isCancelled()) {
emitter.error(clientResponseError);
}
} finally {
response.onComplete();
}
}
} else {
if (log.isWarnEnabled()) {
log.warn("Exception fired after handler completed: " + t.getMessage(), t);
}
}
}
} finally {
if (fullResponse.refCnt() > 0) {
try {
ReferenceCountUtil.release(fullResponse);
} catch (Throwable e) {
if (log.isDebugEnabled()) {
log.debug("Failed to release response: {}", fullResponse);
}
}
}
if (!HttpUtil.isKeepAlive(fullResponse)) {
keepAlive = false;
}
pipeline.remove(this);
}
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
if (channelPool != null) {
if (readTimeoutMillis != null) {
ctx.pipeline().remove(ChannelPipelineCustomizer.HANDLER_READ_TIMEOUT);
}
final Channel ch = ctx.channel();
if (!keepAlive) {
ch.closeFuture().addListener((future -> channelPool.release(ch)));
} else {
channelPool.release(ch);
}
} else {
// just close it to prevent any future reads without a handler registered
ctx.close();
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
if (readTimeoutMillis != null) {
if (httpVersion == io.micronaut.http.HttpVersion.HTTP_2_0) {
Http2SettingsHandler settingsHandler = (Http2SettingsHandler) ctx.pipeline().get(HANDLER_HTTP2_SETTINGS);
if (settingsHandler != null) {
addInstrumentedListener(settingsHandler.promise, future -> {
if (future.isSuccess()) {
pipeline.addBefore(ChannelPipelineCustomizer.HANDLER_HTTP2_CONNECTION, ChannelPipelineCustomizer.HANDLER_READ_TIMEOUT, new ReadTimeoutHandler(readTimeoutMillis, TimeUnit.MILLISECONDS));
}
});
} else {
pipeline.addBefore(ChannelPipelineCustomizer.HANDLER_HTTP2_CONNECTION, ChannelPipelineCustomizer.HANDLER_READ_TIMEOUT, new ReadTimeoutHandler(readTimeoutMillis, TimeUnit.MILLISECONDS));
}
} else {
pipeline.addBefore(ChannelPipelineCustomizer.HANDLER_HTTP_CLIENT_CODEC, ChannelPipelineCustomizer.HANDLER_READ_TIMEOUT, new ReadTimeoutHandler(readTimeoutMillis, TimeUnit.MILLISECONDS));
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
try {
if (complete.compareAndSet(false, true)) {
String message = cause.getMessage();
if (message == null) {
message = cause.getClass().getSimpleName();
}
if (log.isTraceEnabled()) {
log.trace("HTTP Client exception ({}) occurred for request : {} {}", message, request.getMethodName(), request.getUri());
}
if (cause instanceof TooLongFrameException) {
if (!emitter.isCancelled()) {
emitter.error(new ContentLengthExceededException(configuration.getMaxContentLength()));
}
} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
if (!emitter.isCancelled()) {
emitter.error(ReadTimeoutException.TIMEOUT_EXCEPTION);
}
} else {
if (!emitter.isCancelled()) {
emitter.error(new HttpClientException("Error occurred reading HTTP response: " + message, cause));
}
}
}
} finally {
keepAlive = false;
pipeline.remove(this);
}
}
};
pipeline.addLast(ChannelPipelineCustomizer.HANDLER_MICRONAUT_FULL_HTTP_RESPONSE, newHandler);
}
Aggregations