Introducing Java Servlet Container beta.

This commit is contained in:
Max Romanov
2019-02-28 18:02:42 +03:00
parent ec7319d32c
commit 5bfdebb9e4
95 changed files with 13360 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
package nginx.unit;
import javax.servlet.DispatcherType;
public interface DynamicDispatcherRequest
{
public void setDispatcherType(DispatcherType type);
}

View File

@@ -0,0 +1,15 @@
package nginx.unit;
public interface DynamicPathRequest
extends DynamicDispatcherRequest
{
public void setServletPath(String servlet_path, String path_info);
public void setServletPath(String filter_path, String servlet_path, String path_info);
public void setRequestURI(String uri);
public void setQueryString(String query);
public String getFilterPath();
}

View File

@@ -0,0 +1,150 @@
package nginx.unit;
import java.util.List;
import java.util.Map;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.UrlEncoded;
public class ForwardRequestWrapper implements DynamicPathRequest
{
private final Request request_;
private final boolean keep_attrs;
private final String orig_filter_path;
private final String orig_servlet_path;
private final String orig_path_info;
private final String orig_uri;
private final String orig_context_path;
private final String orig_query;
private final DispatcherType orig_dtype;
private MultiMap<String> orig_parameters;
public ForwardRequestWrapper(ServletRequest request)
{
if (request instanceof Request) {
request_ = (Request) request;
} else {
request_ = (Request) request.getAttribute(Request.BARE);
}
keep_attrs = request_.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI) != null;
orig_dtype = request_.getDispatcherType();
orig_filter_path = request_.getFilterPath();
orig_servlet_path = request_.getServletPath();
orig_path_info = request_.getPathInfo();
orig_uri = request_.getRequestURI();
orig_context_path = request_.getContextPath();
orig_query = request_.getQueryString();
}
@Override
public void setDispatcherType(DispatcherType type)
{
request_.setDispatcherType(type);
/*
9.4.2 Forwarded Request Parameters
...
Note that these attributes must always reflect the information in
the original request even under the situation that multiple
forwards and subsequent includes are called.
*/
if (keep_attrs) {
return;
}
/*
9.4.2 Forwarded Request Parameters
...
The values of these attributes must be equal to the return values
of the HttpServletRequest methods getRequestURI, getContextPath,
getServletPath, getPathInfo, getQueryString respectively, invoked
on the request object passed to the first servlet object in the
call chain that received the request from the client.
*/
request_.setAttribute_(RequestDispatcher.FORWARD_SERVLET_PATH, orig_servlet_path);
request_.setAttribute_(RequestDispatcher.FORWARD_PATH_INFO, orig_path_info);
request_.setAttribute_(RequestDispatcher.FORWARD_REQUEST_URI, orig_uri);
request_.setAttribute_(RequestDispatcher.FORWARD_CONTEXT_PATH, orig_context_path);
request_.setAttribute_(RequestDispatcher.FORWARD_QUERY_STRING, orig_query);
}
@Override
public void setServletPath(String servlet_path, String path_info)
{
request_.setServletPath(servlet_path, path_info);
}
@Override
public void setServletPath(String filter_path, String servlet_path, String path_info)
{
request_.setServletPath(filter_path, servlet_path, path_info);
}
@Override
public void setRequestURI(String uri)
{
request_.setRequestURI(uri);
}
@Override
public void setQueryString(String query)
{
if (query != null) {
orig_parameters = request_.getParameters();
MultiMap<String> parameters = new MultiMap<>();
UrlEncoded.decodeUtf8To(query, parameters);
for (Map.Entry<String, List<String>> e: orig_parameters.entrySet()) {
parameters.addValues(e.getKey(), e.getValue());
}
request_.setParameters(parameters);
request_.setQueryString(query);
}
}
@Override
public String getFilterPath()
{
return request_.getFilterPath();
}
public void close()
{
request_.setDispatcherType(orig_dtype);
request_.setRequestURI(orig_uri);
request_.setServletPath(orig_filter_path, orig_servlet_path, orig_path_info);
request_.setQueryString(orig_query);
if (orig_parameters != null) {
request_.setParameters(orig_parameters);
}
if (keep_attrs) {
return;
}
request_.setAttribute_(RequestDispatcher.FORWARD_SERVLET_PATH, null);
request_.setAttribute_(RequestDispatcher.FORWARD_PATH_INFO, null);
request_.setAttribute_(RequestDispatcher.FORWARD_REQUEST_URI, null);
request_.setAttribute_(RequestDispatcher.FORWARD_CONTEXT_PATH, null);
request_.setAttribute_(RequestDispatcher.FORWARD_QUERY_STRING, null);
}
}

View File

