2 // ========================================================================
3 // Copyright (c) 1995-2016 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 return new String(_raw,_host,_port-_host,_charset);
550 public String getPath()
554 return new String(_raw,_path,_param-_path,_charset);
557 public String getDecodedPath()
562 Utf8StringBuilder utf8b=null;
564 for (int i=_path;i<_param;i++)
572 utf8b=new Utf8StringBuilder();
573 utf8b.append(_raw,_path,i-_path);
577 throw new IllegalArgumentException("Bad % encoding: "+this);
581 throw new IllegalArgumentException("Bad %u encoding: "+this);
584 String unicode = new String(Character.toChars(TypeUtil.parseInt(_raw,i+2,4,16)));
585 utf8b.getStringBuilder().append(unicode);
590 throw new RuntimeException(e);
595 b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
601 else if (utf8b!=null)
608 return StringUtil.toUTF8String(_raw, _path, _param-_path);
609 return utf8b.toString();
612 public String getDecodedPath(String encoding)
614 return getDecodedPath(Charset.forName(encoding));
617 public String getDecodedPath(Charset encoding)
622 int length = _param-_path;
626 for (int i=_path;i<_param;i++)
634 bytes=new byte[length];
635 System.arraycopy(_raw,_path,bytes,0,n);
639 throw new IllegalArgumentException("Bad % encoding: "+this);
643 throw new IllegalArgumentException("Bad %u encoding: "+this);
647 String unicode = new String(Character.toChars(TypeUtil.parseInt(_raw,i+2,4,16)));
648 byte[] encoded = unicode.getBytes(encoding);
649 System.arraycopy(encoded,0,bytes,n,encoded.length);
655 throw new RuntimeException(e);
660 b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
666 else if (bytes==null)
677 return new String(_raw,_path,_param-_path,encoding);
679 return new String(bytes,0,n,encoding);
682 public String getPathAndParam()
686 return new String(_raw,_path,_query-_path,_charset);
689 public String getCompletePath()
693 return new String(_raw,_path,_end-_path,_charset);
696 public String getParam()
700 return new String(_raw,_param+1,_query-_param-1,_charset);
703 public String getQuery()
705 if (_query==_fragment)
707 return new String(_raw,_query+1,_fragment-_query-1,_charset);
710 public String getQuery(String encoding)
712 if (_query==_fragment)
714 return StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding);
717 public boolean hasQuery()
719 return (_fragment>_query);
722 public String getFragment()
726 return new String(_raw,_fragment+1,_end-_fragment-1,_charset);
729 public void decodeQueryTo(MultiMap<String> parameters)
731 if (_query==_fragment)
733 if (_charset.equals(StandardCharsets.UTF_8))
734 UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
736 UrlEncoded.decodeTo(new String(_raw,_query+1,_fragment-_query-1,_charset),parameters,_charset,-1);
739 public void decodeQueryTo(MultiMap<String> parameters, String encoding) throws UnsupportedEncodingException
741 if (_query==_fragment)
744 if (encoding==null || StringUtil.isUTF8(encoding))
745 UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
747 UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding,-1);
750 public void decodeQueryTo(MultiMap<String> parameters, Charset encoding) throws UnsupportedEncodingException
752 if (_query==_fragment)
755 if (encoding==null || StandardCharsets.UTF_8.equals(encoding))
756 UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
758 UrlEncoded.decodeTo(new String(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding,-1);
763 _scheme=_authority=_host=_port=_path=_param=_query=_fragment=_end=0;
770 public String toString()
772 if (_rawString==null)
773 _rawString=new String(_raw,_scheme,_end-_scheme,_charset);
777 public void writeTo(Utf8StringBuilder buf)
779 buf.append(_raw,_scheme,_end-_scheme);