View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server;
20  
21  import java.io.BufferedReader;
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.UnsupportedEncodingException;
28  import java.net.InetAddress;
29  import java.net.InetSocketAddress;
30  import java.nio.charset.Charset;
31  import java.nio.charset.UnsupportedCharsetException;
32  import java.security.Principal;
33  import java.util.ArrayList;
34  import java.util.Collection;
35  import java.util.Collections;
36  import java.util.Enumeration;
37  import java.util.EventListener;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Locale;
41  import java.util.Map;
42  import javax.servlet.AsyncContext;
43  import javax.servlet.AsyncListener;
44  import javax.servlet.DispatcherType;
45  import javax.servlet.MultipartConfigElement;
46  import javax.servlet.RequestDispatcher;
47  import javax.servlet.ServletContext;
48  import javax.servlet.ServletException;
49  import javax.servlet.ServletInputStream;
50  import javax.servlet.ServletRequest;
51  import javax.servlet.ServletRequestAttributeEvent;
52  import javax.servlet.ServletRequestAttributeListener;
53  import javax.servlet.ServletRequestEvent;
54  import javax.servlet.ServletRequestListener;
55  import javax.servlet.ServletResponse;
56  import javax.servlet.http.Cookie;
57  import javax.servlet.http.HttpServletRequest;
58  import javax.servlet.http.HttpServletResponse;
59  import javax.servlet.http.HttpSession;
60  import javax.servlet.http.HttpUpgradeHandler;
61  import javax.servlet.http.Part;
62  
63  import org.eclipse.jetty.http.HttpCookie;
64  import org.eclipse.jetty.http.HttpFields;
65  import org.eclipse.jetty.http.HttpHeader;
66  import org.eclipse.jetty.http.HttpMethod;
67  import org.eclipse.jetty.http.HttpStatus;
68  import org.eclipse.jetty.http.HttpURI;
69  import org.eclipse.jetty.http.HttpVersion;
70  import org.eclipse.jetty.http.MimeTypes;
71  import org.eclipse.jetty.server.handler.ContextHandler;
72  import org.eclipse.jetty.server.handler.ContextHandler.Context;
73  import org.eclipse.jetty.server.session.AbstractSession;
74  import org.eclipse.jetty.util.Attributes;
75  import org.eclipse.jetty.util.AttributesMap;
76  import org.eclipse.jetty.util.IO;
77  import org.eclipse.jetty.util.MultiException;
78  import org.eclipse.jetty.util.MultiMap;
79  import org.eclipse.jetty.util.MultiPartInputStreamParser;
80  import org.eclipse.jetty.util.StringUtil;
81  import org.eclipse.jetty.util.URIUtil;
82  import org.eclipse.jetty.util.UrlEncoded;
83  import org.eclipse.jetty.util.log.Log;
84  import org.eclipse.jetty.util.log.Logger;
85  
86  /* ------------------------------------------------------------ */
87  /**
88   * Jetty Request.
89   * <p>
90   * Implements {@link javax.servlet.http.HttpServletRequest} from the <code>javax.servlet.http</code> package.
91   * </p>
92   * <p>
93   * The standard interface of mostly getters, is extended with setters so that the request is mutable by the handlers that it is passed to. This allows the
94   * request object to be as lightweight as possible and not actually implement any significant behavior. For example
95   * <ul>
96   *
97   * <li>The {@link Request#getContextPath()} method will return null, until the request has been passed to a {@link ContextHandler} which matches the
98   * {@link Request#getPathInfo()} with a context path and calls {@link Request#setContextPath(String)} as a result.</li>
99   *
100  * <li>the HTTP session methods will all return null sessions until such time as a request has been passed to a
101  * {@link org.eclipse.jetty.server.session.SessionHandler} which checks for session cookies and enables the ability to create new sessions.</li>
102  *
103  * <li>The {@link Request#getServletPath()} method will return null until the request has been passed to a <code>org.eclipse.jetty.servlet.ServletHandler</code>
104  * and the pathInfo matched against the servlet URL patterns and {@link Request#setServletPath(String)} called as a result.</li>
105  * </ul>
106  *
107  * A request instance is created for each connection accepted by the server and recycled for each HTTP request received via that connection.
108  * An effort is made to avoid reparsing headers and cookies that are likely to be the same for requests from the same connection.
109  *
110  * <p>
111  * The form content that a request can process is limited to protect from Denial of Service attacks. The size in bytes is limited by
112  * {@link ContextHandler#getMaxFormContentSize()} or if there is no context then the "org.eclipse.jetty.server.Request.maxFormContentSize" {@link Server}
113  * attribute. The number of parameters keys is limited by {@link ContextHandler#getMaxFormKeys()} or if there is no context then the
114  * "org.eclipse.jetty.server.Request.maxFormKeys" {@link Server} attribute.
115  *
116  *
117  */
118 public class Request implements HttpServletRequest
119 {
120     public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.jetty.multipartConfig";
121     public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.jetty.multiPartInputStream";
122     public static final String __MULTIPART_CONTEXT = "org.eclipse.jetty.multiPartContext";
123 
124     private static final Logger LOG = Log.getLogger(Request.class);
125     private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
126     private static final int __NONE = 0, _STREAM = 1, __READER = 2;
127 
128     private final HttpChannel<?> _channel;
129     private final HttpFields _fields=new HttpFields();
130     private final List<ServletRequestAttributeListener>  _requestAttributeListeners=new ArrayList<>();
131     private final HttpInput<?> _input;
132     
133     public static class MultiPartCleanerListener implements ServletRequestListener
134     {
135         @Override
136         public void requestDestroyed(ServletRequestEvent sre)
137         {
138             //Clean up any tmp files created by MultiPartInputStream
139             MultiPartInputStreamParser mpis = (MultiPartInputStreamParser)sre.getServletRequest().getAttribute(__MULTIPART_INPUT_STREAM);
140             if (mpis != null)
141             {
142                 ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(__MULTIPART_CONTEXT);
143 
144                 //Only do the cleanup if we are exiting from the context in which a servlet parsed the multipart files
145                 if (context == sre.getServletContext())
146                 {
147                     try
148                     {
149                         mpis.deleteParts();
150                     }
151                     catch (MultiException e)
152                     {
153                         sre.getServletContext().log("Errors deleting multipart tmp files", e);
154                     }
155                 }
156             }
157         }
158 
159         @Override
160         public void requestInitialized(ServletRequestEvent sre)
161         {
162             //nothing to do, multipart config set up by ServletHolder.handle()
163         }
164         
165     }
166     
167     
168 
169     private boolean _secure;
170     private boolean _asyncSupported = true;
171     private boolean _newContext;
172     private boolean _cookiesExtracted = false;
173     private boolean _handled = false;
174     private boolean _paramsExtracted;
175     private boolean _requestedSessionIdFromCookie = false;
176     private volatile Attributes _attributes;
177     private Authentication _authentication;
178     private MultiMap<String> _baseParameters;
179     private String _characterEncoding;
180     private ContextHandler.Context _context;
181     private String _contextPath;
182     private CookieCutter _cookies;
183     private DispatcherType _dispatcherType;
184     private int _inputState = __NONE;
185     private HttpMethod _httpMethod;
186     private String _httpMethodString;
187     private MultiMap<String> _parameters;
188     private String _pathInfo;
189     private int _port;
190     private HttpVersion _httpVersion = HttpVersion.HTTP_1_1;
191     private String _queryEncoding;
192     private String _queryString;
193     private BufferedReader _reader;
194     private String _readerEncoding;
195     private InetSocketAddress _remote;
196     private String _requestedSessionId;
197     private String _requestURI;
198     private Map<Object, HttpSession> _savedNewSessions;
199     private String _scheme = URIUtil.HTTP;
200     private UserIdentity.Scope _scope;
201     private String _serverName;
202     private String _servletPath;
203     private HttpSession _session;
204     private SessionManager _sessionManager;
205     private long _timeStamp;
206     private HttpURI _uri;
207     private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime
208     private AsyncContextState _async;
209     
210     /* ------------------------------------------------------------ */
211     public Request(HttpChannel<?> channel, HttpInput<?> input)
212     {
213         _channel = channel;
214         _input = input;
215     }
216 
217     /* ------------------------------------------------------------ */
218     public HttpFields getHttpFields()
219     {
220         return _fields;
221     }
222 
223     /* ------------------------------------------------------------ */
224     public HttpInput<?> getHttpInput()
225     {
226         return _input;
227     }
228 
229     /* ------------------------------------------------------------ */
230     public void addEventListener(final EventListener listener)
231     {
232         if (listener instanceof ServletRequestAttributeListener)
233             _requestAttributeListeners.add((ServletRequestAttributeListener)listener);
234         if (listener instanceof AsyncListener)
235             throw new IllegalArgumentException(listener.getClass().toString());
236     }
237 
238     /* ------------------------------------------------------------ */
239     /**
240      * Extract Parameters from query string and/or form _content.
241      */
242     public void extractParameters()
243     {
244         if (_baseParameters == null)
245             _baseParameters = new MultiMap<>();
246 
247         if (_paramsExtracted)
248         {
249             if (_parameters == null)
250                 _parameters = _baseParameters;
251             return;
252         }
253 
254         _paramsExtracted = true;
255 
256         try
257         {
258             // Handle query string
259             if (_uri != null && _uri.hasQuery())
260             {
261                 if (_queryEncoding == null)
262                     _uri.decodeQueryTo(_baseParameters);
263                 else
264                 {
265                     try
266                     {
267                         _uri.decodeQueryTo(_baseParameters,_queryEncoding);
268                     }
269                     catch (UnsupportedEncodingException e)
270                     {
271                         if (LOG.isDebugEnabled())
272                             LOG.warn(e);
273                         else
274                             LOG.warn(e.toString());
275                     }
276                 }
277             }
278 
279             // handle any _content.
280             String encoding = getCharacterEncoding();
281             String content_type = getContentType();
282             if (content_type != null && content_type.length() > 0)
283             {
284                 content_type = HttpFields.valueParameters(content_type,null);
285 
286                 if (MimeTypes.Type.FORM_ENCODED.is(content_type) && _inputState == __NONE &&
287                     (HttpMethod.POST.is(getMethod()) || HttpMethod.PUT.is(getMethod())))
288                 {
289                     int content_length = getContentLength();
290                     if (content_length != 0)
291                     {
292                         try
293                         {
294                             int maxFormContentSize = -1;
295                             int maxFormKeys = -1;
296 
297                             if (_context != null)
298                             {
299                                 maxFormContentSize = _context.getContextHandler().getMaxFormContentSize();
300                                 maxFormKeys = _context.getContextHandler().getMaxFormKeys();
301                             }
302                             
303                             if (maxFormContentSize < 0)
304                             {
305                                 Object obj = _channel.getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormContentSize");
306                                 if (obj == null)
307                                     maxFormContentSize = 200000;
308                                 else if (obj instanceof Number)
309                                 {                      
310                                     Number size = (Number)obj;
311                                     maxFormContentSize = size.intValue();
312                                 }
313                                 else if (obj instanceof String)
314                                 {
315                                     maxFormContentSize = Integer.valueOf((String)obj);
316                                 }
317                             }
318                             
319                             if (maxFormKeys < 0)
320                             {
321                                 Object obj = _channel.getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormKeys");
322                                 if (obj == null)
323                                     maxFormKeys = 1000;
324                                 else if (obj instanceof Number)
325                                 {
326                                     Number keys = (Number)obj;
327                                     maxFormKeys = keys.intValue();
328                                 }
329                                 else if (obj instanceof String)
330                                 {
331                                     maxFormKeys = Integer.valueOf((String)obj);
332                                 }
333                             }
334 
335                             if (content_length > maxFormContentSize && maxFormContentSize > 0)
336                             {
337                                 throw new IllegalStateException("Form too large " + content_length + ">" + maxFormContentSize);
338                             }
339                             InputStream in = getInputStream();
340 
341                             // Add form params to query params
342                             UrlEncoded.decodeTo(in,_baseParameters,encoding,content_length < 0?maxFormContentSize:-1,maxFormKeys);
343                         }
344                         catch (IOException e)
345                         {
346                             if (LOG.isDebugEnabled())
347                                 LOG.warn(e);
348                             else
349                                 LOG.warn(e.toString());
350                         }
351                     }
352                 }
353               
354             }
355 
356             if (_parameters == null)
357                 _parameters = _baseParameters;
358             else if (_parameters != _baseParameters)
359             {
360                 // Merge parameters (needed if parameters extracted after a forward).
361                 _parameters.addAllValues(_baseParameters);
362             }
363 
364             if (content_type != null && content_type.length()>0 && content_type.startsWith("multipart/form-data") && getAttribute(__MULTIPART_CONFIG_ELEMENT)!=null)
365             {
366                 try
367                 {
368                     getParts();
369                 }
370                 catch (IOException e)
371                 {
372                     if (LOG.isDebugEnabled())
373                         LOG.warn(e);
374                     else
375                         LOG.warn(e.toString());
376                 }
377                 catch (ServletException e)
378                 {
379                     if (LOG.isDebugEnabled())
380                         LOG.warn(e);
381                     else
382                         LOG.warn(e.toString());
383                 }
384             }
385         }
386         finally
387         {
388             // ensure params always set (even if empty) after extraction
389             if (_parameters == null)
390                 _parameters = _baseParameters;
391         }
392     }
393 
394     /* ------------------------------------------------------------ */
395     @Override
396     public AsyncContext getAsyncContext()
397     {
398         HttpChannelState state = getHttpChannelState();
399         if (_async==null || state.isInitial() && !state.isAsync())
400             throw new IllegalStateException(state.getStatusString());
401         
402         return _async;
403     }
404 
405     /* ------------------------------------------------------------ */
406     public HttpChannelState getHttpChannelState()
407     {
408         return _channel.getState();
409     }
410 
411     /* ------------------------------------------------------------ */
412     /**
413      * Get Request Attribute.
414      * <p>Also supports jetty specific attributes to gain access to Jetty APIs:
415      * <dl>
416      * <dt>org.eclipse.jetty.server.Server</dt><dd>The Jetty Server instance</dd>
417      * <dt>org.eclipse.jetty.server.HttpChannel</dt><dd>The HttpChannel for this request</dd>
418      * <dt>org.eclipse.jetty.server.HttpConnection</dt><dd>The HttpConnection or null if another transport is used</dd>
419      * </dl>
420      * While these attributes may look like security problems, they are exposing nothing that is not already
421      * available via reflection from a Request instance.
422      * </p>
423      * @see javax.servlet.ServletRequest#getAttribute(java.lang.String)
424      */
425     @Override
426     public Object getAttribute(String name)
427     {
428         if (name.startsWith("org.eclipse.jetty"))
429         {
430             if ("org.eclipse.jetty.server.Server".equals(name))
431                 return _channel.getServer();
432             if ("org.eclipse.jetty.server.HttpChannel".equals(name))
433                 return _channel;
434             if ("org.eclipse.jetty.server.HttpConnection".equals(name) &&
435                 _channel.getHttpTransport() instanceof HttpConnection)
436                 return _channel.getHttpTransport();
437         }
438         return (_attributes == null)?null:_attributes.getAttribute(name);
439     }
440 
441     /* ------------------------------------------------------------ */
442     /*
443      * @see javax.servlet.ServletRequest#getAttributeNames()
444      */
445     @Override
446     public Enumeration<String> getAttributeNames()
447     {
448         if (_attributes == null)
449             return Collections.enumeration(Collections.<String>emptyList());
450 
451         return AttributesMap.getAttributeNamesCopy(_attributes);
452     }
453 
454     /* ------------------------------------------------------------ */
455     /*
456      */
457     public Attributes getAttributes()
458     {
459         if (_attributes == null)
460             _attributes = new AttributesMap();
461         return _attributes;
462     }
463 
464     /* ------------------------------------------------------------ */
465     /**
466      * Get the authentication.
467      *
468      * @return the authentication
469      */
470     public Authentication getAuthentication()
471     {
472         return _authentication;
473     }
474 
475     /* ------------------------------------------------------------ */
476     /*
477      * @see javax.servlet.http.HttpServletRequest#getAuthType()
478      */
479     @Override
480     public String getAuthType()
481     {
482         if (_authentication instanceof Authentication.Deferred)
483             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
484 
485         if (_authentication instanceof Authentication.User)
486             return ((Authentication.User)_authentication).getAuthMethod();
487         return null;
488     }
489 
490     /* ------------------------------------------------------------ */
491     /*
492      * @see javax.servlet.ServletRequest#getCharacterEncoding()
493      */
494     @Override
495     public String getCharacterEncoding()
496     {
497         return _characterEncoding;
498     }
499 
500     /* ------------------------------------------------------------ */
501     /**
502      * @return Returns the connection.
503      */
504     public HttpChannel<?> getHttpChannel()
505     {
506         return _channel;
507     }
508 
509     /* ------------------------------------------------------------ */
510     /*
511      * @see javax.servlet.ServletRequest#getContentLength()
512      */
513     @Override
514     public int getContentLength()
515     {
516         return (int)_fields.getLongField(HttpHeader.CONTENT_LENGTH.toString());
517     }
518 
519     /* ------------------------------------------------------------ */
520     /*
521      * @see javax.servlet.ServletRequest.getContentLengthLong()
522      */
523     @Override
524     public long getContentLengthLong()
525     {
526         return _fields.getLongField(HttpHeader.CONTENT_LENGTH.toString());
527     }
528 
529     /* ------------------------------------------------------------ */
530     public long getContentRead()
531     {
532         return _input.getContentRead();
533     }
534     
535     /* ------------------------------------------------------------ */
536     /*
537      * @see javax.servlet.ServletRequest#getContentType()
538      */
539     @Override
540     public String getContentType()
541     {
542         return _fields.getStringField(HttpHeader.CONTENT_TYPE);
543     }
544 
545     /* ------------------------------------------------------------ */
546     /**
547      * @return The current {@link Context context} used for this request, or <code>null</code> if {@link #setContext} has not yet been called.
548      */
549     public Context getContext()
550     {
551         return _context;
552     }
553 
554     /* ------------------------------------------------------------ */
555     /*
556      * @see javax.servlet.http.HttpServletRequest#getContextPath()
557      */
558     @Override
559     public String getContextPath()
560     {
561         return _contextPath;
562     }
563 
564     /* ------------------------------------------------------------ */
565     /*
566      * @see javax.servlet.http.HttpServletRequest#getCookies()
567      */
568     @Override
569     public Cookie[] getCookies()
570     {
571         if (_cookiesExtracted)
572         {
573             if (_cookies == null || _cookies.getCookies().length == 0)
574                 return null;
575             
576             return _cookies.getCookies();
577         }
578 
579         _cookiesExtracted = true;
580 
581         Enumeration<?> enm = _fields.getValues(HttpHeader.COOKIE.toString());
582 
583         // Handle no cookies
584         if (enm != null)
585         {
586             if (_cookies == null)
587                 _cookies = new CookieCutter();
588 
589             while (enm.hasMoreElements())
590             {
591                 String c = (String)enm.nextElement();
592                 _cookies.addCookieField(c);
593             }
594         }
595 
596         //Javadoc for Request.getCookies() stipulates null for no cookies
597         if (_cookies == null || _cookies.getCookies().length == 0)
598             return null;
599         
600         return _cookies.getCookies();
601     }
602 
603     /* ------------------------------------------------------------ */
604     /*
605      * @see javax.servlet.http.HttpServletRequest#getDateHeader(java.lang.String)
606      */
607     @Override
608     public long getDateHeader(String name)
609     {
610         return _fields.getDateField(name);
611     }
612 
613     /* ------------------------------------------------------------ */
614     @Override
615     public DispatcherType getDispatcherType()
616     {
617         return _dispatcherType;
618     }
619 
620     /* ------------------------------------------------------------ */
621     /*
622      * @see javax.servlet.http.HttpServletRequest#getHeader(java.lang.String)
623      */
624     @Override
625     public String getHeader(String name)
626     {
627         return _fields.getStringField(name);
628     }
629 
630     /* ------------------------------------------------------------ */
631     /*
632      * @see javax.servlet.http.HttpServletRequest#getHeaderNames()
633      */
634     @Override
635     public Enumeration<String> getHeaderNames()
636     {
637         return _fields.getFieldNames();
638     }
639 
640     /* ------------------------------------------------------------ */
641     /*
642      * @see javax.servlet.http.HttpServletRequest#getHeaders(java.lang.String)
643      */
644     @Override
645     public Enumeration<String> getHeaders(String name)
646     {
647         Enumeration<String> e = _fields.getValues(name);
648         if (e == null)
649             return Collections.enumeration(Collections.<String>emptyList());
650         return e;
651     }
652 
653     /* ------------------------------------------------------------ */
654     /**
655      * @return Returns the inputState.
656      */
657     public int getInputState()
658     {
659         return _inputState;
660     }
661 
662     /* ------------------------------------------------------------ */
663     /*
664      * @see javax.servlet.ServletRequest#getInputStream()
665      */
666     @Override
667     public ServletInputStream getInputStream() throws IOException
668     {
669         if (_inputState != __NONE && _inputState != _STREAM)
670             throw new IllegalStateException("READER");
671         _inputState = _STREAM;
672 
673         if (_channel.isExpecting100Continue())
674             _channel.continue100(_input.available());
675 
676         return _input;
677     }
678 
679     /* ------------------------------------------------------------ */
680     /*
681      * @see javax.servlet.http.HttpServletRequest#getIntHeader(java.lang.String)
682      */
683     @Override
684     public int getIntHeader(String name)
685     {
686         return (int)_fields.getLongField(name);
687     }
688 
689 
690     /* ------------------------------------------------------------ */
691     /*
692      * @see javax.servlet.ServletRequest#getLocale()
693      */
694     @Override
695     public Locale getLocale()
696     {
697         Enumeration<String> enm = _fields.getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
698 
699         // handle no locale
700         if (enm == null || !enm.hasMoreElements())
701             return Locale.getDefault();
702 
703         // sort the list in quality order
704         List<?> acceptLanguage = HttpFields.qualityList(enm);
705         if (acceptLanguage.size() == 0)
706             return Locale.getDefault();
707 
708         int size = acceptLanguage.size();
709 
710         if (size > 0)
711         {
712             String language = (String)acceptLanguage.get(0);
713             language = HttpFields.valueParameters(language,null);
714             String country = "";
715             int dash = language.indexOf('-');
716             if (dash > -1)
717             {
718                 country = language.substring(dash + 1).trim();
719                 language = language.substring(0,dash).trim();
720             }
721             return new Locale(language,country);
722         }
723 
724         return Locale.getDefault();
725     }
726 
727     /* ------------------------------------------------------------ */
728     /*
729      * @see javax.servlet.ServletRequest#getLocales()
730      */
731     @Override
732     public Enumeration<Locale> getLocales()
733     {
734 
735         Enumeration<String> enm = _fields.getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
736 
737         // handle no locale
738         if (enm == null || !enm.hasMoreElements())
739             return Collections.enumeration(__defaultLocale);
740 
741         // sort the list in quality order
742         List<String> acceptLanguage = HttpFields.qualityList(enm);
743 
744         if (acceptLanguage.size() == 0)
745             return Collections.enumeration(__defaultLocale);
746 
747         List<Locale> langs = new ArrayList<>();
748 
749         // convert to locals
750         for (String language : acceptLanguage)
751         {
752             language = HttpFields.valueParameters(language, null);
753             String country = "";
754             int dash = language.indexOf('-');
755             if (dash > -1)
756             {
757                 country = language.substring(dash + 1).trim();
758                 language = language.substring(0, dash).trim();
759             }
760             langs.add(new Locale(language, country));
761         }
762 
763         if (langs.size() == 0)
764             return Collections.enumeration(__defaultLocale);
765 
766         return Collections.enumeration(langs);
767     }
768 
769     /* ------------------------------------------------------------ */
770     /*
771      * @see javax.servlet.ServletRequest#getLocalAddr()
772      */
773     @Override
774     public String getLocalAddr()
775     {
776         InetSocketAddress local=_channel.getLocalAddress();
777         if (local==null)
778             return "";
779         InetAddress address = local.getAddress();
780         if (address==null)
781             return local.getHostString();
782         return address.getHostAddress();
783     }
784 
785     /* ------------------------------------------------------------ */
786     /*
787      * @see javax.servlet.ServletRequest#getLocalName()
788      */
789     @Override
790     public String getLocalName()
791     {
792         InetSocketAddress local=_channel.getLocalAddress();
793         return local.getHostString();
794     }
795 
796     /* ------------------------------------------------------------ */
797     /*
798      * @see javax.servlet.ServletRequest#getLocalPort()
799      */
800     @Override
801     public int getLocalPort()
802     {
803         InetSocketAddress local=_channel.getLocalAddress();
804         return local.getPort();
805     }
806 
807     /* ------------------------------------------------------------ */
808     /*
809      * @see javax.servlet.http.HttpServletRequest#getMethod()
810      */
811     @Override
812     public String getMethod()
813     {
814         return _httpMethodString;
815     }
816 
817     /* ------------------------------------------------------------ */
818     /*
819      * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
820      */
821     @Override
822     public String getParameter(String name)
823     {
824         if (!_paramsExtracted)
825             extractParameters();
826         return _parameters.getValue(name,0);
827     }
828 
829     /* ------------------------------------------------------------ */
830     /*
831      * @see javax.servlet.ServletRequest#getParameterMap()
832      */
833     @Override
834     public Map<String, String[]> getParameterMap()
835     {
836         if (!_paramsExtracted)
837             extractParameters();
838 
839         return Collections.unmodifiableMap(_parameters.toStringArrayMap());
840     }
841 
842     /* ------------------------------------------------------------ */
843     /*
844      * @see javax.servlet.ServletRequest#getParameterNames()
845      */
846     @Override
847     public Enumeration<String> getParameterNames()
848     {
849         if (!_paramsExtracted)
850             extractParameters();
851         return Collections.enumeration(_parameters.keySet());
852     }
853 
854     /* ------------------------------------------------------------ */
855     /**
856      * @return Returns the parameters.
857      */
858     public MultiMap<String> getParameters()
859     {
860         return _parameters;
861     }
862 
863     /* ------------------------------------------------------------ */
864     /*
865      * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
866      */
867     @Override
868     public String[] getParameterValues(String name)
869     {
870         if (!_paramsExtracted)
871             extractParameters();
872         List<String> vals = _parameters.getValues(name);
873         if (vals == null)
874             return null;
875         return vals.toArray(new String[vals.size()]);
876     }
877 
878     /* ------------------------------------------------------------ */
879     /*
880      * @see javax.servlet.http.HttpServletRequest#getPathInfo()
881      */
882     @Override
883     public String getPathInfo()
884     {
885         return _pathInfo;
886     }
887 
888     /* ------------------------------------------------------------ */
889     /*
890      * @see javax.servlet.http.HttpServletRequest#getPathTranslated()
891      */
892     @Override
893     public String getPathTranslated()
894     {
895         if (_pathInfo == null || _context == null)
896             return null;
897         return _context.getRealPath(_pathInfo);
898     }
899 
900     /* ------------------------------------------------------------ */
901     /*
902      * @see javax.servlet.ServletRequest#getProtocol()
903      */
904     @Override
905     public String getProtocol()
906     {
907         return _httpVersion.toString();
908     }
909 
910     /* ------------------------------------------------------------ */
911     /*
912      * @see javax.servlet.ServletRequest#getProtocol()
913      */
914     public HttpVersion getHttpVersion()
915     {
916         return _httpVersion;
917     }
918 
919     /* ------------------------------------------------------------ */
920     public String getQueryEncoding()
921     {
922         return _queryEncoding;
923     }
924 
925     /* ------------------------------------------------------------ */
926     /*
927      * @see javax.servlet.http.HttpServletRequest#getQueryString()
928      */
929     @Override
930     public String getQueryString()
931     {
932         if (_queryString == null && _uri != null)
933         {
934             if (_queryEncoding == null)
935                 _queryString = _uri.getQuery();
936             else
937                 _queryString = _uri.getQuery(_queryEncoding);
938         }
939         return _queryString;
940     }
941 
942     /* ------------------------------------------------------------ */
943     /*
944      * @see javax.servlet.ServletRequest#getReader()
945      */
946     @Override
947     public BufferedReader getReader() throws IOException
948     {
949         if (_inputState != __NONE && _inputState != __READER)
950             throw new IllegalStateException("STREAMED");
951 
952         if (_inputState == __READER)
953             return _reader;
954 
955         String encoding = getCharacterEncoding();
956         if (encoding == null)
957             encoding = StringUtil.__ISO_8859_1;
958 
959         if (_reader == null || !encoding.equalsIgnoreCase(_readerEncoding))
960         {
961             final ServletInputStream in = getInputStream();
962             _readerEncoding = encoding;
963             _reader = new BufferedReader(new InputStreamReader(in,encoding))
964             {
965                 @Override
966                 public void close() throws IOException
967                 {
968                     in.close();
969                 }
970             };
971         }
972         _inputState = __READER;
973         return _reader;
974     }
975 
976     /* ------------------------------------------------------------ */
977     /*
978      * @see javax.servlet.ServletRequest#getRealPath(java.lang.String)
979      */
980     @Override
981     public String getRealPath(String path)
982     {
983         if (_context == null)
984             return null;
985         return _context.getRealPath(path);
986     }
987 
988     /* ------------------------------------------------------------ */
989     /*
990      * @see javax.servlet.ServletRequest#getRemoteAddr()
991      */
992     @Override
993     public String getRemoteAddr()
994     {
995         InetSocketAddress remote=_remote;
996         if (remote==null)
997             remote=_channel.getRemoteAddress();
998         
999         if (remote==null)
1000             return "";
1001         
1002         InetAddress address = remote.getAddress();
1003         if (address==null)
1004             return remote.getHostString();
1005         
1006         return address.getHostAddress();
1007     }
1008 
1009     /* ------------------------------------------------------------ */
1010     /*
1011      * @see javax.servlet.ServletRequest#getRemoteHost()
1012      */
1013     @Override
1014     public String getRemoteHost()
1015     {
1016         InetSocketAddress remote=_remote;
1017         if (remote==null)
1018             remote=_channel.getRemoteAddress();
1019         return remote==null?"":remote.getHostString();
1020     }
1021 
1022     /* ------------------------------------------------------------ */
1023     /*
1024      * @see javax.servlet.ServletRequest#getRemotePort()
1025      */
1026     @Override
1027     public int getRemotePort()
1028     {
1029         InetSocketAddress remote=_remote;
1030         if (remote==null)
1031             remote=_channel.getRemoteAddress();
1032         return remote==null?0:remote.getPort();
1033     }
1034 
1035     /* ------------------------------------------------------------ */
1036     /*
1037      * @see javax.servlet.http.HttpServletRequest#getRemoteUser()
1038      */
1039     @Override
1040     public String getRemoteUser()
1041     {
1042         Principal p = getUserPrincipal();
1043         if (p == null)
1044             return null;
1045         return p.getName();
1046     }
1047 
1048     /* ------------------------------------------------------------ */
1049     /*
1050      * @see javax.servlet.ServletRequest#getRequestDispatcher(java.lang.String)
1051      */
1052     @Override
1053     public RequestDispatcher getRequestDispatcher(String path)
1054     {
1055         if (path == null || _context == null)
1056             return null;
1057 
1058         // handle relative path
1059         if (!path.startsWith("/"))
1060         {
1061             String relTo = URIUtil.addPaths(_servletPath,_pathInfo);
1062             int slash = relTo.lastIndexOf("/");
1063             if (slash > 1)
1064                 relTo = relTo.substring(0,slash + 1);
1065             else
1066                 relTo = "/";
1067             path = URIUtil.addPaths(relTo,path);
1068         }
1069 
1070         return _context.getRequestDispatcher(path);
1071     }
1072 
1073     /* ------------------------------------------------------------ */
1074     /*
1075      * @see javax.servlet.http.HttpServletRequest#getRequestedSessionId()
1076      */
1077     @Override
1078     public String getRequestedSessionId()
1079     {
1080         return _requestedSessionId;
1081     }
1082 
1083     /* ------------------------------------------------------------ */
1084     /*
1085      * @see javax.servlet.http.HttpServletRequest#getRequestURI()
1086      */
1087     @Override
1088     public String getRequestURI()
1089     {
1090         if (_requestURI == null && _uri != null)
1091             _requestURI = _uri.getPathAndParam();
1092         return _requestURI;
1093     }
1094 
1095     /* ------------------------------------------------------------ */
1096     /*
1097      * @see javax.servlet.http.HttpServletRequest#getRequestURL()
1098      */
1099     @Override
1100     public StringBuffer getRequestURL()
1101     {
1102         final StringBuffer url = new StringBuffer(128);
1103         URIUtil.appendSchemeHostPort(url,getScheme(),getServerName(),getServerPort());
1104         url.append(getRequestURI());
1105         return url;
1106     }
1107 
1108     /* ------------------------------------------------------------ */
1109     public Response getResponse()
1110     {
1111         return _channel.getResponse();
1112     }
1113 
1114     /* ------------------------------------------------------------ */
1115     /**
1116      * Reconstructs the URL the client used to make the request. The returned URL contains a protocol, server name, port number, and, but it does not include a
1117      * path.
1118      * <p>
1119      * Because this method returns a <code>StringBuffer</code>, not a string, you can modify the URL easily, for example, to append path and query parameters.
1120      *
1121      * This method is useful for creating redirect messages and for reporting errors.
1122      *
1123      * @return "scheme://host:port"
1124      */
1125     public StringBuilder getRootURL()
1126     {
1127         StringBuilder url = new StringBuilder(128);
1128         URIUtil.appendSchemeHostPort(url,getScheme(),getServerName(),getServerPort());
1129         return url;
1130     }
1131 
1132     /* ------------------------------------------------------------ */
1133     /*
1134      * @see javax.servlet.ServletRequest#getScheme()
1135      */
1136     @Override
1137     public String getScheme()
1138     {
1139         return _scheme;
1140     }
1141 
1142     /* ------------------------------------------------------------ */
1143     /*
1144      * @see javax.servlet.ServletRequest#getServerName()
1145      */
1146     @Override
1147     public String getServerName()
1148     {
1149         // Return already determined host
1150         if (_serverName != null)
1151             return _serverName;
1152 
1153         if (_uri == null)
1154             throw new IllegalStateException("No uri");
1155 
1156         // Return host from absolute URI
1157         _serverName = _uri.getHost();
1158         if (_serverName != null)
1159         {
1160             _port = _uri.getPort();
1161             return _serverName;
1162         }
1163 
1164         // Return host from header field
1165         String hostPort = _fields.getStringField(HttpHeader.HOST);
1166         
1167         _port=0;
1168         if (hostPort != null)
1169         {
1170             int len=hostPort.length();
1171             loop: for (int i = len; i-- > 0;)
1172             {
1173                 char c2 = (char)(0xff & hostPort.charAt(i));
1174                 switch (c2)
1175                 {
1176                     case ']':
1177                         break loop;
1178 
1179                     case ':':
1180                         try
1181                         {
1182                             len=i;
1183                             _port = StringUtil.toInt(hostPort.substring(i+1));
1184                         }
1185                         catch (NumberFormatException e)
1186                         {
1187                             LOG.warn(e);
1188                             _serverName=hostPort;
1189                             _port=0;
1190                             return _serverName;
1191                         }
1192                         break loop;
1193                 }
1194             }
1195             if (hostPort.charAt(0)=='[')
1196             {
1197                 if (hostPort.charAt(len-1)!=']') 
1198                 {
1199                     LOG.warn("Bad IPv6 "+hostPort);
1200                     _serverName=hostPort;
1201                     _port=0;
1202                     return _serverName;
1203                 }
1204                 _serverName = hostPort.substring(1,len-1);
1205             }
1206             else if (len==hostPort.length())
1207                 _serverName=hostPort;
1208             else
1209                 _serverName = hostPort.substring(0,len);
1210 
1211             return _serverName;
1212         }
1213 
1214         // Return host from connection
1215         if (_channel != null)
1216         {
1217             _serverName = getLocalName();
1218             _port = getLocalPort();
1219             if (_serverName != null && !StringUtil.ALL_INTERFACES.equals(_serverName))
1220                 return _serverName;
1221         }
1222 
1223         // Return the local host
1224         try
1225         {
1226             _serverName = InetAddress.getLocalHost().getHostAddress();
1227         }
1228         catch (java.net.UnknownHostException e)
1229         {
1230             LOG.ignore(e);
1231         }
1232         return _serverName;
1233     }
1234 
1235     /* ------------------------------------------------------------ */
1236     /*
1237      * @see javax.servlet.ServletRequest#getServerPort()
1238      */
1239     @Override
1240     public int getServerPort()
1241     {
1242         if (_port <= 0)
1243         {
1244             if (_serverName == null)
1245                 getServerName();
1246 
1247             if (_port <= 0)
1248             {
1249                 if (_serverName != null && _uri != null)
1250                     _port = _uri.getPort();
1251                 else
1252                 {
1253                     InetSocketAddress local = _channel.getLocalAddress();
1254                     _port = local == null?0:local.getPort();
1255                 }
1256             }
1257         }
1258 
1259         if (_port <= 0)
1260         {
1261             if (getScheme().equalsIgnoreCase(URIUtil.HTTPS))
1262                 return 443;
1263             return 80;
1264         }
1265         return _port;
1266     }
1267 
1268     /* ------------------------------------------------------------ */
1269     @Override
1270     public ServletContext getServletContext()
1271     {
1272         return _context;
1273     }
1274 
1275     /* ------------------------------------------------------------ */
1276     /*
1277      */
1278     public String getServletName()
1279     {
1280         if (_scope != null)
1281             return _scope.getName();
1282         return null;
1283     }
1284 
1285     /* ------------------------------------------------------------ */
1286     /*
1287      * @see javax.servlet.http.HttpServletRequest#getServletPath()
1288      */
1289     @Override
1290     public String getServletPath()
1291     {
1292         if (_servletPath == null)
1293             _servletPath = "";
1294         return _servletPath;
1295     }
1296 
1297     /* ------------------------------------------------------------ */
1298     public ServletResponse getServletResponse()
1299     {
1300         return _channel.getResponse();
1301     }
1302 
1303     /* ------------------------------------------------------------ */
1304     /*
1305      * Add @override when 3.1 api is available
1306      */
1307     public String changeSessionId()
1308     {
1309         HttpSession session = getSession(false);
1310         if (session == null)
1311             throw new IllegalStateException("No session");
1312 
1313         if (session instanceof AbstractSession)
1314         {
1315             AbstractSession abstractSession =  ((AbstractSession)session);
1316             abstractSession.renewId(this);
1317             if (getRemoteUser() != null)
1318                 abstractSession.setAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
1319             if (abstractSession.isIdChanged())
1320                 _channel.getResponse().addCookie(_sessionManager.getSessionCookie(abstractSession, getContextPath(), isSecure()));
1321         }
1322 
1323         return session.getId();
1324     }
1325 
1326     /* ------------------------------------------------------------ */
1327     /*
1328      * @see javax.servlet.http.HttpServletRequest#getSession()
1329      */
1330     @Override
1331     public HttpSession getSession()
1332     {
1333         return getSession(true);
1334     }
1335 
1336     /* ------------------------------------------------------------ */
1337     /*
1338      * @see javax.servlet.http.HttpServletRequest#getSession(boolean)
1339      */
1340     @Override
1341     public HttpSession getSession(boolean create)
1342     {
1343         if (_session != null)
1344         {
1345             if (_sessionManager != null && !_sessionManager.isValid(_session))
1346                 _session = null;
1347             else
1348                 return _session;
1349         }
1350 
1351         if (!create)
1352             return null;
1353 
1354         if (_sessionManager == null)
1355             throw new IllegalStateException("No SessionManager");
1356 
1357         _session = _sessionManager.newHttpSession(this);
1358         HttpCookie cookie = _sessionManager.getSessionCookie(_session,getContextPath(),isSecure());
1359         if (cookie != null)
1360             _channel.getResponse().addCookie(cookie);
1361 
1362         return _session;
1363     }
1364 
1365     /* ------------------------------------------------------------ */
1366     /**
1367      * @return Returns the sessionManager.
1368      */
1369     public SessionManager getSessionManager()
1370     {
1371         return _sessionManager;
1372     }
1373 
1374     /* ------------------------------------------------------------ */
1375     /**
1376      * Get Request TimeStamp
1377      *
1378      * @return The time that the request was received.
1379      */
1380     public long getTimeStamp()
1381     {
1382         return _timeStamp;
1383     }
1384 
1385     /* ------------------------------------------------------------ */
1386     /**
1387      * @return Returns the uri.
1388      */
1389     public HttpURI getUri()
1390     {
1391         return _uri;
1392     }
1393 
1394     /* ------------------------------------------------------------ */
1395     public UserIdentity getUserIdentity()
1396     {
1397         if (_authentication instanceof Authentication.Deferred)
1398             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
1399 
1400         if (_authentication instanceof Authentication.User)
1401             return ((Authentication.User)_authentication).getUserIdentity();
1402         return null;
1403     }
1404 
1405     /* ------------------------------------------------------------ */
1406     /**
1407      * @return The resolved user Identity, which may be null if the {@link Authentication} is not {@link Authentication.User} (eg.
1408      *         {@link Authentication.Deferred}).
1409      */
1410     public UserIdentity getResolvedUserIdentity()
1411     {
1412         if (_authentication instanceof Authentication.User)
1413             return ((Authentication.User)_authentication).getUserIdentity();
1414         return null;
1415     }
1416 
1417     /* ------------------------------------------------------------ */
1418     public UserIdentity.Scope getUserIdentityScope()
1419     {
1420         return _scope;
1421     }
1422 
1423     /* ------------------------------------------------------------ */
1424     /*
1425      * @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
1426      */
1427     @Override
1428     public Principal getUserPrincipal()
1429     {
1430         if (_authentication instanceof Authentication.Deferred)
1431             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
1432 
1433         if (_authentication instanceof Authentication.User)
1434         {
1435             UserIdentity user = ((Authentication.User)_authentication).getUserIdentity();
1436             return user.getUserPrincipal();
1437         }
1438         
1439         return null;
1440     }
1441 
1442 
1443     /* ------------------------------------------------------------ */
1444     public boolean isHandled()
1445     {
1446         return _handled;
1447     }
1448 
1449     @Override
1450     public boolean isAsyncStarted()
1451     {
1452        return getHttpChannelState().isAsyncStarted();
1453     }
1454 
1455 
1456     /* ------------------------------------------------------------ */
1457     @Override
1458     public boolean isAsyncSupported()
1459     {
1460         return _asyncSupported;
1461     }
1462 
1463     /* ------------------------------------------------------------ */
1464     /*
1465      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromCookie()
1466      */
1467     @Override
1468     public boolean isRequestedSessionIdFromCookie()
1469     {
1470         return _requestedSessionId != null && _requestedSessionIdFromCookie;
1471     }
1472 
1473     /* ------------------------------------------------------------ */
1474     /*
1475      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromUrl()
1476      */
1477     @Override
1478     public boolean isRequestedSessionIdFromUrl()
1479     {
1480         return _requestedSessionId != null && !_requestedSessionIdFromCookie;
1481     }
1482 
1483     /* ------------------------------------------------------------ */
1484     /*
1485      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromURL()
1486      */
1487     @Override
1488     public boolean isRequestedSessionIdFromURL()
1489     {
1490         return _requestedSessionId != null && !_requestedSessionIdFromCookie;
1491     }
1492 
1493     /* ------------------------------------------------------------ */
1494     /*
1495      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdValid()
1496      */
1497     @Override
1498     public boolean isRequestedSessionIdValid()
1499     {
1500         if (_requestedSessionId == null)
1501             return false;
1502 
1503         HttpSession session = getSession(false);
1504         return (session != null && _sessionManager.getSessionIdManager().getClusterId(_requestedSessionId).equals(_sessionManager.getClusterId(session)));
1505     }
1506 
1507     /* ------------------------------------------------------------ */
1508     /*
1509      * @see javax.servlet.ServletRequest#isSecure()
1510      */
1511     @Override
1512     public boolean isSecure()
1513     {
1514         return _secure;
1515     }
1516 
1517     /* ------------------------------------------------------------ */
1518     public void setSecure(boolean secure)
1519     {
1520         _secure=secure;
1521     }
1522 
1523     /* ------------------------------------------------------------ */
1524     /*
1525      * @see javax.servlet.http.HttpServletRequest#isUserInRole(java.lang.String)
1526      */
1527     @Override
1528     public boolean isUserInRole(String role)
1529     {
1530         if (_authentication instanceof Authentication.Deferred)
1531             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
1532 
1533         if (_authentication instanceof Authentication.User)
1534             return ((Authentication.User)_authentication).isUserInRole(_scope,role);
1535         return false;
1536     }
1537 
1538     /* ------------------------------------------------------------ */
1539     public HttpSession recoverNewSession(Object key)
1540     {
1541         if (_savedNewSessions == null)
1542             return null;
1543         return _savedNewSessions.get(key);
1544     }
1545 
1546     /* ------------------------------------------------------------ */
1547     protected void recycle()
1548     {
1549         if (_context != null)
1550             throw new IllegalStateException("Request in context!");
1551         
1552         if (_inputState == __READER)
1553         {
1554             try
1555             {
1556                 int r = _reader.read();
1557                 while (r != -1)
1558                     r = _reader.read();
1559             }
1560             catch (Exception e)
1561             {
1562                 LOG.ignore(e);
1563                 _reader = null;
1564             }
1565         }
1566 
1567         _dispatcherType=null;
1568         setAuthentication(Authentication.NOT_CHECKED);
1569         getHttpChannelState().recycle();
1570         if (_async!=null)
1571             _async.reset();
1572         _async=null;
1573         _asyncSupported = true;
1574         _handled = false;
1575         if (_attributes != null)
1576             _attributes.clearAttributes();
1577         _characterEncoding = null;
1578         _contextPath = null;
1579         if (_cookies != null)
1580             _cookies.reset();
1581         _cookiesExtracted = false;
1582         _context = null;
1583         _newContext=false;
1584         _serverName = null;
1585         _httpMethod=null;
1586         _httpMethodString = null;
1587         _pathInfo = null;
1588         _port = 0;
1589         _httpVersion = HttpVersion.HTTP_1_1;
1590         _queryEncoding = null;
1591         _queryString = null;
1592         _requestedSessionId = null;
1593         _requestedSessionIdFromCookie = false;
1594         _secure=false;
1595         _session = null;
1596         _sessionManager = null;
1597         _requestURI = null;
1598         _scope = null;
1599         _scheme = URIUtil.HTTP;
1600         _servletPath = null;
1601         _timeStamp = 0;
1602         _uri = null;
1603         if (_baseParameters != null)
1604             _baseParameters.clear();
1605         _parameters = null;
1606         _paramsExtracted = false;
1607         _inputState = __NONE;
1608 
1609         if (_savedNewSessions != null)
1610             _savedNewSessions.clear();
1611         _savedNewSessions=null;
1612         _multiPartInputStream = null;
1613         _remote=null;
1614         _fields.clear();
1615         _input.recycle();
1616     }
1617 
1618     /* ------------------------------------------------------------ */
1619     /*
1620      * @see javax.servlet.ServletRequest#removeAttribute(java.lang.String)
1621      */
1622     @Override
1623     public void removeAttribute(String name)
1624     {
1625         Object old_value = _attributes == null?null:_attributes.getAttribute(name);
1626 
1627         if (_attributes != null)
1628             _attributes.removeAttribute(name);
1629 
1630         if (old_value != null && !_requestAttributeListeners.isEmpty())
1631         {
1632             final ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(_context,this,name,old_value);
1633             for (ServletRequestAttributeListener listener : _requestAttributeListeners)
1634                 listener.attributeRemoved(event);
1635         }
1636     }
1637 
1638     /* ------------------------------------------------------------ */
1639     public void removeEventListener(final EventListener listener)
1640     {
1641         _requestAttributeListeners.remove(listener);
1642     }
1643 
1644     /* ------------------------------------------------------------ */
1645     public void saveNewSession(Object key, HttpSession session)
1646     {
1647         if (_savedNewSessions == null)
1648             _savedNewSessions = new HashMap<>();
1649         _savedNewSessions.put(key,session);
1650     }
1651 
1652     /* ------------------------------------------------------------ */
1653     public void setAsyncSupported(boolean supported)
1654     {
1655         _asyncSupported = supported;
1656     }
1657 
1658     /* ------------------------------------------------------------ */
1659     /*
1660      * Set a request attribute. if the attribute name is "org.eclipse.jetty.server.server.Request.queryEncoding" then the value is also passed in a call to
1661      * {@link #setQueryEncoding}.
1662      *
1663      * @see javax.servlet.ServletRequest#setAttribute(java.lang.String, java.lang.Object)
1664      */
1665     @Override
1666     public void setAttribute(String name, Object value)
1667     {
1668         Object old_value = _attributes == null?null:_attributes.getAttribute(name);
1669 
1670         if ("org.eclipse.jetty.server.Request.queryEncoding".equals(name))
1671             setQueryEncoding(value == null?null:value.toString());
1672         else if ("org.eclipse.jetty.server.sendContent".equals(name))
1673             LOG.warn("Deprecated: org.eclipse.jetty.server.sendContent");
1674         
1675         if (_attributes == null)
1676             _attributes = new AttributesMap();
1677         _attributes.setAttribute(name,value);
1678 
1679         if (!_requestAttributeListeners.isEmpty())
1680         {
1681             final ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(_context,this,name,old_value == null?value:old_value);
1682             for (ServletRequestAttributeListener l : _requestAttributeListeners)
1683             {
1684                 if (old_value == null)
1685                     l.attributeAdded(event);
1686                 else if (value == null)
1687                     l.attributeRemoved(event);
1688                 else
1689                     l.attributeReplaced(event);
1690             }
1691         }
1692     }
1693 
1694     /* ------------------------------------------------------------ */
1695     /*
1696      */
1697     public void setAttributes(Attributes attributes)
1698     {
1699         _attributes = attributes;
1700     }
1701 
1702     /* ------------------------------------------------------------ */
1703 
1704     /* ------------------------------------------------------------ */
1705     /**
1706      * Set the authentication.
1707      *
1708      * @param authentication
1709      *            the authentication to set
1710      */
1711     public void setAuthentication(Authentication authentication)
1712     {
1713         _authentication = authentication;
1714     }
1715 
1716     /* ------------------------------------------------------------ */
1717     /*
1718      * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)
1719      */
1720     @Override
1721     public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException
1722     {
1723         if (_inputState != __NONE)
1724             return;
1725 
1726         _characterEncoding = encoding;
1727 
1728         // check encoding is supported
1729         if (!StringUtil.isUTF8(encoding))
1730         {
1731             try
1732             {
1733                 Charset.forName(encoding);
1734             }
1735             catch (UnsupportedCharsetException e)
1736             {
1737                 throw new UnsupportedEncodingException(e.getMessage());
1738             }
1739         }
1740     }
1741 
1742     /* ------------------------------------------------------------ */
1743     /*
1744      * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)
1745      */
1746     public void setCharacterEncodingUnchecked(String encoding)
1747     {
1748         _characterEncoding = encoding;
1749     }
1750 
1751     /* ------------------------------------------------------------ */
1752     /*
1753      * @see javax.servlet.ServletRequest#getContentType()
1754      */
1755     public void setContentType(String contentType)
1756     {
1757         _fields.put(HttpHeader.CONTENT_TYPE,contentType);
1758 
1759     }
1760 
1761     /* ------------------------------------------------------------ */
1762     /**
1763      * Set request context
1764      *
1765      * @param context
1766      *            context object
1767      */
1768     public void setContext(Context context)
1769     {
1770         _newContext = _context != context;
1771         _context = context;
1772     }
1773 
1774     /* ------------------------------------------------------------ */
1775     /**
1776      * @return True if this is the first call of {@link #takeNewContext()} since the last
1777      *         {@link #setContext(org.eclipse.jetty.server.handler.ContextHandler.Context)} call.
1778      */
1779     public boolean takeNewContext()
1780     {
1781         boolean nc = _newContext;
1782         _newContext = false;
1783         return nc;
1784     }
1785 
1786     /* ------------------------------------------------------------ */
1787     /**
1788      * Sets the "context path" for this request
1789      *
1790      * @see HttpServletRequest#getContextPath()
1791      */
1792     public void setContextPath(String contextPath)
1793     {
1794         _contextPath = contextPath;
1795     }
1796 
1797     /* ------------------------------------------------------------ */
1798     /**
1799      * @param cookies
1800      *            The cookies to set.
1801      */
1802     public void setCookies(Cookie[] cookies)
1803     {
1804         if (_cookies == null)
1805             _cookies = new CookieCutter();
1806         _cookies.setCookies(cookies);
1807     }
1808 
1809     /* ------------------------------------------------------------ */
1810     public void setDispatcherType(DispatcherType type)
1811     {
1812         _dispatcherType = type;
1813     }
1814 
1815     /* ------------------------------------------------------------ */
1816     public void setHandled(boolean h)
1817     {
1818         _handled = h;
1819         Response r=getResponse();
1820         if (_handled && r.getStatus()==0)
1821             r.setStatus(200);
1822     }
1823 
1824     /* ------------------------------------------------------------ */
1825     /**
1826      * @param method
1827      *            The method to set.
1828      */
1829     public void setMethod(HttpMethod httpMethod, String method)
1830     {
1831         _httpMethod=httpMethod;
1832         _httpMethodString = method;
1833     }
1834 
1835     /* ------------------------------------------------------------ */
1836     public boolean isHead()
1837     {
1838         return HttpMethod.HEAD==_httpMethod;
1839     }
1840 
1841     /* ------------------------------------------------------------ */
1842     /**
1843      * @param parameters
1844      *            The parameters to set.
1845      */
1846     public void setParameters(MultiMap<String> parameters)
1847     {
1848         _parameters = (parameters == null)?_baseParameters:parameters;
1849         if (_paramsExtracted && _parameters == null)
1850             throw new IllegalStateException();
1851     }
1852 
1853     /* ------------------------------------------------------------ */
1854     /**
1855      * @param pathInfo
1856      *            The pathInfo to set.
1857      */
1858     public void setPathInfo(String pathInfo)
1859     {
1860         _pathInfo = pathInfo;
1861     }
1862 
1863     /* ------------------------------------------------------------ */
1864     /**
1865      * @param version
1866      *            The protocol to set.
1867      */
1868     public void setHttpVersion(HttpVersion version)
1869     {
1870         _httpVersion = version;
1871     }
1872 
1873     /* ------------------------------------------------------------ */
1874     /**
1875      * Set the character encoding used for the query string. This call will effect the return of getQueryString and getParamaters. It must be called before any
1876      * geParameter methods.
1877      *
1878      * The request attribute "org.eclipse.jetty.server.server.Request.queryEncoding" may be set as an alternate method of calling setQueryEncoding.
1879      *
1880      * @param queryEncoding
1881      */
1882     public void setQueryEncoding(String queryEncoding)
1883     {
1884         _queryEncoding = queryEncoding;
1885         _queryString = null;
1886     }
1887 
1888     /* ------------------------------------------------------------ */
1889     /**
1890      * @param queryString
1891      *            The queryString to set.
1892      */
1893     public void setQueryString(String queryString)
1894     {
1895         _queryString = queryString;
1896         _queryEncoding = null; //assume utf-8
1897     }
1898 
1899     /* ------------------------------------------------------------ */
1900     /**
1901      * @param addr
1902      *            The address to set.
1903      */
1904     public void setRemoteAddr(InetSocketAddress addr)
1905     {
1906         _remote = addr;
1907     }
1908 
1909     /* ------------------------------------------------------------ */
1910     /**
1911      * @param requestedSessionId
1912      *            The requestedSessionId to set.
1913      */
1914     public void setRequestedSessionId(String requestedSessionId)
1915     {
1916         _requestedSessionId = requestedSessionId;
1917     }
1918 
1919     /* ------------------------------------------------------------ */
1920     /**
1921      * @param requestedSessionIdCookie
1922      *            The requestedSessionIdCookie to set.
1923      */
1924     public void setRequestedSessionIdFromCookie(boolean requestedSessionIdCookie)
1925     {
1926         _requestedSessionIdFromCookie = requestedSessionIdCookie;
1927     }
1928 
1929     /* ------------------------------------------------------------ */
1930     /**
1931      * @param requestURI
1932      *            The requestURI to set.
1933      */
1934     public void setRequestURI(String requestURI)
1935     {
1936         _requestURI = requestURI;
1937     }
1938 
1939     /* ------------------------------------------------------------ */
1940     /**
1941      * @param scheme
1942      *            The scheme to set.
1943      */
1944     public void setScheme(String scheme)
1945     {
1946         _scheme = scheme;
1947     }
1948 
1949     /* ------------------------------------------------------------ */
1950     /**
1951      * @param host
1952      *            The host to set.
1953      */
1954     public void setServerName(String host)
1955     {
1956         _serverName = host;
1957     }
1958 
1959     /* ------------------------------------------------------------ */
1960     /**
1961      * @param port
1962      *            The port to set.
1963      */
1964     public void setServerPort(int port)
1965     {
1966         _port = port;
1967     }
1968 
1969     /* ------------------------------------------------------------ */
1970     /**
1971      * @param servletPath
1972      *            The servletPath to set.
1973      */
1974     public void setServletPath(String servletPath)
1975     {
1976         _servletPath = servletPath;
1977     }
1978 
1979     /* ------------------------------------------------------------ */
1980     /**
1981      * @param session
1982      *            The session to set.
1983      */
1984     public void setSession(HttpSession session)
1985     {
1986         _session = session;
1987     }
1988 
1989     /* ------------------------------------------------------------ */
1990     /**
1991      * @param sessionManager
1992      *            The sessionManager to set.
1993      */
1994     public void setSessionManager(SessionManager sessionManager)
1995     {
1996         _sessionManager = sessionManager;
1997     }
1998 
1999     /* ------------------------------------------------------------ */
2000     public void setTimeStamp(long ts)
2001     {
2002         _timeStamp = ts;
2003     }
2004 
2005     /* ------------------------------------------------------------ */
2006     /**
2007      * @param uri
2008      *            The uri to set.
2009      */
2010     public void setUri(HttpURI uri)
2011     {
2012         _uri = uri;
2013     }
2014 
2015     /* ------------------------------------------------------------ */
2016     public void setUserIdentityScope(UserIdentity.Scope scope)
2017     {
2018         _scope = scope;
2019     }
2020 
2021     /* ------------------------------------------------------------ */
2022     @Override
2023     public AsyncContext startAsync() throws IllegalStateException
2024     {
2025         if (!_asyncSupported)
2026             throw new IllegalStateException("!asyncSupported");
2027         HttpChannelState state = getHttpChannelState();
2028         if (_async==null)
2029             _async=new AsyncContextState(state);
2030         AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,this,getResponse());
2031         state.startAsync(event);
2032         return _async;
2033     }
2034 
2035     /* ------------------------------------------------------------ */
2036     @Override
2037     public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
2038     {
2039         if (!_asyncSupported)
2040             throw new IllegalStateException("!asyncSupported");
2041         HttpChannelState state = getHttpChannelState();
2042         if (_async==null)
2043             _async=new AsyncContextState(state);
2044         AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,servletRequest,servletResponse);
2045         event.setDispatchTarget(getServletContext(),URIUtil.addPaths(getServletPath(),getPathInfo()));
2046         state.startAsync(event);
2047         return _async;
2048     }
2049 
2050     /* ------------------------------------------------------------ */
2051     @Override
2052     public String toString()
2053     {
2054         return (_handled?"[":"(") + getMethod() + " " + _uri + (_handled?"]@":")@") + hashCode() + " " + super.toString();
2055     }
2056 
2057     /* ------------------------------------------------------------ */
2058     @Override
2059     public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
2060     {
2061         if (_authentication instanceof Authentication.Deferred)
2062         {
2063             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this,response));
2064             return !(_authentication instanceof Authentication.ResponseSent);
2065         }
2066         response.sendError(HttpStatus.UNAUTHORIZED_401);
2067         return false;
2068     }
2069 
2070     /* ------------------------------------------------------------ */
2071     @Override
2072     public Part getPart(String name) throws IOException, ServletException
2073     {
2074         getParts();
2075 
2076         return _multiPartInputStream.getPart(name);
2077     }
2078 
2079     /* ------------------------------------------------------------ */
2080     @Override
2081     public Collection<Part> getParts() throws IOException, ServletException
2082     {
2083         if (getContentType() == null || !getContentType().startsWith("multipart/form-data"))
2084             throw new ServletException("Content-Type != multipart/form-data");
2085 
2086         if (_multiPartInputStream == null)
2087             _multiPartInputStream = (MultiPartInputStreamParser)getAttribute(__MULTIPART_INPUT_STREAM);
2088         
2089         if (_multiPartInputStream == null)
2090         {
2091             MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
2092             
2093             if (config == null)
2094                 throw new IllegalStateException("No multipart config for servlet");
2095             
2096             _multiPartInputStream = new MultiPartInputStreamParser(getInputStream(),
2097                                                              getContentType(), config, 
2098                                                              (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
2099             
2100             setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
2101             setAttribute(__MULTIPART_CONTEXT, _context);
2102             Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
2103             for (Part p:parts)
2104             {
2105                 MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p;
2106                 if (mp.getContentDispositionFilename() == null)
2107                 {
2108                     //Servlet Spec 3.0 pg 23, parts without filenames must be put into init params
2109                     String charset = null;
2110                     if (mp.getContentType() != null)
2111                         charset = MimeTypes.getCharsetFromContentType(mp.getContentType());
2112 
2113                     ByteArrayOutputStream os = null;
2114                     InputStream is = mp.getInputStream(); //get the bytes regardless of being in memory or in temp file
2115                     try
2116                     {
2117                         os = new ByteArrayOutputStream();
2118                         IO.copy(is, os);
2119                         String content=new String(os.toByteArray(),charset==null?StringUtil.__UTF8:charset);   
2120                         getParameter(""); //cause params to be evaluated
2121                         getParameters().add(mp.getName(), content);
2122                     }
2123                     finally
2124                     {
2125                         IO.close(os);
2126                         IO.close(is);
2127                     }
2128                 }
2129             }
2130         }
2131 
2132         return _multiPartInputStream.getParts();
2133     }
2134 
2135     /* ------------------------------------------------------------ */
2136     @Override
2137     public void login(String username, String password) throws ServletException
2138     {
2139         if (_authentication instanceof Authentication.Deferred)
2140         {
2141             _authentication=((Authentication.Deferred)_authentication).login(username,password,this);
2142             if (_authentication == null)
2143                 throw new Authentication.Failed("Authentication failed for username '"+username+"'");
2144         }
2145         else
2146         {
2147             throw new Authentication.Failed("Authenticated failed for username '"+username+"'. Already authenticated as "+_authentication);
2148         }
2149     }
2150 
2151     /* ------------------------------------------------------------ */
2152     @Override
2153     public void logout() throws ServletException
2154     {
2155         if (_authentication instanceof Authentication.User)
2156             ((Authentication.User)_authentication).logout();
2157         _authentication=Authentication.UNAUTHENTICATED;
2158     }
2159 
2160     /* ------------------------------------------------------------ */
2161     /**
2162      * Merge in a new query string. The query string is merged with the existing parameters and {@link #setParameters(MultiMap)} and
2163      * {@link #setQueryString(String)} are called with the result. The merge is according to the rules of the servlet dispatch forward method.
2164      *
2165      * @param query
2166      *            The query string to merge into the request.
2167      */
2168     public void mergeQueryString(String query)
2169     {
2170         // extract parameters from dispatch query
2171         MultiMap<String> parameters = new MultiMap<>();
2172         UrlEncoded.decodeTo(query,parameters, StringUtil.__UTF8_CHARSET,-1); //have to assume UTF-8 because we can't know otherwise
2173 
2174         boolean merge_old_query = false;
2175 
2176         // Have we evaluated parameters
2177         if (!_paramsExtracted)
2178             extractParameters();
2179 
2180         // Are there any existing parameters?
2181         if (_parameters != null && _parameters.size() > 0)
2182         {
2183             // Merge parameters; new parameters of the same name take precedence.
2184             merge_old_query = parameters.addAllValues(_parameters);
2185         }
2186 
2187         if (_queryString != null && _queryString.length() > 0)
2188         {
2189             if (merge_old_query)
2190             {
2191                 StringBuilder overridden_query_string = new StringBuilder();
2192                 MultiMap<String> overridden_old_query = new MultiMap<>();
2193                 UrlEncoded.decodeTo(_queryString,overridden_old_query,getQueryEncoding(),-1);//decode using any queryencoding set for the request
2194                 
2195                 
2196                 MultiMap<String> overridden_new_query = new MultiMap<>();
2197                 UrlEncoded.decodeTo(query,overridden_new_query,StringUtil.__UTF8_CHARSET,-1); //have to assume utf8 as we cannot know otherwise
2198 
2199                 for(String name: overridden_old_query.keySet())
2200                 {
2201                     if (!overridden_new_query.containsKey(name))
2202                     {
2203                         List<String> values = overridden_old_query.get(name);
2204                         for(String v: values)
2205                         {
2206                             overridden_query_string.append("&").append(name).append("=").append(v);
2207                         }
2208                     }
2209                 }
2210 
2211                 query = query + overridden_query_string;
2212             }
2213             else
2214             {
2215                 query = query + "&" + _queryString;
2216             }
2217         }
2218 
2219         setParameters(parameters);
2220         setQueryString(query);
2221     }
2222 
2223 
2224    
2225     /** 
2226      * @see javax.servlet.http.HttpServletRequest#upgrade(java.lang.Class)
2227      */
2228     @Override
2229     public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException
2230     {
2231         if (getContext() == null)
2232             throw new ServletException ("Unable to instantiate "+handlerClass);
2233 
2234         try
2235         {
2236             //Instantiate an instance and inject it
2237             T h = getContext().createInstance(handlerClass);
2238             
2239             //TODO handle the rest of the upgrade process
2240             
2241             return h;
2242         }
2243         catch (Exception e)
2244         {
2245             if (e instanceof ServletException)
2246                 throw (ServletException)e;
2247             throw new ServletException(e);
2248         }
2249     }
2250 }