1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.http;
20
21 import java.io.UnsupportedEncodingException;
22 import java.net.URI;
23 import java.net.URISyntaxException;
24 import java.nio.charset.Charset;
25 import java.nio.charset.StandardCharsets;
26
27 import org.eclipse.jetty.util.MultiMap;
28 import org.eclipse.jetty.util.TypeUtil;
29 import org.eclipse.jetty.util.URIUtil;
30 import org.eclipse.jetty.util.UrlEncoded;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public class HttpURI
53 {
54 private enum State {
55 START,
56 HOST_OR_PATH,
57 SCHEME_OR_PATH,
58 HOST,
59 IPV6,
60 PORT,
61 PATH,
62 PARAM,
63 QUERY,
64 FRAGMENT,
65 ASTERISK};
66
67 private String _scheme;
68 private String _user;
69 private String _host;
70 private int _port;
71 private String _path;
72 private String _param;
73 private String _query;
74 private String _fragment;
75
76 String _uri;
77 String _decodedPath;
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 public static HttpURI createHttpURI(String scheme, String host, int port, String path, String param, String query, String fragment)
93 {
94 if (port==80 && HttpScheme.HTTP.is(scheme))
95 port=0;
96 if (port==443 && HttpScheme.HTTPS.is(scheme))
97 port=0;
98 return new HttpURI(scheme,host,port,path,param,query,fragment);
99 }
100
101
102 public HttpURI()
103 {
104 }
105
106
107 public HttpURI(String scheme, String host, int port, String path, String param, String query, String fragment)
108 {
109 _scheme = scheme;
110 _host = host;
111 _port = port;
112 _path = path;
113 _param = param;
114 _query = query;
115 _fragment = fragment;
116 }
117
118
119 public HttpURI(HttpURI uri)
120 {
121 this(uri._scheme,uri._host,uri._port,uri._path,uri._param,uri._query,uri._fragment);
122 }
123
124
125 public HttpURI(String uri)
126 {
127 _port=-1;
128 parse(State.START,uri,0,uri.length());
129 }
130
131
132 public HttpURI(URI uri)
133 {
134 _uri=null;
135
136 _scheme=uri.getScheme();
137 _host=uri.getHost();
138 if (_host==null && uri.getRawSchemeSpecificPart().startsWith("//"))
139 _host="";
140 _port=uri.getPort();
141 _user = uri.getUserInfo();
142 _path=uri.getRawPath();
143
144 _decodedPath = uri.getPath();
145 if (_decodedPath != null)
146 {
147 int p = _decodedPath.lastIndexOf(';');
148 if (p >= 0)
149 _param = _decodedPath.substring(p + 1);
150 }
151 _query=uri.getRawQuery();
152 _fragment=uri.getFragment();
153
154 _decodedPath=null;
155 }
156
157
158 public HttpURI(String scheme, String host, int port, String pathQuery)
159 {
160 _uri=null;
161
162 _scheme=scheme;
163 _host=host;
164 _port=port;
165
166 parse(State.PATH,pathQuery,0,pathQuery.length());
167
168 }
169
170
171 public void parse(String uri)
172 {
173 clear();
174 _uri=uri;
175 parse(State.START,uri,0,uri.length());
176 }
177
178
179 public void parseConnect(String uri)
180 {
181 clear();
182 _uri=uri;
183 _path=uri;
184 }
185
186
187 public void parse(String uri, int offset, int length)
188 {
189 clear();
190 int end=offset+length;
191 _uri=uri.substring(offset,end);
192 parse(State.START,uri,offset,end);
193 }
194
195
196 private void parse(State state, final String uri, final int offset, final int end)
197 {
198 boolean encoded=false;
199 int mark=offset;
200 int path_mark=0;
201
202 for (int i=offset; i<end; i++)
203 {
204 char c=uri.charAt(i);
205
206 switch (state)
207 {
208 case START:
209 {
210 switch(c)
211 {
212 case '/':
213 mark = i;
214 state = State.HOST_OR_PATH;
215 break;
216 case ';':
217 mark=i+1;
218 state=State.PARAM;
219 break;
220 case '?':
221
222 _path = "";
223 mark=i+1;
224 state=State.QUERY;
225 break;
226 case '#':
227 mark=i+1;
228 state=State.FRAGMENT;
229 break;
230 case '*':
231 _path="*";
232 state=State.ASTERISK;
233 break;
234
235 default:
236 mark=i;
237 if (_scheme==null)
238 state=State.SCHEME_OR_PATH;
239 else
240 {
241 path_mark=i;
242 state=State.PATH;
243 }
244 }
245
246 continue;
247 }
248
249 case SCHEME_OR_PATH:
250 {
251 switch (c)
252 {
253 case ':':
254
255 _scheme=uri.substring(mark,i);
256
257 state=State.START;
258 break;
259
260 case '/':
261
262 state=State.PATH;
263 break;
264
265 case ';':
266
267 mark=i+1;
268 state=State.PARAM;
269 break;
270
271 case '?':
272
273 _path=uri.substring(mark,i);
274 mark=i+1;
275 state=State.QUERY;
276 break;
277
278 case '%':
279
280 encoded=true;
281 state=State.PATH;
282 break;
283
284 case '#':
285
286 _path=uri.substring(mark,i);
287 state=State.FRAGMENT;
288 break;
289 }
290 continue;
291 }
292
293 case HOST_OR_PATH:
294 {
295 switch(c)
296 {
297 case '/':
298 _host="";
299 mark=i+1;
300 state=State.HOST;
301 break;
302
303 case '@':
304 _user=uri.substring(mark,i);
305 mark=i+1;
306 break;
307
308 case ';':
309 case '?':
310 case '#':
311
312 i--;
313 path_mark=mark;
314 state=State.PATH;
315 break;
316 default:
317
318 path_mark=mark;
319 state=State.PATH;
320 }
321 continue;
322 }
323
324 case HOST:
325 {
326 switch (c)
327 {
328 case '/':
329 _host = uri.substring(mark,i);
330 path_mark=mark=i;
331 state=State.PATH;
332 break;
333 case ':':
334 if (i > mark)
335 _host=uri.substring(mark,i);
336 mark=i+1;
337 state=State.PORT;
338 break;
339 case '@':
340 _user=uri.substring(mark,i);
341 mark=i+1;
342 break;
343
344 case '[':
345 state=State.IPV6;
346 break;
347 }
348 continue;
349 }
350
351 case IPV6:
352 {
353 switch (c)
354 {
355 case '/':
356 throw new IllegalArgumentException("No closing ']' for ipv6 in " + uri);
357 case ']':
358 c = uri.charAt(++i);
359 _host=uri.substring(mark,i);
360 if (c == ':')
361 {
362 mark=i+1;
363 state=State.PORT;
364 }
365 else
366 {
367 path_mark=mark=i;
368 state=State.PATH;
369 }
370 break;
371 }
372
373 continue;
374 }
375
376 case PORT:
377 {
378 if (c=='/')
379 {
380 _port=TypeUtil.parseInt(uri,mark,i-mark,10);
381 path_mark=mark=i;
382 state=State.PATH;
383 }
384 continue;
385 }
386
387 case PATH:
388 {
389 switch (c)
390 {
391 case ';':
392 mark=i+1;
393 state=State.PARAM;
394 break;
395 case '?':
396 _path=uri.substring(path_mark,i);
397 mark=i+1;
398 state=State.QUERY;
399 break;
400 case '#':
401 _path=uri.substring(path_mark,i);
402 mark=i+1;
403 state=State.FRAGMENT;
404 break;
405 case '%':
406 encoded=true;
407 break;
408 }
409 continue;
410 }
411
412 case PARAM:
413 {
414 switch (c)
415 {
416 case '?':
417 _path=uri.substring(path_mark,i);
418 _param=uri.substring(mark,i);
419 mark=i+1;
420 state=State.QUERY;
421 break;
422 case '#':
423 _path=uri.substring(path_mark,i);
424 _param=uri.substring(mark,i);
425 mark=i+1;
426 state=State.FRAGMENT;
427 break;
428 case '/':
429 encoded=true;
430
431 state=State.PATH;
432 break;
433 case ';':
434
435 mark=i+1;
436 break;
437 }
438 continue;
439 }
440
441 case QUERY:
442 {
443 if (c=='#')
444 {
445 _query=uri.substring(mark,i);
446 mark=i+1;
447 state=State.FRAGMENT;
448 }
449 continue;
450 }
451
452 case ASTERISK:
453 {
454 throw new IllegalArgumentException("only '*'");
455 }
456
457 case FRAGMENT:
458 {
459 _fragment=uri.substring(mark,end);
460 i=end;
461 }
462 }
463 }
464
465
466 switch(state)
467 {
468 case START:
469 break;
470 case SCHEME_OR_PATH:
471 _path=uri.substring(mark,end);
472 break;
473
474 case HOST_OR_PATH:
475 _path=uri.substring(mark,end);
476 break;
477
478 case HOST:
479 if(end>mark)
480 _host=uri.substring(mark,end);
481 break;
482
483 case IPV6:
484 throw new IllegalArgumentException("No closing ']' for ipv6 in " + uri);
485
486 case PORT:
487 _port=TypeUtil.parseInt(uri,mark,end-mark,10);
488 break;
489
490 case ASTERISK:
491 break;
492
493 case FRAGMENT:
494 _fragment=uri.substring(mark,end);
495 break;
496
497 case PARAM:
498 _path=uri.substring(path_mark,end);
499 _param=uri.substring(mark,end);
500 break;
501
502 case PATH:
503 _path=uri.substring(path_mark,end);
504 break;
505
506 case QUERY:
507 _query=uri.substring(mark,end);
508 break;
509 }
510
511 if (!encoded)
512 {
513 if (_param==null)
514 _decodedPath=_path;
515 else
516 _decodedPath=_path.substring(0,_path.length()-_param.length()-1);
517 }
518 }
519
520
521 public String getScheme()
522 {
523 return _scheme;
524 }
525
526
527 public String getHost()
528 {
529
530 if (_host!=null && _host.length()==0)
531 return null;
532 return _host;
533 }
534
535
536 public int getPort()
537 {
538 return _port;
539 }
540
541
542
543
544
545
546
547 public String getPath()
548 {
549 return _path;
550 }
551
552
553 public String getDecodedPath()
554 {
555 if (_decodedPath==null && _path!=null)
556 _decodedPath=URIUtil.decodePath(_path);
557 return _decodedPath;
558 }
559
560
561 public String getParam()
562 {
563 return _param;
564 }
565
566
567 public String getQuery()
568 {
569 return _query;
570 }
571
572
573 public boolean hasQuery()
574 {
575 return _query!=null && _query.length()>0;
576 }
577
578
579 public String getFragment()
580 {
581 return _fragment;
582 }
583
584
585 public void decodeQueryTo(MultiMap<String> parameters)
586 {
587 if (_query==_fragment)
588 return;
589 UrlEncoded.decodeUtf8To(_query,parameters);
590 }
591
592
593 public void decodeQueryTo(MultiMap<String> parameters, String encoding) throws UnsupportedEncodingException
594 {
595 decodeQueryTo(parameters,Charset.forName(encoding));
596 }
597
598
599 public void decodeQueryTo(MultiMap<String> parameters, Charset encoding) throws UnsupportedEncodingException
600 {
601 if (_query==_fragment)
602 return;
603
604 if (encoding==null || StandardCharsets.UTF_8.equals(encoding))
605 UrlEncoded.decodeUtf8To(_query,parameters);
606 else
607 UrlEncoded.decodeTo(_query,parameters,encoding);
608 }
609
610
611 public void clear()
612 {
613 _uri=null;
614
615 _scheme=null;
616 _host=null;
617 _port=-1;
618 _path=null;
619 _param=null;
620 _query=null;
621 _fragment=null;
622
623 _decodedPath=null;
624 }
625
626
627 public boolean isAbsolute()
628 {
629 return _scheme!=null && _scheme.length()>0;
630 }
631
632
633 @Override
634 public String toString()
635 {
636 if (_uri==null)
637 {
638 StringBuilder out = new StringBuilder();
639
640 if (_scheme!=null)
641 out.append(_scheme).append(':');
642
643 if (_host != null)
644 {
645 out.append("//");
646 if (_user != null)
647 out.append(_user).append('@');
648 out.append(_host);
649 }
650
651 if (_port>0)
652 out.append(':').append(_port);
653
654 if (_path!=null)
655 out.append(_path);
656
657 if (_query!=null)
658 out.append('?').append(_query);
659
660 if (_fragment!=null)
661 out.append('#').append(_fragment);
662
663 if (out.length()>0)
664 _uri=out.toString();
665 else
666 _uri="";
667 }
668 return _uri;
669 }
670
671
672 public boolean equals(Object o)
673 {
674 if (o==this)
675 return true;
676 if (!(o instanceof HttpURI))
677 return false;
678 return toString().equals(o.toString());
679 }
680
681
682 public void setScheme(String scheme)
683 {
684 _scheme=scheme;
685 _uri=null;
686 }
687
688
689
690
691
692
693 public void setAuthority(String host, int port)
694 {
695 _host=host;
696 _port=port;
697 _uri=null;
698 }
699
700
701
702
703
704 public void setPath(String path)
705 {
706 _uri=null;
707 _path=path;
708 _decodedPath=null;
709 }
710
711
712 public void setPathQuery(String path)
713 {
714 _uri=null;
715 _path=null;
716 _decodedPath=null;
717 _param=null;
718 _fragment=null;
719 if (path!=null)
720 parse(State.PATH,path,0,path.length());
721 }
722
723
724 public void setQuery(String query)
725 {
726 _query=query;
727 _uri=null;
728 }
729
730
731 public URI toURI() throws URISyntaxException
732 {
733 return new URI(_scheme,null,_host,_port,_path,_query==null?null:UrlEncoded.decodeString(_query),_fragment);
734 }
735
736
737 public String getPathQuery()
738 {
739 if (_query==null)
740 return _path;
741 return _path+"?"+_query;
742 }
743
744
745 public String getAuthority()
746 {
747 if (_port>0)
748 return _host+":"+_port;
749 return _host;
750 }
751
752
753 }