/*
* Copyright 2014 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.platforms.vertx3.http;
import io.apiman.common.util.ApimanPathUtils;
import io.apiman.common.util.ApimanPathUtils.ApiRequestPathInfo;
import io.apiman.gateway.engine.IApiRequestPathParser;
import io.apiman.gateway.engine.beans.ApiRequest;
import io.apiman.gateway.engine.beans.ApiResponse;
import io.apiman.gateway.engine.beans.util.CaseInsensitiveStringMultiMap;
import io.apiman.gateway.engine.beans.util.HeaderMap;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpClientResponse;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.HttpVersion;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Construct {@link ApiRequest} and {@link ApiResponse} objects from {@link HttpServerRequest},
* {@link HttpServerResponse} and {@link HttpClientResponse}
*
* @author Marc Savy {@literal <msavy@redhat.com>}
*/
@SuppressWarnings("nls")
public class HttpApiFactory {
private final static Set<String> IGNORESET = new HashSet<>();
static {
IGNORESET.add(ApimanPathUtils.X_API_VERSION_HEADER);
IGNORESET.add("Host");
}
private static IApiRequestPathParser requestPathParser;
public static void init(IApiRequestPathParser requestPathParser) {
HttpApiFactory.requestPathParser = requestPathParser;
}
public static ApiResponse buildResponse(HttpClientResponse response, Set<String> suppressHeaders) {
ApiResponse apimanResponse = new ApiResponse();
apimanResponse.setCode(response.statusCode());
apimanResponse.setMessage(response.statusMessage());
multimapToMap(apimanResponse.getHeaders(), response.headers(), suppressHeaders);
return apimanResponse;
}
public static void buildResponse(HttpServerResponse httpServerResponse, ApiResponse amanResponse, HttpVersion httpVersion) {
amanResponse.getHeaders().forEach(e -> {
if (httpVersion == HttpVersion.HTTP_1_0 || httpVersion == HttpVersion.HTTP_1_1 || !e.getKey().equals("Connection")) {
httpServerResponse.headers().add(e.getKey(), e.getValue());
}
});
httpServerResponse.setStatusCode(amanResponse.getCode());
httpServerResponse.setStatusMessage(amanResponse.getMessage());
}
public static ApiRequest buildRequest(HttpServerRequest req, boolean isTransportSecure) {
ApiRequest apimanRequest = new ApiRequest();
apimanRequest.setApiKey(parseApiKey(req));
apimanRequest.setRemoteAddr(req.remoteAddress().host());
apimanRequest.setType(req.method().toString());
apimanRequest.setTransportSecure(isTransportSecure);
multimapToMap(apimanRequest.getHeaders(), req.headers(), IGNORESET);
multimapToMap(apimanRequest.getQueryParams(), req.params(), Collections.<String>emptySet());
parsePath(req, apimanRequest);
return apimanRequest;
}
private static void parsePath(HttpServerRequest request, ApiRequest apimanRequest) {
// NB: The apiman version of the headers has already been parsed, so the headers have already been filtered/modified.
// Therefore we wrap the original inbound headers (just get) to efficiently access the necessary data.
ApiRequestPathInfo parsedPath = requestPathParser.parseEndpoint(request.path(), wrapMultiMap(request.headers()));
apimanRequest.setApiOrgId(parsedPath.orgId);
apimanRequest.setApiId(parsedPath.apiId);
apimanRequest.setApiVersion(parsedPath.apiVersion);
apimanRequest.setUrl(request.absoluteURI());
apimanRequest.setDestination(parsedPath.resource);
}
// TODO hacky...
private static HeaderMap wrapMultiMap(MultiMap headers) {
return new HeaderMap() {
private static final long serialVersionUID = -1406124274678587935L;
@Override()
public String get(String key) {
return headers.get(key);
}
};
}
private static void multimapToMap(CaseInsensitiveStringMultiMap map, MultiMap multimap, Set<String> suppressHeaders) {
for (Map.Entry<String, String> entry : multimap) {
if(!suppressHeaders.contains(entry.getKey())) {
String key = entry.getKey();
String val = entry.getValue();
if (key != null && key.equalsIgnoreCase("accept") && val != null
&& val.startsWith("application/apiman.")) { //$NON-NLS-1$
if (val.contains("+json")) { //$NON-NLS-1$
val = "application/json"; //$NON-NLS-1$
} else if (val.contains("+xml")) { //$NON-NLS-1$
val = "text/xml"; //$NON-NLS-1$
}
}
map.add(key, val);
}
}
}
private static String parseApiKey(HttpServerRequest req) {
String headerKey = req.headers().get("X-API-Key");
if (headerKey == null || headerKey.trim().length() == 0) {
headerKey = req.getParam("apikey");
}
return headerKey;
}
}