All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.server.http.HttpServletRequestImpl Maven / Gradle / Ivy

/*
 * Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.server.http;

import java.io.BufferedReader;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.caucho.config.scope.ScopeRemoveListener;
import com.caucho.network.listen.SocketLink;
import com.caucho.network.listen.SocketLinkDuplexController;
import com.caucho.remote.websocket.MaskedFrameInputStream;
import com.caucho.remote.websocket.UnmaskedFrameInputStream;
import com.caucho.remote.websocket.WebSocketConstants;
import com.caucho.security.AbstractLogin;
import com.caucho.security.Login;
import com.caucho.server.cluster.ServletService;
import com.caucho.server.dispatch.Invocation;
import com.caucho.server.session.SessionManager;
import com.caucho.server.webapp.WebApp;
import com.caucho.util.Base64;
import com.caucho.util.CharBuffer;
import com.caucho.util.HashMapImpl;
import com.caucho.util.L10N;
import com.caucho.util.NullEnumeration;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.websocket.WebSocketContext;
import com.caucho.websocket.WebSocketListener;
import com.caucho.websocket.WebSocketServletRequest;

/**
 * User facade for http requests.
 */
public final class HttpServletRequestImpl extends AbstractCauchoRequest
  implements CauchoRequest, WebSocketServletRequest
{
  private static final Logger log
    = Logger.getLogger(HttpServletRequestImpl.class.getName());

  private static final L10N L = new L10N(HttpServletRequestImpl.class);

  private AbstractHttpRequest _request;

  private final HttpServletResponseImpl _response;

  private Boolean _isSecure;

  private Invocation _invocation;

  // session/cookies
  private Cookie []_cookiesIn;

  private boolean _varyCookies;   // True if the page depends on cookies
  private boolean _hasCookie;

  private boolean _isSessionIdFromCookie;

  // security
  private String _runAs;
  private boolean _isLoginRequested;

  // input stream management
  private boolean _hasReader;
  private boolean _hasInputStream;

  // servlet attributes
  private HashMapImpl _attributes;

  // proxy caching
  private boolean _isSyntheticCacheHeader;

  // comet
  private long _asyncTimeout;
  private AsyncContextImpl _asyncContext;

  private ArrayList _closeOnExit;

  /**
   * Create a new Request.  Because the actual initialization occurs with
   * the start() method, this just allocates statics.
   *
   * @param request
   */
  public HttpServletRequestImpl(AbstractHttpRequest request)
  {
    _request = request;

    _response = new HttpServletResponseImpl(this,
                                            request.getAbstractHttpResponse());
  }

  @Override
  public HttpServletResponseImpl getResponse()
  {
    return _response;
  }

  //
  // ServletRequest methods
  //

  /**
   * Returns the prococol, e.g. "HTTP/1.1"
   */
  @Override
  public String getProtocol()
  {
    return _request.getProtocol();
  }

  /**
   * Returns the request scheme, e.g. "http"
   */
  @Override
  public String getScheme()
  {
    String scheme = _request.getScheme();

    // server/12j2, server/1kkg
    if ("http".equals(scheme) || "https".equals(scheme))
      return isSecure() ? "https" : "http";
    else
      return scheme;
  }

  /**
   * Returns the server name handling the request.  When using virtual hosts,
   * this returns the virtual host name, e.g. "vhost1.caucho.com".
   *
   * This call returns the host name as the client sees it, which means that
   * if ipchains, load balancing, or proxying is involved this call returns the
   * correct call for forming urls, but may not contain the host that Resin is
   * actually listening on.
   */
  @Override
  public String getServerName()
  {
    AbstractHttpRequest request = _request;

    return request != null ? request.getServerName() : null;
  }

  /**
   * Returns the server port used by the client, e.g. 80.
   *
   * This call returns the port number as the client sees it, which means that
   * if ipchains, load balancing, or proxying is involved this call returns the
   * correct call for forming urls, but may not return the actual port that
   * Resin is listening on.
   *
   * This call should not be used to test for an ssl connection
   * (getServerPort() == 443), {@link #isSecure()} is provided for
   * that purpose.
   */
  @Override
  public int getServerPort()
  {
    AbstractHttpRequest request = _request;

    return request != null ? request.getServerPort() : 0;
  }

  /**
   * Returns the IP address of the remote host, i.e. the client browser.
   */
  @Override
  public String getRemoteAddr()
  {
    AbstractHttpRequest request = _request;

    return request != null ? request.getRemoteAddr() : null;
  }

  /**
   * Returns the DNS hostname of the remote host, i.e. the client browser.
   */
  @Override
  public String getRemoteHost()
  {
    AbstractHttpRequest request = _request;

    return request != null ? request.getRemoteHost() : null;
  }

  /**
   * Returns the port of the remote host, i.e. the client browser.
   *
   * @since 2.4
   */
  @Override
  public int getRemotePort()
  {
    AbstractHttpRequest request = _request;

    return request != null ? request.getRemotePort() : 0;
  }

  /**
   * This call returns the ip of the host actually used to connect to the Resin
   * server,  which means that if ipchains, load balancing, or proxying is
   * involved this call does not return the correct host for
   * forming urls.
   *
   * @since 2.4
   */
  @Override
  public String getLocalAddr()
  {
    return _request.getLocalHost();
  }

  /**
   * Returns the IP address of the local host, i.e. the server.
   *
   * This call returns the name of the host actually used to connect to the
   * Resin server,  which means that if ipchains, load balancing, or proxying
   * is involved this call does not return the correct host for
   * forming urls.
   *
   * @since 2.4
   */
  @Override
  public String getLocalName()
  {
    return _request.getLocalHost();
  }

  /**
   * Returns the port of the local host.
   *
   * This call returns the port number actually used to connect to the Resin
   * server,  which means that if ipchains, load balancing, or proxying is
   * involved this call does not return the correct port for
   * forming urls.
   *
   * This call should not be used to test for an ssl connection
   * (getServerPort() == 443), {@link #isSecure()} is provided for that purpose.
   *
   * @since 2.4
   */
  @Override
  public int getLocalPort()
  {
    return _request.getLocalPort();
  }

  /**
   * Overrides the character encoding specified in the request.
   * setCharacterEncoding must be called before calling
   * getReader or reading any parameters.
   */
  @Override
  public void setCharacterEncoding(String encoding)
    throws java.io.UnsupportedEncodingException
  {
    _request.setCharacterEncoding(encoding);
  }

  /**
   * Returns an InputStream to retrieve POST data from the request.
   * The stream will automatically end when the end of the POST data
   * is complete.
   */
  @Override
  public ServletInputStream getInputStream()
    throws IOException
  {
    if (_hasReader)
      throw new IllegalStateException(L.l("getInputStream() can't be called after getReader()"));

    _hasInputStream = true;

    return _request.getInputStream();
  }

  /**
   * Returns a reader to read POSTed data.  Character encoding is
   * based on the request data and is the same as
   * getCharacterEncoding()
   */
  @Override
  public BufferedReader getReader()
    throws IOException, IllegalStateException
  {
    if (_hasInputStream)
      throw new IllegalStateException(L.l("getReader() can't be called after getInputStream()"));

    _hasReader = true;

    return _request.getReader();
  }

  /**
   * Returns the character encoding of the POSTed data.
   */
  @Override
  public String getCharacterEncoding()
  {
    return _request.getCharacterEncoding();
  }

  /**
   * Returns the content length of the data.  This value may differ from
   * the actual length of the data.  Newer browsers
   * supporting HTTP/1.1 may use "chunked" encoding which does
   * not make the content length available.
   *
   * 

The upshot is, rely on the input stream to end when the data * completes. */ @Override public int getContentLength() { return _request.getContentLength(); } /** * Returns the request's mime-type. */ @Override public String getContentType() { AbstractHttpRequest request = _request; if (request != null) return request.getContentType(); else return null; } /** * Returns the request's preferred locale, based on the Accept-Language * header. If unspecified, returns the server's default locale. */ @Override public Locale getLocale() { AbstractHttpRequest request = _request; if (request != null) return request.getLocale(); else return Locale.getDefault(); } /** * Returns an enumeration of all locales acceptable by the client. */ @Override public Enumeration getLocales() { AbstractHttpRequest request = _request; if (request != null) return request.getLocales(); else return new Vector().elements(); } /** * Returns true if the connection is secure, e.g. it uses SSL. */ @Override public boolean isSecure() { if (_isSecure != null) return _isSecure; AbstractHttpRequest request = _request; if (request == null) return false; WebApp webApp = request.getWebApp(); if (webApp != null) { Boolean isSecure = webApp.isRequestSecure(); if (isSecure != null) return isSecure; } return request.isSecure(); } // // request attributes // /** * Returns the value of the named request attribute. * * @param name the attribute name. * * @return the attribute value. */ @Override public Object getAttribute(String name) { HashMapImpl attributes = _attributes; if (attributes != null) return attributes.get(name); else if (isSecure()) { _attributes = new HashMapImpl(); attributes = _attributes; _request.initAttributes(this); return attributes.get(name); } else return null; } private boolean isAttributesEmpty() { return _attributes == null; } /** * Returns an enumeration of the request attribute names. */ @Override public Enumeration getAttributeNames() { HashMapImpl attributes = _attributes; if (attributes != null) { return Collections.enumeration(attributes.keySet()); } else if (isSecure()) { _attributes = new HashMapImpl(); attributes = _attributes; _request.initAttributes(this); return Collections.enumeration(attributes.keySet()); } else return NullEnumeration.create(); } /** * Sets the value of the named request attribute. * * @param name the attribute name. * @param value the new attribute value. */ @Override public void setAttribute(String name, Object value) { HashMapImpl attributes = _attributes; if (value != null) { if (attributes == null) { attributes = new HashMapImpl(); _attributes = attributes; _request.initAttributes(this); } Object oldValue = attributes.put(name, value); WebApp webApp = getWebApp(); if (webApp != null) { for (ServletRequestAttributeListener listener : webApp.getRequestAttributeListeners()) { ServletRequestAttributeEvent event; if (oldValue != null) { event = new ServletRequestAttributeEvent(webApp, this, name, oldValue); listener.attributeReplaced(event); } else { event = new ServletRequestAttributeEvent(webApp, this, name, value); listener.attributeAdded(event); } } } } else removeAttribute(name); } /** * Removes the value of the named request attribute. * * @param name the attribute name. */ @Override public void removeAttribute(String name) { HashMapImpl attributes = _attributes; if (attributes == null) return; Object oldValue = attributes.remove(name); WebApp webApp = getWebApp(); if (webApp == null) return; for (ServletRequestAttributeListener listener : webApp.getRequestAttributeListeners()) { ServletRequestAttributeEvent event; event = new ServletRequestAttributeEvent(webApp, this, name, oldValue); listener.attributeRemoved(event); } if (oldValue instanceof ScopeRemoveListener) { ((ScopeRemoveListener) oldValue).removeEvent(this, name); } } // // request dispatching // /** * Returns a request dispatcher for later inclusion or forwarding. This * is the servlet API equivalent to SSI includes. uri * is relative to the request URI. Absolute URIs are relative to * the application prefix (getContextPath()). * *

If getRequestURI() is /myapp/dir/test.jsp and the * uri is "inc.jsp", the resulting page is * /myapp/dir/inc.jsp. *

   *   RequestDispatcher disp;
   *   disp = getRequestDispatcher("inc.jsp?a=b");
   *   disp.include(request, response);
   * 
* * @param path path relative to getRequestURI() * (including query string) for the included file. * @return RequestDispatcher for later inclusion or forwarding. */ @Override public RequestDispatcher getRequestDispatcher(String path) { if (path == null || path.length() == 0) return null; else if (path.charAt(0) == '/') return getWebApp().getRequestDispatcher(path); else { CharBuffer cb = new CharBuffer(); WebApp webApp = getWebApp(); String servletPath = getPageServletPath(); if (servletPath != null) cb.append(servletPath); String pathInfo = getPagePathInfo(); if (pathInfo != null) cb.append(pathInfo); int p = cb.lastIndexOf('/'); if (p >= 0) cb.setLength(p); cb.append('/'); cb.append(path); if (webApp != null) return webApp.getRequestDispatcher(cb.toString()); return null; } } /** * Returns the servlet context for the request * * @since Servlet 3.0 */ @Override public ServletContext getServletContext() { Invocation invocation = _invocation; if (invocation != null) return invocation.getWebApp(); else return null; } /** * Returns the servlet response for the request * * @since Servlet 3.0 */ @Override public ServletResponse getServletResponse() { return _response; } // // HttpServletRequest APIs // /** * Returns the HTTP method, e.g. "GET" or "POST" * *

Equivalent to CGI's REQUEST_METHOD */ @Override public String getMethod() { return _request.getMethod(); } /** * Returns the URI for the request */ @Override public String getRequestURI() { if (_invocation != null) return _invocation.getRawURI(); else return ""; } /** * Returns the URI for the page. getPageURI and getRequestURI differ * for included files. getPageURI gets the URI for the included page. * getRequestURI returns the original URI. */ public String getPageURI() { return _invocation.getRawURI(); } /** * Returns the context part of the uri. The context part is the part * that maps to an webApp. */ public String getContextPath() { if (_invocation != null) return _invocation.getContextPath(); else return ""; } /** * Returns the context part of the uri. For included files, this will * return the included context-path. */ public String getPageContextPath() { return getContextPath(); } /** * Returns the portion of the uri mapped to the servlet for the original * request. */ public String getServletPath() { if (_invocation != null) return _invocation.getServletPath(); else return ""; } /** * Returns the portion of the uri mapped to the servlet for the current * page. */ public String getPageServletPath() { if (_invocation != null) return _invocation.getServletPath(); else return ""; } /** * Returns the portion of the uri after the servlet path for the original * request. */ public String getPathInfo() { if (_invocation != null) return _invocation.getPathInfo(); else return null; } /** * Returns the portion of the uri after the servlet path for the current * page. */ public String getPagePathInfo() { if (_invocation != null) return _invocation.getPathInfo(); else return null; } /** * Returns the URL for the request */ @Override public StringBuffer getRequestURL() { StringBuffer sb = new StringBuffer(); sb.append(getScheme()); sb.append("://"); sb.append(getServerName()); int port = getServerPort(); if (port > 0 && port != 80 && port != 443) { sb.append(":"); sb.append(port); } sb.append(getRequestURI()); return sb; } /** * @deprecated As of JSDK 2.1 */ @Override public String getRealPath(String path) { if (path == null) return null; if (path.length() > 0 && path.charAt(0) == '/') return _invocation.getWebApp().getRealPath(path); String uri = getPageURI(); String context = getPageContextPath(); if (context != null) uri = uri.substring(context.length()); int p = uri.lastIndexOf('/'); if (p >= 0) path = uri.substring(0, p + 1) + path; return _invocation.getWebApp().getRealPath(path); } /** * Returns the real path of pathInfo. */ @Override public String getPathTranslated() { String pathInfo = getPathInfo(); if (pathInfo == null) return null; else return getRealPath(pathInfo); } /** * Returns the current page's query string. */ @Override public String getQueryString() { if (_invocation != null) return _invocation.getQueryString(); else return null; } /** * Returns the current page's query string. */ public String getPageQueryString() { return getQueryString(); } // // header management // /** * Returns the first value for a request header. * *

Corresponds to CGI's HTTP_* * *

   * String userAgent = request.getHeader("User-Agent");
   * 
* * @param name the header name * @return the header value */ @Override public String getHeader(String name) { return _request.getHeader(name); } /** * Returns all the values for a request header. In some rare cases, * like cookies, browsers may return multiple headers. * * @param name the header name * @return an enumeration of the header values. */ @Override public Enumeration getHeaders(String name) { return _request.getHeaders(name); } /** * Returns an enumeration of all headers sent by the client. */ @Override public Enumeration getHeaderNames() { return _request.getHeaderNames(); } /** * Converts a header value to an integer. * * @param name the header name * @return the header value converted to an integer */ @Override public int getIntHeader(String name) { return _request.getIntHeader(name); } /** * Converts a date header to milliseconds since the epoch. * *

   * long mod = request.getDateHeader("If-Modified-Since");
   * 
* * @param name the header name * @return the header value converted to an date */ @Override public long getDateHeader(String name) { return _request.getDateHeader(name); } // // session/cookie management // /** * Returns an array of all cookies sent by the client. */ @Override public Cookie []getCookies() { Cookie []cookiesIn = _cookiesIn; if (cookiesIn == null) { AbstractHttpRequest request = _request; if (request == null) { return null; } SessionManager sessionManager = getSessionManager(); if (sessionManager == null) { return null; } cookiesIn = request.getCookies(); String sessionCookieName = getSessionCookie(sessionManager); for (int i = 0; i < cookiesIn.length; i++) { Cookie cookie = cookiesIn[i]; if (cookie.getName().equals(sessionCookieName) && sessionManager.isSecure()) { cookie.setSecure(true); break; } } _cookiesIn = cookiesIn; /* // The page varies depending on the presense of any cookies setVaryCookie(null); // If any cookies actually exist, the page is not anonymous if (_cookiesIn != null && _cookiesIn.length > 0) setHasCookie(); */ } if (cookiesIn != null && cookiesIn.length > 0) { return cookiesIn; } else { return null; } } /** * Returns the named cookie from the browser */ @Override public Cookie getCookie(String name) { /* // The page varies depending on the presense of any cookies setVaryCookie(name); */ return findCookie(name); } private Cookie findCookie(String name) { Cookie []cookies = getCookies(); if (cookies == null) { return null; } int length = cookies.length; for (int i = 0; i < length; i++) { Cookie cookie = cookies[i]; if (cookie.getName().equals(name)) { setHasCookie(); return cookie; } } return null; } /** * Returns the session id in the HTTP request. The cookie has * priority over the URL. Because the webApp might be using * the cookie to change the page contents, the caching sets * vary: JSESSIONID. */ @Override public String getRequestedSessionId() { SessionManager manager = getSessionManager(); if (manager != null && manager.enableSessionCookies()) { setVaryCookie(getSessionCookie(manager)); String id = findSessionIdFromCookie(); if (id != null) { _isSessionIdFromCookie = true; setHasCookie(); return id; } } String id = findSessionIdFromUrl(); if (id != null) { return id; } if (manager != null && manager.enableSessionCookies()) return null; else return _request.findSessionIdFromConnection(); } /** * Returns the session id in the HTTP request cookies. * Because the webApp might use the cookie to change * the page contents, the caching sets vary: JSESSIONID. */ protected String findSessionIdFromCookie() { SessionManager manager = getSessionManager(); if (manager == null || ! manager.enableSessionCookies()) return null; Cookie cookie = getCookie(getSessionCookie(manager)); if (cookie != null) { _isSessionIdFromCookie = true; return cookie.getValue(); } else return null; } @Override public boolean isSessionIdFromCookie() { return _isSessionIdFromCookie; } @Override public String getSessionId() { String sessionId = getResponse().getSessionId(); if (sessionId != null) return sessionId; else return getRequestedSessionId(); } @Override public void setSessionId(String sessionId) { getResponse().setSessionId(sessionId); } /** * Returns the session id in the HTTP request from the url. */ private String findSessionIdFromUrl() { // server/1319 // setVaryCookie(getSessionCookie(manager)); String id = _invocation != null ? _invocation.getSessionId() : null; if (id != null) setHasCookie(); return id; } /** * Returns true if the current sessionId came from a cookie. */ @Override public boolean isRequestedSessionIdFromCookie() { return findSessionIdFromCookie() != null; } /** * Returns true if the current sessionId came from the url. */ @Override public boolean isRequestedSessionIdFromURL() { return findSessionIdFromUrl() != null; } /** * @deprecated */ @Override public boolean isRequestedSessionIdFromUrl() { return isRequestedSessionIdFromURL(); } /** * Returns the session id in the HTTP request. The cookie has * priority over the URL. Because the webApp might be using * the cookie to change the page contents, the caching sets * vary: JSESSIONID. */ public String getRequestedSessionIdNoVary() { boolean varyCookies = _varyCookies; boolean hasCookie = _hasCookie; boolean privateCache = _response.getPrivateCache(); String id = getRequestedSessionId(); _varyCookies = varyCookies; _hasCookie = hasCookie; _response.setPrivateOrResinCache(privateCache); return id; } // // security // @Override protected String getRunAs() { return _runAs; } /** * Gets the authorization type */ public String getAuthType() { Object login = getAttribute(AbstractLogin.LOGIN_USER_NAME); if (login instanceof X509Certificate) return HttpServletRequest.CLIENT_CERT_AUTH; WebApp app = getWebApp(); if (app != null && app.getLogin() != null && getUserPrincipal() != null) return app.getLogin().getAuthType(); else return null; } /** * Returns the login for the request. */ protected Login getLogin() { WebApp webApp = getWebApp(); if (webApp != null) return webApp.getLogin(); else return null; } /** * Returns true if any authentication is requested */ @Override public boolean isLoginRequested() { return _isLoginRequested; } @Override public void requestLogin() { _isLoginRequested = true; } /** * Gets the remote user from the authorization type */ @Override public String getRemoteUser() { Principal principal = getUserPrincipal(); if (principal != null) return principal.getName(); else return null; } /** * Internal logging return to get the remote user. If the request already * knows the user, get it, otherwise just return null. */ public String getRemoteUser(boolean create) { /* if (getSession(false) == null) return null; */ if (isAttributesEmpty() && ! create) { return null; } Principal user = (Principal) getAttribute(AbstractLogin.LOGIN_USER); if (user == null && create) user = getUserPrincipal(); if (user != null) return user.getName(); else return null; } /** * Logs out the principal. */ @Override public void logout() { Login login = getLogin(); if (login != null) { login.logout(getUserPrincipal(), this, getResponse()); } } /** * Clear the principal from the request object. */ public void logoutUserPrincipal() { // XXX: /* if (_session != null) _session.logout(); */ } /** * Sets the overriding role. */ public String runAs(String role) { String oldRunAs = _runAs; _runAs = role; return oldRunAs; } public void setSecure(Boolean isSecure) { // server/12ds _isSecure = isSecure; } // // deprecated // public ReadStream getStream() throws IOException { return _request.getStream(); } @Override public ReadStream getStream(boolean isFlush) throws IOException { return _request.getStream(isFlush); } public int getRequestDepth(int depth) { return depth; } public void setHeader(String key, String value) { _request.setHeader(key, value); } @Override public void setSyntheticCacheHeader(boolean isSynthetic) { _isSyntheticCacheHeader = isSynthetic; } @Override public boolean isSyntheticCacheHeader() { return _isSyntheticCacheHeader; } /** * Called if the page depends on a cookie. If the cookie is null, then * the page depends on all cookies. * * @param cookie the cookie the page depends on. */ public void setVaryCookie(String cookie) { _varyCookies = true; // XXX: server/1315 vs 2671 // _response.setPrivateOrResinCache(true); } /** * Returns true if the page depends on cookies. */ public boolean getVaryCookies() { return _varyCookies; } /** * Set when the page actually has a cookie. */ public void setHasCookie() { _hasCookie = true; // XXX: 1171 vs 1240 // _response.setPrivateOrResinCache(true); } /** * True if this page uses cookies. */ public boolean getHasCookie() { if (_hasCookie) return true; else if (_invocation != null) return _invocation.getSessionId() != null; else return false; } public boolean isTop() { return true; } public boolean isComet() { return _request.isCometActive(); } /** * Adds a file to be removed at the end. */ public void addCloseOnExit(Path path) { if (_closeOnExit == null) _closeOnExit = new ArrayList(); _closeOnExit.add(path); } public boolean isDuplex() { return _request.isDuplex(); } @Override public void killKeepalive(String reason) { _request.killKeepalive(reason); } public boolean isConnectionClosed() { return _request.isConnectionClosed(); } public SocketLink getConnection() { return _request.getConnection(); } // // HttpServletRequestImpl methods // @Override public AbstractHttpRequest getAbstractHttpRequest() { return _request; } public boolean isSuspend() { return _request.isSuspend(); } public boolean hasRequest() { return _request.hasRequest(); } public void setInvocation(Invocation invocation) { _invocation = invocation; } public Invocation getInvocation() { return _invocation; } public long getStartTime() { return _request.getStartTime(); } public void finishInvocation() { // server/11d4 /* AsyncContextImpl asyncContext = _asyncContext; if (asyncContext != null) asyncContext.onComplete(); */ _request.finishInvocation(); } // // servlet 3.0 async support // /** * Returns true if the request is in async. * * @since Servlet 3.0 */ @Override public boolean isAsyncStarted() { AsyncContextImpl asyncContext = _asyncContext; if (asyncContext == null) { return false; } return asyncContext.isAsyncStarted(); } /** * Returns true if the request supports async * * @since Servlet 3.0 */ @Override public boolean isAsyncSupported() { Invocation invocation = _invocation; if (invocation != null) { return invocation.isAsyncSupported(); } else { return false; } } /** * Starts an async mode * * @since Servlet 3.0 */ @Override public AsyncContext startAsync() { return startAsync(this, _response); } /** * Starts an async mode * * @since Servlet 3.0 */ @Override public AsyncContext startAsync(ServletRequest request, ServletResponse response) { if (! isAsyncSupported()) throw new IllegalStateException(L.l("The servlet '{0}' at '{1}' does not support async because the servlet or one of the filters does not support asynchronous mode. The servlet should be annotated with a @WebServlet(asyncSupported=true) annotation or have a tag in the web.xml.", getServletName(), getServletPath())); if (_request.isCometActive()) { throw new IllegalStateException(L.l("startAsync may not be called twice on the same dispatch.")); } boolean isOriginal = (request == this && response == _response); if (_asyncContext == null) { _asyncContext = new AsyncContextImpl(_request); if (_asyncTimeout > 0) _asyncContext.setTimeout(_asyncTimeout); } else { _asyncContext.restart(); } _asyncContext.init(request, response, isOriginal); return _asyncContext; } /** * Returns the async context for the request * * @since Servlet 3.0 */ @Override public AsyncContextImpl getAsyncContext() { if (_asyncContext != null) return _asyncContext; else throw new IllegalStateException(L.l("getAsyncContext() must be called after asyncStarted() has started a new AsyncContext.")); } // // WebSocket // @Override public WebSocketContext startWebSocket(WebSocketListener listener) throws IOException { if (log.isLoggable(Level.FINE)) log.fine(this + " upgrade HTTP to WebSocket " + listener); String method = getMethod(); if (! "GET".equals(method)) { getResponse().sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); throw new IllegalStateException(L.l("HTTP Method must be 'GET', because the WebSocket protocol requires 'GET'.\n remote-IP: {0}", getRemoteAddr())); } String connection = getHeader("Connection"); String upgrade = getHeader("Upgrade"); if (! "WebSocket".equalsIgnoreCase(upgrade)) { getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST); throw new IllegalStateException(L.l("HTTP Upgrade header '{0}' must be 'WebSocket', because the WebSocket protocol requires an Upgrade: WebSocket header.\n remote-IP: {1}", upgrade, getRemoteAddr())); } if (connection == null || connection.toLowerCase().indexOf("upgrade") < 0) { getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST); throw new IllegalStateException(L.l("HTTP Connection header '{0}' must be 'Upgrade', because the WebSocket protocol requires a Connection: Upgrade header.\n remote-IP: {1}", connection, getRemoteAddr())); } String key = getHeader("Sec-WebSocket-Key"); if (key == null) { getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST); throw new IllegalStateException(L.l("HTTP Sec-WebSocket-Key header is required, because the WebSocket protocol requires an Origin header.\n remote-IP: {0}", getRemoteAddr())); } else if (key.length() != 24) { getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST); throw new IllegalStateException(L.l("HTTP Sec-WebSocket-Key header is invalid '{0}' because it's not a 16-byte value.\n remote-IP: {1}", key, getRemoteAddr())); } String version = getHeader("Sec-WebSocket-Version"); String requiredVersion = WebSocketConstants.VERSION; if (! requiredVersion.equals(version)) { getResponse().sendError(HttpServletResponse.SC_BAD_REQUEST); throw new IllegalStateException(L.l("HTTP Sec-WebSocket-Version header with value '{0}' is required, because the WebSocket protocol requires an Sec-WebSocket-Version header.\n remote-IP: {1}", requiredVersion, getRemoteAddr())); } String extensions = getHeader("Sec-WebSocket-Extensions"); boolean isMasked = true; if (extensions != null && extensions.indexOf("x-unmasked") >= 0) { isMasked = false; } String serverExtensions = null; if (! isMasked) serverExtensions = "x-unmasked"; _response.setStatus(101);//, "Switching Protocols"); _response.setHeader("Upgrade", "websocket"); String accept = calculateWebSocketAccept(key); _response.setHeader("Sec-WebSocket-Accept", accept); if (serverExtensions != null) _response.setHeader("Sec-WebSocket-Extensions", serverExtensions); _response.setContentLength(0); WebSocketContextImpl webSocket; if (isMasked) { webSocket = new WebSocketContextImpl(this, _response, listener, new MaskedFrameInputStream()); } else { webSocket = new WebSocketContextImpl(this, _response, listener, new UnmaskedFrameInputStream()); } SocketLinkDuplexController controller = _request.startDuplex(webSocket); webSocket.setController(controller); try { _response.getOutputStream().flush(); webSocket.flush(); webSocket.onStart(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } return webSocket; } private String calculateWebSocketAccept(String key) { try { MessageDigest md = MessageDigest.getInstance("SHA1"); int length = key.length(); for (int i = 0; i < length; i++) { md.update((byte) key.charAt(i)); } String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; length = guid.length(); for (int i = 0; i < length; i++) { md.update((byte) guid.charAt(i)); } byte []digest = md.digest(); return Base64.encode(digest); } catch (Exception e) { throw new RuntimeException(e); } } int getAvailable() throws IOException { if (_request != null) return _request.getAvailable(); else return -1; } int getBufferAvailable() throws IOException { if (_request != null) return _request.getBufferAvailable(); else return -1; } public DispatcherType getDispatcherType() { return DispatcherType.REQUEST; } @Override protected void finishRequest() throws IOException { AsyncContextImpl async = _asyncContext; _asyncContext = null; /* server/1ld5 if (comet != null) { comet.onComplete(); } */ if (async != null) { async.onComplete(); } super.finishRequest(); // ioc/0a10 cleanup(); if (_closeOnExit != null) { for (int i = _closeOnExit.size() - 1; i >= 0; i--) { Path path = _closeOnExit.get(i); try { path.remove(); } catch (Throwable e) { log.log(Level.FINE, e.toString(), e); } } } // server/1lg0 _response.closeImpl(); _cookiesIn = null; _request = null; } public void cleanup() { HashMapImpl attributes = _attributes; if (attributes != null) { for (Map.Entry entry : attributes.entrySet()) { Object value = entry.getValue(); if (value instanceof ScopeRemoveListener) { ((ScopeRemoveListener) value).removeEvent(this, entry.getKey()); } } } } // // XXX: unsorted // /** * Returns the servlet name. */ public String getServletName() { if (_invocation != null) { return _invocation.getServletName(); } else return null; } public final ServletService getServer() { return _request.getServer(); } /** * Returns the invocation's webApp. */ public final WebApp getWebApp() { if (_invocation != null) return _invocation.getWebApp(); else return null; } public boolean isClosed() { return _request == null; } @Override public String toString() { return getClass().getSimpleName() + "[" + _request + "]"; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy