| /******************************************************************************* |
| * Copyright (c) 2005-2007 Cognos Incorporated, IBM Corporation and others |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Cognos Incorporated - initial API and implementation |
| * IBM Corporation - bug fixes and enhancements |
| *******************************************************************************/ |
| package org.eclipse.equinox.http.servlet.internal; |
| |
| import java.io.*; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.security.*; |
| import javax.servlet.ServletContext; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import org.osgi.service.http.HttpContext; |
| |
| public class ResourceRegistration extends Registration { |
| private static final String LAST_MODIFIED = "Last-Modified"; //$NON-NLS-1$ |
| private static final String IF_MODIFIED_SINCE = "If-Modified-Since"; //$NON-NLS-1$ |
| private static final String IF_NONE_MATCH = "If-None-Match"; //$NON-NLS-1$ |
| private static final String ETAG = "ETag"; //$NON-NLS-1$ |
| |
| private String internalName; |
| HttpContext httpContext; |
| ServletContext servletContext; |
| private AccessControlContext acc; |
| |
| public ResourceRegistration(String internalName, HttpContext context, ServletContext servletContext, AccessControlContext acc) { |
| this.internalName = internalName; |
| if (internalName.equals("/")) { //$NON-NLS-1$ |
| this.internalName = ""; //$NON-NLS-1$ |
| } |
| this.httpContext = context; |
| this.servletContext = servletContext; |
| this.acc = acc; |
| } |
| |
| public boolean handleRequest(HttpServletRequest req, final HttpServletResponse resp, String alias) throws IOException { |
| if (httpContext.handleSecurity(req, resp)) { |
| |
| String method = req.getMethod(); |
| if (method.equals("GET") || method.equals("POST") || method.equals("HEAD")) { //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ |
| |
| String pathInfo = HttpServletRequestAdaptor.getDispatchPathInfo(req); |
| int aliasLength = alias.equals("/") ? 0 : alias.length(); //$NON-NLS-1$ |
| String resourcePath = internalName + pathInfo.substring(aliasLength); |
| URL testURL = httpContext.getResource(resourcePath); |
| if (testURL == null || resourcePath.endsWith("/")) { //$NON-NLS-1$ |
| return false; |
| } |
| return writeResource(req, resp, resourcePath); |
| } |
| resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); |
| } |
| return true; |
| } |
| |
| private boolean writeResource(final HttpServletRequest req, final HttpServletResponse resp, final String resourcePath) throws IOException { |
| Boolean result = Boolean.TRUE; |
| try { |
| result = (Boolean) AccessController.doPrivileged(new PrivilegedExceptionAction() { |
| |
| public Object run() throws Exception { |
| URL url = httpContext.getResource(resourcePath); |
| if (url == null) |
| return Boolean.FALSE; |
| |
| URLConnection connection = url.openConnection(); |
| long lastModified = connection.getLastModified(); |
| int contentLength = connection.getContentLength(); |
| |
| // check to ensure that we're dealing with a real resource and in particular not a directory |
| if (contentLength <= 0) |
| return Boolean.FALSE; |
| |
| String etag = null; |
| if (lastModified != -1 && contentLength != -1) |
| etag = "W/\"" + contentLength + "-" + lastModified + "\""; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ |
| |
| // Check for cache revalidation. |
| // We should prefer ETag validation as the guarantees are stronger and all HTTP 1.1 clients should be using it |
| String ifNoneMatch = req.getHeader(IF_NONE_MATCH); |
| if (ifNoneMatch != null && etag != null && ifNoneMatch.indexOf(etag) != -1) { |
| resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); |
| return Boolean.TRUE; |
| } else { |
| long ifModifiedSince = req.getDateHeader(IF_MODIFIED_SINCE); |
| // for purposes of comparison we add 999 to ifModifiedSince since the fidelity |
| // of the IMS header generally doesn't include milli-seconds |
| if (ifModifiedSince > -1 && lastModified > 0 && lastModified <= (ifModifiedSince + 999)) { |
| resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); |
| return Boolean.TRUE; |
| } |
| } |
| |
| // return the full contents regularly |
| if (contentLength != -1) |
| resp.setContentLength(contentLength); |
| |
| String contentType = httpContext.getMimeType(resourcePath); |
| if (contentType == null) |
| contentType = servletContext.getMimeType(resourcePath); |
| |
| if (contentType != null) |
| resp.setContentType(contentType); |
| |
| if (lastModified > 0) |
| resp.setDateHeader(LAST_MODIFIED, lastModified); |
| |
| if (etag != null) |
| resp.setHeader(ETAG, etag); |
| |
| try { |
| OutputStream os = resp.getOutputStream(); |
| int writtenContentLength = writeResourceToOutputStream(connection, os); |
| if (contentLength == -1 || contentLength != writtenContentLength) |
| resp.setContentLength(writtenContentLength); |
| } catch (IllegalStateException e) { // can occur if the response output is already open as a Writer |
| Writer writer = resp.getWriter(); |
| writeResourceToWriter(connection, writer); |
| // Since ContentLength is a measure of the number of bytes contained in the body |
| // of a message when we use a Writer we lose control of the exact byte count and |
| // defer the problem to the Servlet Engine's Writer implementation. |
| } |
| return Boolean.TRUE; |
| } |
| }, acc); |
| } catch (PrivilegedActionException e) { |
| throw (IOException) e.getException(); |
| } |
| return result.booleanValue(); |
| } |
| |
| int writeResourceToOutputStream(URLConnection connection, OutputStream os) throws IOException { |
| InputStream is = connection.getInputStream(); |
| try { |
| byte[] buffer = new byte[8192]; |
| int bytesRead = is.read(buffer); |
| int writtenContentLength = 0; |
| while (bytesRead != -1) { |
| os.write(buffer, 0, bytesRead); |
| writtenContentLength += bytesRead; |
| bytesRead = is.read(buffer); |
| } |
| return writtenContentLength; |
| } finally { |
| if (is != null) |
| is.close(); |
| } |
| } |
| |
| void writeResourceToWriter(URLConnection connection, Writer writer) throws IOException { |
| InputStream is = connection.getInputStream(); |
| try { |
| Reader reader = new InputStreamReader(connection.getInputStream()); |
| try { |
| char[] buffer = new char[8192]; |
| int charsRead = reader.read(buffer); |
| while (charsRead != -1) { |
| writer.write(buffer, 0, charsRead); |
| charsRead = reader.read(buffer); |
| } |
| } finally { |
| if (reader != null) { |
| reader.close(); // will also close input stream |
| is = null; |
| } |
| } |
| } finally { |
| if (is != null) |
| is.close(); |
| } |
| } |
| } |