| /* |
| * 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; |
| } |
| } |