@@ -0,0 +1,42 @@
package nginx.unit;
import java.lang.String;
import java.util.Enumeration;
import java.util.NoSuchElementException;
public class HeaderNamesEnumeration implements Enumeration<String> {
private long headers_ptr;
private long size;
private long pos = 0;
public HeaderNamesEnumeration(long _headers_ptr, long _size) {
headers_ptr = _headers_ptr;
size = _size;
}
@Override
public boolean hasMoreElements()
{
if (pos >= size) {
return false;
}
pos = nextElementPos(headers_ptr, size, pos);
return pos < size;
}
static private native long nextElementPos(long headers_ptr, long size, long pos);
@Override
public String nextElement()
{
if (pos >= size) {
throw new NoSuchElementException();
}
return nextElement(headers_ptr, size, pos++);
}
static private native String nextElement(long headers_ptr, long size, long pos);
}

View File

@@ -0,0 +1,40 @@
package nginx.unit;
import java.lang.String;
import java.util.Enumeration;
public class HeadersEnumeration implements Enumeration<String> {
private long headers_ptr;
private long size;
private long initial_pos;
private long pos;
public HeadersEnumeration(long _headers_ptr, long _size, long _initial_pos) {
headers_ptr = _headers_ptr;
size = _size;
initial_pos = _initial_pos;
pos = _initial_pos;
}
@Override
public boolean hasMoreElements()
{
if (pos >= size) {
return false;
}
pos = nextElementPos(headers_ptr, size, initial_pos, pos);
return pos < size;
}
static private native long nextElementPos(long headers_ptr, long size, long initial_pos, long pos);
@Override
public String nextElement()
{
return nextElement(headers_ptr, size, initial_pos, pos++);
}
static private native String nextElement(long headers_ptr, long size, long initial_pos, long pos);
}

View File

@@ -0,0 +1,88 @@
package nginx.unit;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
public class IncludeRequestWrapper implements DynamicPathRequest
{
private final Request request_;
private final Object orig_servlet_path_attr;
private final Object orig_path_info_attr;
private final Object orig_uri_attr;
private final Object orig_context_path_attr;
private final Object orig_query_string_attr;
private final DispatcherType orig_dtype;
private String filter_path_;
public IncludeRequestWrapper(ServletRequest request)
{
if (request instanceof Request) {
request_ = (Request) request;
} else {
request_ = (Request) request.getAttribute(Request.BARE);
}
orig_servlet_path_attr = request_.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
orig_path_info_attr = request_.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
orig_uri_attr = request_.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
orig_context_path_attr = request_.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH);
orig_query_string_attr = request_.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
orig_dtype = request_.getDispatcherType();
request_.setAttribute_(RequestDispatcher.INCLUDE_CONTEXT_PATH, request_.getContextPath());
}
@Override
public void setDispatcherType(DispatcherType type)
{
request_.setDispatcherType(type);
}
@Override
public void setServletPath(String servlet_path, String path_info)
{
setServletPath(servlet_path, servlet_path, path_info);
}
@Override
public void setServletPath(String filter_path, String servlet_path, String path_info)
{
request_.setAttribute_(RequestDispatcher.INCLUDE_SERVLET_PATH, servlet_path);
request_.setAttribute_(RequestDispatcher.INCLUDE_PATH_INFO, path_info);
filter_path_ = filter_path;
}
@Override
public void setRequestURI(String uri)
{
request_.setAttribute_(RequestDispatcher.INCLUDE_REQUEST_URI, uri);
}
@Override
public void setQueryString(String query)
{
request_.setAttribute_(RequestDispatcher.INCLUDE_QUERY_STRING, query);
}
@Override
public String getFilterPath()
{
return filter_path_;
}
public void close()
{
request_.setDispatcherType(orig_dtype);
request_.setAttribute_(RequestDispatcher.INCLUDE_SERVLET_PATH, orig_servlet_path_attr);
request_.setAttribute_(RequestDispatcher.INCLUDE_PATH_INFO, orig_path_info_attr);
request_.setAttribute_(RequestDispatcher.INCLUDE_REQUEST_URI, orig_uri_attr);
request_.setAttribute_(RequestDispatcher.INCLUDE_CONTEXT_PATH, orig_context_path_attr);
request_.setAttribute_(RequestDispatcher.INCLUDE_QUERY_STRING, orig_query_string_attr);
}
}

View File

