/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.ext.servlet.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Principal;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.restlet.Response;
import org.restlet.Server;
import org.restlet.data.Form;
import org.restlet.data.Header;
import org.restlet.data.MediaType;
import org.restlet.data.Protocol;
import org.restlet.data.Status;
import org.restlet.engine.adapter.ServerCall;
import org.restlet.engine.header.HeaderConstants;
import org.restlet.engine.header.LanguageReader;
import org.restlet.engine.io.UnclosableInputStream;
import org.restlet.engine.io.UnclosableOutputStream;
import org.restlet.representation.Representation;
import org.restlet.util.Series;
/**
* Call that is used by the Servlet HTTP server connector.
*
* @author Jerome Louvel
*/
public class ServletCall extends ServerCall {
/** The HTTP Servlet request to wrap. */
private volatile HttpServletRequest request;
/** The request headers. */
private volatile Series<Header> requestHeaders;
/** The HTTP Servlet response to wrap. */
private volatile HttpServletResponse response;
/**
* Constructor.
*
* @param server
* The parent server.
* @param request
* The HTTP Servlet request to wrap.
* @param response
* The HTTP Servlet response to wrap.
*/
public ServletCall(Server server, HttpServletRequest request,
HttpServletResponse response) {
super(server);
this.request = request;
this.response = response;
}
/**
* Constructor.
*
* @param serverAddress
* The server IP address.
* @param serverPort
* The server port.
* @param request
* The Servlet request
* @param response
* The Servlet response.
*/
public ServletCall(String serverAddress, int serverPort,
HttpServletRequest request, HttpServletResponse response) {
super(serverAddress, serverPort);
this.request = request;
this.response = response;
}
/**
* Not supported. Always returns false.
*/
@Override
public boolean abort() {
return false;
}
@Override
public void flushBuffers() throws IOException {
getResponse().flushBuffer();
}
@Override
public List<Certificate> getCertificates() {
Certificate[] certificateArray = (Certificate[]) getRequest()
.getAttribute("javax.servlet.request.X509Certificate");
if (certificateArray != null) {
return Arrays.asList(certificateArray);
}
return null;
}
@Override
public String getCipherSuite() {
return (String) getRequest().getAttribute(
"javax.servlet.request.cipher_suite");
}
@Override
public String getClientAddress() {
return getRequest().getRemoteAddr();
}
@Override
public int getClientPort() {
return getRequest().getRemotePort();
}
/**
* Returns the server domain name.
*
* @return The server domain name.
*/
@Override
public String getHostDomain() {
return getRequest().getServerName();
}
/**
* Returns the request method.
*
* @return The request method.
*/
@Override
public String getMethod() {
return getRequest().getMethod();
}
/**
* Returns the server protocol.
*
* @return The server protocol.
*/
@Override
public Protocol getProtocol() {
return Protocol.valueOf(getRequest().getScheme());
}
/**
* Returns the HTTP Servlet request.
*
* @return The HTTP Servlet request.
*/
public HttpServletRequest getRequest() {
return this.request;
}
// [ifdef gae] method
@Override
public Representation getRequestEntity() {
Representation result = null;
if (getRequest().getContentType() != null
&& MediaType.APPLICATION_WWW_FORM.isCompatible(new MediaType(
getRequest().getContentType()))) {
Form form = new Form();
Map<String, String[]> map = request.getParameterMap();
for (Entry<String, String[]> entry : map.entrySet()) {
for (int i = 0; i < entry.getValue().length; i++) {
form.add(entry.getKey(), entry.getValue()[i]);
}
}
result = form.getWebRepresentation();
// Extract some interesting header values
Header header = getRequestHeaders().getFirst(
HeaderConstants.HEADER_CONTENT_LANGUAGE, true);
if (header != null) {
new LanguageReader(header.getValue()).addValues(result
.getLanguages());
}
} else {
result = super.getRequestEntity();
}
return result;
}
@Override
public InputStream getRequestEntityStream(long size) {
try {
return new UnclosableInputStream(getRequest().getInputStream());
} catch (IOException e) {
return null;
}
}
/**
* Returns the list of request headers.
*
* @return The list of request headers.
*/
@Override
public Series<Header> getRequestHeaders() {
if (this.requestHeaders == null) {
this.requestHeaders = new Series<Header>(Header.class);
// Copy the headers from the request object
String headerName;
String headerValue;
for (Enumeration<String> names = getRequest().getHeaderNames(); names
.hasMoreElements();) {
headerName = names.nextElement();
for (Enumeration<String> values = getRequest().getHeaders(
headerName); values.hasMoreElements();) {
headerValue = values.nextElement();
this.requestHeaders.add(headerName, headerValue);
}
}
}
return this.requestHeaders;
}
@Override
public InputStream getRequestHeadStream() {
// Not available
return null;
}
/**
* Returns the full request URI.
*
* @return The full request URI.
*/
@Override
public String getRequestUri() {
final String queryString = getRequest().getQueryString();
if ((queryString == null) || (queryString.equals(""))) {
return getRequest().getRequestURI();
}
return getRequest().getRequestURI() + '?' + queryString;
}
/**
* Returns the HTTP Servlet response.
*
* @return The HTTP Servlet response.
*/
public HttpServletResponse getResponse() {
return this.response;
}
/**
* Returns the response stream if it exists, null otherwise.
*
* @return The response stream if it exists, null otherwise.
*/
@Override
public OutputStream getResponseEntityStream() {
try {
return new UnclosableOutputStream(getResponse().getOutputStream());
} catch (IOException e) {
return null;
}
}
/**
* Returns the response address.<br>
* Corresponds to the IP address of the responding server.
*
* @return The response address.
*/
@Override
public String getServerAddress() {
return getRequest().getLocalAddr();
}
/**
* Returns the server port.
*
* @return The server port.
*/
@Override
public int getServerPort() {
return getRequest().getServerPort();
}
@Override
public Integer getSslKeySize() {
Integer keySize = (Integer) getRequest().getAttribute(
"javax.servlet.request.key_size");
if (keySize == null) {
keySize = super.getSslKeySize();
}
return keySize;
}
@Override
public String getSslSessionId() {
Object sessionId = getRequest().getAttribute(
"javax.servlet.request.ssl_session_id");
if ((sessionId != null) && (sessionId instanceof String)) {
return (String) sessionId;
}
/*
* The following is for the non-standard, pre-Servlet 3 spec used by
* Tomcat/Coyote.
*/
sessionId = getRequest().getAttribute(
"javax.servlet.request.ssl_session");
if (sessionId instanceof String) {
return (String) sessionId;
}
return null;
}
@Override
public Principal getUserPrincipal() {
return getRequest().getUserPrincipal();
}
@Override
public String getVersion() {
String result = null;
final int index = getRequest().getProtocol().indexOf('/');
if (index != -1) {
result = getRequest().getProtocol().substring(index + 1);
}
return result;
}
/**
* Indicates if the request was made using a confidential mean.<br>
*
* @return True if the request was made using a confidential mean.<br>
*/
@Override
public boolean isConfidential() {
return getRequest().isSecure();
}
/**
* Sends the response back to the client. Commits the status, headers and
* optional entity and send them on the network.
*
* @param response
* The high-level response.
*/
@Override
public void sendResponse(Response response) throws IOException {
// Set the status code in the response. We do this after adding the
// headers because when we have to rely on the 'sendError' method,
// the Servlet containers are expected to commit their response.
if (Status.isError(getStatusCode()) && (response.getEntity() == null)) {
try {
// Add the response headers
Header header;
for (Iterator<Header> iter = getResponseHeaders().iterator(); iter
.hasNext();) {
header = iter.next();
// We don't need to set the content length, especially
// because it could send the response too early on some
// containers (ex: Tomcat 5.0).
if (!header.getName().equals(
HeaderConstants.HEADER_CONTENT_LENGTH)) {
getResponse().addHeader(header.getName(),
header.getValue());
}
}
getResponse().sendError(getStatusCode(), getReasonPhrase());
} catch (IOException ioe) {
getLogger().log(Level.WARNING,
"Unable to set the response error status", ioe);
}
} else {
// Send the response entity
getResponse().setStatus(getStatusCode());
// Add the response headers after setting the status because
// otherwise some containers (ex: Tomcat 5.0) immediately send
// the response if a "Content-Length: 0" header is found.
Header header;
Header contentLengthHeader = null;
for (Iterator<Header> iter = getResponseHeaders().iterator(); iter
.hasNext();) {
header = iter.next();
if (header.getName().equals(
HeaderConstants.HEADER_CONTENT_LENGTH)) {
contentLengthHeader = header;
} else {
getResponse()
.addHeader(header.getName(), header.getValue());
}
}
if (contentLengthHeader != null) {
getResponse().addHeader(contentLengthHeader.getName(),
contentLengthHeader.getValue());
}
super.sendResponse(response);
}
}
}