/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.cxf.transport.http.netty.server.servlet; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.security.Principal; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.servlet.RequestDispatcher; import javax.servlet.ServletInputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.cxf.transport.http.netty.server.util.Utils; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.cookie.ServerCookieDecoder; import io.netty.handler.ssl.SslHandler; import static io.netty.handler.codec.http.HttpHeaders.Names.COOKIE; public class NettyHttpServletRequest implements HttpServletRequest { private static final String SSL_CIPHER_SUITE_ATTRIBUTE = "javax.servlet.request.cipher_suite"; private static final String SSL_PEER_CERT_CHAIN_ATTRIBUTE = "javax.servlet.request.X509Certificate"; private static final Locale DEFAULT_LOCALE = Locale.getDefault(); private URIParser uriParser; private HttpRequest originalRequest; private NettyServletInputStream inputStream; private BufferedReader reader; private QueryStringDecoder queryStringDecoder; private Map<String, Object> attributes = new ConcurrentHashMap<String, Object>(); private String characterEncoding; private String contextPath; private ChannelHandlerContext channelHandlerContext; public NettyHttpServletRequest(HttpRequest request, String contextPath, ChannelHandlerContext ctx) { this.originalRequest = request; this.contextPath = contextPath; this.uriParser = new URIParser(contextPath); uriParser.parse(request.getUri()); this.inputStream = new NettyServletInputStream((HttpContent)request); this.reader = new BufferedReader(new InputStreamReader(inputStream)); this.queryStringDecoder = new QueryStringDecoder(request.getUri()); // setup the SSL security attributes this.channelHandlerContext = ctx; SslHandler sslHandler = channelHandlerContext.pipeline().get(SslHandler.class); if (sslHandler != null) { SSLSession session = sslHandler.engine().getSession(); if (session != null) { attributes.put(SSL_CIPHER_SUITE_ATTRIBUTE, session.getCipherSuite()); try { attributes.put(SSL_PEER_CERT_CHAIN_ATTRIBUTE, session.getPeerCertificates()); } catch (SSLPeerUnverifiedException ex) { // do nothing here } } } } public HttpRequest getOriginalRequest() { return originalRequest; } @Override public String getContextPath() { return contextPath; } @Override public Cookie[] getCookies() { String cookieString = this.originalRequest.headers().get(COOKIE); if (cookieString != null) { Set<io.netty.handler.codec.http.cookie.Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieString); if (!cookies.isEmpty()) { Cookie[] cookiesArray = new Cookie[cookies.size()]; int indx = 0; for (io.netty.handler.codec.http.cookie.Cookie c : cookies) { Cookie cookie = new Cookie(c.name(), c.value()); cookie.setDomain(c.domain()); cookie.setMaxAge((int)c.maxAge()); cookie.setPath(c.path()); cookie.setSecure(c.isSecure()); cookiesArray[indx] = cookie; indx++; } return cookiesArray; } } return null; } @Override public long getDateHeader(String name) { String longVal = getHeader(name); if (longVal == null) { return -1; } return Long.parseLong(longVal); } @Override public String getHeader(String name) { return HttpHeaders.getHeader(this.originalRequest, name); } @SuppressWarnings("rawtypes") @Override public Enumeration getHeaderNames() { return Utils.enumeration(this.originalRequest.headers().names()); } @SuppressWarnings("rawtypes") @Override public Enumeration getHeaders(String name) { return Utils.enumeration(this.originalRequest.headers().getAll(name)); } @Override public int getIntHeader(String name) { return HttpHeaders.getIntHeader(this.originalRequest, name, -1); } @Override public String getMethod() { return this.originalRequest.getMethod().name(); } @Override public String getQueryString() { return this.uriParser.getQueryString(); } @Override public String getRequestURI() { return this.uriParser.getRequestUri(); } @Override public StringBuffer getRequestURL() { StringBuffer url = new StringBuffer(); String scheme = this.getScheme(); int port = this.getServerPort(); String urlPath = this.getRequestURI(); url.append(scheme); // http, https url.append("://"); url.append(this.getServerName()); if (("http".equalsIgnoreCase(scheme) && port != 80) || ("https".equalsIgnoreCase(scheme) && port != 443)) { url.append(':'); url.append(this.getServerPort()); } url.append(urlPath); return url; } @Override public int getContentLength() { return (int) HttpHeaders.getContentLength(this.originalRequest, -1); } @Override public String getContentType() { return HttpHeaders.getHeader(this.originalRequest, HttpHeaders.Names.CONTENT_TYPE); } @Override public ServletInputStream getInputStream() throws IOException { return this.inputStream; } @Override public String getCharacterEncoding() { if (characterEncoding == null) { characterEncoding = Utils.getCharsetFromContentType(this.getContentType()); } return this.characterEncoding; } @Override public String getParameter(String name) { String[] values = getParameterValues(name); return values != null ? values[0] : null; } @SuppressWarnings("rawtypes") @Override public Map getParameterMap() { return this.queryStringDecoder.parameters(); } @SuppressWarnings("rawtypes") @Override public Enumeration getParameterNames() { return Utils.enumerationFromKeys(this.queryStringDecoder.parameters()); } @Override public String[] getParameterValues(String name) { List<String> values = this.queryStringDecoder.parameters().get(name); if (values == null || values.isEmpty()) { return null; } return values.toArray(new String[values.size()]); } @Override public String getProtocol() { return this.originalRequest.getProtocolVersion().toString(); } @Override public Object getAttribute(String name) { if (attributes != null) { return this.attributes.get(name); } return null; } @SuppressWarnings("rawtypes") @Override public Enumeration getAttributeNames() { return Utils.enumerationFromKeys(this.attributes); } @Override public void removeAttribute(String name) { if (this.attributes != null) { this.attributes.remove(name); } } @Override public void setAttribute(String name, Object o) { this.attributes.put(name, o); } @Override public BufferedReader getReader() throws IOException { return this.reader; } @Override public String getRequestedSessionId() { // doesn't implement it yet return null; } @Override public HttpSession getSession() { // doesn't implement it yet return null; } @Override public HttpSession getSession(boolean create) { // doesn't implement it yet return null; } @Override public String getPathInfo() { return this.uriParser.getPathInfo(); } @Override public Locale getLocale() { String locale = HttpHeaders.getHeader(this.originalRequest, Names.ACCEPT_LANGUAGE, DEFAULT_LOCALE.toString()); return new Locale(locale); } @Override public String getRemoteAddr() { InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get().remoteAddress(); return addr.getAddress().getHostAddress(); } @Override public String getRemoteHost() { InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get().remoteAddress(); return addr.getHostName(); } @Override public int getRemotePort() { InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get().remoteAddress(); return addr.getPort(); } @Override public String getServerName() { InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get().localAddress(); return addr.getHostName(); } @Override public int getServerPort() { InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get().localAddress(); return addr.getPort(); } @Override public String getServletPath() { String servletPath = this.uriParser.getServletPath(); if ("/".equals(servletPath)) { return ""; } return servletPath; } @Override public String getScheme() { return this.isSecure() ? "https" : "http"; } @Override public boolean isSecure() { return ChannelThreadLocal.get().pipeline().get(SslHandler.class) != null; } @Override public boolean isRequestedSessionIdFromCookie() { return true; } @Override public String getLocalAddr() { InetSocketAddress addr = (InetSocketAddress) ChannelThreadLocal.get().localAddress(); return addr.getAddress().getHostAddress(); } @Override public String getLocalName() { return getServerName(); } @Override public int getLocalPort() { return getServerPort(); } @Override public void setCharacterEncoding(String env) throws UnsupportedEncodingException { this.characterEncoding = env; } @SuppressWarnings("rawtypes") @Override public Enumeration getLocales() { Collection<Locale> locales = Utils .parseAcceptLanguageHeader(HttpHeaders .getHeader(this.originalRequest, HttpHeaders.Names.ACCEPT_LANGUAGE)); if (locales == null || locales.isEmpty()) { locales = new ArrayList<>(); locales.add(Locale.getDefault()); } return Utils.enumeration(locales); } @Override public String getAuthType() { // CXF Calls this method to cache the Request informaiton return null; } @Override public String getPathTranslated() { // CXF Calls this method to cache the Request informaiton return null; } @Override public String getRemoteUser() { // CXF Calls this method to cache the Request informaiton return null; } @Override public Principal getUserPrincipal() { // CXF will call this method to setup user information for Authentication return null; } @Override public boolean isRequestedSessionIdFromURL() { throw new IllegalStateException( "Method 'isRequestedSessionIdFromURL' not yet implemented!"); } @Override public boolean isRequestedSessionIdFromUrl() { throw new IllegalStateException( "Method 'isRequestedSessionIdFromUrl' not yet implemented!"); } @Override public boolean isRequestedSessionIdValid() { return false; } @Override public boolean isUserInRole(String role) { throw new IllegalStateException( "Method 'isUserInRole' not yet implemented!"); } @Override public String getRealPath(String path) { throw new IllegalStateException( "Method 'getRealPath' not yet implemented!"); } @Override public RequestDispatcher getRequestDispatcher(String path) { throw new IllegalStateException( "Method 'getRequestDispatcher' not yet implemented!"); } }