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

org.atmosphere.cpr.AtmosphereRequestImpl Maven / Gradle / Ivy

There is a newer version: 3.0.13
Show newest version
/*
 * Copyright 2008-2024 Async-IO.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.atmosphere.cpr;

import jakarta.servlet.ReadListener;
import jakarta.servlet.http.HttpUpgradeHandler;
import org.atmosphere.util.FakeHttpSession;
import org.atmosphere.util.QueryStringDecoder;
import org.atmosphere.util.ReaderInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.servlet.AsyncContext;
import jakarta.servlet.AsyncListener;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.Part;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
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.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.atmosphere.cpr.HeaderConfig.X_ATMOSPHERE;

/**
 * An Atmosphere request representation. An {@link AtmosphereRequest} is a two-way communication channel between the
 * client and the server. If the {@link AtmosphereRequestImpl#isDestroyable()} is set to false, or if its
 * associated {@link AtmosphereResource} has been suspended, this object can be re-used at any moment between requests.
 * You can use its associated {@link AtmosphereResponse} to write bytes at any moment, making this object bi-directional.
 * 
* * @author Jeanfrancois Arcand */ public class AtmosphereRequestImpl extends HttpServletRequestWrapper implements AtmosphereRequest { private final static Logger logger = LoggerFactory.getLogger(AtmosphereRequestImpl.class); private ServletInputStream bis; private BufferedReader br; private final Builder b; private final AtomicBoolean destroyed = new AtomicBoolean(false); private boolean queryComputed; private boolean cookieComputed; private volatile BufferedReader voidReader; private final ServletInputStream voidStream = new IS(new ByteArrayInputStream(new byte[0])); private AtomicBoolean streamSet = new AtomicBoolean(); private AtomicBoolean readerSet = new AtomicBoolean(); private String uuid; private boolean noopsAsyncContextStarted; private AtmosphereRequestImpl(Builder b) { super(b.request == null ? new NoOpsRequest() : b.request); if (b.request == null) b.request(new NoOpsRequest()); this.b = b; this.uuid = resource() != null ? resource().uuid() : "0"; } private BufferedReader getVoidReader() { if (voidReader == null) { voidReader = new BufferedReader(new StringReader(""), 5); } return voidReader; } private void configureStream() { if (bis == null && !streamSet.getAndSet(true)) { if (b.inputStream != null) { bis = new IS(b.inputStream); } else if (b.reader == null) { if (b.body.dataBytes != null) { bis = new ByteInputStream(b.body.dataBytes, b.body.offset, b.body.length); } else if (b.body.data != null) { byte[] bytes = b.body.data.getBytes(StandardCharsets.UTF_8); bis = new ByteInputStream(bytes, 0, bytes.length); } } else { bis = new IS(new ReaderInputStream(b.reader)); } } } private void configureReader() { if (br == null && !readerSet.getAndSet(false)) { if (b.reader != null) { br = new BufferedReader(b.reader); } else if (b.inputStream == null) { try { if (b.body.dataBytes != null) { br = new BufferedReader(new StringReader(new String(b.body.dataBytes, b.body.offset, b.body.length, b.encoding))); } else if (b.body.data != null) { br = new BufferedReader(new StringReader(b.body.data)); } } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } else { br = new BufferedReader(new InputStreamReader(b.inputStream)); } } } @Override public boolean destroyed() { return destroyed.get(); } @Override public AtmosphereRequest destroyable(boolean destroyable) { b.destroyable = destroyable; return this; } @Override public String getPathInfo() { return !Objects.equals(b.pathInfo, "") ? b.pathInfo : isNotNoOps() ? b.request.getPathInfo() : ""; } @Override public String getPathTranslated() { return b.request.getPathTranslated(); } @Override public String getQueryString() { return !Objects.equals(b.queryString, "") ? b.queryString : isNotNoOps() ? b.request.getQueryString() : toQs(); } private String toQs() { StringBuilder q = new StringBuilder(); for (Map.Entry e : b.queryStrings.entrySet()) { for (String k : e.getValue()) { q.append(e.getKey()).append("=").append(k).append("&"); } } if (q.length() > 0) q.deleteCharAt(q.length() - 1); return q.toString(); } @Override public String getRemoteUser() { return b.principal != null ? b.principal.getName() : b.request.getRemoteUser(); } @Override public String getRequestedSessionId() { return b.request.getRequestedSessionId(); } @Override public String getMethod() { return b.methodType != null ? b.methodType : b.request.getMethod(); } @Override public Part getPart(String name) throws IOException, ServletException { return b.request.getPart(name); } @Override public Collection getParts() throws IOException, ServletException { return b.request.getParts(); } @Override public String getContentType() { return b.contentType != null || b.noContentType ? b.contentType : b.request.getContentType(); } @Override public DispatcherType getDispatcherType() { return b.request.getDispatcherType(); } @Override public String getServletPath() { return !Objects.equals(b.servletPath, "") ? b.servletPath : (isNotNoOps() ? b.request.getServletPath() : ""); } @Override public String getRequestURI() { return b.requestURI != null ? b.requestURI : (isNotNoOps() ? b.request.getRequestURI() : ""); } @Override public StringBuffer getRequestURL() { return b.requestURL != null ? new StringBuffer(b.requestURL) : (isNotNoOps() ? b.request.getRequestURL() : new StringBuffer()); } @Override public Enumeration getHeaders(String name) { ArrayList list = new ArrayList<>(); // Never override the parent Request if (!name.equalsIgnoreCase("content-type")) { Enumeration e = b.request.getHeaders(name); while (e.hasMoreElements()) list.add(e.nextElement()); } if (name.equalsIgnoreCase("content-type")) { String s = getContentType(); if (s != null) { list.add(s); } } else { if (b.headers.get(name) != null) { list.add(b.headers.get(name)); } if (isNotNoOps()) { if (list.isEmpty() && name.startsWith(X_ATMOSPHERE)) { if (attributeWithoutException(b.request, name) != null) { list.add(attributeWithoutException(b.request, name)); } } } } return Collections.enumeration(list); } @Override public int getIntHeader(String name) { return b.request.getIntHeader(name); } @Override public Enumeration getHeaderNames() { Set list = new HashSet<>(); list.addAll(b.headers.keySet()); list.addAll(Collections.list(b.request.getHeaderNames())); if (b.request != null) { Enumeration e = b.request.getAttributeNames(); while (e.hasMoreElements()) { String name = e.nextElement(); if (name.startsWith(X_ATMOSPHERE)) { list.add(name); } } } if (b.contentType != null) { list.add("Content-Type"); } return Collections.enumeration(list); } @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { return b.request.authenticate(response); } @Override public String getAuthType() { return b.authType != null ? b.authType : b.request.getAuthType(); } @Override public String getContextPath() { String c = "/"; try { c = isNotNoOps() && b.request.getContextPath() != null ? b.request.getContextPath() : b.contextPath != null ? b.contextPath : ""; } catch (NullPointerException ex) { // https://github.com/Atmosphere/atmosphere/issues/1804 logger.warn("Unexpected getContextPath exception. Forcing `/`", ex); } return c; } @Override public Cookie[] getCookies() { if (!cookieComputed) { if (b.cookies == null) return new Cookie[0]; cookieComputed = true; Cookie[] c = b.request.getCookies(); if (c != null && c.length > 0) { b.cookies.addAll(Arrays.asList(c)); } } return b.cookies.toArray(new Cookie[]{}); } @Override public long getDateHeader(String name) { return b.request.getDateHeader(name); } @Override public String getHeader(String s) { return getHeader(s, true); } @Override public HttpServletRequest wrappedRequest() { return b.request; } @Override public String getHeader(String s, boolean checkCase) { if ("content-type".equalsIgnoreCase(s)) { return getContentType(); } String name = b.request.getHeader(s); if (name == null) { if (b.headers.get(s) != null) { return b.headers.get(s); } if (s.startsWith(X_ATMOSPHERE) && isNotNoOps()) { // Craziness with Struts 2 who wraps String attribute as BigDecimal // https://github.com/Atmosphere/atmosphere/issues/1367 Object o = attributeWithoutException(b.request, s); if (o == null || String.class.isAssignableFrom(o.getClass())) { name = (String) o; } else { try { if (HttpServletRequestWrapper.class.isAssignableFrom(b.request.getClass())) { HttpServletRequest hsr = b.request; while (hsr instanceof HttpServletRequestWrapper) { hsr = (HttpServletRequest) ((HttpServletRequestWrapper) hsr).getRequest(); o = attributeWithoutException(hsr, s); if (o == null || String.class.isAssignableFrom(o.getClass())) { name = (String) o; break; } } } } catch (Exception ex) { logger.warn("", ex); } } } } if (name == null && checkCase) { return getHeader(s.toLowerCase(), false); } if (name == null && "connection".equalsIgnoreCase(s)) { return "keep-alive"; } return name; } @Override public String getParameter(String s) { String name = isNotNoOps() ? b.request.getParameter(s) : null; if (name == null) { String[] values = b.queryStrings.get(s); if (values != null) { return values[0]; } } return name; } @Override public Map getParameterMap() { if (!queryComputed) { queryComputed = true; Map m = (isNotNoOps() ? b.request.getParameterMap() : Collections.emptyMap()); for (String e : m.keySet()) { b.queryStrings.put(e, getParameterValues(e)); } } return Collections.unmodifiableMap(b.queryStrings); } @Override public Enumeration getParameterNames() { return Collections.enumeration(getParameterMap().keySet()); } @Override public String[] getParameterValues(String s) { String[] list = b.request.getParameterValues(s); if (list != null && b.queryStrings.get(s) != null) { String[] newList = b.queryStrings.get(s); if (!Arrays.deepEquals(list, newList)) { String[] s1 = new String[list.length + newList.length]; System.arraycopy(list, 0, s1, 0, list.length); System.arraycopy(newList, 0, s1, list.length, newList.length); return s1; } else { return list; } } else { return list == null ? b.queryStrings.get(s) : list; } } @Override public String getProtocol() { return b.request.getProtocol(); } @Override public ServletInputStream getInputStream() throws IOException { if (b.body.isEmpty()) { configureStream(); return bis == null ? (isNotNoOps() ? b.request.getInputStream() : voidStream) : bis; } else if (b.body.hasString()) { bis = new IS(new ByteArrayInputStream(b.body.asString().getBytes())); } else if (b.body.hasBytes()) { bis = new IS(new ByteArrayInputStream(b.body.asBytes(), b.body.offset, b.body.byteLength())); } return bis; } @Override public BufferedReader getReader() throws IOException { if (b.body.isEmpty()) { configureReader(); return br == null ? (isNotNoOps() ? b.request.getReader() : getVoidReader()) : br; } else if (b.body.hasString()) { br = new BufferedReader(new StringReader(body().asString())); } else if (b.body.hasBytes()) { br = new BufferedReader(new StringReader(new String(body().asBytes(), body().byteOffset(), body().length))); } return br; } @Override public String getRealPath(String path) { return b.request.getRealPath(path); } @Override public AtmosphereRequest headers(Map headers) { b.headers.putAll(headers); return this; } @Override public AtmosphereRequest header(String name, String value) { b.headers.put(name, value); return this; } @Override public AtmosphereRequest queryString(String qs) { if (qs == null) return this; if (!qs.isEmpty()) { QueryStringDecoder decoder = new QueryStringDecoder(getRequestURI() + "?" + qs); Map> m = decoder.getParameters(); Map newM = new HashMap<>(); for (Map.Entry> q : m.entrySet()) { newM.put(q.getKey(), q.getValue().toArray(new String[0])); } b.queryStrings(newM); } b.queryString = qs; return this; } @Override public Map headersMap() { return b.headers; } @Override public Map queryStringsMap() { return b.queryStrings; } @Override public AtmosphereRequest method(String m) { b.method(m); return this; } @Override public AtmosphereRequest contentType(String m) { b.contentType(m); return this; } @Override public AtmosphereRequest body(String body) { b.body = new Body(body, null, 0, 0); return this; } @Override public AtmosphereRequest body(byte[] bytes) { b.body = new Body(null, bytes, 0, bytes.length); return this; } @Override public AtmosphereRequest body(InputStream body) { bis = new IS(body); br = new BufferedReader(new InputStreamReader(body)); return this; } @Override public AtmosphereRequest body(Reader body) { bis = new IS(new ReaderInputStream(body)); br = new BufferedReader(body); return this; } @Override public Body body() { return b.body; } @Override public AtmosphereRequest servletPath(String servletPath) { b.servletPath = servletPath; return this; } @Override public AtmosphereRequest contextPath(String contextPath) { b.contextPath = contextPath; return this; } @Override public AtmosphereRequest requestURI(String requestURI) { b.requestURI = requestURI; return this; } private final static class ByteInputStream extends ServletInputStream { private final ByteArrayInputStream bis; public ByteInputStream(byte[] data, int offset, int length) { this.bis = new ByteArrayInputStream(data, offset, length); } @Override public int read() throws IOException { return bis.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } } @Override public void setAttribute(String s, Object o) { if (o == null) { removeAttribute(s); return; } b.localAttributes.put(s, o); if (isNotNoOps() && !destroyed.get()) { try { synchronized (b.request) { b.request.setAttribute(s, o); } } catch (NullPointerException ex) { // https://github.com/Atmosphere/atmosphere/issues/1806 logger.trace("", ex); } } } @Override public void setCharacterEncoding(String env) throws UnsupportedEncodingException { b.request.setCharacterEncoding(env); } @Override public AsyncContext startAsync() { AsyncContext ac; if (AtmosphereResource.TRANSPORT.WEBSOCKET == resource().transport()) { noopsAsyncContextStarted = true; ac = new NoOpsAsyncContext(getRequest(), resource().getResponse().getResponse()); } else { ac = b.request.startAsync(); } return isCompletionAware() ? new CompletionAwareAsyncContext(ac, (CompletionAware) resource().getResponse()) : ac; } @Override public AsyncContext startAsync(ServletRequest request, ServletResponse response) { AsyncContext ac; if (AtmosphereResource.TRANSPORT.WEBSOCKET == resource().transport()) { noopsAsyncContextStarted = true; ac = new NoOpsAsyncContext(request, response); } else { ac = b.request.startAsync(request, response); } return isCompletionAware() ? new CompletionAwareAsyncContext(ac, (CompletionAware) resource().getResponse()) : ac; } @Override public AsyncContext getAsyncContext() { AsyncContext ac; if (AtmosphereResource.TRANSPORT.WEBSOCKET == resource().transport()) { noopsAsyncContextStarted = true; ac = new NoOpsAsyncContext(getRequest(), resource().getResponse().getResponse()); } else { ac = b.request.getAsyncContext(); } return isCompletionAware() ? new CompletionAwareAsyncContext(ac, (CompletionAware) resource().getResponse()) : ac; } @Override public Object getAttribute(String s) { return b.localAttributes.get(s) != null ? b.localAttributes.get(s) : (isNotNoOps() ? attributeWithoutException(b.request, s) : null); } @Override public void removeAttribute(String name) { b.localAttributes.remove(name); if (isNotNoOps() && !destroyed.get()) { synchronized (b.request) { b.request.removeAttribute(name); } } } @Override public LocalAttributes attributes() { return b.localAttributes; } @Override public HttpSession getSession() { return getSession(true); } @Override public HttpSession getSession(boolean create) { if (b.webSocketFakeSession != null) { return b.webSocketFakeSession; } if (resource() != null) { // UGLY, but we need to prevent looping here. HttpSession session = ((AtmosphereResourceImpl) resource()).session; try { if (session != null) { // check if session is valid (isNew() will throw if not) session.isNew(); return session; } } catch (IllegalStateException e) { // session has been invalidated } } try { return b.request.getSession(create); } catch (java.lang.IllegalStateException ex) { // JettyAsyncSupportListenerAdapter if (ex.getMessage() != null && ex.getMessage().equals("No SessionManager")) { return null; } throw ex; } catch (NullPointerException ex) { // GLASSFISH http://java.net/jira/browse/GLASSFISH-18856 try { return b.request.getSession(create); } catch (Exception e) { logger.trace("", ex); return null; } } catch (RuntimeException ex) { // https://github.com/Atmosphere/atmosphere/issues/1974 logger.trace("", ex); if (ex.getMessage() != null && ex.getMessage().contains("SESN0007E")) { return null; } throw ex; } } @Override public Principal getUserPrincipal() { return b.principal != null ? b.principal : b.request.getUserPrincipal(); } @Override public boolean isRequestedSessionIdFromCookie() { return b.request.isRequestedSessionIdFromCookie(); } @Override public boolean isRequestedSessionIdFromUrl() { return b.request.isRequestedSessionIdFromUrl(); } @Override public boolean isRequestedSessionIdFromURL() { return b.request.isRequestedSessionIdFromURL(); } @Override public boolean isRequestedSessionIdValid() { return b.request.isRequestedSessionIdValid(); } @Override public boolean isUserInRole(String role) { return b.request.isUserInRole(role); } @Override public void login(String username, String password) throws ServletException { b.request.login(username, password); } @Override public void logout() throws ServletException { b.request.logout(); } @Override public String getRemoteAddr() { return isNotNoOps() ? b.request.getRemoteAddr() : b.lazyRemote != null ? b.lazyRemote.getHostAddress() : b.remoteAddr; } @Override public String getRemoteHost() { return isNotNoOps() ? b.request.getRemoteHost() : b.lazyRemote != null ? b.lazyRemote.getHostName() : b.remoteHost; } @Override public int getRemotePort() { return isNotNoOps() ? b.request.getRemotePort() : b.lazyRemote != null ? b.lazyRemote.getPort() : b.remotePort; } @Override public RequestDispatcher getRequestDispatcher(String path) { return b.request.getRequestDispatcher(path); } @Override public String getScheme() { return b.request.getScheme(); } @Override public String getServerName() { return !b.serverName.equals("") ? b.serverName : b.request.getServerName(); } @Override public int getServerPort() { return b.serverPort != 0 ? b.serverPort : b.request.getServerPort(); } @Override public ServletContext getServletContext() { return b.request.getServletContext(); } @Override public boolean isAsyncStarted() { if (AtmosphereResource.TRANSPORT.WEBSOCKET == resource().transport()) { return noopsAsyncContextStarted; } try { return b.request.isAsyncStarted(); } catch (Throwable ex) { logger.trace("", ex); return false; } } @Override public boolean isAsyncSupported() { try { return b.request.isAsyncSupported(); } catch (Throwable ex) { // Servlet 2.5 incompatible API. logger.trace("", ex); return false; } } @Override public boolean isSecure() { return isNotNoOps() ? b.request.isSecure() : b.isSecure; } @Override public String getLocalName() { return isNotNoOps() ? b.request.getLocalName() : b.lazyLocal != null ? b.lazyLocal.getHostName() : b.localName; } @Override public int getLocalPort() { return isNotNoOps() ? b.request.getLocalPort() : b.lazyLocal != null ? b.lazyLocal.getPort() : b.localPort; } @Override public String getLocalAddr() { return isNotNoOps() ? b.request.getLocalAddr() : b.lazyLocal != null ? b.lazyLocal.getHostAddress() : b.localAddr; } private boolean isNotNoOps() { return !NoOpsRequest.class.isAssignableFrom(b.request.getClass()); } @Override public Locale getLocale() { return isNotNoOps() ? b.request.getLocale() : b.locales.iterator().hasNext() ? b.locales.iterator().next() : Locale.getDefault(); } @Override public AtmosphereResource resource() { try { Object o = getAttribute(FrameworkConfig.ATMOSPHERE_RESOURCE); if (!(o instanceof AtmosphereResource)) { // WebSphere is in trouble. return null; } return (AtmosphereResource) getAttribute(FrameworkConfig.ATMOSPHERE_RESOURCE); } catch (Exception ex) { logger.warn("", ex); return null; } } @Override public Enumeration getLocales() { return isNotNoOps() ? b.request.getLocales() : Collections.enumeration(b.locales); } @Override public boolean dispatchRequestAsynchronously() { return b.dispatchRequestAsynchronously; } @Override public boolean isDestroyable() { return b.destroyable; } @Override public AtmosphereRequest pathInfo(String pathInfo) { b.pathInfo = pathInfo; return this; } @Override public Enumeration getAttributeNames() { Set l = new HashSet<>(b.localAttributes.unmodifiableMap().keySet()); if (isNotNoOps()) { synchronized (b.request) { l.addAll(Collections.list(b.request.getAttributeNames())); } } return Collections.enumeration(l); } private static Object attributeWithoutException(HttpServletRequest request, String attribute) { try { return request.getAttribute(attribute); } catch (NullPointerException ex) { // https://github.com/Atmosphere/atmosphere/issues/1732 logger.trace("Unexpected NPE", ex); return ""; } } @Override public LocalAttributes localAttributes() { return b.localAttributes; } @Override public String getCharacterEncoding() { return b.request.getCharacterEncoding() == null ? b.encoding : b.request.getCharacterEncoding(); } @Override public int getContentLength() { if (b.contentLength == null) { return b.request.getContentLength(); } else { return b.contentLength.intValue(); } } @Override public String uuid() { if (Objects.equals(uuid, "0")) { this.uuid = resource() != null ? resource().uuid() : "0"; } return uuid; } @Override public void destroy() { destroy(b.destroyable); } @Override public void destroy(boolean force) { if (!force) return; destroyed.set(true); b.localAttributes.clear(); if (bis != null) { try { bis.close(); } catch (IOException e) { } } if (br != null) { try { br.close(); } catch (IOException e) { } } b.headers.clear(); b.queryStrings.clear(); } @Override public void setRequest(ServletRequest request) { super.setRequest(request); if (HttpServletRequest.class.isAssignableFrom(request.getClass())) { b.request = (HttpServletRequest) request; } } public final static class Builder implements AtmosphereRequest.Builder { private final static Body NULL_BODY = new Body(null, null, 0, 0); private HttpServletRequest request; private String pathInfo = ""; private String encoding = "UTF-8"; private String methodType; private String contentType; private boolean noContentType; private Long contentLength; private Map headers = Collections.synchronizedMap(new HashMap<>()); private Map queryStrings = Collections.synchronizedMap(new HashMap<>()); private String servletPath = ""; private String requestURI; private String requestURL; private InputStream inputStream; private Reader reader; private String remoteAddr = ""; private String remoteHost = ""; private int remotePort; private String localAddr = ""; private String localName = ""; private int localPort; private boolean dispatchRequestAsynchronously; private boolean destroyable = true; private Set cookies = Collections.synchronizedSet(new HashSet<>()); private final Set locales = Collections.synchronizedSet(new HashSet<>()); private Principal principal; private String authType; private String contextPath = ""; private String serverName = ""; private int serverPort; private HttpSession webSocketFakeSession; private String queryString = ""; private boolean isSecure; // Callable to lazily execute. private LazyComputation lazyRemote; private LazyComputation lazyLocal; public Body body; private LocalAttributes localAttributes = new LocalAttributes(); public Builder() { } @Override public Builder destroyable(boolean destroyable) { this.destroyable = destroyable; return this; } @Override public Builder headers(Map headers) { this.headers = Collections.synchronizedMap(headers); return this; } @Override public Builder cookies(Set cookies) { this.cookies = cookies; return this; } @Override public Builder dispatchRequestAsynchronously(boolean dispatchRequestAsynchronously) { this.dispatchRequestAsynchronously = dispatchRequestAsynchronously; return this; } @Override public Builder remoteAddr(String remoteAddr) { this.remoteAddr = remoteAddr; return this; } @Override public Builder remoteHost(String remoteHost) { this.remoteHost = remoteHost; return this; } @Override public Builder remotePort(int remotePort) { this.remotePort = remotePort; return this; } @Override public Builder localAddr(String localAddr) { this.localAddr = localAddr; return this; } @Override public Builder localName(String localName) { this.localName = localName; return this; } @Override public Builder localPort(int localPort) { this.localPort = localPort; return this; } @Override public Builder remoteInetSocketAddress(Callable remoteAddr) { this.lazyRemote = new LazyComputation(remoteAddr); return this; } @Override public Builder localInetSocketAddress(Callable localAddr) { this.lazyLocal = new LazyComputation(localAddr); return this; } @Override public Builder attributes(Map attributes) { localAttributes = new LocalAttributes(attributes); return this; } @Override public Builder request(HttpServletRequest request) { this.request = request; return this; } @Override public Builder servletPath(String servletPath) { this.servletPath = servletPath; return this; } @Override public Builder requestURI(String requestURI) { this.requestURI = requestURI; return this; } @Override public Builder requestURL(String requestURL) { this.requestURL = requestURL; return this; } @Override public Builder pathInfo(String pathInfo) { this.pathInfo = pathInfo; return this; } @Override public Builder queryString(String queryString) { this.queryString = queryString; return this; } @Override public Builder body(byte[] dataBytes) { return body(dataBytes, 0, dataBytes.length); } @Override public Builder body(byte[] dataBytes, int offset, int length) { this.body = new Body(null, dataBytes, offset, length); return this; } @Override public Builder encoding(String encoding) { this.encoding = encoding; return this; } @Override public Builder method(String methodType) { this.methodType = methodType; return this; } @Override public Builder contentType(String contentType) { this.contentType = contentType; if (contentType == null) { noContentType = true; } return this; } @Override public Builder contentLength(Long contentLength) { this.contentLength = contentLength; return this; } @Override public Builder body(String data) { this.body = new Body(data, null, 0, 0); return this; } @Override public Builder inputStream(InputStream inputStream) { this.inputStream = inputStream; return this; } @Override public Builder reader(Reader reader) { this.reader = reader; return this; } @Override public AtmosphereRequest build() { if (body == null) { body = NULL_BODY; } return new AtmosphereRequestImpl(this); } @Override public Builder queryStrings(Map queryStrings) { this.queryStrings = Collections.synchronizedMap(queryStrings); return this; } @Override public Builder contextPath(String contextPath) { this.contextPath = contextPath == null ? "" : contextPath; return this; } @Override public Builder serverName(String serverName) { this.serverName = serverName; return this; } @Override public Builder serverPort(int serverPort) { this.serverPort = serverPort; return this; } @Override public Builder session(HttpSession session) { if (request == null) { request = new NoOpsRequest(); } if (NoOpsRequest.class.isAssignableFrom(request.getClass())) { ((NoOpsRequest) request).fake = session; } else { webSocketFakeSession = session; } return this; } @Override public Builder principal(Principal principal) { this.principal = principal; return this; } @Override public Builder authType(String authType) { this.authType = authType; return this; } @Override public Builder isSSecure(boolean isSecure) { this.isSecure = isSecure; return this; } @Override public Builder locale(Locale locale) { locales.add(locale); return this; } @Override public Builder userPrincipal(Principal userPrincipal) { this.principal = userPrincipal; return this; } } public final static class Body { private final String data; private final byte[] dataBytes; private final int offset; private final int length; private final boolean isEmpty; public Body(String data, byte[] dataBytes, int offset, int length) { this.data = data; this.dataBytes = dataBytes; this.offset = offset; this.length = length; isEmpty = data == null && dataBytes == null; } /** * True is the body is a String * * @return True is the body is a String */ public boolean hasString() { return data != null; } /** * True is the body is a byte array * * @return True is the body is a byte array */ public boolean hasBytes() { return dataBytes != null; } /** * Return the request body as a String. If the body was a byte array, this method will return null. * * @return the request body as a String. If the body was a byte array, this method will return null. */ public String asString() { return data; } /** * Return the request body as a byte array. If the body was String, this method will return null. * * @return the request body as a byte array. If the body was String, this method will return null. */ public byte[] asBytes() { return dataBytes; } /** * The {@link #asBytes()} offset * * @return The {@link #asBytes()} offset */ public int byteOffset() { return offset; } /** * The {@link #asBytes()} length * * @return The {@link #asBytes()} length */ public int byteLength() { return length; } /** * True if this object is empty * * @return True if this object is empty */ public boolean isEmpty() { return isEmpty; } } private final static class IS extends ServletInputStream { private final InputStream innerStream; public IS(InputStream innerStream) { super(); this.innerStream = innerStream; } public int read() throws java.io.IOException { return innerStream.read(); } public int read(byte[] bytes) throws java.io.IOException { return innerStream.read(bytes); } public int read(byte[] bytes, int i, int i1) throws java.io.IOException { return innerStream.read(bytes, i, i1); } public long skip(long l) throws java.io.IOException { return innerStream.skip(l); } public int available() throws java.io.IOException { return innerStream.available(); } public void close() throws java.io.IOException { innerStream.close(); } public synchronized void mark(int i) { innerStream.mark(i); } public synchronized void reset() throws java.io.IOException { innerStream.reset(); } public boolean markSupported() { return innerStream.markSupported(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } } /** * Create an instance of this class without an associated {@link HttpServletRequest}. * * @return an instance of this class without an associated {@link HttpServletRequest} */ public static AtmosphereRequest newInstance() { return new Builder().build(); } /** * Wrap an {@link HttpServletRequest}. * * @param request {@link HttpServletRequest} * @return an {@link AtmosphereRequest} */ public static AtmosphereRequest wrap(HttpServletRequest request) { // Do not rewrap. if (AtmosphereRequestImpl.class.isAssignableFrom(request.getClass())) { return (AtmosphereRequestImpl) request; } Builder b = new Builder(); Enumeration e = request.getAttributeNames(); String s; while (e.hasMoreElements()) { s = e.nextElement(); Object value = attributeWithoutException(request, s); if (value != null) { // Check for null values b.localAttributes.put(s, value); } else { logger.warn("Attribute {} is null", s); } } return b.request(request).build(); } /** * Copy the HttpServletRequest content inside an AtmosphereRequest. By default the returned AtmosphereRequest * is not destroyable. * * @param request {@link HttpServletRequest} * @return an {@link AtmosphereRequest} */ public static AtmosphereRequest cloneRequest(HttpServletRequest request, boolean loadInMemory, boolean copySession, boolean isDestroyable, boolean createSession) { Builder b; HttpServletRequest r; Cookie[] cs = request.getCookies(); Set hs = Collections.synchronizedSet(new HashSet<>()); if (cs != null) { Collections.addAll(hs, cs); } boolean isWrapped = false; if (AtmosphereRequestImpl.class.isAssignableFrom(request.getClass())) { b = ((AtmosphereRequestImpl) request).b; isWrapped = true; } else { b = new Builder(); b.request(request); } HttpSession session = request.getSession(false); if (copySession) { session = request.getSession(createSession); if (session != null) { session = new FakeHttpSession(session); } else { session = new FakeHttpSession("", null, System.currentTimeMillis(), -1); } } b.servletPath(request.getServletPath()) .pathInfo(request.getPathInfo()) .contextPath(request.getContextPath()) .requestURI(request.getRequestURI()) .requestURL(request.getRequestURL().toString()) .method(request.getMethod()) .serverName(request.getServerName()) .serverPort(request.getServerPort()) .remoteAddr(request.getRemoteAddr()) .remoteHost(request.getRemoteHost()) .remotePort(request.getRemotePort()) .destroyable(isDestroyable) .cookies(hs) .session(session) .principal(request.getUserPrincipal()) .authType(request.getAuthType()) .isSSecure(request.isSecure()); if (loadInMemory) { String s = (String) attributeWithoutException(request, FrameworkConfig.THROW_EXCEPTION_ON_CLONED_REQUEST); boolean throwException = Boolean.parseBoolean(s); r = new NoOpsRequest(throwException); if (isWrapped) { load(b.request, b); } else { load(request, b); } b.request(r); } return isWrapped ? (AtmosphereRequestImpl) request : b.build(); } public final static class NoOpsRequest implements HttpServletRequest { private final boolean throwExceptionOnCloned; public HttpSession fake; private final static Enumeration EMPTY_ENUM_STRING = Collections.enumeration(Collections.emptyList()); private final static Enumeration EMPTY_ENUM_LOCALE = Collections.enumeration(Collections.emptyList()); private final static List EMPTY_ENUM_PART = Collections.emptyList(); private final static Map EMPTY_MAP_STRING = Collections.emptyMap(); private final static String[] EMPTY_ARRAY = new String[0]; private final StringBuffer EMPTY_STRING_BUFFER = new StringBuffer(); private final static Cookie[] EMPTY_COOKIE = new Cookie[0]; private volatile BufferedReader voidReader; private final ServletInputStream voidStream = new AtmosphereRequestImpl.IS(new ByteArrayInputStream(new byte[0])); public NoOpsRequest() { this.throwExceptionOnCloned = false; } public NoOpsRequest(boolean throwExceptionOnCloned) { this.throwExceptionOnCloned = throwExceptionOnCloned; } private BufferedReader getVoidReader() { if (voidReader == null) { voidReader = new BufferedReader(new StringReader("")); } return voidReader; } @Override public boolean authenticate(HttpServletResponse response) { return false; } @Override public String getAuthType() { return null; } @Override public String getContextPath() { return ""; } @Override public Cookie[] getCookies() { return EMPTY_COOKIE; } @Override public long getDateHeader(String name) { return 0; } @Override public String getHeader(String name) { return null; } @Override public Enumeration getHeaderNames() { return EMPTY_ENUM_STRING; } @Override public Enumeration getHeaders(String name) { return EMPTY_ENUM_STRING; } @Override public int getIntHeader(String name) { return 0; } @Override public String getMethod() { return "GET"; } @Override public Part getPart(String name) throws IOException, ServletException { return null; } @Override public T upgrade(Class handlerClass) throws IOException, ServletException { return null; } @Override public Collection getParts() throws IOException, ServletException { return EMPTY_ENUM_PART; } @Override public String getPathInfo() { return ""; } @Override public String getPathTranslated() { return ""; } @Override public String getQueryString() { return ""; } @Override public String getRemoteUser() { return ""; } @Override public String getRequestedSessionId() { return ""; } @Override public String getRequestURI() { return "/"; } @Override public StringBuffer getRequestURL() { return EMPTY_STRING_BUFFER; } @Override public String getServletPath() { return ""; } @Override public HttpSession getSession() { return fake; } @Override public String changeSessionId() { return getSession(false).getId(); } @Override public HttpSession getSession(boolean create) { if (create && fake == null) { fake = new FakeHttpSession("", null, System.currentTimeMillis(), -1) { @Override public void invalidate() { fake = null; super.invalidate(); } }; } return fake; } @Override public Principal getUserPrincipal() { return null; } @Override public boolean isRequestedSessionIdFromCookie() { return false; } @Override public boolean isRequestedSessionIdFromUrl() { return false; } @Override public boolean isRequestedSessionIdFromURL() { return false; } @Override public boolean isRequestedSessionIdValid() { return false; } @Override public boolean isUserInRole(String role) { if (this.throwExceptionOnCloned) { throw new UnsupportedOperationException(); } return false; } @Override public void login(String username, String password) throws ServletException { if (this.throwExceptionOnCloned) { throw new ServletException(); } } @Override public void logout() throws ServletException { if (this.throwExceptionOnCloned) { throw new ServletException(); } } @Override public AsyncContext getAsyncContext() { return null; } @Override public Object getAttribute(String name) { return null; } @Override public Enumeration getAttributeNames() { return EMPTY_ENUM_STRING; } @Override public String getCharacterEncoding() { return null; } @Override public int getContentLength() { return 0; } @Override public long getContentLengthLong() { return 0; } @Override public String getContentType() { return "text/plain"; } @Override public DispatcherType getDispatcherType() { return DispatcherType.REQUEST; } @Override public ServletInputStream getInputStream() throws IOException { return voidStream; } @Override public Locale getLocale() { return null; } @Override public Enumeration getLocales() { return EMPTY_ENUM_LOCALE; } @Override public String getLocalName() { return null; } @Override public int getLocalPort() { return 0; } @Override public String getLocalAddr() { return ""; } @Override public String getParameter(String name) { return ""; } @Override public Map getParameterMap() { return EMPTY_MAP_STRING; } @Override public Enumeration getParameterNames() { return EMPTY_ENUM_STRING; } @Override public String[] getParameterValues(String name) { return EMPTY_ARRAY; } @Override public String getProtocol() { return "HTTP/1.1"; } @Override public BufferedReader getReader() throws IOException { return getVoidReader(); } @Override public String getRealPath(String path) { return path; } @Override public String getRemoteAddr() { return ""; } @Override public String getRemoteHost() { return ""; } @Override public int getRemotePort() { return 0; } @Override public RequestDispatcher getRequestDispatcher(String path) { return null; } @Override public String getScheme() { return "ws"; } @Override public String getServerName() { return ""; } @Override public int getServerPort() { return 0; } @Override public ServletContext getServletContext() { return null; } @Override public boolean isAsyncStarted() { return false; } @Override public boolean isAsyncSupported() { return true; } @Override public boolean isSecure() { return false; } @Override public void removeAttribute(String name) { } @Override public void setAttribute(String name, Object o) { } @Override public void setCharacterEncoding(String env) throws UnsupportedEncodingException { } @Override public AsyncContext startAsync() { return null; } @Override public AsyncContext startAsync(ServletRequest request, ServletResponse response) { return null; } } public static final class LazyComputation { private final Callable callable; private InetSocketAddress address; public LazyComputation(Callable callable) { this.callable = callable; } public InetSocketAddress address() { if (address == null) { try { address = callable.call(); } catch (Exception e) { logger.warn("", e); } // Falback if (address == null) { address = new InetSocketAddress(8080); } } return address; } public int getPort() { return address().getPort(); } public String getHostAddress() { return address().getAddress().getHostAddress(); } public String getHostName() { return address().getHostName(); } } private boolean isCompletionAware() { return Boolean.parseBoolean(resource().getAtmosphereConfig() .getInitParameter(ApplicationConfig.RESPONSE_COMPLETION_AWARE)); } private class CompletionAwareAsyncContext implements AsyncContext { private AsyncContext context; private CompletionAware callback; public CompletionAwareAsyncContext(AsyncContext context, CompletionAware callback) { this.context = context; this.callback = callback; } public void addListener(AsyncListener listener) throws IllegalStateException { context.addListener(listener); } public void addListener(AsyncListener listener, ServletRequest request, ServletResponse response) throws IllegalStateException { context.addListener(listener, request, response); } public void complete() { context.complete(); callback.onComplete(); } public T createListener(Class clazz) throws ServletException { return context.createListener(clazz); } public void dispatch() throws IllegalStateException { context.dispatch(); } public void dispatch(ServletContext servletContext, String path) throws IllegalStateException { context.dispatch(servletContext, path); } public void dispatch(String path) throws IllegalStateException { context.dispatch(path); } public ServletRequest getRequest() { return context.getRequest(); } public ServletResponse getResponse() { return context.getResponse(); } public long getTimeout() { return context.getTimeout(); } public boolean hasOriginalRequestAndResponse() { return context.hasOriginalRequestAndResponse(); } public void setTimeout(long timeoutMilliseconds) throws IllegalStateException { context.setTimeout(timeoutMilliseconds); } public void start(Runnable run) { context.start(run); } } private class NoOpsAsyncContext implements AsyncContext { private final ServletRequest request; private final ServletResponse response; private long timeout; public NoOpsAsyncContext(ServletRequest request, ServletResponse response) { this.request = request; this.response = response; this.timeout = -1; } @Override public void addListener(AsyncListener listener) throws IllegalStateException { } @Override public void addListener(AsyncListener listener, ServletRequest request, ServletResponse response) throws IllegalStateException { } @Override public void complete() { } @Override public T createListener(Class clazz) throws ServletException { throw new ServletException("Not supported"); } @Override public void dispatch() throws IllegalStateException { } @Override public void dispatch(ServletContext servletContext, String path) throws IllegalStateException { } @Override public void dispatch(String path) throws IllegalStateException { } @Override public ServletRequest getRequest() { return request; } @Override public ServletResponse getResponse() { return response; } @Override public long getTimeout() { return timeout; } @Override public boolean hasOriginalRequestAndResponse() { return false; } @Override public void setTimeout(long timeout) throws IllegalStateException { this.timeout = timeout; } @Override public void start(Runnable run) { throw new RuntimeException("Not supported"); } } private static void load(HttpServletRequest request, Builder b) { Enumeration e = request.getHeaderNames(); String s; while (e.hasMoreElements()) { s = e.nextElement(); b.headers.put(s, request.getHeader(s)); } synchronized (b.request) { e = request.getAttributeNames(); } while (e.hasMoreElements()) { s = e.nextElement(); b.localAttributes.put(s, attributeWithoutException(request, s)); } e = request.getParameterNames(); while (e.hasMoreElements()) { s = e.nextElement(); b.queryStrings.put(s, request.getParameterValues(s)); } b.queryString = request.getQueryString(); Enumeration l = request.getLocales(); while (l.hasMoreElements()) { b.locale(l.nextElement()); } } @Override public String toString() { try { return "AtmosphereRequest{" + " method=" + getMethod() + " contextPath=" + getContextPath() + " servletPath=" + getServletPath() + " pathInfo=" + getPathInfo() + " requestURI=" + getRequestURI() + " requestURL=" + getRequestURL() + " AtmosphereResource UUID=" + (resource() != null ? resource().uuid() : "") + " destroyable=" + b.destroyable + '}'; } catch (Exception e) { // Jetty error return "AtmosphereRequest{" + " contextPath=" + getContextPath() + " servletPath=" + getServletPath() + " pathInfo=" + getPathInfo() + " requestURI=" + getRequestURI() + " destroyable=" + b.destroyable + '}'; } } @Override public String requestURL() { return b.requestURL; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy