/*
* Copyright 2017 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.apiman.gateway.engine.vertx.polling.fetchers;
import io.apiman.gateway.engine.vertx.polling.exceptions.BadResponseCodeError;
import io.apiman.gateway.engine.vertx.polling.fetchers.auth.AuthType;
import io.apiman.gateway.engine.vertx.polling.fetchers.auth.Authenticator;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.impl.Arguments;
import java.net.URI;
import java.util.Map;
import org.apache.commons.lang3.EnumUtils;
/**
* Fetch HTTP and HTTPS resources, with Auth options including BASIC and various OAuth2
* permutations.
*
* <ul>
* <li>auth: auth type, one of {@link AuthType}. Otherwise <tt>None</tt> by default.<li>
* </ul>
*
* Refer to {@link AuthType} for available options, such as BASIC, OAuth2, Keycloak, etc.
*
* @see AuthType
* @author Marc Savy {@literal <marc@rhymewithgravy.com>}
*/
@SuppressWarnings("nls")
public class HttpResourceFetcher implements ResourceFetcher {
private URI uri;
private boolean isHttps;
private Vertx vertx;
private Buffer rawData = Buffer.buffer();
private Handler<Throwable> exceptionHandler;
private Map<String, String> config;
private Authenticator authenticator;
public HttpResourceFetcher(Vertx vertx, URI uri, Map<String, String> config, boolean isHttps) {
this.vertx = vertx;
this.uri = uri;
this.isHttps = isHttps;
this.config = config;
String authString = config.getOrDefault("auth", "NONE").toUpperCase();
Arguments.require(EnumUtils.isValidEnum(AuthType.class, authString), "auth must be one of: " + AuthType.all());
authenticator = AuthType.valueOf(authString).getAuthenticator();
authenticator.validateConfig(config);
}
@Override
public void fetch(Handler<Buffer> resultHandler) {
int port = uri.getPort();
if (port == -1) {
if (isHttps) {
port = 443;
} else {
port = 80;
}
}
HttpClientRequest httpClientRequest = vertx.createHttpClient(new HttpClientOptions().setSsl(isHttps))
.get(port, uri.getHost(), uri.getPath(), clientResponse -> {
if (clientResponse.statusCode() / 100 == 2) {
clientResponse.handler(data -> {
rawData.appendBuffer(data);
})
.endHandler(end -> resultHandler.handle(rawData))
.exceptionHandler(exceptionHandler);
} else {
exceptionHandler.handle(new BadResponseCodeError("Unexpected response code when trying to retrieve config: " //$NON-NLS-1$
+ clientResponse.statusCode()));
}
})
.exceptionHandler(exceptionHandler);
authenticator.authenticate(vertx, config, httpClientRequest.headers(), authResult -> {
if (authResult.succeeded()) {
// The client request is executed when HttpClientRequest#end is invoked.
httpClientRequest.end();
} else {
exceptionHandler.handle(authResult.cause());
}
});
}
@Override
public HttpResourceFetcher exceptionHandler(Handler<Throwable> exceptionHandler) {
this.exceptionHandler = exceptionHandler;
return this;
}
}