Skip to content

Commit

Permalink
[SCB-1961] add rest server codec filter
Browse files Browse the repository at this point in the history
  • Loading branch information
wujimin committed May 30, 2020
1 parent 4637981 commit c3491e3
Show file tree
Hide file tree
Showing 9 changed files with 443 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;

import javax.ws.rs.core.HttpHeaders;
Expand All @@ -33,6 +32,7 @@
import org.apache.servicecomb.common.rest.definition.RestOperationMeta;
import org.apache.servicecomb.common.rest.filter.HttpServerFilter;
import org.apache.servicecomb.common.rest.filter.HttpServerFilterBeforeSendResponseExecutor;
import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter;
import org.apache.servicecomb.common.rest.locator.OperationLocator;
import org.apache.servicecomb.common.rest.locator.ServicePathManager;
import org.apache.servicecomb.core.Const;
Expand Down Expand Up @@ -268,16 +268,8 @@ protected void sendResponseQuietly(Response response) {

@SuppressWarnings("deprecation")
protected void sendResponse(Response response) {
if (response.getHeaders().getHeaderMap() != null) {
for (Entry<String, List<Object>> entry : response.getHeaders().getHeaderMap().entrySet()) {
for (Object value : entry.getValue()) {
if (!entry.getKey().equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)
&& !entry.getKey().equalsIgnoreCase("Transfer-Encoding")) {
responseEx.addHeader(entry.getKey(), String.valueOf(value));
}
}
}
}
RestServerCodecFilter.copyHeadersToHttpResponse(response.getHeaders().getHeaderMap(), responseEx);

responseEx.setStatus(response.getStatusCode(), response.getReasonPhrase());
responseEx.setAttribute(RestConst.INVOCATION_HANDLER_RESPONSE, response);
responseEx.setAttribute(RestConst.INVOCATION_HANDLER_PROCESSOR, produceProcessor);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.common.rest.filter;

import java.util.Arrays;
import java.util.List;

import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter;
import org.apache.servicecomb.core.filter.Filter;
import org.apache.servicecomb.core.filter.FilterProvider;

public class RestFilterProvider implements FilterProvider {
@Override
public List<Class<? extends Filter>> getFilters() {
return Arrays.asList(
RestServerCodecFilter.class
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* 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.common.rest.filter.inner;

import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static org.apache.servicecomb.core.exception.Exceptions.exceptionToResponse;
import static org.apache.servicecomb.swagger.invocation.InvocationType.PRODUCER;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;

import javax.servlet.http.Part;

import org.apache.servicecomb.common.rest.HttpTransportContext;
import org.apache.servicecomb.common.rest.RestConst;
import org.apache.servicecomb.common.rest.codec.RestCodec;
import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
import org.apache.servicecomb.common.rest.definition.RestOperationMeta;
import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.definition.OperationMeta;
import org.apache.servicecomb.core.filter.Filter;
import org.apache.servicecomb.core.filter.FilterMeta;
import org.apache.servicecomb.core.filter.FilterNode;
import org.apache.servicecomb.foundation.common.utils.AsyncUtils;
import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream;
import org.apache.servicecomb.swagger.invocation.Response;

import io.netty.buffer.Unpooled;

@FilterMeta(name = "rest-server-codec", invocationType = PRODUCER)
public class RestServerCodecFilter implements Filter {
@Override
public CompletableFuture<Response> onFilter(Invocation invocation, FilterNode nextNode) {
return CompletableFuture.completedFuture(invocation)
.thenCompose(this::decodeRequest)
.thenCompose(nextNode::onFilter)
.exceptionally(exception -> exceptionToResponse(invocation, exception, INTERNAL_SERVER_ERROR))
.thenCompose(response -> encodeResponse(invocation, response));
}

private CompletableFuture<Invocation> decodeRequest(Invocation invocation) {
HttpTransportContext transportContext = invocation.getTransportContext();
HttpServletRequestEx requestEx = transportContext.getRequestEx();

OperationMeta operationMeta = invocation.getOperationMeta();
RestOperationMeta restOperationMeta = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
Map<String, Object> swaggerArguments = RestCodec.restToArgs(requestEx, restOperationMeta);
invocation.setSwaggerArguments(swaggerArguments);

return CompletableFuture.completedFuture(invocation);
}

private CompletableFuture<Response> encodeResponse(Invocation invocation, Response response) {
HttpTransportContext transportContext = invocation.getTransportContext();
ProduceProcessor produceProcessor = transportContext.getProduceProcessor();
HttpServletResponseEx responseEx = transportContext.getResponseEx();
boolean download = isDownloadFileResponseType(invocation, response);

return encodeResponse(response, download, produceProcessor, responseEx);
}

@SuppressWarnings("deprecation")
public static CompletableFuture<Response> encodeResponse(Response response, boolean download,
ProduceProcessor produceProcessor, HttpServletResponseEx responseEx) {
responseEx.setStatus(response.getStatusCode(), response.getReasonPhrase());
copyHeadersToHttpResponse(response.getHeaders().getHeaderMap(), responseEx);

if (download) {
return CompletableFuture.completedFuture(response);
}

responseEx.setContentType(produceProcessor.getName() + "; charset=utf-8");
try (BufferOutputStream output = new BufferOutputStream(Unpooled.compositeBuffer())) {
produceProcessor.encodeResponse(output, response.getResult());

responseEx.setBodyBuffer(output.getBuffer());

return CompletableFuture.completedFuture(response);
} catch (Throwable e) {
return AsyncUtils.completeExceptionally(e);
}
}

/**
* Check whether this response is a downloaded file response,
* according to the schema recorded in {@link org.apache.servicecomb.swagger.invocation.response.ResponsesMeta}
* and response status code.
* @return true if this response is a downloaded file, otherwise false.
*/
public static boolean isDownloadFileResponseType(Invocation invocation, Response response) {
return Part.class.isAssignableFrom(
invocation.findResponseType(response.getStatusCode()).getRawClass());
}

public static void copyHeadersToHttpResponse(Map<String, List<Object>> headerMap, HttpServletResponseEx responseEx) {
if (headerMap == null) {
return;
}

for (Entry<String, List<Object>> entry : headerMap.entrySet()) {
for (Object value : entry.getValue()) {
if (!entry.getKey().equalsIgnoreCase(CONTENT_LENGTH)
&& !entry.getKey().equalsIgnoreCase(TRANSFER_ENCODING)) {
responseEx.addHeader(entry.getKey(), String.valueOf(value));
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@

package org.apache.servicecomb.common.rest.filter.inner;

import static org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter.isDownloadFileResponseType;

import java.util.Map;
import java.util.concurrent.CompletableFuture;

import javax.servlet.http.Part;

import org.apache.servicecomb.common.rest.RestConst;
import org.apache.servicecomb.common.rest.codec.RestCodec;
import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
Expand Down Expand Up @@ -91,15 +91,4 @@ public CompletableFuture<Void> beforeSendResponseAsync(Invocation invocation, Ht
}
return future;
}

/**
* Check whether this response is a downloaded file response,
* according to the schema recorded in {@link org.apache.servicecomb.swagger.invocation.response.ResponsesMeta}
* and response status code.
* @return true if this response is a downloaded file, otherwise false.
*/
private boolean isDownloadFileResponseType(Invocation invocation, Response response) {
return Part.class.isAssignableFrom(
invocation.findResponseType(response.getStatusCode()).getRawClass());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#
# 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.
#

org.apache.servicecomb.common.rest.filter.RestFilterProvider
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class RestProducerInvocationCreatorTest {
static SCBEngine engine;

@BeforeClass
public static void beforeClass() throws Exception {
public static void beforeClass() {
ArchaiusUtils.resetConfig();
ConfigUtil.installDynamicConfig();

Expand All @@ -87,7 +87,7 @@ public static void beforeClass() throws Exception {
}

@AfterClass
public static void afterClass() throws Exception {
public static void afterClass() {
engine.destroy();
ArchaiusUtils.resetConfig();
}
Expand Down

0 comments on commit c3491e3

Please sign in to comment.