@@ -0,0 +1,117 @@
package nginx.unit;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class IncludeResponseWrapper extends HttpServletResponseWrapper {
private static final Charset UTF_8 = StandardCharsets.UTF_8;
public IncludeResponseWrapper(ServletResponse response)
{
super((HttpServletResponse) response);
}
@Override
public void addCookie(Cookie cookie)
{
trace("addCookie: " + cookie.getName() + "=" + cookie.getValue());
}
@Override
public void addDateHeader(String name, long date)
{
trace("addDateHeader: " + name + ": " + date);
}
@Override
public void addHeader(String name, String value)
{
trace("addHeader: " + name + ": " + value);
}
@Override
public void addIntHeader(String name, int value)
{
trace("addIntHeader: " + name + ": " + value);
}
@Override
public void sendRedirect(String location) throws IOException
{
trace("sendRedirect: " + location);
}
@Override
public void setDateHeader(String name, long date)
{
trace("setDateHeader: " + name + ": " + date);
}
@Override
public void setHeader(String name, String value)
{
trace("setHeader: " + name + ": " + value);
}
@Override
public void setIntHeader(String name, int value)
{
trace("setIntHeader: " + name + ": " + value);
}
@Override
public void setStatus(int sc)
{
trace("setStatus: " + sc);
}
@Override
@Deprecated
public void setStatus(int sc, String sm)
{
trace("setStatus: " + sc + "; " + sm);
}
@Override
public void reset()
{
trace("reset");
}
@Override
public void setCharacterEncoding(String charset)
{
trace("setCharacterEncoding " + charset);
}
@Override
public void setContentLength(int len)
{
trace("setContentLength: " + len);
}
@Override
public void setContentLengthLong(long len)
{
trace("setContentLengthLong: " + len);
}
@Override
public void setContentType(String type)
{
trace("setContentType: " + type);
}
private void trace(String msg)
{
msg = "IncludeResponse." + msg;
Response.trace(0, msg.getBytes(UTF_8));
}
}

View File

@@ -0,0 +1,7 @@
package nginx.unit;
public interface InitParams {
public boolean setInitParameter(String name, String value);
public String getInitParameter(String name);
}

View File

@@ -0,0 +1,90 @@
package nginx.unit;
import java.io.IOException;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
public class InputStream extends ServletInputStream {
private long req_info_ptr;
public InputStream(long ptr)
{
req_info_ptr = ptr;
}
@Override
public int readLine(byte[] b, int off, int len) throws IOException {
if (len <= 0) {
return 0;
}
return readLine(req_info_ptr, b, off, len);
}
private static native int readLine(long req_info_ptr, byte[] b, int off, int len);
@Override
public boolean isFinished()
{
return isFinished(req_info_ptr);
}
private static native boolean isFinished(long req_info_ptr);
@Override
public boolean isReady()
{
return true;
}
@Override
public void setReadListener(ReadListener listener)
{
}
@Override
public int read() throws IOException
{
return read(req_info_ptr);
}
private static native int read(long req_info_ptr);
@Override
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
return read(req_info_ptr, b, off, len);
}
private static native int read(long req_info_ptr, byte b[], int off, int len);
@Override
public long skip(long n) throws IOException {
return skip(req_info_ptr, n);
}
private static native long skip(long req_info_ptr, long n);
@Override
public int available() throws IOException {
return available(req_info_ptr);
}
private static native int available(long req_info_ptr);
}

View File

@@ -0,0 +1,169 @@
package nginx.unit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.descriptor.JspPropertyGroupDescriptor;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class JspPropertyGroup implements JspPropertyGroupDescriptor
{
private final List<String> url_patterns_ = new ArrayList<>();
private String el_ignored_ = null;
private String page_encoding_ = null;
private String scripting_invalid_ = null;
private String is_xml_ = null;
private final List<String> include_preludes_ = new ArrayList<>();
private final List<String> include_codas_ = new ArrayList<>();
private String deffered_syntax_allowed_as_literal_ = null;
private String trim_directive_whitespaces_ = null;
private String default_content_type_ = null;
private String buffer_ = null;
private String error_on_undeclared_namespace_ = null;
public JspPropertyGroup(NodeList nodes)
{
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String tag_name = node.getNodeName();
if (tag_name.equals("url-pattern")) {
url_patterns_.add(node.getTextContent().trim());
continue;
}
if (tag_name.equals("el-ignored")) {
el_ignored_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("page-encoding")) {
page_encoding_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("scripting-invalid")) {
scripting_invalid_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("is-xml")) {
is_xml_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("include-prelude")) {
include_preludes_.add(node.getTextContent().trim());
continue;
}
if (tag_name.equals("include-coda")) {
include_codas_.add(node.getTextContent().trim());
continue;
}
if (tag_name.equals("deferred-syntax-allowed-as-literal")) {
deffered_syntax_allowed_as_literal_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("trim-directive-whitespaces")) {
trim_directive_whitespaces_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("default-content-type")) {
default_content_type_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("buffer")) {
buffer_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("error-on-undeclared-namespace")) {
error_on_undeclared_namespace_ = node.getTextContent().trim();
continue;
}
}
}
@Override
public Collection<String> getUrlPatterns()
{
return new ArrayList<>(url_patterns_);
}
@Override
public String getElIgnored()
{
return el_ignored_;
}
@Override
public String getPageEncoding()
{
return page_encoding_;
}
@Override
public String getScriptingInvalid()
{
return scripting_invalid_;
}
@Override
public String getIsXml()
{
return is_xml_;
}
@Override
public Collection<String> getIncludePreludes()
{
return new ArrayList<>(include_preludes_);
}
@Override
public Collection<String> getIncludeCodas()
{
return new ArrayList<>(include_codas_);
}
@Override
public String getDeferredSyntaxAllowedAsLiteral()
{
return deffered_syntax_allowed_as_literal_;
}
@Override
public String getTrimDirectiveWhitespaces()
{
return trim_directive_whitespaces_;
}
@Override
public String getDefaultContentType()
{
return default_content_type_;
}
@Override
public String getBuffer()
{
return buffer_;
}
@Override
public String getErrorOnUndeclaredNamespace()
{
return error_on_undeclared_namespace_;
}
}

