blob: 30cc9b74dacc8b30908319ee217210396237ed46 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.servicecomb.transport.rest.client.http;
import java.util.Collection;
import javax.ws.rs.core.HttpHeaders;
import org.apache.servicecomb.common.rest.RestConst;
import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager;
import org.apache.servicecomb.common.rest.definition.RestOperationMeta;
import org.apache.servicecomb.common.rest.filter.HttpClientFilter;
import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.definition.OperationMeta;
import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
import org.apache.servicecomb.swagger.invocation.Response;
import org.apache.servicecomb.swagger.invocation.context.HttpStatus;
import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JavaType;
import com.netflix.config.DynamicPropertyFactory;
public class DefaultHttpClientFilter implements HttpClientFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultHttpClientFilter.class);
private static final boolean enabled = DynamicPropertyFactory.getInstance().getBooleanProperty
("servicecomb.http.filter.client.default.enabled", true).get();
@Override
public int getOrder() {
return 10000;
}
@Override
public boolean enabled() {
return enabled;
}
protected ProduceProcessor findProduceProcessor(RestOperationMeta restOperation,
HttpServletResponseEx responseEx) {
String contentType = responseEx.getHeader(HttpHeaders.CONTENT_TYPE);
if (contentType == null) {
return null;
}
String contentTypeForFind = contentType;
int idx = contentType.indexOf(';');
if (idx != -1) {
contentTypeForFind = contentType.substring(0, idx);
}
return restOperation.findProduceProcessor(contentTypeForFind);
}
protected Response extractResponse(Invocation invocation, HttpServletResponseEx responseEx) {
Object result = invocation.getHandlerContext().get(RestConst.READ_STREAM_PART);
if (result != null) {
return Response.create(responseEx.getStatusType(), result);
}
OperationMeta operationMeta = invocation.getOperationMeta();
JavaType responseType = invocation.findResponseType(responseEx.getStatus());
RestOperationMeta swaggerRestOperation = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
ProduceProcessor produceProcessor = findProduceProcessor(swaggerRestOperation, responseEx);
if (produceProcessor == null) {
// This happens outside the runtime such as Servlet filter response. Here we give a default json parser to it
// and keep user data not get lose.
String msg =
String.format("method %s, path %s, statusCode %d, reasonPhrase %s, response content-type %s is not supported",
swaggerRestOperation.getHttpMethod(),
swaggerRestOperation.getAbsolutePath(),
responseEx.getStatus(),
responseEx.getStatusType().getReasonPhrase(),
responseEx.getHeader(HttpHeaders.CONTENT_TYPE));
LOGGER.warn(msg);
produceProcessor = ProduceProcessorManager.INSTANCE.findDefaultProcessor();
}
try {
result = produceProcessor.decodeResponse(responseEx.getBodyBuffer(), responseType);
Response response = Response.create(responseEx.getStatusType(), result);
if (response.isFailed()) {
LOGGER.warn("invoke operation [{}] failed, status={}, msg={}", invocation.getMicroserviceQualifiedName(),
responseEx.getStatusType().getStatusCode(), result == null ? "" : result.toString());
}
return response;
} catch (Exception e) {
LOGGER.error("failed to decode response body, exception is [{}]", e.getMessage());
String msg =
String.format("method %s, path %s, statusCode %d, reasonPhrase %s, response content-type %s is not supported",
swaggerRestOperation.getHttpMethod(),
swaggerRestOperation.getAbsolutePath(),
responseEx.getStatus(),
responseEx.getStatusType().getReasonPhrase(),
responseEx.getHeader(HttpHeaders.CONTENT_TYPE));
if (HttpStatus.isSuccess(responseEx.getStatus())) {
return Response.createConsumerFail(
new InvocationException(400, responseEx.getStatusType().getReasonPhrase(),
new CommonExceptionData(msg), e));
}
return Response.createConsumerFail(
new InvocationException(responseEx.getStatus(), responseEx.getStatusType().getReasonPhrase(),
new CommonExceptionData(msg), e));
}
}
@Override
public Response afterReceiveResponse(Invocation invocation, HttpServletResponseEx responseEx) {
Response response = extractResponse(invocation, responseEx);
for (String headerName : responseEx.getHeaderNames()) {
if (headerName.equals(":status")) {
continue;
}
Collection<String> headerValues = responseEx.getHeaders(headerName);
for (String headerValue : headerValues) {
response.addHeader(headerName, headerValue);
}
}
return response;
}
}