2 // ========================================================================
3 // Copyright (c) 1995-2014 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.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.http;
21 import java.io.UnsupportedEncodingException;
23 import java.nio.charset.Charset;
24 import java.nio.charset.StandardCharsets;
26 import org.eclipse.jetty.util.MultiMap;
27 import org.eclipse.jetty.util.StringUtil;
28 import org.eclipse.jetty.util.TypeUtil;
29 import org.eclipse.jetty.util.URIUtil;
30 import org.eclipse.jetty.util.UrlEncoded;
31 import org.eclipse.jetty.util.Utf8StringBuilder;
34 /* ------------------------------------------------------------ */
36 * Parse a HTTP URI from a string or byte array. Given a URI
37 * <code>http://user@host:port/path/info;param?query#fragment</code>
38 * this class will split it into the following undecoded optional elements:<ul>
39 * <li>{@link #getScheme()} - http:</li>
40 * <li>{@link #getAuthority()} - //name@host:port</li>
41 * <li>{@link #getHost()} - host</li>
42 * <li>{@link #getPort()} - port</li>
43 * <li>{@link #getPath()} - /path/info</li>
44 * <li>{@link #getParam()} - param</li>
45 * <li>{@link #getQuery()} - query</li>
46 * <li>{@link #getFragment()} - fragment</li>
52 private static final byte[] __empty={};
53 private final static int
65 final Charset _charset;
66 boolean _partial=false;
79 boolean _encoded=false;
83 _charset = URIUtil.__CHARSET;
86 public HttpURI(Charset charset)
91 /* ------------------------------------------------------------ */
93 * @param parsePartialAuth If True, parse auth without prior scheme, else treat all URIs starting with / as paths
95 public HttpURI(boolean parsePartialAuth)
97 _partial=parsePartialAuth;
98 _charset = URIUtil.__CHARSET;
101 public HttpURI(String raw)
104 byte[] b = raw.getBytes(StandardCharsets.UTF_8);
106 _charset = URIUtil.__CHARSET;
109 public HttpURI(byte[] raw,int offset, int length)
111 parse2(raw,offset,length);
112 _charset = URIUtil.__CHARSET;
115 public HttpURI(URI uri)
117 parse(uri.toASCIIString());
118 _charset = URIUtil.__CHARSET;
121 public void parse(String raw)
123 byte[] b = StringUtil.getUtf8Bytes(raw);
124 parse2(b,0,b.length);
128 public void parseConnect(String raw)
130 byte[] b = StringUtil.getBytes(raw);
131 parseConnect(b,0,b.length);
135 public void parse(byte[] raw,int offset, int length)
138 parse2(raw,offset,length);
142 public void parseConnect(byte[] raw,int offset, int length)
163 char c=(char)(0xff&_raw[i]);
192 throw new IllegalArgumentException("No closing ']' for " + new String(_raw,offset,length,_charset));
207 _portValue=TypeUtil.parseInt(_raw, _port+1, _path-_port-1,10);
209 throw new IllegalArgumentException("No port");
214 private void parse2(byte[] raw,int offset, int length)
234 char c=(char)(0xff&_raw[i]);
237 state: switch (state)
267 state=SCHEME_OR_PATH;
275 if ((_partial||_scheme!=_authority) && c=='/')
282 else if (c==';' || c=='?' || c=='#')
298 // short cut for http and https
299 if (length>6 && c=='t')
301 if (_raw[offset+3]==':')
307 else if (_raw[offset+4]==':')
313 else if (_raw[offset+5]==':')
328 c = (char)(0xff & _raw[i]);
330 state = AUTH_OR_PATH;
411 throw new IllegalArgumentException("No closing ']' for " + new String(_raw,offset,length,_charset));
429 if (_port<=_authority)
500 throw new IllegalArgumentException("only '*'");
506 _portValue=TypeUtil.parseInt(_raw, _port+1, _path-_port-1,10);
509 public String getScheme()
511 if (_scheme==_authority)
513 int l=_authority-_scheme;
515 _raw[_scheme]=='h' &&
516 _raw[_scheme+1]=='t' &&
517 _raw[_scheme+2]=='t' &&
518 _raw[_scheme+3]=='p' )
519 return HttpScheme.HTTP.asString();
521 _raw[_scheme]=='h' &&
522 _raw[_scheme+1]=='t' &&
523 _raw[_scheme+2]=='t' &&
524 _raw[_scheme+3]=='p' &&
525 _raw[_scheme+4]=='s' )
526 return HttpScheme.HTTPS.asString();
528 return new String(_raw,_scheme,_authority-_scheme-1,_charset);
531 public String getAuthority()
533 if (_authority==_path)
535 return new String(_raw,_authority,_path-_authority,_charset);
538 public String getHost()
542 if (_raw[_host]=='[')
543 return new String(_raw,_host+1,_port-_host-2,_charset);
544 return new String(_raw,_host,_port-_host,_charset);
552 public String getPath()
556 return new String(_raw,_path,_param-_path,_charset);
559 public String getDecodedPath()
564 Utf8StringBuilder utf8b=null;
566 for (int i=_path;i<_param;i++)
574 utf8b=new Utf8StringBuilder();
575 utf8b.append(_raw,_path,i-_path);
579 throw new IllegalArgumentException("Bad % encoding: "+this);
583 throw new IllegalArgumentException("Bad %u encoding: "+this);
586 String unicode = new String(Character.toChars(TypeUtil.parseInt(_raw,i+2,4,16)));
587 utf8b.getStringBuilder().append(unicode);
592 throw new RuntimeException(e);
597 b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
603 else if (utf8b!=null)
610 return StringUtil.toUTF8String(_raw, _path, _param-_path);
611 return utf8b.toString();
614 public String getDecodedPath(String encoding)
616 return getDecodedPath(Charset.forName(encoding));
619 public String getDecodedPath(Charset encoding)
624 int length = _param-_path;
628 for (int i=_path;i<_param;i++)
636 bytes=new byte[length];
637 System.arraycopy(_raw,_path,bytes,0,n);
641 throw new IllegalArgumentException("Bad % encoding: "+this);
645 throw new IllegalArgumentException("Bad %u encoding: "+this);
649 String unicode = new String(Character.toChars(TypeUtil.parseInt(_raw,i+2,4,16)));
650 byte[] encoded = unicode.getBytes(encoding);
651 System.arraycopy(encoded,0,bytes,n,encoded.length);
657 throw new RuntimeException(e);
662 b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
668 else if (bytes==null)
679 return new String(_raw,_path,_param-_path,encoding);
681 return new String(bytes,0,n,encoding);
684 public String getPathAndParam()
688 return new String(_raw,_path,_query-_path,_charset);
691 public String getCompletePath()
695 return new String(_raw,_path,_end-_path,_charset);
698 public String getParam()
702 return new String(_raw,_param+1,_query-_param-1,_charset);
705 public String getQuery()
707 if (_query==_fragment)
709 return new String(_raw,_query+1,_fragment-_query-1,_charset);
712 public String getQuery(String encoding)
714 if (_query==_fragment)
716 return StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding);
719 public boolean hasQuery()
721 return (_fragment>_query);
724 public String getFragment()
728 return new String(_raw,_fragment+1,_end-_fragment-1,_charset);
731 public void decodeQueryTo(MultiMap<String> parameters)
733 if (_query==_fragment)
735 if (_charset.equals(StandardCharsets.UTF_8))
736 UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
738 UrlEncoded.decodeTo(new String(_raw,_query+1,_fragment-_query-1,_charset),parameters,_charset,-1);
741 public void decodeQueryTo(MultiMap<String> parameters, String encoding) throws UnsupportedEncodingException
743 if (_query==_fragment)
746 if (encoding==null || StringUtil.isUTF8(encoding))
747 UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
749 UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding,-1);
752 public void decodeQueryTo(MultiMap<String> parameters, Charset encoding) throws UnsupportedEncodingException
754 if (_query==_fragment)
757 if (encoding==null || StandardCharsets.UTF_8.equals(encoding))
758 UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
760 UrlEncoded.decodeTo(new String(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding,-1);
765 _scheme=_authority=_host=_port=_path=_param=_query=_fragment=_end=0;
772 public String toString()
774 if (_rawString==null)
775 _rawString=new String(_raw,_scheme,_end-_scheme,_charset);
779 public void writeTo(Utf8StringBuilder buf)
781 buf.append(_raw,_scheme,_end-_scheme);