Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
package io.servicecomb.common.rest;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Response.Status;

import org.apache.commons.lang3.StringUtils;
Expand All @@ -30,6 +32,7 @@
import io.servicecomb.common.rest.codec.produce.ProduceProcessor;
import io.servicecomb.common.rest.codec.produce.ProduceProcessorManager;
import io.servicecomb.common.rest.definition.RestOperationMeta;
import io.servicecomb.common.rest.filter.HttpServerFilter;
import io.servicecomb.common.rest.locator.OperationLocator;
import io.servicecomb.common.rest.locator.ServicePathManager;
import io.servicecomb.core.Const;
Expand All @@ -40,6 +43,7 @@
import io.servicecomb.core.definition.OperationMeta;
import io.servicecomb.core.invocation.InvocationFactory;
import io.servicecomb.foundation.common.utils.JsonUtils;
import io.servicecomb.foundation.common.utils.SPIServiceUtils;
import io.servicecomb.serviceregistry.RegistryUtils;
import io.servicecomb.swagger.invocation.Response;
import io.servicecomb.swagger.invocation.exception.InvocationException;
Expand All @@ -50,6 +54,14 @@ public abstract class AbstractRestServer<HTTP_RESPONSE> {
// 所属的Transport
protected Transport transport;

protected List<HttpServerFilter> httpServerFilters = SPIServiceUtils.getSortedService(HttpServerFilter.class);;

public AbstractRestServer() {
for (HttpServerFilter filter : httpServerFilters) {
LOGGER.info("Found HttpServerFilter: {}.", filter.getClass().getName());
}
}

public void setTransport(Transport transport) {
this.transport = transport;
}
Expand Down Expand Up @@ -79,20 +91,20 @@ protected void handleRequest(RestServerRequestInternal restRequest, HTTP_RESPONS
runOnExecutor(restRequest, restOperation, httpResponse);
} catch (Exception e) {
LOGGER.error("rest server onRequest error", e);
sendFailResponse(restRequest, httpResponse, e);
sendFailResponse(null, restRequest, httpResponse, e);
}
});
} catch (Exception e) {
LOGGER.error("rest server onRequest error", e);
sendFailResponse(restRequest, httpResponse, e);
sendFailResponse(null, restRequest, httpResponse, e);
}
}