View File

@@ -0,0 +1,68 @@
package nginx.unit;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
public class OutputStream extends ServletOutputStream {
private long req_info_ptr;
public OutputStream(long ptr) {
req_info_ptr = ptr;
}
@Override
public void write(int b) throws IOException
{
write(req_info_ptr, b);
}
private static native void write(long req_info_ptr, int b);
@Override
public void write(byte b[], int off, int len) throws IOException
{
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
write(req_info_ptr, b, off, len);
}
private static native void write(long req_info_ptr, byte b[], int off, int len);
@Override
public void flush()
{
flush(req_info_ptr);
}
private static native void flush(long req_info_ptr);
@Override
public void close()
{
close(req_info_ptr);
}
private static native void close(long req_info_ptr);
@Override
public boolean isReady()
{
return true;
}
@Override
public void setWriteListener(WriteListener listener)
{
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
package nginx.unit;
import java.util.List;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
public class RequestAttrProxy implements ServletRequestAttributeListener
{
private final List<ServletRequestAttributeListener> listeners_;
public RequestAttrProxy(List<ServletRequestAttributeListener> listeners)
{
listeners_ = listeners;
}
@Override
public void attributeAdded(ServletRequestAttributeEvent srae)
{
for (ServletRequestAttributeListener l : listeners_) {
l.attributeAdded(srae);
}
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae)
{
for (ServletRequestAttributeListener l : listeners_) {
l.attributeReplaced(srae);
}
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae)
{
for (ServletRequestAttributeListener l : listeners_) {
l.attributeRemoved(srae);
}
}
}

View File

@@ -0,0 +1,817 @@
package nginx.unit;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.IllegalArgumentException;
import java.lang.String;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Vector;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.util.StringUtil;
public class Response implements HttpServletResponse {
private long req_info_ptr;
private static final String defaultCharacterEncoding = "iso-8859-1";
private String characterEncoding = defaultCharacterEncoding;
private String contentType = null;
private String contentTypeHeader = null;
private static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
private static final Charset UTF_8 = StandardCharsets.UTF_8;
private static final String CONTENT_TYPE = "Content-Type";
private static final byte[] SET_COOKIE_BYTES = "Set-Cookie".getBytes(ISO_8859_1);
private static final byte[] EXPIRES_BYTES = "Expires".getBytes(ISO_8859_1);
/**
* The only date format permitted when generating HTTP headers.
*/
public static final String RFC1123_DATE =
"EEE, dd MMM yyyy HH:mm:ss zzz";
private static final SimpleDateFormat format =
new SimpleDateFormat(RFC1123_DATE, Locale.US);
private static final String ZERO_DATE_STRING = dateToString(0);
private static final byte[] ZERO_DATE_BYTES = ZERO_DATE_STRING.getBytes(ISO_8859_1);
/**
* If this string is found within the comment of a cookie added with {@link #addCookie(Cookie)}, then the cookie
* will be set as HTTP ONLY.
*/
public final static String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
private OutputStream outputStream = null;
private PrintWriter writer = null;
public Response(long ptr) {
req_info_ptr = ptr;
}
/**
* Format a set cookie value by RFC6265
*
* @param name the name
* @param value the value
* @param domain the domain
* @param path the path
* @param maxAge the maximum age
* @param isSecure true if secure cookie
* @param isHttpOnly true if for http only
*/
public void addSetRFC6265Cookie(
final String name,
final String value,
final String domain,
final String path,
final long maxAge,
final boolean isSecure,
final boolean isHttpOnly)
{
// Check arguments
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("Bad cookie name");
}
// Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
// Per RFC6265, Cookie.name follows RFC2616 Section 2.2 token rules
//Syntax.requireValidRFC2616Token(name, "RFC6265 Cookie name");
// Ensure that Per RFC6265, Cookie.value follows syntax rules
//Syntax.requireValidRFC6265CookieValue(value);
// Format value and params
StringBuilder buf = new StringBuilder();
buf.append(name).append('=').append(value == null ? "" : value);
// Append path
if (path != null && path.length() > 0) {
buf.append(";Path=").append(path);
}
// Append domain
if (domain != null && domain.length() > 0) {
buf.append(";Domain=").append(domain);
}
// Handle max-age and/or expires
if (maxAge >= 0) {
// Always use expires
// This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
buf.append(";Expires=");
if (maxAge == 0)
buf.append(ZERO_DATE_STRING);
else
buf.append(dateToString(System.currentTimeMillis() + 1000L * maxAge));
buf.append(";Max-Age=");
buf.append(maxAge);
}
// add the other fields
if (isSecure)
buf.append(";Secure");
if (isHttpOnly)
buf.append(";HttpOnly");
// add the set cookie
addHeader(req_info_ptr, SET_COOKIE_BYTES,
buf.toString().getBytes(ISO_8859_1));
// Expire responses with set-cookie headers so they do not get cached.
setHeader(req_info_ptr, EXPIRES_BYTES, ZERO_DATE_BYTES);
}
@Override
public void addCookie(Cookie cookie)
{
trace("addCookie: " + cookie.getName() + "=" + cookie.getValue());
if (StringUtil.isBlank(cookie.getName())) {
throw new IllegalArgumentException("Cookie.name cannot be blank/null");
}
if (isCommitted()) {
return;
}
addCookie_(cookie);
}
private void addCookie_(Cookie cookie)
{
String comment = cookie.getComment();
boolean httpOnly = false;
if (comment != null && comment.contains(HTTP_ONLY_COMMENT)) {
httpOnly = true;
}
addSetRFC6265Cookie(cookie.getName(),
cookie.getValue(),
cookie.getDomain(),
cookie.getPath(),
cookie.getMaxAge(),
cookie.getSecure(),
httpOnly || cookie.isHttpOnly());
}
public void addSessionIdCookie(Cookie cookie)
{
trace("addSessionIdCookie: " + cookie.getName() + "=" + cookie.getValue());
if (isCommitted()) {
/*
9.3 The Include Method
... any call to HttpServletRequest.getSession() or
HttpServletRequest.getSession(boolean) that would require
adding a Cookie response header must throw an
IllegalStateException if the response has been committed.
*/
throw new IllegalStateException("Response already sent");
}
addCookie_(cookie);
}
@Override
public void addDateHeader(String name, long date)
{
trace("addDateHeader: " + name + ": " + date);
if (isCommitted()) {
return;
}
String value = dateToString(date);
addHeader(req_info_ptr, name.getBytes(ISO_8859_1),
value.getBytes(ISO_8859_1));
}
private static String dateToString(long date)
{
Date dateValue = new Date(date);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
return format.format(dateValue);
}
@Override
public void addHeader(String name, String value)
{
trace("addHeader: " + name + ": " + value);
if (value == null) {
return;
}
if (isCommitted()) {
return;
}
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
setContentType(value);
return;
}
addHeader(req_info_ptr, name.getBytes(ISO_8859_1),
value.getBytes(ISO_8859_1));
}
private static native void addHeader(long req_info_ptr, byte[] name, byte[] value);
@Override
public void addIntHeader(String name, int value)
{
trace("addIntHeader: " + name + ": " + value);
if (isCommitted()) {
return;
}
addIntHeader(req_info_ptr, name.getBytes(ISO_8859_1), value);
}
private static native void addIntHeader(long req_info_ptr, byte[] name, int value);
@Override
public boolean containsHeader(String name)
{
trace("containsHeader: " + name);
return containsHeader(req_info_ptr, name.getBytes(ISO_8859_1));
}
private static native boolean containsHeader(long req_info_ptr, byte[] name);
@Override
@Deprecated
public String encodeRedirectUrl(String url)
{
return encodeRedirectURL(url);
}
@Override
public String encodeRedirectURL(String url)
{
log("encodeRedirectURL: " + url);
return url;
}
@Override
@Deprecated
public String encodeUrl(String url)
{
return encodeURL(url);
}
@Override
public String encodeURL(String url)
{
log("encodeURL: " + url);
return url;
}
@Override
public String getHeader(String name)
{
trace("getHeader: " + name);
return getHeader(req_info_ptr, name.getBytes(ISO_8859_1));
}
private static native String getHeader(long req_info_ptr, byte[] name);
@Override
public Collection<String> getHeaderNames()
{
trace("getHeaderNames");
Enumeration<String> e = getHeaderNames(req_info_ptr);
if (e == null) {
return Collections.emptyList();
}
return Collections.list(e);
}
private static native Enumeration<String> getHeaderNames(long req_info_ptr);
@Override
public Collection<String> getHeaders(String name)
{
trace("getHeaders: " + name);
Enumeration<String> e = getHeaders(req_info_ptr, name.getBytes(ISO_8859_1));
if (e == null) {
return Collections.emptyList();
}
return Collections.list(e);
}
private static native Enumeration<String> getHeaders(long req_info_ptr, byte[] name);
@Override
public int getStatus()
{
trace("getStatus");
return getStatus(req_info_ptr);
}
private static native int getStatus(long req_info_ptr);
@Override
public void sendError(int sc) throws IOException
{
sendError(sc, null);
}
@Override
public void sendError(int sc, String msg) throws IOException
{
trace("sendError: " + sc + ", " + msg);
if (isCommitted()) {
throw new IllegalStateException("Response already sent");
}
setStatus(sc);
Request request = getRequest(req_info_ptr);
// If we are allowed to have a body, then produce the error page.
if (sc != SC_NO_CONTENT && sc != SC_NOT_MODIFIED &&
sc != SC_PARTIAL_CONTENT && sc >= SC_OK)
{
request.setAttribute_(RequestDispatcher.ERROR_STATUS_CODE, sc);
request.setAttribute_(RequestDispatcher.ERROR_MESSAGE, msg);
request.setAttribute_(RequestDispatcher.ERROR_REQUEST_URI,
request.getRequestURI());
/*
request.setAttribute_(RequestDispatcher.ERROR_SERVLET_NAME,
request.getServletName());
*/
}
/*
Avoid commit and give chance for error handlers.
if (!request.isAsyncStarted()) {
commit();
}
*/
}
private static native Request getRequest(long req_info_ptr);
private void commit()
{
if (writer != null) {
writer.close();
} else if (outputStream != null) {
outputStream.close();
} else {
commit(req_info_ptr);
}
}
private static native void commit(long req_info_ptr);
@Override
public void sendRedirect(String location) throws IOException
{
trace("sendRedirect: " + location);
if (isCommitted()) {
return;
}
try {
URI uri = new URI(location);
if (!uri.isAbsolute()) {
URI req_uri = new URI(getRequest(req_info_ptr).getRequestURL().toString());
uri = req_uri.resolve(uri);
location = uri.toString();
}
} catch (URISyntaxException e) {
log("sendRedirect: failed to send redirect: " + e);
return;
}
sendRedirect(req_info_ptr, location.getBytes(ISO_8859_1));
}
private static native void sendRedirect(long req_info_ptr, byte[] location);
@Override
public void setDateHeader(String name, long date)
{
trace("setDateHeader: " + name + ": " + date);
if (isCommitted()) {
return;
}
String value = dateToString(date);
setHeader(req_info_ptr, name.getBytes(ISO_8859_1),
value.getBytes(ISO_8859_1));
}
@Override
public void setHeader(String name, String value)
{
trace("setHeader: " + name + ": " + value);
if (isCommitted()) {
return;
}
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
setContentType(value);
return;
}
/*
* When value is null container behaviour is undefined.
* - Tomcat ignores setHeader call;
* - Jetty & Resin acts as removeHeader;
*/
if (value == null) {
removeHeader(req_info_ptr, name.getBytes(ISO_8859_1));
return;
}
setHeader(req_info_ptr, name.getBytes(ISO_8859_1),
value.getBytes(ISO_8859_1));
}
private static native void setHeader(long req_info_ptr, byte[] name, byte[] value);
private static native void removeHeader(long req_info_ptr, byte[] name);
@Override
public void setIntHeader(String name, int value)
{
trace("setIntHeader: " + name + ": " + value);
if (isCommitted()) {
return;
}
setIntHeader(req_info_ptr, name.getBytes(ISO_8859_1), value);
}
private static native void setIntHeader(long req_info_ptr, byte[] name, int value);
@Override
public void setStatus(int sc)
{
trace("setStatus: " + sc);
if (isCommitted()) {
return;
}
setStatus(req_info_ptr, sc);
}
private static native void setStatus(long req_info_ptr, int sc);
@Override
@Deprecated
public void setStatus(int sc, String sm)
{
trace("setStatus: " + sc + "; " + sm);
if (isCommitted()) {
return;
}
setStatus(req_info_ptr, sc);
}
@Override
public void flushBuffer() throws IOException
{
trace("flushBuffer");
if (writer != null) {
writer.flush();
}
if (outputStream != null) {
outputStream.flush();
}
}
@Override
public int getBufferSize()
{
trace("getBufferSize");
return getBufferSize(req_info_ptr);
}
public static native int getBufferSize(long req_info_ptr);
@Override
public String getCharacterEncoding()
{
trace("getCharacterEncoding");
return characterEncoding;
}
@Override
public String getContentType()
{
/* In JIRA decorator get content type called after commit. */
String res = contentTypeHeader;
trace("getContentType: " + res);
return res;
}
private static native String getContentType(long req_info_ptr);
@Override
public Locale getLocale()
{
log("getLocale");
return null;
}
@Override
public ServletOutputStream getOutputStream() throws IOException
{
trace("getOutputStream");
if (writer != null) {
throw new IllegalStateException("Writer already created");
}
if (outputStream == null) {
outputStream = new OutputStream(req_info_ptr);
}
return outputStream;
}
@Override
public PrintWriter getWriter() throws IOException
{
trace("getWriter ( characterEncoding = '" + characterEncoding + "' )");
if (outputStream != null) {
throw new IllegalStateException("OutputStream already created");
}
if (writer == null) {
ServletOutputStream stream = new OutputStream(req_info_ptr);
writer = new PrintWriter(
new OutputStreamWriter(stream, Charset.forName(characterEncoding)),
false);
}
return writer;
}
@Override
public boolean isCommitted()
{
trace("isCommitted");
return isCommitted(req_info_ptr);
}
public static native boolean isCommitted(long req_info_ptr);
@Override
public void reset()
{
trace("reset");
if (isCommitted()) {
return;
}
reset(req_info_ptr);
writer = null;
outputStream = null;
}
public static native void reset(long req_info_ptr);
@Override
public void resetBuffer()
{
trace("resetBuffer");
resetBuffer(req_info_ptr);
writer = null;
outputStream = null;
}
public static native void resetBuffer(long req_info_ptr);
@Override
public void setBufferSize(int size)
{
trace("setBufferSize: " + size);
setBufferSize(req_info_ptr, size);
}
public static native void setBufferSize(long req_info_ptr, int size);
@Override
public void setCharacterEncoding(String charset)
{
trace("setCharacterEncoding " + charset);
if (isCommitted()) {
return;
}
if (charset == null) {
if (writer != null
&& !characterEncoding.equalsIgnoreCase(defaultCharacterEncoding))
{
/* TODO throw */
return;
}
characterEncoding = defaultCharacterEncoding;
} else {
if (writer != null
&& !characterEncoding.equalsIgnoreCase(charset))
{
/* TODO throw */
return;
}
characterEncoding = charset;
}
if (contentType != null) {
String type = contentType + ";charset=" + characterEncoding;
contentTypeHeader = type;
setContentType(req_info_ptr, type.getBytes(ISO_8859_1));
}
}
@Override
public void setContentLength(int len)
{
trace("setContentLength: " + len);
if (isCommitted()) {
return;
}
setContentLength(req_info_ptr, len);
}
@Override
public void setContentLengthLong(long len)
{
trace("setContentLengthLong: " + len);
if (isCommitted()) {
return;
}
setContentLength(req_info_ptr, len);
}
private static native void setContentLength(long req_info_ptr, long len);
@Override
public void setContentType(String type)
{
trace("setContentType: " + type);
if (isCommitted()) {
return;
}
if (type == null) {
removeContentType(req_info_ptr);
contentType = null;
contentTypeHeader = null;
return;
}
String charset = MimeTypes.getCharsetFromContentType(type);
String ctype = MimeTypes.getContentTypeWithoutCharset(type);
if (writer != null
&& charset != null
&& !characterEncoding.equalsIgnoreCase(charset))
{
/* To late to change character encoding */
charset = characterEncoding;
type = ctype + ";charset=" + characterEncoding;
}
if (charset == null) {
type = type + ";charset=" + characterEncoding;
} else {
characterEncoding = charset;
}
contentType = ctype;
contentTypeHeader = type;
setContentType(req_info_ptr, type.getBytes(ISO_8859_1));
}
private static native void setContentType(long req_info_ptr, byte[] type);
private static native void removeContentType(long req_info_ptr);
@Override
public void setLocale(Locale loc)
{
log("setLocale: " + loc);
}
private void log(String msg)
{
msg = "Response." + msg;
log(req_info_ptr, msg.getBytes(UTF_8));
}
public static native void log(long req_info_ptr, byte[] msg);
private void trace(String msg)
{
msg = "Response." + msg;
trace(req_info_ptr, msg.getBytes(UTF_8));
}
public static native void trace(long req_info_ptr, byte[] msg);
}

