You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
7.9 KiB
216 lines
7.9 KiB
/* |
|
* Copyright (C) 2009-2010, Google Inc. |
|
* and other copyright owners as documented in the project's IP log. |
|
* |
|
* This program and the accompanying materials are made available |
|
* under the terms of the Eclipse Distribution License v1.0 which |
|
* accompanies this distribution, is reproduced below, and is |
|
* available at http://www.eclipse.org/org/documents/edl-v10.php |
|
* |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or |
|
* without modification, are permitted provided that the following |
|
* conditions are met: |
|
* |
|
* - Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* |
|
* - Redistributions in binary form must reproduce the above |
|
* copyright notice, this list of conditions and the following |
|
* disclaimer in the documentation and/or other materials provided |
|
* with the distribution. |
|
* |
|
* - Neither the name of the Eclipse Foundation, Inc. nor the |
|
* names of its contributors may be used to endorse or promote |
|
* products derived from this software without specific prior |
|
* written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
|
|
package org.eclipse.jgit.http.server; |
|
|
|
import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP; |
|
import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING; |
|
import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING; |
|
import static org.eclipse.jgit.util.HttpSupport.HDR_ETAG; |
|
import static org.eclipse.jgit.util.HttpSupport.TEXT_PLAIN; |
|
|
|
import java.io.ByteArrayOutputStream; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.io.OutputStream; |
|
import java.security.MessageDigest; |
|
import java.util.zip.GZIPInputStream; |
|
import java.util.zip.GZIPOutputStream; |
|
|
|
import javax.servlet.ServletRequest; |
|
import javax.servlet.http.HttpServletRequest; |
|
import javax.servlet.http.HttpServletResponse; |
|
|
|
import org.eclipse.jgit.lib.Constants; |
|
import org.eclipse.jgit.lib.ObjectId; |
|
import org.eclipse.jgit.lib.Repository; |
|
|
|
/** Common utility functions for servlets. */ |
|
public final class ServletUtils { |
|
/** Request attribute which stores the {@link Repository} instance. */ |
|
public static final String ATTRIBUTE_REPOSITORY = "org.eclipse.jgit.Repository"; |
|
|
|
/** |
|
* Get the selected repository from the request. |
|
* |
|
* @param req |
|
* the current request. |
|
* @return the repository; never null. |
|
* @throws IllegalStateException |
|
* the repository was not set by the filter, the servlet is |
|
* being invoked incorrectly and the programmer should ensure |
|
* the filter runs before the servlet. |
|
* @see #ATTRIBUTE_REPOSITORY |
|
*/ |
|
public static Repository getRepository(final ServletRequest req) { |
|
Repository db = (Repository) req.getAttribute(ATTRIBUTE_REPOSITORY); |
|
if (db == null) |
|
throw new IllegalStateException("Expected Repository attribute"); |
|
return db; |
|
} |
|
|
|
/** |
|
* Open the request input stream, automatically inflating if necessary. |
|
* <p> |
|
* This method automatically inflates the input stream if the request |
|
* {@code Content-Encoding} header was set to {@code gzip} or the legacy |
|
* {@code x-gzip}. |
|
* |
|
* @param req |
|
* the incoming request whose input stream needs to be opened. |
|
* @return an input stream to read the raw, uncompressed request body. |
|
* @throws IOException |
|
* if an input or output exception occurred. |
|
*/ |
|
public static InputStream getInputStream(final HttpServletRequest req) |
|
throws IOException { |
|
InputStream in = req.getInputStream(); |
|
final String enc = req.getHeader(HDR_CONTENT_ENCODING); |
|
if (ENCODING_GZIP.equals(enc) || "x-gzip".equals(enc)) //$NON-NLS-1$ |
|
in = new GZIPInputStream(in); |
|
else if (enc != null) |
|
throw new IOException(HDR_CONTENT_ENCODING + " \"" + enc + "\"" |
|
+ ": not supported by this library."); |
|
return in; |
|
} |
|
|
|
/** |
|
* Send a plain text response to a {@code GET} or {@code HEAD} HTTP request. |
|
* <p> |
|
* The text response is encoded in the Git character encoding, UTF-8. |
|
* <p> |
|
* If the user agent supports a compressed transfer encoding and the content |
|
* is large enough, the content may be compressed before sending. |
|
* <p> |
|
* The {@code ETag} and {@code Content-Length} headers are automatically set |
|
* by this method. {@code Content-Encoding} is conditionally set if the user |
|
* agent supports a compressed transfer. Callers are responsible for setting |
|
* any cache control headers. |
|
* |
|
* @param content |
|
* to return to the user agent as this entity's body. |
|
* @param req |
|
* the incoming request. |
|
* @param rsp |
|
* the outgoing response. |
|
* @throws IOException |
|
* the servlet API rejected sending the body. |
|
*/ |
|
public static void sendPlainText(final String content, |
|
final HttpServletRequest req, final HttpServletResponse rsp) |
|
throws IOException { |
|
final byte[] raw = content.getBytes(Constants.CHARACTER_ENCODING); |
|
rsp.setContentType(TEXT_PLAIN); |
|
rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING); |
|
send(raw, req, rsp); |
|
} |
|
|
|
/** |
|
* Send a response to a {@code GET} or {@code HEAD} HTTP request. |
|
* <p> |
|
* If the user agent supports a compressed transfer encoding and the content |
|
* is large enough, the content may be compressed before sending. |
|
* <p> |
|
* The {@code ETag} and {@code Content-Length} headers are automatically set |
|
* by this method. {@code Content-Encoding} is conditionally set if the user |
|
* agent supports a compressed transfer. Callers are responsible for setting |
|
* {@code Content-Type} and any cache control headers. |
|
* |
|
* @param content |
|
* to return to the user agent as this entity's body. |
|
* @param req |
|
* the incoming request. |
|
* @param rsp |
|
* the outgoing response. |
|
* @throws IOException |
|
* the servlet API rejected sending the body. |
|
*/ |
|
public static void send(byte[] content, final HttpServletRequest req, |
|
final HttpServletResponse rsp) throws IOException { |
|
content = sendInit(content, req, rsp); |
|
final OutputStream out = rsp.getOutputStream(); |
|
try { |
|
out.write(content); |
|
out.flush(); |
|
} finally { |
|
out.close(); |
|
} |
|
} |
|
|
|
private static byte[] sendInit(byte[] content, |
|
final HttpServletRequest req, final HttpServletResponse rsp) |
|
throws IOException { |
|
rsp.setHeader(HDR_ETAG, etag(content)); |
|
if (256 < content.length && acceptsGzipEncoding(req)) { |
|
content = compress(content); |
|
rsp.setHeader(HDR_CONTENT_ENCODING, ENCODING_GZIP); |
|
} |
|
rsp.setContentLength(content.length); |
|
return content; |
|
} |
|
|
|
private static boolean acceptsGzipEncoding(final HttpServletRequest req) { |
|
final String accepts = req.getHeader(HDR_ACCEPT_ENCODING); |
|
return accepts != null && 0 <= accepts.indexOf(ENCODING_GZIP); |
|
} |
|
|
|
private static byte[] compress(final byte[] raw) throws IOException { |
|
final int maxLen = raw.length + 32; |
|
final ByteArrayOutputStream out = new ByteArrayOutputStream(maxLen); |
|
final GZIPOutputStream gz = new GZIPOutputStream(out); |
|
gz.write(raw); |
|
gz.finish(); |
|
gz.flush(); |
|
return out.toByteArray(); |
|
} |
|
|
|
private static String etag(final byte[] content) { |
|
final MessageDigest md = Constants.newMessageDigest(); |
|
md.update(content); |
|
return ObjectId.fromRaw(md.digest()).getName(); |
|
} |
|
|
|
private ServletUtils() { |
|
// static utility class only |
|
} |
|
}
|
|
|