1339 lines
32 KiB
Java
1339 lines
32 KiB
Java
package nginx.unit;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.InputStreamReader;
|
|
import java.io.IOException;
|
|
import java.io.UnsupportedEncodingException;
|
|
|
|
import java.lang.IllegalArgumentException;
|
|
import java.lang.IllegalStateException;
|
|
import java.lang.Object;
|
|
import java.lang.String;
|
|
import java.lang.StringBuffer;
|
|
|
|
import java.net.URI;
|
|
import java.net.URISyntaxException;
|
|
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.charset.Charset;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
import java.text.ParseException;
|
|
import java.text.SimpleDateFormat;
|
|
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.Date;
|
|
import java.util.Enumeration;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import java.security.Principal;
|
|
|
|
import javax.servlet.AsyncContext;
|
|
import javax.servlet.DispatcherType;
|
|
import javax.servlet.MultipartConfigElement;
|
|
import javax.servlet.RequestDispatcher;
|
|
import javax.servlet.ServletContext;
|
|
import javax.servlet.ServletException;
|
|
import javax.servlet.ServletInputStream;
|
|
import javax.servlet.ServletRequest;
|
|
import javax.servlet.ServletRequestAttributeEvent;
|
|
import javax.servlet.ServletRequestAttributeListener;
|
|
import javax.servlet.ServletResponse;
|
|
import javax.servlet.SessionTrackingMode;
|
|
import javax.servlet.SessionCookieConfig;
|
|
import javax.servlet.http.Cookie;
|
|
import javax.servlet.http.HttpSession;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
import javax.servlet.http.HttpUpgradeHandler;
|
|
import javax.servlet.http.Part;
|
|
|
|
import org.eclipse.jetty.util.IO;
|
|
import org.eclipse.jetty.util.MultiMap;
|
|
import org.eclipse.jetty.util.UrlEncoded;
|
|
import org.eclipse.jetty.util.StringUtil;
|
|
|
|
import org.eclipse.jetty.server.CookieCutter;
|
|
import org.eclipse.jetty.http.MultiPartFormInputStream;
|
|
import org.eclipse.jetty.http.HttpFields;
|
|
import org.eclipse.jetty.http.MimeTypes;
|
|
|
|
import nginx.unit.websocket.WsSession;
|
|
import nginx.unit.websocket.WsIOException;
|
|
|
|
public class Request implements HttpServletRequest, DynamicPathRequest
|
|
{
|
|
private final Context context;
|
|
private final long req_info_ptr;
|
|
private final long req_ptr;
|
|
|
|
protected String authType = null;
|
|
|
|
protected boolean cookiesParsed = false;
|
|
|
|
protected CookieCutter cookies = null;
|
|
|
|
private final Map<String, Object> attributes = new HashMap<>();
|
|
|
|
private MultiMap<String> parameters = null;
|
|
|
|
private final String context_path;
|
|
private String filter_path = null;
|
|
private String servlet_path = null;
|
|
private String path_info = null;
|
|
private String request_uri = null;
|
|
private String query_string = null;
|
|
private boolean query_string_valid = false;
|
|
|
|
private DispatcherType dispatcher_type = DispatcherType.REQUEST;
|
|
|
|
private String characterEncoding = null;
|
|
|
|
/**
|
|
* 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 formats[] = {
|
|
new SimpleDateFormat(RFC1123_DATE, Locale.US),
|
|
new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
|
|
new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
|
|
};
|
|
|
|
private InputStream inputStream = null;
|
|
private BufferedReader reader = null;
|
|
|
|
private boolean request_session_id_parsed = false;
|
|
private String request_session_id = null;
|
|
private boolean request_session_id_from_cookie = false;
|
|
private boolean request_session_id_from_url = false;
|
|
private Session session = null;
|
|
|
|
private WsSession wsSession = null;
|
|
private boolean skip_close_ws = false;
|
|
|
|
private final ServletRequestAttributeListener attr_listener;
|
|
|
|
public static final String BARE = "nginx.unit.request.bare";
|
|
|
|
private MultiPartFormInputStream multi_parts;
|
|
private MultipartConfigElement multipart_config;
|
|
|
|
public Request(Context ctx, long req_info, long req) {
|
|
context = ctx;
|
|
req_info_ptr = req_info;
|
|
req_ptr = req;
|
|
|
|
attr_listener = context.getRequestAttributeListener();
|
|
context_path = context.getContextPath();
|
|
}
|
|
|
|
@Override
|
|
public boolean authenticate(HttpServletResponse response)
|
|
throws IOException, ServletException
|
|
{
|
|
log("authenticate");
|
|
|
|
if (response.isCommitted()) {
|
|
throw new IllegalStateException();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public String getAuthType()
|
|
{
|
|
log("getAuthType");
|
|
|
|
return authType;
|
|
}
|
|
|
|
@Override
|
|
public String getContextPath()
|
|
{
|
|
trace("getContextPath: " + context_path);
|
|
|
|
return context_path;
|
|
}
|
|
|
|
@Override
|
|
public Cookie[] getCookies()
|
|
{
|
|
trace("getCookies");
|
|
|
|
if (!cookiesParsed) {
|
|
parseCookies();
|
|
}
|
|
|
|
//Javadoc for Request.getCookies() stipulates null for no cookies
|
|
if (cookies == null || cookies.getCookies().length == 0) {
|
|
return null;
|
|
}
|
|
|
|
return cookies.getCookies();
|
|
}
|
|
|
|
protected void parseCookies()
|
|
{
|
|
cookiesParsed = true;
|
|
|
|
cookies = new CookieCutter();
|
|
|
|
Enumeration<String> cookie_headers = getHeaders("Cookie");
|
|
|
|
while (cookie_headers.hasMoreElements()) {
|
|
cookies.addCookieField(cookie_headers.nextElement());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public long getDateHeader(String name)
|
|
{
|
|
trace("getDateHeader: " + name);
|
|
|
|
String value = getHeader(name);
|
|
if (value == null) {
|
|
return -1L;
|
|
}
|
|
|
|
long res = parseDate(value);
|
|
if (res == -1L) {
|
|
throw new IllegalArgumentException(value);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
protected long parseDate(String value)
|
|
{
|
|
Date date = null;
|
|
for (int i = 0; (date == null) && (i < formats.length); i++) {
|
|
try {
|
|
date = formats[i].parse(value);
|
|
} catch (ParseException e) {
|
|
// Ignore
|
|
}
|
|
}
|
|
if (date == null) {
|
|
return -1L;
|
|
}
|
|
return date.getTime();
|
|
}
|
|
|
|
@Override
|
|
public String getHeader(String name)
|
|
{
|
|
String res = getHeader(req_ptr, name, name.length());
|
|
|
|
trace("getHeader: " + name + " = '" + res + "'");
|
|
|
|
return res;
|
|
}
|
|
|
|
private static native String getHeader(long req_ptr, String name, int name_len);
|
|
|
|
|
|
@Override
|
|
public Enumeration<String> getHeaderNames()
|
|
{
|
|
trace("getHeaderNames");
|
|
|
|
return getHeaderNames(req_ptr);
|
|
}
|
|
|
|
private static native Enumeration<String> getHeaderNames(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public Enumeration<String> getHeaders(String name)
|
|
{
|
|
trace("getHeaders: " + name);
|
|
|
|
return getHeaders(req_ptr, name, name.length());
|
|
}
|
|
|
|
private static native Enumeration<String> getHeaders(long req_ptr, String name, int name_len);
|
|
|
|
|
|
@Override
|
|
public int getIntHeader(String name)
|
|
{
|
|
trace("getIntHeader: " + name);
|
|
|
|
return getIntHeader(req_ptr, name, name.length());
|
|
}
|
|
|
|
private static native int getIntHeader(long req_ptr, String name, int name_len);
|
|
|
|
|
|
@Override
|
|
public String getMethod()
|
|
{
|
|
trace("getMethod");
|
|
|
|
return getMethod(req_ptr);
|
|
}
|
|
|
|
private static native String getMethod(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public Part getPart(String name) throws IOException, ServletException
|
|
{
|
|
trace("getPart: " + name);
|
|
|
|
if (multi_parts == null) {
|
|
parseMultiParts();
|
|
}
|
|
|
|
return multi_parts.getPart(name);
|
|
}
|
|
|
|
@Override
|
|
public Collection<Part> getParts() throws IOException, ServletException
|
|
{
|
|
trace("getParts");
|
|
|
|
if (multi_parts == null) {
|
|
parseMultiParts();
|
|
}
|
|
|
|
return multi_parts.getParts();
|
|
}
|
|
|
|
private boolean checkMultiPart(String content_type)
|
|
{
|
|
return content_type != null
|
|
&& MimeTypes.Type.MULTIPART_FORM_DATA.is(HttpFields.valueParameters(content_type, null));
|
|
}
|
|
|
|
private void parseMultiParts() throws IOException, ServletException, IllegalStateException
|
|
{
|
|
String content_type = getContentType();
|
|
|
|
if (!checkMultiPart(content_type)) {
|
|
throw new ServletException("Content-Type != multipart/form-data");
|
|
}
|
|
|
|
if (multipart_config == null) {
|
|
throw new IllegalStateException("No multipart config for servlet");
|
|
}
|
|
|
|
parseMultiParts(content_type);
|
|
}
|
|
|
|
private void parseMultiParts(String content_type) throws IOException
|
|
{
|
|
File tmpDir = (File) context.getAttribute(ServletContext.TEMPDIR);
|
|
|
|
multi_parts = new MultiPartFormInputStream(getInputStream(),
|
|
content_type, multipart_config, tmpDir);
|
|
}
|
|
|
|
public void setMultipartConfig(MultipartConfigElement mce)
|
|
{
|
|
multipart_config = mce;
|
|
}
|
|
|
|
public MultipartConfigElement getMultipartConfig()
|
|
{
|
|
return multipart_config;
|
|
}
|
|
|
|
@Override
|
|
public String getPathInfo()
|
|
{
|
|
trace("getPathInfo: " + path_info);
|
|
|
|
return path_info;
|
|
}
|
|
|
|
@Override
|
|
public String getPathTranslated()
|
|
{
|
|
trace("getPathTranslated");
|
|
|
|
if (path_info == null) {
|
|
return null;
|
|
}
|
|
|
|
return context.getRealPath(path_info);
|
|
}
|
|
|
|
@Override
|
|
public String getQueryString()
|
|
{
|
|
if (!query_string_valid) {
|
|
query_string = getQueryString(req_ptr);
|
|
query_string_valid = true;
|
|
}
|
|
|
|
trace("getQueryString: " + query_string);
|
|
|
|
return query_string;
|
|
}
|
|
|
|
private static native String getQueryString(long req_ptr);
|
|
|
|
@Override
|
|
public void setQueryString(String query)
|
|
{
|
|
trace("setQueryString: " + query);
|
|
|
|
query_string = query;
|
|
query_string_valid = true;
|
|
}
|
|
|
|
@Override
|
|
public String getRemoteUser()
|
|
{
|
|
log("getRemoteUser");
|
|
|
|
/* TODO */
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public String getRequestedSessionId()
|
|
{
|
|
trace("getRequestedSessionId");
|
|
|
|
if (!request_session_id_parsed) {
|
|
parseRequestSessionId();
|
|
}
|
|
|
|
return request_session_id;
|
|
}
|
|
|
|
private void parseRequestSessionId()
|
|
{
|
|
request_session_id_parsed = true;
|
|
|
|
Cookie[] cookies = getCookies();
|
|
if (cookies == null) {
|
|
return;
|
|
}
|
|
|
|
if (context.getEffectiveSessionTrackingModes().contains(
|
|
SessionTrackingMode.COOKIE))
|
|
{
|
|
final String name = context.getSessionCookieConfig().getName();
|
|
|
|
for (Cookie c : cookies) {
|
|
if (c.getName().equals(name)) {
|
|
request_session_id = c.getValue();
|
|
request_session_id_from_cookie = true;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String getRequestURI()
|
|
{
|
|
if (request_uri == null) {
|
|
request_uri = getRequestURI(req_ptr);
|
|
}
|
|
|
|
trace("getRequestURI: " + request_uri);
|
|
|
|
return request_uri;
|
|
}
|
|
|
|
private static native String getRequestURI(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public void setRequestURI(String uri)
|
|
{
|
|
trace("setRequestURI: " + uri);
|
|
|
|
request_uri = uri;
|
|
}
|
|
|
|
|
|
@Override
|
|
public StringBuffer getRequestURL()
|
|
{
|
|
String host = getHeader("Host");
|
|
String uri = getRequestURI();
|
|
StringBuffer res = new StringBuffer("http://" + host + uri);
|
|
|
|
trace("getRequestURL: " + res);
|
|
|
|
return res;
|
|
}
|
|
|
|
@Override
|
|
public String getServletPath()
|
|
{
|
|
trace("getServletPath: " + servlet_path);
|
|
|
|
return servlet_path;
|
|
}
|
|
|
|
@Override
|
|
public void setServletPath(String servlet_path, String path_info)
|
|
{
|
|
trace("setServletPath: " + servlet_path);
|
|
|
|
this.filter_path = servlet_path;
|
|
this.servlet_path = servlet_path;
|
|
this.path_info = path_info;
|
|
}
|
|
|
|
@Override
|
|
public void setServletPath(String filter_path, String servlet_path, String path_info)
|
|
{
|
|
trace("setServletPath: " + filter_path + ", " + servlet_path);
|
|
|
|
this.filter_path = filter_path;
|
|
this.servlet_path = servlet_path;
|
|
this.path_info = path_info;
|
|
}
|
|
|
|
@Override
|
|
public String getFilterPath()
|
|
{
|
|
return filter_path;
|
|
}
|
|
|
|
@Override
|
|
public HttpSession getSession()
|
|
{
|
|
return getSession(true);
|
|
}
|
|
|
|
@Override
|
|
public HttpSession getSession(boolean create)
|
|
{
|
|
if (session != null) {
|
|
if (context.isSessionIdValid(session.getId())) {
|
|
trace("getSession(" + create + "): " + session.getId());
|
|
|
|
return session;
|
|
}
|
|
|
|
session = null;
|
|
}
|
|
|
|
if (!request_session_id_parsed) {
|
|
parseRequestSessionId();
|
|
|
|
session = context.getSession(request_session_id);
|
|
}
|
|
|
|
if (session != null || !create) {
|
|
trace("getSession(" + create + "): " + (session != null ? session.getId() : "null"));
|
|
|
|
return session;
|
|
}
|
|
|
|
session = context.createSession();
|
|
|
|
if (context.getEffectiveSessionTrackingModes().contains(
|
|
SessionTrackingMode.COOKIE))
|
|
{
|
|
setSessionIdCookie();
|
|
}
|
|
|
|
trace("getSession(" + create + "): " + session.getId());
|
|
|
|
return session;
|
|
}
|
|
|
|
private void setSessionIdCookie()
|
|
{
|
|
SessionCookieConfig config = context.getSessionCookieConfig();
|
|
|
|
Cookie c = new Cookie(config.getName(), session.getId());
|
|
|
|
c.setComment(config.getComment());
|
|
if (!StringUtil.isBlank(config.getDomain())) {
|
|
c.setDomain(config.getDomain());
|
|
}
|
|
|
|
c.setHttpOnly(config.isHttpOnly());
|
|
if (!StringUtil.isBlank(config.getPath())) {
|
|
c.setPath(config.getPath());
|
|
}
|
|
|
|
c.setMaxAge(config.getMaxAge());
|
|
|
|
getResponse(req_info_ptr).addSessionIdCookie(c);
|
|
}
|
|
|
|
@Override
|
|
public Principal getUserPrincipal()
|
|
{
|
|
log("getUserPrincipal");
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean isRequestedSessionIdFromCookie()
|
|
{
|
|
trace("isRequestedSessionIdFromCookie");
|
|
|
|
if (!request_session_id_parsed) {
|
|
parseRequestSessionId();
|
|
}
|
|
|
|
return request_session_id_from_cookie;
|
|
}
|
|
|
|
@Override
|
|
@Deprecated
|
|
public boolean isRequestedSessionIdFromUrl()
|
|
{
|
|
trace("isRequestedSessionIdFromUrl");
|
|
|
|
if (!request_session_id_parsed) {
|
|
parseRequestSessionId();
|
|
}
|
|
|
|
return request_session_id_from_url;
|
|
}
|
|
|
|
@Override
|
|
public boolean isRequestedSessionIdFromURL()
|
|
{
|
|
trace("isRequestedSessionIdFromURL");
|
|
|
|
if (!request_session_id_parsed) {
|
|
parseRequestSessionId();
|
|
}
|
|
|
|
return request_session_id_from_url;
|
|
}
|
|
|
|
@Override
|
|
public boolean isRequestedSessionIdValid()
|
|
{
|
|
trace("isRequestedSessionIdValid");
|
|
|
|
if (!request_session_id_parsed) {
|
|
parseRequestSessionId();
|
|
}
|
|
|
|
return context.isSessionIdValid(request_session_id);
|
|
}
|
|
|
|
@Override
|
|
public boolean isUserInRole(String role)
|
|
{
|
|
log("isUserInRole: " + role);
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void login(String username, String password) throws ServletException
|
|
{
|
|
log("login: " + username + "," + password);
|
|
}
|
|
|
|
@Override
|
|
public void logout() throws ServletException
|
|
{
|
|
log("logout");
|
|
}
|
|
|
|
|
|
@Override
|
|
public AsyncContext getAsyncContext()
|
|
{
|
|
log("getAsyncContext");
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Object getAttribute(String name)
|
|
{
|
|
if (BARE.equals(name)) {
|
|
return this;
|
|
}
|
|
|
|
Object o = attributes.get(name);
|
|
|
|
trace("getAttribute: " + name + " = " + o);
|
|
|
|
return o;
|
|
}
|
|
|
|
@Override
|
|
public Enumeration<String> getAttributeNames()
|
|
{
|
|
trace("getAttributeNames");
|
|
|
|
Set<String> names = attributes.keySet();
|
|
return Collections.enumeration(names);
|
|
}
|
|
|
|
@Override
|
|
public String getCharacterEncoding()
|
|
{
|
|
trace("getCharacterEncoding");
|
|
|
|
if (characterEncoding != null) {
|
|
return characterEncoding;
|
|
}
|
|
|
|
getContentType();
|
|
|
|
return characterEncoding;
|
|
}
|
|
|
|
@Override
|
|
public int getContentLength()
|
|
{
|
|
trace("getContentLength");
|
|
|
|
return (int) getContentLength(req_ptr);
|
|
}
|
|
|
|
private static native long getContentLength(long req_ptr);
|
|
|
|
@Override
|
|
public long getContentLengthLong()
|
|
{
|
|
trace("getContentLengthLong");
|
|
|
|
return getContentLength(req_ptr);
|
|
}
|
|
|
|
@Override
|
|
public String getContentType()
|
|
{
|
|
trace("getContentType");
|
|
|
|
String content_type = getContentType(req_ptr);
|
|
|
|
if (characterEncoding == null && content_type != null) {
|
|
MimeTypes.Type mime = MimeTypes.CACHE.get(content_type);
|
|
String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(content_type) : mime.getCharset().toString();
|
|
if (charset != null) {
|
|
characterEncoding = charset;
|
|
}
|
|
}
|
|
|
|
return content_type;
|
|
}
|
|
|
|
private static native String getContentType(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public DispatcherType getDispatcherType()
|
|
{
|
|
trace("getDispatcherType: " + dispatcher_type);
|
|
|
|
return dispatcher_type;
|
|
}
|
|
|
|
@Override
|
|
public void setDispatcherType(DispatcherType type)
|
|
{
|
|
trace("setDispatcherType: " + type);
|
|
|
|
dispatcher_type = type;
|
|
}
|
|
|
|
@Override
|
|
public ServletInputStream getInputStream() throws IOException
|
|
{
|
|
trace("getInputStream");
|
|
|
|
if (reader != null) {
|
|
throw new IllegalStateException("getInputStream: getReader() already used");
|
|
}
|
|
|
|
if (inputStream == null) {
|
|
inputStream = new InputStream(req_info_ptr);
|
|
}
|
|
|
|
return inputStream;
|
|
}
|
|
|
|
@Override
|
|
public String getLocalAddr()
|
|
{
|
|
trace("getLocalAddr");
|
|
|
|
return getLocalAddr(req_ptr);
|
|
}
|
|
|
|
private static native String getLocalAddr(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public Locale getLocale()
|
|
{
|
|
log("getLocale");
|
|
|
|
return Locale.getDefault();
|
|
}
|
|
|
|
@Override
|
|
public Enumeration<Locale> getLocales()
|
|
{
|
|
log("getLocales");
|
|
|
|
return Collections.emptyEnumeration();
|
|
}
|
|
|
|
@Override
|
|
public String getLocalName()
|
|
{
|
|
trace("getLocalName");
|
|
|
|
return getLocalName(req_ptr);
|
|
}
|
|
|
|
private static native String getLocalName(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public int getLocalPort()
|
|
{
|
|
trace("getLocalPort");
|
|
|
|
return getLocalPort(req_ptr);
|
|
}
|
|
|
|
private static native int getLocalPort(long req_ptr);
|
|
|
|
|
|
public MultiMap<String> getParameters()
|
|
{
|
|
if (parameters != null) {
|
|
return parameters;
|
|
}
|
|
|
|
parameters = new MultiMap<>();
|
|
|
|
String query = getQueryString();
|
|
|
|
if (query != null) {
|
|
UrlEncoded.decodeUtf8To(query, parameters);
|
|
}
|
|
|
|
int content_length = getContentLength();
|
|
|
|
if (content_length == 0 || !getMethod().equals("POST")) {
|
|
return parameters;
|
|
}
|
|
|
|
String content_type = getContentType();
|
|
|
|
try {
|
|
if (content_type.startsWith("application/x-www-form-urlencoded")) {
|
|
UrlEncoded.decodeUtf8To(new InputStream(req_info_ptr),
|
|
parameters, content_length, -1);
|
|
} else if (checkMultiPart(content_type) && multipart_config != null) {
|
|
if (multi_parts == null) {
|
|
parseMultiParts(content_type);
|
|
}
|
|
|
|
if (multi_parts != null) {
|
|
Collection<Part> parts = multi_parts.getParts();
|
|
|
|
String _charset_ = null;
|
|
Part charset_part = multi_parts.getPart("_charset_");
|
|
if (charset_part != null) {
|
|
try (java.io.InputStream is = charset_part.getInputStream())
|
|
{
|
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
|
IO.copy(is, os);
|
|
_charset_ = new String(os.toByteArray(),StandardCharsets.UTF_8);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Select Charset to use for this part. (NOTE: charset behavior is for the part value only and not the part header/field names)
|
|
1. Use the part specific charset as provided in that part's Content-Type header; else
|
|
2. Use the overall default charset. Determined by:
|
|
a. if part name _charset_ exists, use that part's value.
|
|
b. if the request.getCharacterEncoding() returns a value, use that.
|
|
(note, this can be either from the charset field on the request Content-Type
|
|
header, or from a manual call to request.setCharacterEncoding())
|
|
c. use utf-8.
|
|
*/
|
|
Charset def_charset;
|
|
if (_charset_ != null) {
|
|
def_charset = Charset.forName(_charset_);
|
|
} else if (getCharacterEncoding() != null) {
|
|
def_charset = Charset.forName(getCharacterEncoding());
|
|
} else {
|
|
def_charset = StandardCharsets.UTF_8;
|
|
}
|
|
|
|
ByteArrayOutputStream os = null;
|
|
for (Part p : parts) {
|
|
if (p.getSubmittedFileName() != null) {
|
|
continue;
|
|
}
|
|
|
|
// Servlet Spec 3.0 pg 23, parts without filename must be put into params.
|
|
String charset = null;
|
|
if (p.getContentType() != null) {
|
|
charset = MimeTypes.getCharsetFromContentType(p.getContentType());
|
|
}
|
|
|
|
try (java.io.InputStream is = p.getInputStream())
|
|
{
|
|
if (os == null) {
|
|
os = new ByteArrayOutputStream();
|
|
}
|
|
IO.copy(is, os);
|
|
|
|
String content = new String(os.toByteArray(), charset == null ? def_charset : Charset.forName(charset));
|
|
parameters.add(p.getName(), content);
|
|
}
|
|
os.reset();
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
log("Unhandled IOException: " + e);
|
|
}
|
|
|
|
return parameters;
|
|
}
|
|
|
|
public void setParameters(MultiMap<String> p)
|
|
{
|
|
parameters = p;
|
|
}
|
|
|
|
@Override
|
|
public String getParameter(String name)
|
|
{
|
|
trace("getParameter: " + name);
|
|
|
|
return getParameters().getValue(name, 0);
|
|
}
|
|
|
|
@Override
|
|
public Map<String,String[]> getParameterMap()
|
|
{
|
|
trace("getParameterMap");
|
|
|
|
return Collections.unmodifiableMap(getParameters().toStringArrayMap());
|
|
}
|
|
|
|
@Override
|
|
public Enumeration<String> getParameterNames()
|
|
{
|
|
trace("getParameterNames");
|
|
|
|
return Collections.enumeration(getParameters().keySet());
|
|
}
|
|
|
|
@Override
|
|
public String[] getParameterValues(String name)
|
|
{
|
|
trace("getParameterValues: " + name);
|
|
|
|
List<String> vals = getParameters().getValues(name);
|
|
if (vals == null)
|
|
return null;
|
|
return vals.toArray(new String[vals.size()]);
|
|
}
|
|
|
|
@Override
|
|
public String getProtocol()
|
|
{
|
|
trace("getProtocol");
|
|
|
|
return getProtocol(req_ptr);
|
|
}
|
|
|
|
private static native String getProtocol(long req_ptr);
|
|
|
|
@Override
|
|
public BufferedReader getReader() throws IOException
|
|
{
|
|
trace("getReader");
|
|
|
|
if (inputStream != null) {
|
|
throw new IllegalStateException("getReader: getInputStream() already used");
|
|
}
|
|
|
|
if (reader == null) {
|
|
reader = new BufferedReader(new InputStreamReader(new InputStream(req_info_ptr)));
|
|
}
|
|
|
|
return reader;
|
|
}
|
|
|
|
@Override
|
|
@Deprecated
|
|
public String getRealPath(String path)
|
|
{
|
|
trace("getRealPath: " + path);
|
|
|
|
return context.getRealPath(path);
|
|
}
|
|
|
|
@Override
|
|
public String getRemoteAddr()
|
|
{
|
|
String res = getRemoteAddr(req_ptr);
|
|
|
|
trace("getRemoteAddr: " + res);
|
|
|
|
return res;
|
|
}
|
|
|
|
private static native String getRemoteAddr(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public String getRemoteHost()
|
|
{
|
|
String res = getRemoteHost(req_ptr);
|
|
|
|
trace("getRemoteHost: " + res);
|
|
|
|
return res;
|
|
}
|
|
|
|
private static native String getRemoteHost(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public int getRemotePort()
|
|
{
|
|
int res = getRemotePort(req_ptr);
|
|
|
|
trace("getRemotePort: " + res);
|
|
|
|
return res;
|
|
}
|
|
|
|
private static native int getRemotePort(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public RequestDispatcher getRequestDispatcher(String path)
|
|
{
|
|
trace("getRequestDispatcher: " + path);
|
|
|
|
if (path.startsWith("/")) {
|
|
return context.getRequestDispatcher(path);
|
|
}
|
|
|
|
try {
|
|
URI uri = new URI(getRequestURI());
|
|
uri = uri.resolve(path);
|
|
|
|
return context.getRequestDispatcher(uri);
|
|
} catch (URISyntaxException e) {
|
|
log("getRequestDispatcher: failed to create dispatcher: " + e);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
@Override
|
|
public String getScheme()
|
|
{
|
|
trace("getScheme");
|
|
|
|
return getScheme(req_ptr);
|
|
}
|
|
|
|
private static native String getScheme(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public String getServerName()
|
|
{
|
|
String res = getServerName(req_ptr);
|
|
|
|
trace("getServerName: " + res);
|
|
|
|
return res;
|
|
}
|
|
|
|
private static native String getServerName(long req_ptr);
|
|
|
|
|
|
@Override
|
|
public int getServerPort()
|
|
{
|
|
int res = getServerPort(req_ptr);
|
|
|
|
trace("getServerPort: " + res);
|
|
|
|
return res;
|
|
}
|
|
|
|
private static native int getServerPort(long req_ptr);
|
|
|
|
@Override
|
|
public ServletContext getServletContext()
|
|
{
|
|
trace("getServletContext");
|
|
|
|
return context;
|
|
}
|
|
|
|
@Override
|
|
public boolean isAsyncStarted()
|
|
{
|
|
log("isAsyncStarted");
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isAsyncSupported()
|
|
{
|
|
log("isAsyncSupported");
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSecure()
|
|
{
|
|
trace("isSecure");
|
|
|
|
return isSecure(req_ptr);
|
|
}
|
|
|
|
private static native boolean isSecure(long req_ptr);
|
|
|
|
@Override
|
|
public void removeAttribute(String name)
|
|
{
|
|
trace("removeAttribute: " + name);
|
|
|
|
Object prev = attributes.remove(name);
|
|
|
|
if (attr_listener == null || prev == null) {
|
|
return;
|
|
}
|
|
|
|
attr_listener.attributeRemoved(
|
|
new ServletRequestAttributeEvent(context, this, name, prev));
|
|
}
|
|
|
|
@Override
|
|
public void setAttribute(String name, Object o)
|
|
{
|
|
trace("setAttribute: " + name + ", " + o);
|
|
|
|
Object prev;
|
|
|
|
if (o != null) {
|
|
prev = attributes.put(name, o);
|
|
} else {
|
|
prev = attributes.remove(name);
|
|
}
|
|
|
|
if (attr_listener == null) {
|
|
return;
|
|
}
|
|
|
|
if (prev == null) {
|
|
if (o == null) {
|
|
return;
|
|
}
|
|
|
|
attr_listener.attributeAdded(new ServletRequestAttributeEvent(
|
|
context, this, name, o));
|
|
} else {
|
|
if (o != null) {
|
|
attr_listener.attributeReplaced(
|
|
new ServletRequestAttributeEvent(context, this, name, prev));
|
|
} else {
|
|
attr_listener.attributeRemoved(
|
|
new ServletRequestAttributeEvent(context, this, name, prev));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setAttribute_(String name, Object o)
|
|
{
|
|
trace("setAttribute_: " + name + ", " + o);
|
|
|
|
if (o != null) {
|
|
attributes.put(name, o);
|
|
} else {
|
|
attributes.remove(name);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setCharacterEncoding(String env) throws UnsupportedEncodingException
|
|
{
|
|
trace("setCharacterEncoding: " + env);
|
|
|
|
characterEncoding = env;
|
|
}
|
|
|
|
@Override
|
|
public AsyncContext startAsync() throws IllegalStateException
|
|
{
|
|
log("startAsync");
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
|
|
{
|
|
log("startAsync(Req, resp)");
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public <T extends HttpUpgradeHandler> T upgrade(
|
|
Class<T> httpUpgradeHandlerClass) throws java.io.IOException, ServletException
|
|
{
|
|
trace("upgrade: " + httpUpgradeHandlerClass.getName());
|
|
|
|
T handler;
|
|
|
|
try {
|
|
handler = httpUpgradeHandlerClass.getConstructor().newInstance();
|
|
} catch (Exception e) {
|
|
throw new ServletException(e);
|
|
}
|
|
|
|
upgrade(req_info_ptr);
|
|
|
|
return handler;
|
|
}
|
|
|
|
private static native void upgrade(long req_info_ptr);
|
|
|
|
public boolean isUpgrade()
|
|
{
|
|
return isUpgrade(req_info_ptr);
|
|
}
|
|
|
|
private static native boolean isUpgrade(long req_info_ptr);
|
|
|
|
@Override
|
|
public String changeSessionId()
|
|
{
|
|
trace("changeSessionId");
|
|
|
|
getSession(false);
|
|
|
|
if (session == null) {
|
|
return null;
|
|
}
|
|
|
|
context.changeSessionId(session);
|
|
|
|
if (context.getEffectiveSessionTrackingModes().contains(
|
|
SessionTrackingMode.COOKIE))
|
|
{
|
|
setSessionIdCookie();
|
|
}
|
|
|
|
return session.getId();
|
|
}
|
|
|
|
private void log(String msg)
|
|
{
|
|
msg = "Request." + msg;
|
|
log(req_info_ptr, msg, msg.length());
|
|
}
|
|
|
|
public static native void log(long req_info_ptr, String msg, int msg_len);
|
|
|
|
|
|
private void trace(String msg)
|
|
{
|
|
msg = "Request." + msg;
|
|
trace(req_info_ptr, msg, msg.length());
|
|
}
|
|
|
|
public static native void trace(long req_info_ptr, String msg, int msg_len);
|
|
|
|
private static native Response getResponse(long req_info_ptr);
|
|
|
|
|
|
public void setWsSession(WsSession s)
|
|
{
|
|
wsSession = s;
|
|
}
|
|
|
|
private void processWsFrame(ByteBuffer buf, byte opCode, boolean last)
|
|
throws IOException
|
|
{
|
|
trace("processWsFrame: " + opCode + ", [" + buf.position() + ", " + buf.limit() + "]");
|
|
try {
|
|
wsSession.processFrame(buf, opCode, last);
|
|
} catch (WsIOException e) {
|
|
wsSession.onClose(e.getCloseReason());
|
|
}
|
|
}
|
|
|
|
private void closeWsSession()
|
|
{
|
|
trace("closeWsSession");
|
|
skip_close_ws = true;
|
|
|
|
wsSession.onClose();
|
|
}
|
|
|
|
public void sendWsFrame(ByteBuffer payload, byte opCode, boolean last,
|
|
long timeoutExpiry) throws IOException
|
|
{
|
|
trace("sendWsFrame: " + opCode + ", [" + payload.position() +
|
|
", " + payload.limit() + "]");
|
|
|
|
if (payload.isDirect()) {
|
|
sendWsFrame(req_info_ptr, payload, payload.position(),
|
|
payload.limit() - payload.position(), opCode, last);
|
|
} else {
|
|
sendWsFrame(req_info_ptr, payload.array(), payload.position(),
|
|
payload.limit() - payload.position(), opCode, last);
|
|
}
|
|
}
|
|
|
|
private static native void sendWsFrame(long req_info_ptr,
|
|
ByteBuffer buf, int pos, int len, byte opCode, boolean last);
|
|
|
|
private static native void sendWsFrame(long req_info_ptr,
|
|
byte[] arr, int pos, int len, byte opCode, boolean last);
|
|
|
|
|
|
public void closeWs()
|
|
{
|
|
if (skip_close_ws) {
|
|
return;
|
|
}
|
|
|
|
trace("closeWs");
|
|
|
|
closeWs(req_info_ptr);
|
|
}
|
|
|
|
private static native void closeWs(long req_info_ptr);
|
|
}
|
|
|