View File

@@ -0,0 +1,251 @@
package nginx.unit;
import java.io.Serializable;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* @author Andrey Kazankov
*/
public class Session implements HttpSession, Serializable
{
private final Map<String, Object> attributes = new HashMap<>();
private final long creation_time = new Date().getTime();
private long last_access_time = creation_time;
private long access_time = creation_time;
private int max_inactive_interval;
private String id;
private final Context context;
private boolean is_new = true;
private final HttpSessionAttributeListener attr_listener;
public Session(Context context, String id,
HttpSessionAttributeListener al, int max_inactive_interval)
{
this.id = id;
this.context = context;
attr_listener = al;
this.max_inactive_interval = max_inactive_interval;
}
public void setId(String id)
{
this.id = id;
}
@Override
public long getCreationTime()
{
return creation_time;
}
@Override
public String getId()
{
return id;
}
@Override
public long getLastAccessedTime()
{
return last_access_time;
}
@Override
public ServletContext getServletContext()
{
return context;
}
@Override
public void setMaxInactiveInterval(int i)
{
max_inactive_interval = i;
}
@Override
public int getMaxInactiveInterval()
{
return max_inactive_interval;
}
@Deprecated
@Override
public javax.servlet.http.HttpSessionContext getSessionContext()
{
return null;
}
@Override
public Object getAttribute(String s)
{
synchronized (attributes) {
return attributes.get(s);
}
}
@Deprecated
@Override
public Object getValue(String s)
{
return getAttribute(s);
}
@Override
public Enumeration<String> getAttributeNames()
{
synchronized (attributes) {
return Collections.enumeration(attributes.keySet());
}
}
@Deprecated
@Override
public String[] getValueNames()
{
synchronized (attributes) {
return attributes.keySet().toArray(new String[attributes.keySet().size()]);
}
}
@Override
public void setAttribute(String s, Object o)
{
Object old;
if (o != null && o instanceof HttpSessionBindingListener) {
HttpSessionBindingListener l = (HttpSessionBindingListener) o;
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s);
l.valueBound(e);
}
synchronized (attributes) {
if (o != null) {
old = attributes.put(s, o);
} else {
old = attributes.remove(s);
}
}
if (old != null && old instanceof HttpSessionBindingListener) {
HttpSessionBindingListener l = (HttpSessionBindingListener) old;
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s);
l.valueUnbound(e);
}
if (attr_listener == null) {
return;
}
if (o == null) {
if (old != null) {
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s, old);
attr_listener.attributeRemoved(e);
}
return;
}
if (old != null) {
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s, old);
attr_listener.attributeReplaced(e);
} else {
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s, o);
attr_listener.attributeAdded(e);
}
}
@Deprecated
@Override
public void putValue(String s, Object o)
{
setAttribute(s,o);
}
@Override
public void removeAttribute(String s)
{
Object o;
synchronized (attributes) {
o = attributes.remove(s);
}
if (o != null && o instanceof HttpSessionBindingListener) {
HttpSessionBindingListener l = (HttpSessionBindingListener) o;
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s);
l.valueUnbound(e);
}
if (attr_listener == null || o == null) {
return;
}
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, s, o);
attr_listener.attributeRemoved(e);
}
@Deprecated
@Override
public void removeValue(String s)
{
removeAttribute(s);
}
@Override
public void invalidate()
{
context.invalidateSession(this);
unboundAttributes();
}
private void unboundAttributes()
{
for (Map.Entry<String, Object> a : attributes.entrySet()) {
Object o = a.getValue();
if (o != null && o instanceof HttpSessionBindingListener) {
HttpSessionBindingListener l = (HttpSessionBindingListener) o;
HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, a.getKey());
l.valueUnbound(e);
}
}
attributes.clear();
}
@Override
public boolean isNew()
{
return is_new;
}
public void accessed() {
synchronized (this) {
is_new = false;
last_access_time = access_time;
access_time = new Date().getTime();
}
}
public boolean checkTimeOut()
{
return (max_inactive_interval > 0) &&
(access_time - last_access_time > max_inactive_interval * 1000);
}
}