protected void runOnExecutor(RestServerRequestInternal restRequest, RestOperationMeta restOperation,
HTTP_RESPONSE httpResponse) throws Exception {
String acceptType = restRequest.getHeaderParam("Accept");
ProduceProcessor produceProcessor =
locateProduceProcessor(restRequest, httpResponse, restOperation, acceptType);
locateProduceProcessor(null, restRequest, httpResponse, restOperation, acceptType);
if (produceProcessor == null) {
// locateProduceProcessor内部已经应答了
return;
Expand All @@ -107,8 +119,18 @@ protected void runOnExecutor(RestServerRequestInternal restRequest, RestOperatio
this.setContext(invocation, restRequest);
this.setHttpRequestContext(invocation, restRequest);

if (HttpServletRequest.class.isInstance(restRequest.getHttpRequest())) {
for (HttpServerFilter filter : httpServerFilters) {
Response response = filter.afterReceiveRequest(invocation, restRequest.getHttpRequest());
if (response != null) {
sendResponse(invocation, restRequest, httpResponse, produceProcessor, response);
return;
}
}
}

invocation.next(resp -> {
sendResponse(restRequest, httpResponse, produceProcessor, resp);
sendResponse(invocation, restRequest, httpResponse, produceProcessor, resp);
});
}

Expand All @@ -134,7 +156,7 @@ protected RestOperationMeta findRestOperation(RestServerRequestInternal restRequ
}

// 找不到processor,则已经完成了应答,外界不必再处理
protected ProduceProcessor locateProduceProcessor(RestServerRequestInternal restRequest,
protected ProduceProcessor locateProduceProcessor(Invocation invocation, RestServerRequestInternal restRequest,
HTTP_RESPONSE httpResponse,
RestOperationMeta restOperation, String acceptType) {
ProduceProcessor produceProcessor = restOperation.ensureFindProduceProcessor(acceptType);
Expand All @@ -144,21 +166,22 @@ protected ProduceProcessor locateProduceProcessor(RestServerRequestInternal rest

String msg = String.format("Accept %s is not supported", acceptType);
InvocationException exception = new InvocationException(Status.NOT_ACCEPTABLE, msg);
sendFailResponse(restRequest, httpResponse, exception);
sendFailResponse(invocation, restRequest, httpResponse, exception);
return null;
}

public void sendFailResponse(RestServerRequestInternal restRequest, HTTP_RESPONSE httpResponse,
public void sendFailResponse(Invocation invocation, RestServerRequestInternal restRequest, HTTP_RESPONSE httpResponse,
Throwable throwable) {
Response response = Response.createProducerFail(throwable);
sendResponse(restRequest, httpResponse, ProduceProcessorManager.DEFAULT_PROCESSOR, response);
sendResponse(invocation, restRequest, httpResponse, ProduceProcessorManager.DEFAULT_PROCESSOR, response);
}

// 成功、失败的统一应答处理,这里不能再出异常了,再出了异常也没办法处理
protected void sendResponse(RestServerRequestInternal restRequest, HTTP_RESPONSE httpServerResponse,
protected void sendResponse(Invocation invocation, RestServerRequestInternal restRequest,
HTTP_RESPONSE httpServerResponse,
ProduceProcessor produceProcessor, Response response) {
try {
doSendResponse(httpServerResponse, produceProcessor, response);
doSendResponse(invocation, httpServerResponse, produceProcessor, response);
} catch (Throwable e) {
// 这只能是bug,没有办法再兜底了,只能记录日志
// 如果统一处理为500错误,也无法确定swagger中500对应的数据模型
Expand All @@ -172,7 +195,8 @@ protected void sendResponse(RestServerRequestInternal restRequest, HTTP_RESPONSE
}

// 成功、失败的统一应答处理
protected abstract void doSendResponse(HTTP_RESPONSE httpServerResponse, ProduceProcessor produceProcessor,
protected abstract void doSendResponse(Invocation invocation, HTTP_RESPONSE httpServerResponse,
ProduceProcessor produceProcessor,
Response response) throws Exception;

// 将http request注入到invocation的handler context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public final class RestConst {
private RestConst() {
}

public static final String CONFIG_COPY_REQUEST = "servicecomb.rest.copy-request";
public static final String HTTP_REQUEST_CREATOR = "server-http-request-creator";

public static final String REST_CLIENT_REQUEST_PATH = "rest-client-request-path";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
* 在写cookie参数时,没办法多次添加cookie,所以只能进行接口包装
*/
public interface RestClientRequest {

void write(Buffer bodyBuffer);

void end() throws Exception;
Expand All @@ -33,4 +32,6 @@ public interface RestClientRequest {
void putHeader(String name, String value);

void addForm(String name, Object value);

Buffer getBodyBuffer() throws Exception;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package io.servicecomb.common.rest.codec;

import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
Expand All @@ -31,6 +32,7 @@ public final class RestObjectMapper extends ObjectMapper {
private RestObjectMapper() {
// swagger中要求date使用ISO8601格式传递,这里与之做了功能绑定,这在cse中是没有问题的
setDateFormat(new ISO8601DateFormat());
getFactory().disable(Feature.AUTO_CLOSE_SOURCE);
}

public String convertToString(Object value) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.InputStream;
import java.lang.reflect.Type;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;

import org.apache.commons.io.IOUtils;
Expand Down Expand Up @@ -70,6 +71,7 @@ public Object getValue(RestServerRequest request) throws Exception {
@Override
public void setValue(RestClientRequest clientRequest, Object arg) throws Exception {
try (BufferOutputStream output = new BufferOutputStream()) {
clientRequest.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
RestObjectMapper.INSTANCE.writeValue(output, arg);
clientRequest.write(output.getBuffer());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ public void write(Buffer bodyBuffer) {
this.bodyBuffer = bodyBuffer;
}

@Override
public Buffer getBodyBuffer() throws Exception {
genBodyBuffer();
return bodyBuffer;
}

@Override
public void end() throws Exception {
writeCookies();
Expand All @@ -63,8 +69,6 @@ public void end() throws Exception {
}

private void genBodyBuffer() throws Exception {
request.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);

if (bodyBuffer != null) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2017 Huawei Technologies Co., Ltd
*
* 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.servicecomb.common.rest.filter;

import io.servicecomb.core.Invocation;
import io.servicecomb.swagger.invocation.Response;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpClientResponse;

public interface HttpClientFilter {
int getOrder();

// bodyBytes maybe be null
void beforeSendRequest(Invocation invocation, HttpClientRequest clientRequest, Buffer requestBodyBuffer);

// if finished, then return a none null response
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to give a java doc comment. spell non null ? or a response that is not null

// if return a null response, then sdk will call next filter.afterReceive
Response afterReceiveResponse(Invocation invocation, HttpClientResponse httpResponse, Buffer responseBodyBuffer)
throws Exception;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2017 Huawei Technologies Co., Ltd
*
* 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.servicecomb.common.rest.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import io.servicecomb.core.Invocation;
import io.servicecomb.swagger.invocation.Response;

public interface HttpServerFilter {
int getOrder();

//if check failed, then return failed response
Response afterReceiveRequest(Invocation invocation, HttpServletRequest request);

// invocation maybe null
void beforeSendResponse(Invocation invocation, HttpServletResponse response, byte[] bodyBytes, int length);
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public void before() throws Exception {
restRequest = Mockito.mock(RestServerRequestInternal.class);
restServer = new AbstractRestServer<Response>() {
@Override
protected void doSendResponse(Response httpServerResponse, ProduceProcessor produceProcessor,
protected void doSendResponse(Invocation invocation, Response httpServerResponse,
ProduceProcessor produceProcessor,
Response response) throws Exception {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2017 Huawei Technologies Co., Ltd
*
* 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.servicecomb.common.rest.codec;

import org.junit.Assert;
import org.junit.Test;

import com.fasterxml.jackson.core.JsonParser.Feature;

public class TestRestObjectMapper {
@Test
public void testAutoCloseSource() {
Assert.assertFalse(RestObjectMapper.INSTANCE.getFactory().isEnabled(Feature.AUTO_CLOSE_SOURCE));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,23 @@

package io.servicecomb.common.rest.codec.param;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import io.servicecomb.common.rest.codec.RestClientRequest;
import io.servicecomb.common.rest.codec.param.BodyProcessorCreator.RawJsonBodyProcessor;
import io.servicecomb.swagger.generator.core.SwaggerConst;
import io.swagger.models.parameters.BodyParameter;
import mockit.Mock;
import mockit.MockUp;

public class TestBodyProcessorCreator {
private static BodyProcessorCreator bodyCreator;
Expand All @@ -50,4 +58,21 @@ public void testBodyProcessorCreator() throws Exception {
processor = bodyCreator.create(bp, List.class);
Assert.assertFalse(RawJsonBodyProcessor.class.isInstance(processor));
}

@Test
public void testSetValue() throws Exception {
Map<String, String> header = new HashMap<>();
RestClientRequest clientRequest = new MockUp<RestClientRequest>() {
@Mock
public void putHeader(String name, String value) {
header.put(name, value);
}
}.getMockInstance();

BodyParameter bp = new BodyParameter();
ParamValueProcessor processor = bodyCreator.create(bp, String.class);
processor.setValue(clientRequest, "abc");

Assert.assertEquals(MediaType.APPLICATION_JSON, header.get(HttpHeaders.CONTENT_TYPE));
}
}
Loading