FELIX-4420 - Implement sendRedirect:
- rewrite redirects if the redirect-URL is at the same host as the rewritten URLs;
- added some tests in order to verify the correctness.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1597554 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilter.java b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilter.java
index 21ec51b..877a555 100644
--- a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilter.java
+++ b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilter.java
@@ -18,6 +18,9 @@
*/
package org.apache.felix.http.sslfilter.internal;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HDR_X_FORWARDED_SSL;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HDR_X_FORWARDED_SSL_CERTIFICATE;
+
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.Dictionary;
@@ -29,17 +32,19 @@
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.log.LogService;
+@SuppressWarnings("rawtypes")
public class SslFilter implements Filter
{
public static final String PID = "org.apache.felix.http.sslfilter.SslFilter";
- private static final String DEFAULT_SSL_HEADER = "X-Forwarded-SSL";
+ private static final String DEFAULT_SSL_HEADER = HDR_X_FORWARDED_SSL;
private static final String DEFAULT_SSL_VALUE = "on";
- private static final String DEFAULT_CERT_HEADER = "X-Forwarded-SSL-Certificate";
+ private static final String DEFAULT_CERT_HEADER = HDR_X_FORWARDED_SSL_CERTIFICATE;
private static final String PROP_SSL_HEADER = "ssl-forward.header";
private static final String PROP_SSL_VALUE = "ssl-forward.value";
@@ -62,12 +67,14 @@
final ConfigHolder cfg = this.config;
HttpServletRequest httpReq = (HttpServletRequest) req;
+ HttpServletResponse httpResp = (HttpServletResponse) res;
+
if (cfg.sslValue.equalsIgnoreCase(httpReq.getHeader(cfg.sslHeader)))
{
try
{
- // In case this fails, we fall back to the original HTTP
- // request, which is better than nothing...
+ httpResp = new SslFilterResponse(httpResp, httpReq);
+ // In case this fails, we fall back to the original HTTP request, which is better than nothing...
httpReq = new SslFilterRequest(httpReq, httpReq.getHeader(cfg.certHeader));
}
catch (CertificateException e)
@@ -76,11 +83,10 @@
}
}
- // forward the request making sure any certificate is removed
- // again after the request processing gets back here
+ // forward the request making sure any certificate is removed again after the request processing gets back here
try
{
- chain.doFilter(httpReq, res);
+ chain.doFilter(httpReq, httpResp);
}
finally
{
@@ -96,7 +102,7 @@
// make sure there is some configuration
}
- void configure(@SuppressWarnings("rawtypes") final Dictionary properties) throws ConfigurationException
+ void configure(Dictionary properties) throws ConfigurationException
{
String certHeader = DEFAULT_CERT_HEADER;
String sslHeader = DEFAULT_SSL_HEADER;
@@ -115,7 +121,7 @@
+ certHeader + "'.");
}
- private String getOptionalString(@SuppressWarnings("rawtypes") Dictionary properties, String key) throws ConfigurationException
+ private String getOptionalString(Dictionary properties, String key) throws ConfigurationException
{
Object raw = properties.get(key);
if (raw == null || "".equals(((String) raw).trim()))
@@ -129,7 +135,7 @@
return ((String) raw).trim();
}
- private String getMandatoryString(@SuppressWarnings("rawtypes") Dictionary properties, String key) throws ConfigurationException
+ private String getMandatoryString(Dictionary properties, String key) throws ConfigurationException
{
String value = getOptionalString(properties, key);
if (value == null)
diff --git a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterConstants.java b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterConstants.java
new file mode 100644
index 0000000..1b00449
--- /dev/null
+++ b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterConstants.java
@@ -0,0 +1,78 @@
+/*
+ * 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.felix.http.sslfilter.internal;
+
+/**
+ * Provides constants used in the SSL filter.
+ */
+interface SslFilterConstants
+{
+ /**
+ * If there is an SSL certificate associated with the request, it must be exposed by the servlet container to the
+ * servlet programmer as an array of objects of type java.security.cert.X509Certificate and accessible via a
+ * ServletRequest attribute of <tt>javax.servlet.request.X509Certificate</tt>.
+ * <p>
+ * The order of this array is defined as being in ascending order of trust. The first certificate in the chain is
+ * the one set by the client, the next is the one used to authenticate the first, and so on.
+ */
+ String ATTR_SSL_CERTIFICATE = "javax.servlet.request.X509Certificate";
+
+ /**
+ * De-facto header used to inform what protocol the forwarded client used to connect to the proxy, such as "https".
+ */
+ String HDR_X_FORWARDED_PROTO = "X-Forwarded-Proto";
+ /**
+ * De-facto header used to inform what port the forwarded client used to connect to the proxy, such as "433".
+ */
+ String HDR_X_FORWARDED_PORT = "X-Forwarded-Port";
+ /**
+ * De-facto header used to inform that the proxy is forwarding a SSL request.
+ */
+ String HDR_X_FORWARDED_SSL = "X-Forwarded-SSL";
+ /**
+ * De-facto(?) header used to pass the certificate the client used to connect to the proxy, in X.509 format.
+ */
+ String HDR_X_FORWARDED_SSL_CERTIFICATE = "X-Forwarded-SSL-Certificate";
+
+ /**
+ * HTTP header used to explain the client it should redirect to another URL.
+ */
+ String HDR_LOCATION = "Location";
+
+ /**
+ * HTTP protocol/scheme.
+ */
+ String HTTP = "http";
+ /**
+ * Default port used for HTTP.
+ */
+ int HTTP_PORT = 80;
+
+ /**
+ * HTTPS protocol/scheme.
+ */
+ String HTTPS = "https";
+ /**
+ * Default port used for HTTPS.
+ */
+ int HTTPS_PORT = 443;
+
+ String UTF_8 = "UTF-8";
+ String X_509 = "X.509";
+}
diff --git a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterRequest.java b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterRequest.java
index 63450af..d0c2289 100644
--- a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterRequest.java
+++ b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterRequest.java
@@ -18,6 +18,11 @@
*/
package org.apache.felix.http.sslfilter.internal;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.ATTR_SSL_CERTIFICATE;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTPS;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.UTF_8;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.X_509;
+
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
@@ -32,45 +37,29 @@
class SslFilterRequest extends HttpServletRequestWrapper
{
- // The HTTPS scheme name
- private static final String HTTPS_SCHEME = "https";
-
// The HTTP scheme prefix in an URL
private static final String HTTP_SCHEME_PREFIX = "http://";
// pattern to convert the header to a PEM certificate for parsing
// by replacing spaces with line breaks
- private static Pattern HEADER_TO_CERT = Pattern.compile("(?! CERTIFICATE)(?= ) ");
-
- // character encoding for the client certificate header
- private static final String UTF_8 = "UTF-8";
-
- /**
- * If there is an SSL certificate associated with the request, it must be
- * exposed by the servlet container to the servlet programmer as an array of
- * objects of type java.security.cert.X509Certificate and accessible via a
- * ServletRequest attribute of javax.servlet.request.X509Certificate.
- * <p>
- * The order of this array is defined as being in ascending order of trust.
- * The first certificate in the chain is the one set by the client, the next
- * is the one used to authenticate the first, and so on.
- */
- protected static final String ATTR_SSL_CERTIFICATE = "javax.servlet.request.X509Certificate";
-
- private String requestURL;
+ private static final Pattern HEADER_TO_CERT = Pattern.compile("(?! CERTIFICATE)(?= ) ");
@SuppressWarnings("unchecked")
SslFilterRequest(HttpServletRequest request, String clientCertHeader) throws CertificateException
{
super(request);
+
+ // TODO jawi: perhaps we should make this class a little smarter wrt the given request:
+ // it now always assumes it should rewrite its URL, while this might not always be the
+ // case...
- if (clientCertHeader != null && clientCertHeader.length() > 0)
+ if (clientCertHeader != null && !"".equals(clientCertHeader.trim()))
{
final String clientCert = HEADER_TO_CERT.matcher(clientCertHeader).replaceAll("\n");
try
{
- CertificateFactory fac = CertificateFactory.getInstance("X.509");
+ CertificateFactory fac = CertificateFactory.getInstance(X_509);
InputStream instream = new ByteArrayInputStream(clientCert.getBytes(UTF_8));
@@ -92,7 +81,7 @@
public String getScheme()
{
- return HTTPS_SCHEME;
+ return HTTPS;
}
public boolean isSecure()
@@ -102,19 +91,13 @@
public StringBuffer getRequestURL()
{
- if (this.requestURL == null)
+ StringBuffer tmp = new StringBuffer(super.getRequestURL());
+ // In case the request happened over http, simply insert an additional 's'
+ // to make the request appear to be done over https...
+ if (tmp.indexOf(HTTP_SCHEME_PREFIX) == 0)
{
- StringBuffer tmp = super.getRequestURL();
- if (tmp.indexOf(HTTP_SCHEME_PREFIX) == 0)
- {
- this.requestURL = HTTPS_SCHEME.concat(tmp.substring(4));
- }
- else
- {
- this.requestURL = tmp.toString();
- }
+ tmp.insert(4, 's');
}
-
- return new StringBuffer(this.requestURL);
+ return tmp;
}
}
diff --git a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterResponse.java b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterResponse.java
new file mode 100644
index 0000000..e7102b7
--- /dev/null
+++ b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterResponse.java
@@ -0,0 +1,148 @@
+/*
+ * 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.felix.http.sslfilter.internal;
+
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HDR_LOCATION;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HDR_X_FORWARDED_PORT;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HDR_X_FORWARDED_PROTO;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTP;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTPS;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTPS_PORT;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTP_PORT;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * Provides a custom {@link HttpServletResponse} for use in SSL filter.
+ */
+class SslFilterResponse extends HttpServletResponseWrapper
+{
+ private final String serverName;
+ private final String serverProto;
+ private final int serverPort;
+ private final String clientProto;
+ private final int clientPort;
+
+ public SslFilterResponse(HttpServletResponse response, HttpServletRequest request)
+ {
+ super(response);
+
+ // Only rewrite URLs for the host & port the request was sent to...
+ this.serverName = request.getServerName();
+ this.serverPort = request.getServerPort();
+
+ String proto = request.getHeader(HDR_X_FORWARDED_PROTO);
+ if (HTTP.equalsIgnoreCase(proto))
+ {
+ // Not really a useful scenario: client is talking HTTP to proxy, and we should rewrite all HTTPS-based URLs...
+ this.clientProto = HTTP;
+ this.serverProto = HTTPS;
+ }
+ else
+ {
+ // Client is talking HTTPS to proxy, so we should rewrite all HTTP-based URLs...
+ this.clientProto = HTTPS;
+ this.serverProto = HTTP;
+ }
+
+ int port;
+ try
+ {
+ String fwdPort = request.getHeader(HDR_X_FORWARDED_PORT);
+ port = Integer.valueOf(fwdPort);
+ }
+ catch (Exception e)
+ {
+ // Use default port for the used protocol...
+ port = -1;
+ }
+ // Normalize the protocol port...
+ if ((port > 0) && ((HTTPS.equals(this.clientProto) && (port == HTTPS_PORT)) || (HTTP.equals(this.clientProto) && (port == HTTP_PORT))))
+ {
+ // Port is the default one, do not use it...
+ port = -1;
+ }
+
+ this.clientPort = port;
+ }
+
+ @Override
+ public void setHeader(String name, String value)
+ {
+ if (HDR_LOCATION.equalsIgnoreCase(name))
+ {
+ URL rewritten = rewriteUrlIfNeeded(value);
+ // Trying to set a redirect location to the original client-side URL, which should be https...
+ if (rewritten != null)
+ {
+ value = rewritten.toExternalForm();
+ }
+ }
+ super.setHeader(name, value);
+ }
+
+ private int normalizePort(String protocol, int port)
+ {
+ if (port > 0)
+ {
+ return port;
+ }
+ if (HTTPS.equalsIgnoreCase(protocol))
+ {
+ return HTTPS_PORT;
+ }
+ return HTTP_PORT;
+ }
+
+ private URL rewriteUrlIfNeeded(String value)
+ {
+ try
+ {
+ URL url = new URL(value);
+
+ String actualProto = url.getProtocol();
+
+ if (!this.serverProto.equalsIgnoreCase(actualProto))
+ {
+ return null;
+ }
+
+ if (!this.serverName.equals(url.getHost()))
+ {
+ return null;
+ }
+
+ if (normalizePort(this.serverProto, this.serverPort) != normalizePort(actualProto, url.getPort()))
+ {
+ return null;
+ }
+
+ return new URL(this.clientProto, this.serverName, this.clientPort, url.getFile());
+ }
+ catch (MalformedURLException e)
+ {
+ return null;
+ }
+ }
+}
diff --git a/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterResponseTest.java b/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterResponseTest.java
new file mode 100644
index 0000000..f73db5a
--- /dev/null
+++ b/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterResponseTest.java
@@ -0,0 +1,378 @@
+/*
+ * 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.felix.http.sslfilter.internal;
+
+import static junit.framework.Assert.assertEquals;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTP;
+import static org.apache.felix.http.sslfilter.internal.SslFilterConstants.HTTPS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+
+public class SslFilterResponseTest
+{
+ private static final String BACKEND_SERVER = "backend.server";
+ private static final String OTHER_SERVER = "other.server";
+
+ private static final String DEFAULT_HTTP_PORT = "80";
+ private static final String ALT_HTTP_PORT = "8080";
+ private static final String DEFAULT_HTTPS_PORT = "443";
+ private static final String ALT_HTTPS_PORT = "8443";
+
+ private static final String LOCATION = "Location";
+
+ @Test
+ public void testSetHttpLocationHeaderToNullValue() throws Exception
+ {
+ HttpServletResponse resp = createServletResponse();
+ HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+
+ SslFilterResponse sresp = new SslFilterResponse(resp, req);
+
+ sresp.setHeader(LOCATION, null);
+
+ assertEquals(null, sresp.getHeader(LOCATION));
+ }
+
+ @Test
+ public void testSetHttpsLocationHeaderToOriginalRequestURI() throws Exception
+ {
+ String location, expected;
+
+ HttpServletResponse resp = createServletResponse();
+ HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+
+ SslFilterResponse sresp = new SslFilterResponse(resp, req);
+
+ location = HTTPS + "://" + BACKEND_SERVER + "/foo";
+ expected = location;
+
+ sresp.setHeader(LOCATION, location);
+
+ assertEquals(expected, sresp.getHeader(LOCATION));
+ }
+
+ @Test
+ public void testSetHttpLocationHeaderToOriginalRequestURI() throws Exception
+ {
+ String location, expected;
+
+ HttpServletResponse resp = createServletResponse();
+ HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+
+ SslFilterResponse sresp = new SslFilterResponse(resp, req);
+
+ location = HTTP + "://" + BACKEND_SERVER + "/foo";
+ expected = HTTPS + "://" + BACKEND_SERVER + "/foo";
+
+ sresp.setHeader(LOCATION, location);
+
+ assertEquals(expected, sresp.getHeader(LOCATION));
+ }
+
+ @Test
+ public void testSetHttpLocationHeaderToOriginalRequestWithExplicitPort() throws Exception
+ {
+ String location, expected;
+
+ HttpServletResponse resp = createServletResponse();
+ HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+
+ SslFilterResponse sresp = new SslFilterResponse(resp, req);
+
+ location = HTTP + "://" + BACKEND_SERVER + ":" + DEFAULT_HTTP_PORT + "/foo";
+ expected = HTTPS + "://" + BACKEND_SERVER + "/foo";
+
+ sresp.setHeader(LOCATION, location);
+
+ assertEquals(expected, sresp.getHeader(LOCATION));
+ }
+
+ @Test
+ public void testSetHttpLocationHeaderToOriginalRequestWithForwardedPort() throws Exception
+ {
+ String location, expected;
+
+ HttpServletResponse resp = createServletResponse();
+ HttpServletRequest req = createServletRequest(BACKEND_SERVER, DEFAULT_HTTP_PORT, HTTPS, ALT_HTTPS_PORT);
+
+ SslFilterResponse sresp = new SslFilterResponse(resp, req);
+
+ location = HTTP + "://" + BACKEND_SERVER + "/foo";
+ expected = HTTPS + "://" + BACKEND_SERVER + ":" + ALT_HTTPS_PORT + "/foo";
+
+ sresp.setHeader(LOCATION, location);
+
+ assertEquals(expected, sresp.getHeader(LOCATION));
+ }
+
+ @Test
+ public void testSetHttpLocationHeaderToOriginalRequestWithDifferentPort() throws Exception
+ {
+ String location, expected;
+
+ HttpServletResponse resp = createServletResponse();
+ HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+
+ SslFilterResponse sresp = new SslFilterResponse(resp, req);
+
+ location = HTTP + "://" + BACKEND_SERVER + ":" + ALT_HTTP_PORT + "/foo";
+ expected = location;
+
+ sresp.setHeader(LOCATION, location);
+
+ assertEquals(expected, sresp.getHeader(LOCATION));
+ }
+
+ @Test
+ public void testSetHttpLocationHeaderToOtherRequestURI() throws Exception
+ {
+ HttpServletResponse resp = createServletResponse();
+ HttpServletRequest req = createServletRequest(BACKEND_SERVER);
+
+ SslFilterResponse sresp = new SslFilterResponse(resp, req);
+
+ String location = HTTP + "://" + OTHER_SERVER + "/foo";
+ String expected = location;
+
+ sresp.setHeader(LOCATION, location);
+
+ assertEquals(expected, sresp.getHeader(LOCATION));
+ }
+
+ private HttpServletRequest createServletRequest(String serverName)
+ {
+ return createServletRequest(serverName, DEFAULT_HTTP_PORT, HTTPS, DEFAULT_HTTPS_PORT);
+ }
+
+ private HttpServletRequest createServletRequest(String serverName, String serverPort, String forwardedProto, String forwardedPort)
+ {
+ HttpServletRequest req = mock(HttpServletRequest.class);
+ when(req.getServerName()).thenReturn(serverName);
+ when(req.getServerPort()).thenReturn(Integer.parseInt(serverPort));
+ when(req.getHeader("X-Forwarded-Proto")).thenReturn(forwardedProto);
+ when(req.getHeader("X-Forwarded-Port")).thenReturn(forwardedPort);
+ return req;
+ }
+
+ private HttpServletResponse createServletResponse()
+ {
+ HttpServletResponse resp = new HttpServletResponse()
+ {
+ private final Map<String, String> headers = new HashMap<String, String>();
+ private int status = -1;
+ private boolean committed = false;
+
+ public void setLocale(Locale loc)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setContentType(String type)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setContentLength(int len)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setCharacterEncoding(String charset)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setBufferSize(int size)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void resetBuffer()
+ {
+ }
+
+ public void reset()
+ {
+ }
+
+ public boolean isCommitted()
+ {
+ return this.committed;
+ }
+
+ public PrintWriter getWriter() throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public Locale getLocale()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getContentType()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getCharacterEncoding()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getBufferSize()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void flushBuffer() throws IOException
+ {
+ committed = true;
+ }
+
+ public void setStatus(int sc, String sm)
+ {
+ status = sc;
+ committed = true;
+ }
+
+ public void setStatus(int sc)
+ {
+ status = sc;
+ committed = true;
+ }
+
+ public void setIntHeader(String name, int value)
+ {
+ headers.put(name, Integer.toString(value));
+ }
+
+ public void setHeader(String name, String value)
+ {
+ headers.put(name, value);
+ }
+
+ public void setDateHeader(String name, long date)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void sendRedirect(String location) throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void sendError(int sc, String msg) throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void sendError(int sc) throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getStatus()
+ {
+ return status;
+ }
+
+ public Collection<String> getHeaders(String name)
+ {
+ return Collections.singleton(headers.get(name));
+ }
+
+ public Collection<String> getHeaderNames()
+ {
+ return headers.keySet();
+ }
+
+ public String getHeader(String name)
+ {
+ return headers.get(name);
+ }
+
+ public String encodeUrl(String url)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String encodeURL(String url)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String encodeRedirectUrl(String url)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String encodeRedirectURL(String url)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean containsHeader(String name)
+ {
+ return headers.containsKey(name);
+ }
+
+ public void addIntHeader(String name, int value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addHeader(String name, String value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addDateHeader(String name, long date)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addCookie(Cookie cookie)
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ return resp;
+ }
+}