View File

@@ -0,0 +1,40 @@
package nginx.unit;
import java.util.List;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
public class SessionAttrProxy implements HttpSessionAttributeListener
{
private final List<HttpSessionAttributeListener> listeners_;
public SessionAttrProxy(List<HttpSessionAttributeListener> listeners)
{
listeners_ = listeners;
}
@Override
public void attributeAdded(HttpSessionBindingEvent event)
{
for (HttpSessionAttributeListener l : listeners_) {
l.attributeAdded(event);
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event)
{
for (HttpSessionAttributeListener l : listeners_) {
l.attributeRemoved(event);
}
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event)
{
for (HttpSessionAttributeListener l : listeners_) {
l.attributeReplaced(event);
}
}
}

View File

@@ -0,0 +1,44 @@
package nginx.unit;
import javax.servlet.descriptor.TaglibDescriptor;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class Taglib implements TaglibDescriptor
{
private String uri_ = null;
private String location_ = null;
public Taglib(NodeList nodes)
{
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String tag_name = node.getNodeName();
if (tag_name.equals("taglib-uri")) {
uri_ = node.getTextContent().trim();
continue;
}
if (tag_name.equals("taglib-location")) {
location_ = node.getTextContent().trim();
continue;
}
}
}
@Override
public String getTaglibURI()
{
return uri_;
}
@Override
public String getTaglibLocation()
{
return location_;
}
}

