我理解,在GWT的RPC上,使用ClientSerializationStreamWriter在客户端上序列化参数,并使用ServerSerializationStreamReader在服务器上反序列化,另一方面,使用ServerSerializationStreamWriter在服务器上序列化返回值并反序列化在客户端上使用ClientserializationStreamReader。
我试图在发件人方面计算一个校验和或类似的东西,它基于序列化参数(客户端 - >服务器)或返回值(服务器 - >客户端)并添加它到序列化流。在接收方的一侧,校验和从序列化和接收的参数或返回值(当然没有额外的校验和)重新计算,并与接收的校验和进行比较。
为了使校验和计算与具体参数/返回值类型无关,我更倾向于将计算基于序列化值而不是原始类型。这将节省我对对象图的额外遍历,从而解决另一个问题:考虑对象图中的所有值,而不可能依赖Java的反射API(客户端上不可用)。
我首先考虑引入参数对象(由任何给定RPC的实际参数组成)和返回值包装器,然后为它们引入自定义字段序列化器。在序列化时,我可以首先序列化参数/返回值,从* SerializationStreamWriter访问序列化值,计算校验和并使用相同的* SerializationStreamReader写入校验和。然而,在接收器端,似乎我无法访问序列化值而没有使用* SerializationStreamReader实际读取它们,因为在调用第一个自定义字段序列化器时序列化流已经被标记化。
所以问题是:
答案 0 :(得分:0)
Actually Thomas (see comment added to question) did answer the question. @Thomas: thanks, that was exactly what I missed in my previous Research.
I can add some details to it.
As proposed by Thomas, on the client side a custom RpcRequestBuilder Needs to be set on the async service Interface:
IMyServiceAsync service = GWT.create(IMyService.class);
((ServiceDefTarget)service).setRpcRequestBuilder(new MyCustomRpcRequestBuilder());
The custom RpcRequestBuilder needs to override doSetRequestData
to manipulate the request that is about to be sent and doSetCallback
to get its hands on the response before it is processed:
private final class MyCustomRpcRequestBuilder extends RpcRequestBuilder {
@Override
protected void doSetRequestData(RequestBuilder rb, String payload) {
// do whatever you need to do on the payload, request header, ...
String checksum = calculateChecksum(payload);
rb.setHeader(MY_CUSTOM_HEADER_NAME, checksum);
super.doSetRequestData(rb, payload);
}
@Override
protected void doSetCallback(RequestBuilder rb, final RequestCallback callback) {
super.doSetCallback(rb, new RequestCallback() {
@Override
public void onResponseReceived(Request request, Response response) {
// do whatever you need to do on the response header, ...
String receivedChecksum = response.getHeader(MY_CUSTOM_HEADER_NAME);
if (receivedChecksum.equals(calculateChecksum(response.getText())) {
callback.onResponseReceived(request, response);
}
else {
callback.onError(request, new InvalidChecksumException(...));
}
}
@Override
public void onError(Request request, Throwable exception) {
callback.onError(request, exception);
}
});
}
}
On the server side a servlet filter can be used. GWT's RPCServletUtils has some useful methods herefore:
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest)req;
HttpServletResponse httpResp = (HttpServletResponse)resp;
String receivedChecksum = httpReq.getHeader(MY_CUSTOM_HEADER_NAME);
String reqPayload = RPCServletUtils.readContentAsGwtRpc(httpReq);
if (receivedChecksum.equals(calculateChecksum(reqPayload)) {
final ServletInputStream inputStream = new ByteArrayServletInputStream(reqPayload.getBytes(RPCServletUtils.CHARSET_UTF8));
HttpServletRequestWrapper reqWrapper = new HttpServletRequestWrapper(httpReq) {
@Override
public ServletInputStream getInputStream() throws IOException {
return inputStream;
}
};
final CachingServletOutputStream outputStream = new CachingServletOutputStream();
HttpServletResponseWrapper respWrapper = new HttpServletResponseWrapper(httpResp) {
@Override
public ServletOutputStream getOutputStream() throws IOException {
return outputStream;
}
};
filterChain.doFilter(reqWrapper, respWrapper);
// possibly you also need to handle gzipped payload
String respPayload = new String(outputStream.getCachedDataAsBytes(), RPCServletUtils.CHARSET_UTF8);
String checksum = calculateChecksum(respPayload);
httpResp.setHeader(MY_CUSTOM_HEADER_NAME, checksum);
httpResp.getOutputStream().write(outputStream.getCachedDataAsBytes());
} else {
httpResp.setContentType("text/plain");
httpResp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
httpResp.getOutputStream().write("Invalid checksum".getBytes(RPCServletUtils.CHARSET_UTF8));
}
}
static class ByteArrayServletInputStream extends ServletInputStream {
private final InputStream delegate;
ByteArrayServletInputStream(byte[] data) throws IOException, ServletException {
delegate = new ByteArrayInputStream(data);
}
@Override
public int read() throws IOException {
return delegate.read();
}
}
static class CachingServletOutputStream extends ServletOutputStream {
private final ByteArrayOutputStream cache;
CachingServletOutputStream() {
this.cache = new ByteArrayOutputStream(4096);
}
@Override
public void write(int b) throws IOException {
cache.write(b);
}
@Override
public void flush() throws IOException {
cache.flush();
}
byte[] getCachedDataAsBytes() {
return cache.toByteArray();
}
}
}