001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.remoting.httpinvoker; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.ObjectInputStream; 022import java.io.ObjectOutputStream; 023import java.io.OutputStream; 024 025import com.sun.net.httpserver.HttpExchange; 026import com.sun.net.httpserver.HttpHandler; 027 028import org.springframework.lang.UsesSunHttpServer; 029import org.springframework.remoting.rmi.RemoteInvocationSerializingExporter; 030import org.springframework.remoting.support.RemoteInvocation; 031import org.springframework.remoting.support.RemoteInvocationResult; 032 033/** 034 * HTTP request handler that exports the specified service bean as 035 * HTTP invoker service endpoint, accessible via an HTTP invoker proxy. 036 * Designed for Sun's JRE 1.6 HTTP server, implementing the 037 * {@link com.sun.net.httpserver.HttpHandler} interface. 038 * 039 * <p>Deserializes remote invocation objects and serializes remote invocation 040 * result objects. Uses Java serialization just like RMI, but provides the 041 * same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols. 042 * 043 * <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b> 044 * It is more powerful and more extensible than Hessian and Burlap, at the 045 * expense of being tied to Java. Nevertheless, it is as easy to set up as 046 * Hessian and Burlap, which is its main advantage compared to RMI. 047 * 048 * <p><b>WARNING: Be aware of vulnerabilities due to unsafe Java deserialization: 049 * Manipulated input streams could lead to unwanted code execution on the server 050 * during the deserialization step. As a consequence, do not expose HTTP invoker 051 * endpoints to untrusted clients but rather just between your own services.</b> 052 * In general, we strongly recommend any other message format (e.g. JSON) instead. 053 * 054 * @author Juergen Hoeller 055 * @since 2.5.1 056 * @see org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor 057 * @see org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean 058 * @see org.springframework.remoting.caucho.SimpleHessianServiceExporter 059 * @see org.springframework.remoting.caucho.SimpleBurlapServiceExporter 060 */ 061@UsesSunHttpServer 062public class SimpleHttpInvokerServiceExporter extends RemoteInvocationSerializingExporter implements HttpHandler { 063 064 /** 065 * Reads a remote invocation from the request, executes it, 066 * and writes the remote invocation result to the response. 067 * @see #readRemoteInvocation(HttpExchange) 068 * @see #invokeAndCreateResult(RemoteInvocation, Object) 069 * @see #writeRemoteInvocationResult(HttpExchange, RemoteInvocationResult) 070 */ 071 @Override 072 public void handle(HttpExchange exchange) throws IOException { 073 try { 074 RemoteInvocation invocation = readRemoteInvocation(exchange); 075 RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy()); 076 writeRemoteInvocationResult(exchange, result); 077 exchange.close(); 078 } 079 catch (ClassNotFoundException ex) { 080 exchange.sendResponseHeaders(500, -1); 081 logger.error("Class not found during deserialization", ex); 082 } 083 } 084 085 /** 086 * Read a RemoteInvocation from the given HTTP request. 087 * <p>Delegates to {@link #readRemoteInvocation(HttpExchange, InputStream)} 088 * with the {@link HttpExchange#getRequestBody()} request's input stream}. 089 * @param exchange current HTTP request/response 090 * @return the RemoteInvocation object 091 * @throws java.io.IOException in case of I/O failure 092 * @throws ClassNotFoundException if thrown by deserialization 093 */ 094 protected RemoteInvocation readRemoteInvocation(HttpExchange exchange) 095 throws IOException, ClassNotFoundException { 096 097 return readRemoteInvocation(exchange, exchange.getRequestBody()); 098 } 099 100 /** 101 * Deserialize a RemoteInvocation object from the given InputStream. 102 * <p>Gives {@link #decorateInputStream} a chance to decorate the stream 103 * first (for example, for custom encryption or compression). Creates a 104 * {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream} 105 * and calls {@link #doReadRemoteInvocation} to actually read the object. 106 * <p>Can be overridden for custom serialization of the invocation. 107 * @param exchange current HTTP request/response 108 * @param is the InputStream to read from 109 * @return the RemoteInvocation object 110 * @throws java.io.IOException in case of I/O failure 111 * @throws ClassNotFoundException if thrown during deserialization 112 */ 113 protected RemoteInvocation readRemoteInvocation(HttpExchange exchange, InputStream is) 114 throws IOException, ClassNotFoundException { 115 116 ObjectInputStream ois = createObjectInputStream(decorateInputStream(exchange, is)); 117 return doReadRemoteInvocation(ois); 118 } 119 120 /** 121 * Return the InputStream to use for reading remote invocations, 122 * potentially decorating the given original InputStream. 123 * <p>The default implementation returns the given stream as-is. 124 * Can be overridden, for example, for custom encryption or compression. 125 * @param exchange current HTTP request/response 126 * @param is the original InputStream 127 * @return the potentially decorated InputStream 128 * @throws java.io.IOException in case of I/O failure 129 */ 130 protected InputStream decorateInputStream(HttpExchange exchange, InputStream is) throws IOException { 131 return is; 132 } 133 134 /** 135 * Write the given RemoteInvocationResult to the given HTTP response. 136 * @param exchange current HTTP request/response 137 * @param result the RemoteInvocationResult object 138 * @throws java.io.IOException in case of I/O failure 139 */ 140 protected void writeRemoteInvocationResult(HttpExchange exchange, RemoteInvocationResult result) 141 throws IOException { 142 143 exchange.getResponseHeaders().set("Content-Type", getContentType()); 144 exchange.sendResponseHeaders(200, 0); 145 writeRemoteInvocationResult(exchange, result, exchange.getResponseBody()); 146 } 147 148 /** 149 * Serialize the given RemoteInvocation to the given OutputStream. 150 * <p>The default implementation gives {@link #decorateOutputStream} a chance 151 * to decorate the stream first (for example, for custom encryption or compression). 152 * Creates an {@link java.io.ObjectOutputStream} for the final stream and calls 153 * {@link #doWriteRemoteInvocationResult} to actually write the object. 154 * <p>Can be overridden for custom serialization of the invocation. 155 * @param exchange current HTTP request/response 156 * @param result the RemoteInvocationResult object 157 * @param os the OutputStream to write to 158 * @throws java.io.IOException in case of I/O failure 159 * @see #decorateOutputStream 160 * @see #doWriteRemoteInvocationResult 161 */ 162 protected void writeRemoteInvocationResult( 163 HttpExchange exchange, RemoteInvocationResult result, OutputStream os) throws IOException { 164 165 ObjectOutputStream oos = createObjectOutputStream(decorateOutputStream(exchange, os)); 166 doWriteRemoteInvocationResult(result, oos); 167 oos.flush(); 168 } 169 170 /** 171 * Return the OutputStream to use for writing remote invocation results, 172 * potentially decorating the given original OutputStream. 173 * <p>The default implementation returns the given stream as-is. 174 * Can be overridden, for example, for custom encryption or compression. 175 * @param exchange current HTTP request/response 176 * @param os the original OutputStream 177 * @return the potentially decorated OutputStream 178 * @throws java.io.IOException in case of I/O failure 179 */ 180 protected OutputStream decorateOutputStream(HttpExchange exchange, OutputStream os) throws IOException { 181 return os; 182 } 183 184}