View File

@@ -0,0 +1,110 @@
package nginx.unit;
import javax.servlet.SessionCookieConfig;
/*
<session-config>
<session-timeout>60</session-timeout>
<cookie-config></cookie-config>
<tracking-mode></tracking-mode>
</session-config>
*/
public class UnitSessionCookieConfig implements SessionCookieConfig {
private static final String default_name = "JSESSIONID";
private String name = default_name;
private String domain;
private String path;
private String comment;
private boolean httpOnly = true;
private boolean secure = false;
private int maxAge = -1;
@Override
public void setName(String name)
{
this.name = name;
}
@Override
public String getName()
{
return name;
}
@Override
public void setDomain(String domain)
{
this.domain = domain;
}
@Override
public String getDomain()
{
return domain;
}
@Override
public void setPath(String path)
{
this.path = path;
}
@Override
public String getPath()
{
return path;
}
@Override
public void setComment(String comment)
{
this.comment = comment;
}
@Override
public String getComment()
{
return comment;
}
@Override
public void setHttpOnly(boolean httpOnly)
{
this.httpOnly = httpOnly;
}
@Override
public boolean isHttpOnly()
{
return httpOnly;
}
@Override
public void setSecure(boolean secure)
{
this.secure = secure;
}
@Override
public boolean isSecure()
{
return secure;
}
@Override
public void setMaxAge(int maxAge)
{
this.maxAge = maxAge;
}
@Override
public int getMaxAge()
{
return maxAge;
}
}