com.github.netty.protocol.servlet.ServletHttpServletRequest Maven / Gradle / Ivy
package com.github.netty.protocol.servlet;
import com.github.netty.core.util.*;
import com.github.netty.protocol.servlet.util.*;
import io.netty.handler.codec.CodecException;
import io.netty.handler.codec.DateFormatter;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.multipart.*;
import io.netty.util.internal.PlatformDependent;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.security.Principal;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
/**
* The servlet request
*
* @author wangzihao
* 2018/7/15/015
*/
public class ServletHttpServletRequest implements HttpServletRequest, Recyclable {
private static final Recycler RECYCLER = new Recycler<>(ServletHttpServletRequest::new);
private static final Locale[] DEFAULT_LOCALS = {Locale.getDefault()};
private static final Map RESOURCE_MANAGER_MAP = new HashMap<>(2);
private static final SnowflakeIdWorker SNOWFLAKE_ID_WORKER = new SnowflakeIdWorker();
private final AtomicBoolean decodeBodyFlag = new AtomicBoolean();
private final Map attributeMap = new LinkedHashMap<>(32);
private final LinkedMultiValueMap parameterMap = new LinkedMultiValueMap<>(16);
private final Map unmodifiableParameterMap = new AbstractMap() {
@Override
public Set keySet() {
return parameterMap.keySet();
}
@Override
public Collection values() {
if (parameterMap.isEmpty()) {
return Collections.emptySet();
}
List result = new ArrayList<>(6);
Set>> entries = parameterMap.entrySet();
for (Entry> entry : entries) {
List value = entry.getValue();
String[] valueArr = value != null ? value.toArray(new String[value.size()]) : null;
result.add(valueArr);
}
return result;
}
@Override
public Set> entrySet() {
if (parameterMap.isEmpty()) {
return Collections.emptySet();
}
HashSet> result = new LinkedHashSet<>(6);
Set>> entries = parameterMap.entrySet();
for (Entry> entry : entries) {
List value = entry.getValue();
String[] valueArr = value != null ? value.toArray(new String[value.size()]) : null;
result.add(new SimpleImmutableEntry<>(entry.getKey(), valueArr));
}
return result;
}
@Override
public String[] get(Object key) {
List value = parameterMap.get(key);
if (value == null) {
return null;
} else {
return value.toArray(new String[value.size()]);
}
}
@Override
public boolean containsKey(Object key) {
return parameterMap.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
if (value instanceof String[]) {
value = Arrays.asList((String[]) value);
} else if (value instanceof String) {
value = Collections.singletonList(value);
}
return parameterMap.containsValue(value);
}
@Override
public int size() {
return parameterMap.size();
}
@Override
public boolean isEmpty() {
return parameterMap.isEmpty();
}
};
private final List fileUploadList = new ArrayList<>();
private ServletHttpExchange httpExchange;
private ServletHttpSession httpSession;
private ServletAsyncContext asyncContext;
private DispatcherType dispatcherType = null;
private String serverName;
private int serverPort;
private String remoteHost;
private String scheme;
private String servletPath;
private String queryString;
private String pathInfo;
private String requestURI;
private String characterEncoding;
private String sessionId;
private SessionTrackingMode sessionIdSource;
private MultipartConfigElement multipartConfigElement;
private ServletSecurityElement servletSecurityElement;
private ServletRequestDispatcher dispatcher;
private volatile ResourceManager resourceManager;
private final Supplier resourceManagerSupplier = () -> {
if (resourceManager == null) {
synchronized (this) {
if (resourceManager == null) {
String location = null;
if (multipartConfigElement != null) {
location = multipartConfigElement.getLocation();
}
ResourceManager resourceManager;
if (location != null && !location.isEmpty()) {
resourceManager = RESOURCE_MANAGER_MAP.get(location);
if (resourceManager == null) {
resourceManager = new ResourceManager(location);
RESOURCE_MANAGER_MAP.put(location, resourceManager);
}
} else {
resourceManager = getServletContext().getResourceManager();
}
this.resourceManager = resourceManager;
}
}
}
return resourceManager;
};
private boolean decodePathsFlag = false;
private boolean decodeCookieFlag = false;
private boolean decodeParameterByUrlFlag = false;
private volatile InterfaceHttpPostRequestDecoder postRequestDecoder = null;
private boolean remoteSchemeFlag = false;
private boolean usingInputStreamFlag = false;
private BufferedReader reader;
private HttpRequest nettyRequest;
private boolean isMultipart;
private boolean isFormUrlEncoder;
private final Supplier postRequestDecoderSupplier = () -> {
if (!isMultipart && !isFormUrlEncoder) {
return null;
}
if (this.postRequestDecoder == null) {
synchronized (this) {
if (this.postRequestDecoder == null) {
Charset charset = Charset.forName(getCharacterEncoding());
HttpDataFactory httpDataFactory = getHttpDataFactory(charset);
InterfaceHttpPostRequestDecoder postRequestDecoder;
if (isMultipart) {
postRequestDecoder = new HttpPostMultipartRequestDecoder(httpDataFactory, nettyRequest, charset);
} else if (isFormUrlEncoder) {
postRequestDecoder = new CompatibleHttpPostStandardRequestDecoder(httpDataFactory, nettyRequest, charset);
} else {
return null;
}
this.postRequestDecoder = postRequestDecoder;
}
}
}
return this.postRequestDecoder;
};
private final ServletInputStreamWrapper inputStream = new ServletInputStreamWrapper(postRequestDecoderSupplier, resourceManagerSupplier);
private Cookie[] cookies;
private Locale[] locales;
private Boolean asyncSupportedFlag;
protected ServletHttpServletRequest() {
}
public static ServletHttpServletRequest newInstance(ServletHttpExchange exchange, HttpRequest httpRequest, long contentLength) {
ServletHttpServletRequest instance = RECYCLER.getInstance();
instance.httpExchange = exchange;
instance.nettyRequest = httpRequest;
try {
instance.isMultipart = HttpPostRequestDecoder.isMultipart(httpRequest);
} catch (DecoderException e) {
instance.isMultipart = false;
}
if (instance.isMultipart) {
instance.isFormUrlEncoder = false;
} else {
instance.isFormUrlEncoder = HttpHeaderUtil.isFormUrlEncoder(instance.getContentType());
}
instance.resourceManager = null;
if (instance.postRequestDecoder != null) {
try {
instance.postRequestDecoder.destroy();
} catch (IllegalStateException ignored) {
}
instance.postRequestDecoder = null;
}
instance.inputStream.wrap(exchange.getChannelHandlerContext().alloc().compositeBuffer(Integer.MAX_VALUE));
instance.inputStream.setHttpExchange(exchange);
instance.inputStream.setFileSizeThreshold(instance.getFileSizeThreshold());
instance.inputStream.setFileUploadTimeoutMs(exchange.getServletContext().getUploadFileTimeoutMs());
instance.inputStream.setContentLength(contentLength);
return instance;
}
void setMultipartConfigElement(MultipartConfigElement multipartConfigElement) {
this.multipartConfigElement = multipartConfigElement;
}
void setServletSecurityElement(ServletSecurityElement servletSecurityElement) {
this.servletSecurityElement = servletSecurityElement;
}
void setDispatcher(ServletRequestDispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public int getFileSizeThreshold() {
int fileSizeThreshold;
if (multipartConfigElement != null) {
fileSizeThreshold = Math.max(multipartConfigElement.getFileSizeThreshold(), ServletContext.MIN_FILE_SIZE_THRESHOLD);
} else {
fileSizeThreshold = Math.max((int) getServletContext().getFileSizeThreshold(), ServletContext.MIN_FILE_SIZE_THRESHOLD);
}
return fileSizeThreshold;
}
public boolean isMultipart() {
return isMultipart;
}
public boolean isAsync() {
return asyncContext != null;
}
void setAsyncSupportedFlag(Boolean asyncSupportedFlag) {
this.asyncSupportedFlag = asyncSupportedFlag;
}
public ServletHttpExchange getHttpExchange() {
return httpExchange;
}
public HttpRequest getNettyRequest() {
return nettyRequest;
}
public HttpHeaders getNettyHeaders() {
return nettyRequest.headers();
}
private Map getAttributeMap() {
return attributeMap;
}
/**
* Parse request scheme
*/
private void decodeScheme() {
String proto = getHeader(HttpHeaderConstants.X_FORWARDED_PROTO.toString());
if (HttpConstants.HTTPS.equalsIgnoreCase(proto)) {
this.scheme = HttpConstants.HTTPS;
this.remoteSchemeFlag = true;
} else if (HttpConstants.HTTP.equalsIgnoreCase(proto)) {
this.scheme = HttpConstants.HTTP;
this.remoteSchemeFlag = true;
} else {
this.scheme = String.valueOf(nettyRequest.protocolVersion().protocolName()).toLowerCase();
this.remoteSchemeFlag = false;
}
}
/**
* Parse area
*/
private void decodeLocale() {
Locale[] locales;
String headerValue = getHeader(HttpHeaderConstants.ACCEPT_LANGUAGE.toString());
if (headerValue == null) {
locales = DEFAULT_LOCALS;
} else {
String[] values = headerValue.split(",");
int length = values.length;
locales = new Locale[length];
for (int i = 0; i < length; i++) {
String value = values[i];
String[] valueSp = value.split(";",2);
Locale locale;
if (valueSp.length > 0) {
locale = Locale.forLanguageTag(valueSp[0]);
} else {
locale = Locale.forLanguageTag(value);
}
locales[i] = locale;
}
}
this.locales = locales;
}
/**
* Parsing coding
*/
private void decodeCharacterEncoding() {
String characterEncoding = ServletUtil.decodeCharacterEncoding(getContentType());
if (characterEncoding == null) {
characterEncoding = getServletContext().getRequestCharacterEncoding();
}
this.characterEncoding = characterEncoding;
}
private HttpDataFactory getHttpDataFactory(Charset charset) {
HttpDataFactory factory = getServletContext().getHttpDataFactory(charset);
if (multipartConfigElement != null) {
factory.setMaxLimit(multipartConfigElement.getMaxFileSize());
}
return factory;
}
/**
* parse parameter specification
*
* the getParameterValues method returns an array of String objects containing all the parameter values associated with the parameter name. The getParameter
* The return value of the * method must be the first value in the String object array returned by the getParameterValues method. GetParameterMap method
* returns a java.util.map object of the request parameter, with the parameter name as the Map key and the parameter value as the Map value.
* the query string and the data from the POST request are aggregated into the set of request parameters. The query string data is sent before the POST data. For example,
* if the request consists of the query string a= hello and the POST data a=goodbye&a=world, the resulting parameter set order will be =(hello,goodbye,world).
* these apis do not expose the path parameters of GET requests (as defined in HTTP 1.1). They must be from the getRequestURI method or getPathInfo
* is resolved in the string value returned by the.
*
* the following conditions must be met before the POST form data is populated into the parameter set:
* 1. The request is an HTTP or HTTPS request.
* 2. The HTTP method is POST.
* 3. Content type is application/x-www-form-urlencoded.
* 4. The servlet has made an initial call to any getParameter method of the request object.
* if these conditions are not met and POST form data is not included in the parameter set, the servlet must be able to pass the input of the request object
* stream gets POST data. If these conditions are met, it is no longer valid to read the POST data directly from the input stream of the request object.
*/
private void decodeBody() {
//wait LastHttpContent
try {
inputStream.awaitDataIfNeed(-1);
} catch (IOException e) {
PlatformDependent.throwException(e);
}
if (postRequestDecoder == null) {
return;
}
/*
* There are three types of HttpDataType
* Attribute, FileUpload, InternalAttribute
*/
while (true) {
InterfaceHttpData interfaceData;
try {
interfaceData = postRequestDecoder.next();
if (interfaceData == null) {
return;
}
} catch (HttpPostRequestDecoder.EndOfDataDecoderException e) {
return;
}
InterfaceHttpData.HttpDataType httpDataType = interfaceData.getHttpDataType();
if (httpDataType == InterfaceHttpData.HttpDataType.Attribute) {
Attribute data = (Attribute) interfaceData;
String name = data.getName();
String value;
try {
value = data.getValue();
} catch (IOException e) {
value = "";
}
parameterMap.add(name, value);
if (isMultipart) {
ServletTextPart part = new ServletTextPart(data, resourceManagerSupplier);
fileUploadList.add(part);
}
} else if (httpDataType == InterfaceHttpData.HttpDataType.FileUpload) {
FileUpload data = (FileUpload) interfaceData;
ServletFilePart part = new ServletFilePart(data, resourceManagerSupplier);
fileUploadList.add(part);
}
}
}
/**
* Parsing URL parameters
*/
private void decodeUrlParameter() {
Charset charset = Charset.forName(getCharacterEncoding());
ServletUtil.decodeByUrl(parameterMap, nettyRequest.uri(), charset);
this.decodeParameterByUrlFlag = true;
}
/**
* Parsing the cookie
*/
private void decodeCookie() {
String value = getHeader(HttpHeaderConstants.COOKIE.toString());
if (value != null && !value.isEmpty()) {
this.cookies = ServletUtil.decodeCookie(value);
}
this.decodeCookieFlag = true;
}
/**
* Returns the fully qualified name of the client
* or the last proxy that sent the request.
* If the engine cannot or chooses not to resolve the hostname
* (to improve performance), this method returns the dotted-string form of
* the IP address. For HTTP servlets, same as the value of the CGI variable
* REMOTE_HOST
.
* qualified name of the client
*/
private void decodeRemoteHost() {
if (getServletContext().isEnableLookupFlag()) {
InetSocketAddress inetSocketAddress = httpExchange.getRemoteAddress();
if (inetSocketAddress == null) {
throw new IllegalStateException("request invalid");
}
try {
this.remoteHost = InetAddress.getByName(
inetSocketAddress.getHostName()).getHostName();
} catch (IOException e) {
//Ignore
}
} else {
this.remoteHost = getRemoteAddr();
}
}
/**
* decode the host name of the server to which the request was sent.
* It is the value of the part before ":" in the Host
* header value, if any, or the resolved server name, or the server IP
* address.
*/
private void decodeServerNameAndPort() {
String host = getHeader(HttpHeaderConstants.HOST.toString());
StringBuilder sb;
if (host != null && !host.isEmpty()) {
sb = RecyclableUtil.newStringBuilder();
int i = 0, length = host.length();
boolean hasPort = false;
while (i < length) {
char c = host.charAt(i);
if (c == ':') {
serverName = sb.toString();
sb.setLength(0);
hasPort = true;
} else {
sb.append(c);
}
i++;
}
if (hasPort && sb.length() > 0) {
serverPort = Integer.parseInt(sb.toString());
} else {
serverName = sb.toString();
sb.setLength(0);
}
} else {
serverName = getRemoteHost();
}
if (serverPort == 0) {
String scheme = getScheme();
if (remoteSchemeFlag) {
if (HttpConstants.HTTPS.equalsIgnoreCase(scheme)) {
serverPort = HttpConstants.HTTPS_PORT;
} else {
serverPort = HttpConstants.HTTP_PORT;
}
} else {
serverPort = HttpConstants.HTTP_PORT;
}
}
}
/**
* Parsing path
*/
private void decodePaths() {
String requestURI = nettyRequest.uri();
String queryString;
int queryInx = requestURI.indexOf('?');
if (queryInx != -1) {
queryString = requestURI.substring(queryInx + 1);
requestURI = requestURI.substring(0, queryInx);
} else {
queryString = null;
}
this.requestURI = com.github.netty.protocol.servlet.ServletContext.normPath(requestURI);
this.queryString = queryString;
this.decodePathsFlag = true;
}
/**
* New session ID
*
* @return session ID
*/
private String newSessionId() {
return String.valueOf(SNOWFLAKE_ID_WORKER.nextId());
}
@Override
public Cookie[] getCookies() {
if (decodeCookieFlag) {
return cookies;
}
decodeCookie();
return cookies;
}
/**
* servlet standard:
*
* returns the value of the specified request header
* is the long value, representing a
* date object. Using this method
* contains a header for the date, for example
* Return date is
* The number of milliseconds since January 1, 1970.
* The first name is case insensitive.
* , if the request does not have a header
* specify a name, and this method returns -1. If the header Cannot convert to date,
*
* @param name ,Specifies the name of the title
* @return Indicates the specified date in milliseconds as of January 1, 1970, or -1, if a title is specified. Not included in request
* @throws IllegalArgumentException IllegalArgumentException
*/
@Override
public long getDateHeader(String name) throws IllegalArgumentException {
String value = getHeader(name);
if (value == null || value.isEmpty()) {
return -1;
}
Date date = DateFormatter.parseHttpDate(value);
if (date == null) {
throw new IllegalArgumentException(value);
}
return date.getTime();
}
/**
* The getHeader method returns the header for the given header name. Multiple headers can have the same name, such as the cache-control header in an HTTP request.
* if multiple headers have the same name, the getHeader method returns the first header in the request. The getHeaders method allowed access to all names with specific headers
* calls the associated header value and returns an enumeration of the String object.
* the header can contain int or Date data in the form of a String. The HttpServletRequest interface provides the following convenient methods to access these types of headers
* data: the header can contain int or Date data in the form of a String. The HttpServletRequest interface provides the following convenient methods to access these types of headers
* getIntHeader
* getDateHeader
* if the getIntHeader method cannot be converted to a header value of int, then a NumberFormatException is thrown. If getDateHeader side
* method cannot convert the head into a Date object, then an IllegalArgumentException is thrown.
*
* @param name name
* @return header value
*/
@Override
public String getHeader(String name) {
Object value = getNettyHeaders().get((CharSequence) name);
return value == null ? null : value.toString();
}
@Override
public Enumeration getHeaderNames() {
Set nameSet = getNettyHeaders().names();
return new Enumeration() {
private final Iterator iterator = nameSet.iterator();
@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
@Override
public String nextElement() {
return iterator.next().toString();
}
};
}
/**
* Copy the implementation of tomcat
*
* @return RequestURL
*/
@Override
public StringBuffer getRequestURL() {
StringBuffer url = new StringBuffer();
String scheme = getScheme();
int port = getServerPort();
if (port < 0) {
port = HttpConstants.HTTP_PORT;
}
url.append(scheme);
url.append("://");
url.append(getServerName());
if ((HttpConstants.HTTP.equals(scheme) && (port != HttpConstants.HTTP_PORT))
|| (HttpConstants.HTTPS.equals(scheme) && (port != HttpConstants.HTTPS_PORT))) {
url.append(':');
url.append(port);
}
url.append(getRequestURI());
return url;
}
/**
* PathInfo:Part of the request Path that is not part of the Context Path or Servlet Path. If there's no extra path, it's either null,
* Or a string that starts with '/'.
*
* @return pathInfo
*/
@Override
public String getPathInfo() {
if (this.pathInfo == null && dispatcher != null) {
this.pathInfo = ServletRequestDispatcher.getPathInfo(dispatcher.getPath(), dispatcher.getMapperElement());
}
return this.pathInfo;
}
// now set PathInfo to null and ServletPath to ur-contextpath
// satisfies the requirements of SpringBoot, but not the semantics of ServletPath and PathInfo
// need to be set when RequestUrlPatternMapper matches, pass MapperData when new NettyRequestDispatcher
@Override
public String getQueryString() {
if (!decodePathsFlag) {
decodePaths();
}
return this.queryString;
}
@Override
public String getRequestURI() {
if (!decodePathsFlag) {
decodePaths();
}
return this.requestURI;
}
/**
* Servlet Path: the Path section corresponds directly to the mapping of the activation request. The path starts with the "/" character, if the request is in the "/ *" or "" mode."
* matches, in which case it is an empty string.
*
* @return servletPath
*/
@Override
public String getServletPath() {
if (this.servletPath == null) {
String servletPath = getServletContext().getServletPath(getRequestURI());
String contextPath = getServletContext().getContextPath();
if (!contextPath.isEmpty()) {
servletPath = servletPath.replaceFirst(contextPath, "");
}
this.servletPath = ServletContext.normPath(servletPath);
}
return this.servletPath;
}
@Override
public Enumeration getHeaders(String name) {
Collection collection = getNettyHeaders().getAll((CharSequence) name);
return new Enumeration() {
private final Iterator iterator = collection.iterator();
@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
@Override
public String nextElement() {
return iterator.next().toString();
}
};
}
/**
* servlet standard:
*
* returns the value of the specified request header
* as int. If the request has no title
* the name specified by this method returns -1. if This method does not convert headers to integers
* throws a NumberFormatException code. The first name is case insensitive.
*
* @param name specifies the name of the request header
* @return An integer request header representing a value or -1 if the request does not return -1 for the header with this name
* @throws NumberFormatException If the header value cannot be converted to an int。
*/
@Override
public int getIntHeader(String name) {
String headerStringValue = getHeader(name);
if (headerStringValue == null) {
return -1;
}
return Integer.parseInt(headerStringValue);
}
@Override
public String getMethod() {
return nettyRequest.method().toString();
}
/**
* Context Path: the Path prefix associated with the ServletContext is part of this servlet. If the context is web-based
* the server's URL namespace based on the "default" context, then the path will be an empty string. Otherwise, if the context is not
* server-based namespaces, so the path starts with /, but does not end with /
*/
@Override
public String getContextPath() {
return getServletContext().getContextPath();
}
@Override
public ServletHttpSession getSession(boolean create) {
// Scope in request cache. This can reduce multiple acquisitions.
if (httpSession != null) {
return httpSession;
}
// 1. find trace ID
String sessionId = getRequestedSessionId0();
// 2. fast return. If dont need create.
boolean existSessionId = sessionId != null && !sessionId.isEmpty();
if (!existSessionId && !create) {
return null;
}
ServletContext servletContext;
// 3. Scope in TCP connection. The session has already been created.
ServletHttpSession httpSession = httpExchange.getHttpSession();
if (existSessionId && httpSession != null && sessionId.equals(httpSession.getId()) && httpSession.isValid()) {
this.httpSession = httpSession;
httpSession.access();
return httpSession;
} else {
servletContext = getServletContext();
}
// 4. Scope in store. The session has already been created.
Session session = existSessionId ? servletContext.getSessionService().getSession(sessionId) : null;
if (session == null) {
if (create) {
// 5. Create a new internal session. It doesn't exist
if (!existSessionId) {
this.sessionId = newSessionId();
}
session = new Session(this.sessionId, servletContext.getSessionTimeout());
} else {
return null;
}
}
// 6. Transformation decorate HttpSession
httpSession = new ServletHttpSession(session, servletContext);
httpSession.access();
// 7. bind to current request cache.
this.httpSession = httpSession;
// 8. bind to current TCP connection.
httpExchange.setHttpSession(httpSession);
return httpSession;
}
@Override
public ServletHttpSession getSession() {
return getSession(true);
}
@Override
public String changeSessionId() {
ServletHttpSession httpSession = getSession(true);
String oldSessionId = httpSession.getId();
String newSessionId = newSessionId();
ServletContext servletContext = getServletContext();
servletContext.getSessionService().changeSessionId(oldSessionId, newSessionId);
sessionId = newSessionId;
httpSession.setId(sessionId);
ServletEventListenerManager listenerManager = servletContext.getServletEventListenerManager();
if (listenerManager.hasHttpSessionIdListener()) {
listenerManager.onHttpSessionIdChanged(new HttpSessionEvent(httpSession), oldSessionId);
}
return newSessionId;
}
@Override
public boolean isRequestedSessionIdValid() {
getRequestedSessionId0();
return sessionIdSource == SessionTrackingMode.COOKIE ||
sessionIdSource == SessionTrackingMode.URL;
}
@Override
public boolean isRequestedSessionIdFromCookie() {
getRequestedSessionId0();
return sessionIdSource == SessionTrackingMode.COOKIE;
}
@Override
public boolean isRequestedSessionIdFromURL() {
return isRequestedSessionIdFromUrl();
}
@Override
public boolean isRequestedSessionIdFromUrl() {
getRequestedSessionId0();
return sessionIdSource == SessionTrackingMode.URL;
}
private String getRequestedSessionId0() {
if (sessionId != null) {
return sessionId;
}
//If the user sets the sessionCookie name, the user set the sessionCookie name
String userSettingCookieName = getServletContext().getSessionCookieConfig().getName();
String cookieSessionName = userSettingCookieName != null && !userSettingCookieName.isEmpty() ?
userSettingCookieName : HttpConstants.JSESSION_ID_COOKIE;
//Find the value of sessionCookie first from cookie, then from url parameter
String sessionId = ServletUtil.getCookieValue(getCookies(), cookieSessionName);
if (sessionId != null && !sessionId.isEmpty()) {
sessionIdSource = SessionTrackingMode.COOKIE;
} else {
String queryString = getQueryString();
if (queryString != null && queryString.contains(HttpConstants.JSESSION_ID_URL)) {
sessionId = getParameter(HttpConstants.JSESSION_ID_URL);
}
if (sessionId != null && !sessionId.isEmpty()) {
sessionIdSource = SessionTrackingMode.URL;
} else {
sessionIdSource = null;
}
}
return this.sessionId = sessionId;
}
@Override
public String getRequestedSessionId() {
String sessionId = getRequestedSessionId0();
if (sessionId == null || sessionId.isEmpty()) {
sessionId = newSessionId();
}
this.sessionId = sessionId;
return sessionId;
}
@Override
public Object getAttribute(String name) {
return getAttributeMap().get(name);
}
@Override
public Enumeration getAttributeNames() {
return Collections.enumeration(getAttributeMap().keySet());
}
@Override
public String getCharacterEncoding() {
if (characterEncoding == null) {
decodeCharacterEncoding();
}
return characterEncoding;
}
@Override
public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
characterEncoding = env;
}
@Override
public int getContentLength() {
return (int) getContentLengthLong();
}
@Override
public long getContentLengthLong() {
return inputStream.getContentLength();
}
@Override
public String getContentType() {
return getHeader(HttpHeaderConstants.CONTENT_TYPE.toString());
}
@Override
public ServletInputStreamWrapper getInputStream() {
if (reader != null) {
throw new IllegalStateException("getReader() has already been called for this request");
}
usingInputStreamFlag = true;
return inputStream;
}
ServletInputStreamWrapper getInputStream0() {
return inputStream;
}
@Override
public String getParameter(String name) {
String[] values;
if (getServletContext().getNotExistBodyParameters().contains(name)) {
if (!decodeParameterByUrlFlag) {
decodeUrlParameter();
}
values = unmodifiableParameterMap.get(name);
} else {
values = getParameterMap().get(name);
}
if (values == null || values.length == 0) {
return null;
}
return values[0];
}
@Override
public Enumeration getParameterNames() {
return Collections.enumeration(getParameterMap().keySet());
}
@Override
public String[] getParameterValues(String name) {
return getParameterMap().get(name);
}
@Override
public Map getParameterMap() {
if (!decodeParameterByUrlFlag) {
decodeUrlParameter();
}
if (decodeBodyFlag.compareAndSet(false, true)) {
if (inputStream.getContentLength() > 0) {
decodeBody();
}
} else {
try {
inputStream.awaitDataIfNeed(-1);
} catch (IOException e) {
PlatformDependent.throwException(e);
}
}
return unmodifiableParameterMap;
}
@Override
public String getProtocol() {
Protocol protocol = httpExchange.getProtocol();
if (protocol.isHttp2()) {
return "HTTP/2.0";
} else {
return nettyRequest.protocolVersion().toString();
}
}
@Override
public String getScheme() {
if (scheme == null) {
decodeScheme();
}
return scheme;
}
@Override
public String getServerName() {
if (serverName == null) {
decodeServerNameAndPort();
}
return serverName;
}
@Override
public int getServerPort() {
if (serverPort == 0) {
decodeServerNameAndPort();
}
return serverPort;
}
@Override
public BufferedReader getReader() throws IOException {
if (usingInputStreamFlag) {
throw new IllegalStateException("getInputStream() has already been called for this request");
}
if (reader == null) {
synchronized (this) {
if (reader == null) {
String charset = getCharacterEncoding();
if (charset == null) {
charset = getServletContext().getRequestCharacterEncoding();
}
reader = new BufferedReader(new InputStreamReader(inputStream, charset));
}
}
}
return reader;
}
@Override
public String getRemoteAddr() {
InetSocketAddress inetSocketAddress = httpExchange.getRemoteAddress();
if (inetSocketAddress == null) {
return null;
}
InetAddress inetAddress = inetSocketAddress.getAddress();
if (inetAddress == null) {
return null;
}
return inetAddress.getHostAddress();
}
@Override
public String getRemoteHost() {
if (remoteHost == null) {
decodeRemoteHost();
}
return remoteHost;
}
@Override
public int getRemotePort() {
InetSocketAddress inetSocketAddress = httpExchange.getRemoteAddress();
if (inetSocketAddress == null) {
return 0;
}
return inetSocketAddress.getPort();
}
@Override
public void setAttribute(String name, Object object) {
Objects.requireNonNull(name);
if (object == null) {
removeAttribute(name);
return;
}
Object oldObject = getAttributeMap().put(name, object);
ServletContext servletContext = getServletContext();
ServletEventListenerManager listenerManager = servletContext.getServletEventListenerManager();
if (listenerManager.hasServletRequestAttributeListener()) {
listenerManager.onServletRequestAttributeAdded(new ServletRequestAttributeEvent(servletContext, this, name, object));
if (oldObject != null) {
listenerManager.onServletRequestAttributeReplaced(new ServletRequestAttributeEvent(servletContext, this, name, oldObject));
}
}
}
@Override
public void removeAttribute(String name) {
Object oldObject = getAttributeMap().remove(name);
ServletContext servletContext = getServletContext();
ServletEventListenerManager listenerManager = servletContext.getServletEventListenerManager();
if (listenerManager.hasServletRequestAttributeListener()) {
listenerManager.onServletRequestAttributeRemoved(new ServletRequestAttributeEvent(servletContext, this, name, oldObject));
}
}
@Override
public Locale getLocale() {
if (this.locales == null) {
decodeLocale();
}
Locale[] locales = this.locales;
if (locales == null || locales.length == 0) {
return null;
}
return locales[0];
}
@Override
public Enumeration getLocales() {
if (this.locales == null) {
decodeLocale();
}
return new Enumeration() {
private int index = 0;
@Override
public boolean hasMoreElements() {
return index < locales.length;
}
@Override
public Locale nextElement() {
Locale locale = locales[index];
index++;
return locale;
}
};
}
@Override
public boolean isSecure() {
return HttpConstants.HTTPS.equals(getScheme());
}
@Override
public ServletRequestDispatcher getRequestDispatcher(String path) {
return getServletContext().getRequestDispatcher(path, getDispatcherType());
}
@Override
public String getRealPath(String path) {
return getServletContext().getRealPath(path);
}
@Override
public String getLocalName() {
return getServletContext().getServerAddress().getHostName();
}
@Override
public String getLocalAddr() {
return getServletContext().getServerAddress().getAddress().getHostAddress();
}
@Override
public int getLocalPort() {
return getServletContext().getServerAddress().getPort();
}
@Override
public ServletContext getServletContext() {
return httpExchange.getServletContext();
}
@Override
public ServletAsyncContext startAsync() throws IllegalStateException {
return startAsync(this, httpExchange.getResponse());
}
@Override
public ServletAsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
if (!isAsyncSupported()) {
throw new IllegalStateException("Asynchronous is not supported");
}
ServletContext servletContext = getServletContext();
if (asyncContext == null) {
asyncContext = new ServletAsyncContext(httpExchange, servletContext, servletContext.getExecutor());
asyncContext.setTimeout(servletContext.getAsyncTimeout());
}
asyncContext.setServletRequest(servletRequest);
asyncContext.setServletResponse(servletResponse);
asyncContext.setStart();
return asyncContext;
}
@Override
public boolean isAsyncStarted() {
return asyncContext != null && asyncContext.isStarted();
}
@Override
public boolean isAsyncSupported() {
if (asyncSupportedFlag == null) {
return true;
}
return asyncSupportedFlag;
}
@Override
public ServletAsyncContext getAsyncContext() {
return asyncContext;
}
@Override
public DispatcherType getDispatcherType() {
if (dispatcherType == null) {
return DispatcherType.REQUEST;
}
return this.dispatcherType;
}
void setDispatcherType(DispatcherType dispatcherType) {
this.dispatcherType = dispatcherType;
}
@Override
public String getPathTranslated() {
ServletContext servletContext = getServletContext();
String contextPath = servletContext.getContextPath();
if (contextPath == null || contextPath.isEmpty()) {
return null;
}
String pathInfo = getPathInfo();
if (pathInfo == null) {
return null;
}
return servletContext.getRealPath(pathInfo);
}
/**
* "BASIC", or "DIGEST", or "SSL".
*
* @return Authentication type
*/
@Override
public String getAuthType() {
// TODO: 10-16/0016 Authentication: gets the authentication type
return null;
}
@Override
public String getRemoteUser() {
Principal principal = getUserPrincipal();
if (principal != null) {
return principal.getName();
}
return null;
}
@Override
public boolean isUserInRole(String role) {
// TODO: 10-16/0016 Authentication: whether you have a permission
return false;
}
@Override
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
// TODO: 10-16/0016 Authentication interface
return true;
}
@Override
public Principal getUserPrincipal() {
return null;
}
@Override
public void login(String username, String password) throws ServletException {
// TODO: 10-16/0016 Authentication interface: login
}
@Override
public void logout() throws ServletException {
}
@Override
public Collection getParts() throws IOException, ServletException {
if (decodeBodyFlag.compareAndSet(false, true)) {
try {
decodeBody();
} catch (CodecException e) {
Throwable cause = getCause(e);
if (cause instanceof IOException) {
setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpServletResponse.SC_BAD_REQUEST);
setAttribute(RequestDispatcher.ERROR_EXCEPTION, cause);
throw (IOException) cause;
} else if (cause instanceof IllegalStateException) {
setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpServletResponse.SC_BAD_REQUEST);
setAttribute(RequestDispatcher.ERROR_EXCEPTION, cause);
throw (IllegalStateException) cause;
} else if (cause instanceof IllegalArgumentException) {
IllegalStateException illegalStateException = new IllegalStateException("HttpServletRequest.getParts() -> decodeFile() fail : " + cause.getMessage(), cause);
illegalStateException.setStackTrace(cause.getStackTrace());
setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpServletResponse.SC_BAD_REQUEST);
setAttribute(RequestDispatcher.ERROR_EXCEPTION, illegalStateException);
throw illegalStateException;
} else {
ServletException servletException;
if (cause != null) {
servletException = new ServletException("HttpServletRequest.getParts() -> decodeFile() fail : " + cause.getMessage(), cause);
servletException.setStackTrace(cause.getStackTrace());
} else {
servletException = new ServletException("HttpServletRequest.getParts() -> decodeFile() fail : " + e.getMessage(), e);
servletException.setStackTrace(e.getStackTrace());
}
setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpServletResponse.SC_BAD_REQUEST);
setAttribute(RequestDispatcher.ERROR_EXCEPTION, servletException);
throw servletException;
}
} catch (IllegalArgumentException e) {
IllegalStateException illegalStateException = new IllegalStateException("HttpServletRequest.getParts() -> decodeFile() fail : " + e.getMessage(), e);
illegalStateException.setStackTrace(e.getStackTrace());
setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpServletResponse.SC_BAD_REQUEST);
setAttribute(RequestDispatcher.ERROR_EXCEPTION, illegalStateException);
throw illegalStateException;
}
} else {
inputStream.awaitDataIfNeed(-1);
}
return fileUploadList;
}
private Throwable getCause(Throwable throwable) {
if (throwable.getCause() == null) {
return null;
}
while (true) {
Throwable cause = throwable;
throwable = throwable.getCause();
if (throwable == null) {
return cause;
}
}
}
@Override
public Part getPart(String name) throws IOException, ServletException {
for (Part part : getParts()) {
if (name.equals(part.getName())) {
return part;
}
}
return null;
}
@Override
public T upgrade(Class httpUpgradeHandlerClass) throws IOException, ServletException {
try {
// servletHttpExchange.getHttpServletResponse().setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
// servletHttpExchange.getHttpServletResponse().getOutputStream().close();
T handler = httpUpgradeHandlerClass.newInstance();
return handler;
} catch (Exception e) {
throw new ServletException(e.getMessage(), e);
}
}
@Override
public void recycle() {
ServletHttpSession httpSession = this.httpSession;
if (httpSession != null) {
httpSession.save();
}
this.inputStream.recycle();
if (!fileUploadList.isEmpty()) {
Part[] parts = fileUploadList.toArray(new Part[fileUploadList.size()]);
ServletContext.asyncClose(()->{
for (Part part : parts) {
try {
part.delete();
} catch (IOException ignored) {
}
}
});
}
InterfaceHttpPostRequestDecoder postRequestDecoder = this.postRequestDecoder;
ServletContext.asyncClose(() -> {
if (postRequestDecoder != null) {
try {
postRequestDecoder.destroy();
} catch (IllegalStateException ignored) {
}
}
});
this.postRequestDecoder = null;
if (httpExchange.isAbort()) {
return;
}
this.httpSession = null;
this.nettyRequest = null;
this.decodeBodyFlag.set(false);
this.decodeParameterByUrlFlag = false;
this.remoteSchemeFlag = false;
this.decodeCookieFlag = false;
this.decodePathsFlag = false;
this.usingInputStreamFlag = false;
this.reader = null;
this.sessionIdSource = null;
this.remoteHost = null;
this.serverName = null;
this.serverPort = 0;
this.scheme = null;
this.servletPath = null;
this.queryString = null;
this.pathInfo = null;
this.requestURI = null;
this.characterEncoding = null;
this.sessionId = null;
this.cookies = null;
this.locales = null;
this.asyncContext = null;
this.httpExchange = null;
this.multipartConfigElement = null;
this.servletSecurityElement = null;
this.dispatcherType = null;
this.dispatcher = null;
this.parameterMap.clear();
this.fileUploadList.clear();
this.attributeMap.clear();
RECYCLER.recycleInstance(this);
}
}