blob: 6bcf88b5dc9614a4402600bf904fc46ee70ac1f5 [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.provider.springmvc.reference;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import com.google.common.annotations.VisibleForTesting;
import org.apache.servicecomb.common.rest.RestConst;
import org.apache.servicecomb.common.rest.codec.RestCodec;
import org.apache.servicecomb.common.rest.definition.RestOperationMeta;
import org.apache.servicecomb.common.rest.locator.OperationLocator;
import org.apache.servicecomb.common.rest.locator.ServicePathManager;
import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.SCBEngine;
import org.apache.servicecomb.core.definition.MicroserviceMeta;
import org.apache.servicecomb.core.definition.OperationMeta;
import org.apache.servicecomb.core.invocation.InvocationFactory;
import org.apache.servicecomb.core.provider.consumer.InvokerUtils;
import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig;
import org.apache.servicecomb.core.provider.consumer.ReferenceConfig;
import org.apache.servicecomb.swagger.invocation.Response;
import org.apache.servicecomb.swagger.invocation.context.InvocationContext;
import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import com.fasterxml.jackson.databind.type.TypeFactory;
import io.netty.handler.codec.http.QueryStringDecoder;
public class CseClientHttpRequest implements ClientHttpRequest {
// URL format:cse://microserviceName/business url
private URI uri;
// business url
private String path;
private HttpMethod method;
private HttpHeaders httpHeaders = new HttpHeaders();
private InvocationContext context;
private Object requestBody;
private Map<String, List<String>> queryParams;
private RequestMeta requestMeta;
private Type responseType;
public CseClientHttpRequest() {
}
public CseClientHttpRequest(URI uri, HttpMethod method) {
this.uri = uri;
this.method = method;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
protected RequestMeta getRequestMeta() {
return requestMeta;
}
protected void setRequestMeta(RequestMeta requestMeta) {
this.requestMeta = requestMeta;
}
public void setUri(URI uri) {
this.uri = uri;
}
public void setMethod(HttpMethod method) {
this.method = method;
}
protected void setQueryParams(Map<String, List<String>> queryParams) {
this.queryParams = queryParams;
}
public InvocationContext getContext() {
return context;
}
public void setContext(InvocationContext context) {
this.context = context;
}
public Type getResponseType() {
return responseType;
}
public void setResponseType(Type responseType) {
this.responseType = responseType;
}
public void setRequestBody(Object requestBody) {
this.requestBody = requestBody;
}
public void setHttpHeaders(HttpHeaders headers) {
if (headers != null) {
this.httpHeaders = headers;
}
}
@Override
public HttpMethod getMethod() {
return method;
}
@Override
public String getMethodValue() {
return method.name();
}
@Override
public URI getURI() {
return uri;
}
@Override
public HttpHeaders getHeaders() {
return httpHeaders;
}
@Override
public OutputStream getBody() {
return null;
}
@Override
public ClientHttpResponse execute() {
path = findUriPath(uri);
requestMeta = createRequestMeta(method.name(), uri);
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri.getRawSchemeSpecificPart());
queryParams = queryStringDecoder.parameters();
Map<String, Object> swaggerArguments = this.collectArguments();
// 异常流程,直接抛异常出去
return this.invoke(swaggerArguments);
}
protected RequestMeta createRequestMeta(String httpMethod, URI uri) {
String microserviceName = uri.getAuthority();
MicroserviceReferenceConfig microserviceReferenceConfig = SCBEngine.getInstance()
.createMicroserviceReferenceConfig(microserviceName);
MicroserviceMeta microserviceMeta = microserviceReferenceConfig.getLatestMicroserviceMeta();
ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta);
if (servicePathManager == null) {
throw new Error(String.format("no schema defined for %s:%s",
microserviceMeta.getAppId(),
microserviceMeta.getMicroserviceName()));
}
OperationLocator locator = servicePathManager.consumerLocateOperation(path, httpMethod);
RestOperationMeta swaggerRestOperation = locator.getOperation();
OperationMeta operationMeta = locator.getOperation().getOperationMeta();
ReferenceConfig referenceConfig = microserviceReferenceConfig.createReferenceConfig(operationMeta);
Map<String, String> pathParams = locator.getPathVarMap();
return new RequestMeta(referenceConfig, swaggerRestOperation, pathParams);
}
protected String findUriPath(URI uri) {
return uri.getRawPath();
}
protected Invocation prepareInvocation(Map<String, Object> swaggerArguments) {
Invocation invocation =
InvocationFactory.forConsumer(requestMeta.getReferenceConfig(),
requestMeta.getOperationMeta(),
requestMeta.getOperationMeta().buildBaseConsumerRuntimeType(),
swaggerArguments);
invocation.getHandlerContext().put(RestConst.REST_CLIENT_REQUEST_PATH,
path + (this.uri.getRawQuery() == null ? "" : "?" + this.uri.getRawQuery()));
if (context != null) {
invocation.addContext(context.getContext());
invocation.addLocalContext(context.getLocalContext());
}
if (responseType != null &&
!(responseType instanceof Class && Part.class.isAssignableFrom((Class<?>) responseType))) {
invocation.setSuccessResponseType(TypeFactory.defaultInstance().constructType(responseType));
}
invocation.getHandlerContext().put(RestConst.CONSUMER_HEADER, httpHeaders);
return invocation;
}
@VisibleForTesting
CseClientHttpResponse invoke(Map<String, Object> swaggerArguments) {
Invocation invocation = prepareInvocation(swaggerArguments);
Response response = doInvoke(invocation);
if (response.isSucceed()) {
return new CseClientHttpResponse(response);
}
throw ExceptionFactory.convertConsumerException(response.getResult());
}
protected Response doInvoke(Invocation invocation) {
return InvokerUtils.innerSyncInvoke(invocation);
}
protected Map<String, Object> collectArguments() {
HttpServletRequest mockRequest = new CommonToHttpServletRequest(requestMeta.getPathParams(), queryParams,
httpHeaders, requestBody, requestMeta.getSwaggerRestOperation().isFormData(),
requestMeta.getSwaggerRestOperation().getFileKeys());
// no types info, so will not convert any parameters
return RestCodec.restToArgs(mockRequest, requestMeta.getSwaggerRestOperation());
}
}