Source file src/pkg/net/url/url.go
1
2
3
4
5
6 package url
7
8
9
10
11
12
13 import (
14 "errors"
15 "fmt"
16 "sort"
17 "strconv"
18 "strings"
19 )
20
21
22 type Error struct {
23 Op string
24 URL string
25 Err error
26 }
27
28 func (e *Error) Unwrap() error { return e.Err }
29 func (e *Error) Error() string { return e.Op + " " + e.URL + ": " + e.Err.Error() }
30
31 func (e *Error) Timeout() bool {
32 t, ok := e.Err.(interface {
33 Timeout() bool
34 })
35 return ok && t.Timeout()
36 }
37
38 func (e *Error) Temporary() bool {
39 t, ok := e.Err.(interface {
40 Temporary() bool
41 })
42 return ok && t.Temporary()
43 }
44
45 func ishex(c byte) bool {
46 switch {
47 case '0' <= c && c <= '9':
48 return true
49 case 'a' <= c && c <= 'f':
50 return true
51 case 'A' <= c && c <= 'F':
52 return true
53 }
54 return false
55 }
56
57 func unhex(c byte) byte {
58 switch {
59 case '0' <= c && c <= '9':
60 return c - '0'
61 case 'a' <= c && c <= 'f':
62 return c - 'a' + 10
63 case 'A' <= c && c <= 'F':
64 return c - 'A' + 10
65 }
66 return 0
67 }
68
69 type encoding int
70
71 const (
72 encodePath encoding = 1 + iota
73 encodePathSegment
74 encodeHost
75 encodeZone
76 encodeUserPassword
77 encodeQueryComponent
78 encodeFragment
79 )
80
81 type EscapeError string
82
83 func (e EscapeError) Error() string {
84 return "invalid URL escape " + strconv.Quote(string(e))
85 }
86
87 type InvalidHostError string
88
89 func (e InvalidHostError) Error() string {
90 return "invalid character " + strconv.Quote(string(e)) + " in host name"
91 }
92
93
94
95
96
97
98 func shouldEscape(c byte, mode encoding) bool {
99
100 if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
101 return false
102 }
103
104 if mode == encodeHost || mode == encodeZone {
105
106
107
108
109
110
111
112
113
114 switch c {
115 case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']', '<', '>', '"':
116 return false
117 }
118 }
119
120 switch c {
121 case '-', '_', '.', '~':
122 return false
123
124 case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@':
125
126
127 switch mode {
128 case encodePath:
129
130
131
132
133 return c == '?'
134
135 case encodePathSegment:
136
137
138 return c == '/' || c == ';' || c == ',' || c == '?'
139
140 case encodeUserPassword:
141
142
143
144
145 return c == '@' || c == '/' || c == '?' || c == ':'
146
147 case encodeQueryComponent:
148
149 return true
150
151 case encodeFragment:
152
153
154 return false
155 }
156 }
157
158 if mode == encodeFragment {
159
160
161
162
163
164
165 switch c {
166 case '!', '(', ')', '*':
167 return false
168 }
169 }
170
171
172 return true
173 }
174
175
176
177
178
179
180 func QueryUnescape(s string) (string, error) {
181 return unescape(s, encodeQueryComponent)
182 }
183
184
185
186
187
188
189
190
191 func PathUnescape(s string) (string, error) {
192 return unescape(s, encodePathSegment)
193 }
194
195
196
197 func unescape(s string, mode encoding) (string, error) {
198
199 n := 0
200 hasPlus := false
201 for i := 0; i < len(s); {
202 switch s[i] {
203 case '%':
204 n++
205 if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
206 s = s[i:]
207 if len(s) > 3 {
208 s = s[:3]
209 }
210 return "", EscapeError(s)
211 }
212
213
214
215
216
217
218 if mode == encodeHost && unhex(s[i+1]) < 8 && s[i:i+3] != "%25" {
219 return "", EscapeError(s[i : i+3])
220 }
221 if mode == encodeZone {
222
223
224
225
226
227
228
229 v := unhex(s[i+1])<<4 | unhex(s[i+2])
230 if s[i:i+3] != "%25" && v != ' ' && shouldEscape(v, encodeHost) {
231 return "", EscapeError(s[i : i+3])
232 }
233 }
234 i += 3
235 case '+':
236 hasPlus = mode == encodeQueryComponent
237 i++
238 default:
239 if (mode == encodeHost || mode == encodeZone) && s[i] < 0x80 && shouldEscape(s[i], mode) {
240 return "", InvalidHostError(s[i : i+1])
241 }
242 i++
243 }
244 }
245
246 if n == 0 && !hasPlus {
247 return s, nil
248 }
249
250 var t strings.Builder
251 t.Grow(len(s) - 2*n)
252 for i := 0; i < len(s); i++ {
253 switch s[i] {
254 case '%':
255 t.WriteByte(unhex(s[i+1])<<4 | unhex(s[i+2]))
256 i += 2
257 case '+':
258 if mode == encodeQueryComponent {
259 t.WriteByte(' ')
260 } else {
261 t.WriteByte('+')
262 }
263 default:
264 t.WriteByte(s[i])
265 }
266 }
267 return t.String(), nil
268 }
269
270
271
272 func QueryEscape(s string) string {
273 return escape(s, encodeQueryComponent)
274 }
275
276
277
278 func PathEscape(s string) string {
279 return escape(s, encodePathSegment)
280 }
281
282 func escape(s string, mode encoding) string {
283 spaceCount, hexCount := 0, 0
284 for i := 0; i < len(s); i++ {
285 c := s[i]
286 if shouldEscape(c, mode) {
287 if c == ' ' && mode == encodeQueryComponent {
288 spaceCount++
289 } else {
290 hexCount++
291 }
292 }
293 }
294
295 if spaceCount == 0 && hexCount == 0 {
296 return s
297 }
298
299 var buf [64]byte
300 var t []byte
301
302 required := len(s) + 2*hexCount
303 if required <= len(buf) {
304 t = buf[:required]
305 } else {
306 t = make([]byte, required)
307 }
308
309 if hexCount == 0 {
310 copy(t, s)
311 for i := 0; i < len(s); i++ {
312 if s[i] == ' ' {
313 t[i] = '+'
314 }
315 }
316 return string(t)
317 }
318
319 j := 0
320 for i := 0; i < len(s); i++ {
321 switch c := s[i]; {
322 case c == ' ' && mode == encodeQueryComponent:
323 t[j] = '+'
324 j++
325 case shouldEscape(c, mode):
326 t[j] = '%'
327 t[j+1] = "0123456789ABCDEF"[c>>4]
328 t[j+2] = "0123456789ABCDEF"[c&15]
329 j += 3
330 default:
331 t[j] = s[i]
332 j++
333 }
334 }
335 return string(t)
336 }
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356 type URL struct {
357 Scheme string
358 Opaque string
359 User *Userinfo
360 Host string
361 Path string
362 RawPath string
363 ForceQuery bool
364 RawQuery string
365 Fragment string
366 }
367
368
369
370 func User(username string) *Userinfo {
371 return &Userinfo{username, "", false}
372 }
373
374
375
376
377
378
379
380
381
382 func UserPassword(username, password string) *Userinfo {
383 return &Userinfo{username, password, true}
384 }
385
386
387
388
389
390 type Userinfo struct {
391 username string
392 password string
393 passwordSet bool
394 }
395
396
397 func (u *Userinfo) Username() string {
398 if u == nil {
399 return ""
400 }
401 return u.username
402 }
403
404
405 func (u *Userinfo) Password() (string, bool) {
406 if u == nil {
407 return "", false
408 }
409 return u.password, u.passwordSet
410 }
411
412
413
414 func (u *Userinfo) String() string {
415 if u == nil {
416 return ""
417 }
418 s := escape(u.username, encodeUserPassword)
419 if u.passwordSet {
420 s += ":" + escape(u.password, encodeUserPassword)
421 }
422 return s
423 }
424
425
426
427
428 func getscheme(rawurl string) (scheme, path string, err error) {
429 for i := 0; i < len(rawurl); i++ {
430 c := rawurl[i]
431 switch {
432 case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
433
434 case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
435 if i == 0 {
436 return "", rawurl, nil
437 }
438 case c == ':':
439 if i == 0 {
440 return "", "", errors.New("missing protocol scheme")
441 }
442 return rawurl[:i], rawurl[i+1:], nil
443 default:
444
445
446 return "", rawurl, nil
447 }
448 }
449 return "", rawurl, nil
450 }
451
452
453
454
455 func split(s string, c string, cutc bool) (string, string) {
456 i := strings.Index(s, c)
457 if i < 0 {
458 return s, ""
459 }
460 if cutc {
461 return s[:i], s[i+len(c):]
462 }
463 return s[:i], s[i:]
464 }
465
466
467
468
469
470
471
472 func Parse(rawurl string) (*URL, error) {
473
474 u, frag := split(rawurl, "#", true)
475 url, err := parse(u, false)
476 if err != nil {
477 return nil, &Error{"parse", u, err}
478 }
479 if frag == "" {
480 return url, nil
481 }
482 if url.Fragment, err = unescape(frag, encodeFragment); err != nil {
483 return nil, &Error{"parse", rawurl, err}
484 }
485 return url, nil
486 }
487
488
489
490
491
492
493 func ParseRequestURI(rawurl string) (*URL, error) {
494 url, err := parse(rawurl, true)
495 if err != nil {
496 return nil, &Error{"parse", rawurl, err}
497 }
498 return url, nil
499 }
500
501
502
503
504
505 func parse(rawurl string, viaRequest bool) (*URL, error) {
506 var rest string
507 var err error
508
509 if stringContainsCTLByte(rawurl) {
510 return nil, errors.New("net/url: invalid control character in URL")
511 }
512
513 if rawurl == "" && viaRequest {
514 return nil, errors.New("empty url")
515 }
516 url := new(URL)
517
518 if rawurl == "*" {
519 url.Path = "*"
520 return url, nil
521 }
522
523
524
525 if url.Scheme, rest, err = getscheme(rawurl); err != nil {
526 return nil, err
527 }
528 url.Scheme = strings.ToLower(url.Scheme)
529
530 if strings.HasSuffix(rest, "?") && strings.Count(rest, "?") == 1 {
531 url.ForceQuery = true
532 rest = rest[:len(rest)-1]
533 } else {
534 rest, url.RawQuery = split(rest, "?", true)
535 }
536
537 if !strings.HasPrefix(rest, "/") {
538 if url.Scheme != "" {
539
540 url.Opaque = rest
541 return url, nil
542 }
543 if viaRequest {
544 return nil, errors.New("invalid URI for request")
545 }
546
547
548
549
550
551
552
553 colon := strings.Index(rest, ":")
554 slash := strings.Index(rest, "/")
555 if colon >= 0 && (slash < 0 || colon < slash) {
556
557 return nil, errors.New("first path segment in URL cannot contain colon")
558 }
559 }
560
561 if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") {
562 var authority string
563 authority, rest = split(rest[2:], "/", false)
564 url.User, url.Host, err = parseAuthority(authority)
565 if err != nil {
566 return nil, err
567 }
568 }
569
570
571
572
573 if err := url.setPath(rest); err != nil {
574 return nil, err
575 }
576 return url, nil
577 }
578
579 func parseAuthority(authority string) (user *Userinfo, host string, err error) {
580 i := strings.LastIndex(authority, "@")
581 if i < 0 {
582 host, err = parseHost(authority)
583 } else {
584 host, err = parseHost(authority[i+1:])
585 }
586 if err != nil {
587 return nil, "", err
588 }
589 if i < 0 {
590 return nil, host, nil
591 }
592 userinfo := authority[:i]
593 if !validUserinfo(userinfo) {
594 return nil, "", errors.New("net/url: invalid userinfo")
595 }
596 if !strings.Contains(userinfo, ":") {
597 if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil {
598 return nil, "", err
599 }
600 user = User(userinfo)
601 } else {
602 username, password := split(userinfo, ":", true)
603 if username, err = unescape(username, encodeUserPassword); err != nil {
604 return nil, "", err
605 }
606 if password, err = unescape(password, encodeUserPassword); err != nil {
607 return nil, "", err
608 }
609 user = UserPassword(username, password)
610 }
611 return user, host, nil
612 }
613
614
615
616 func parseHost(host string) (string, error) {
617 if strings.HasPrefix(host, "[") {
618
619
620 i := strings.LastIndex(host, "]")
621 if i < 0 {
622 return "", errors.New("missing ']' in host")
623 }
624 colonPort := host[i+1:]
625 if !validOptionalPort(colonPort) {
626 return "", fmt.Errorf("invalid port %q after host", colonPort)
627 }
628
629
630
631
632
633
634
635 zone := strings.Index(host[:i], "%25")
636 if zone >= 0 {
637 host1, err := unescape(host[:zone], encodeHost)
638 if err != nil {
639 return "", err
640 }
641 host2, err := unescape(host[zone:i], encodeZone)
642 if err != nil {
643 return "", err
644 }
645 host3, err := unescape(host[i:], encodeHost)
646 if err != nil {
647 return "", err
648 }
649 return host1 + host2 + host3, nil
650 }
651 } else if i := strings.LastIndex(host, ":"); i != -1 {
652 colonPort := host[i:]
653 if !validOptionalPort(colonPort) {
654 return "", fmt.Errorf("invalid port %q after host", colonPort)
655 }
656 }
657
658 var err error
659 if host, err = unescape(host, encodeHost); err != nil {
660 return "", err
661 }
662 return host, nil
663 }
664
665
666
667
668
669
670
671
672
673 func (u *URL) setPath(p string) error {
674 path, err := unescape(p, encodePath)
675 if err != nil {
676 return err
677 }
678 u.Path = path
679 if escp := escape(path, encodePath); p == escp {
680
681 u.RawPath = ""
682 } else {
683 u.RawPath = p
684 }
685 return nil
686 }
687
688
689
690
691
692
693
694
695
696
697 func (u *URL) EscapedPath() string {
698 if u.RawPath != "" && validEncodedPath(u.RawPath) {
699 p, err := unescape(u.RawPath, encodePath)
700 if err == nil && p == u.Path {
701 return u.RawPath
702 }
703 }
704 if u.Path == "*" {
705 return "*"
706 }
707 return escape(u.Path, encodePath)
708 }
709
710
711
712 func validEncodedPath(s string) bool {
713 for i := 0; i < len(s); i++ {
714
715
716
717
718
719 switch s[i] {
720 case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '@':
721
722 case '[', ']':
723
724 case '%':
725
726 default:
727 if shouldEscape(s[i], encodePath) {
728 return false
729 }
730 }
731 }
732 return true
733 }
734
735
736
737 func validOptionalPort(port string) bool {
738 if port == "" {
739 return true
740 }
741 if port[0] != ':' {
742 return false
743 }
744 for _, b := range port[1:] {
745 if b < '0' || b > '9' {
746 return false
747 }
748 }
749 return true
750 }
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773 func (u *URL) String() string {
774 var buf strings.Builder
775 if u.Scheme != "" {
776 buf.WriteString(u.Scheme)
777 buf.WriteByte(':')
778 }
779 if u.Opaque != "" {
780 buf.WriteString(u.Opaque)
781 } else {
782 if u.Scheme != "" || u.Host != "" || u.User != nil {
783 if u.Host != "" || u.Path != "" || u.User != nil {
784 buf.WriteString("//")
785 }
786 if ui := u.User; ui != nil {
787 buf.WriteString(ui.String())
788 buf.WriteByte('@')
789 }
790 if h := u.Host; h != "" {
791 buf.WriteString(escape(h, encodeHost))
792 }
793 }
794 path := u.EscapedPath()
795 if path != "" && path[0] != '/' && u.Host != "" {
796 buf.WriteByte('/')
797 }
798 if buf.Len() == 0 {
799
800
801
802
803
804
805 if i := strings.IndexByte(path, ':'); i > -1 && strings.IndexByte(path[:i], '/') == -1 {
806 buf.WriteString("./")
807 }
808 }
809 buf.WriteString(path)
810 }
811 if u.ForceQuery || u.RawQuery != "" {
812 buf.WriteByte('?')
813 buf.WriteString(u.RawQuery)
814 }
815 if u.Fragment != "" {
816 buf.WriteByte('#')
817 buf.WriteString(escape(u.Fragment, encodeFragment))
818 }
819 return buf.String()
820 }
821
822
823
824
825
826 type Values map[string][]string
827
828
829
830
831
832 func (v Values) Get(key string) string {
833 if v == nil {
834 return ""
835 }
836 vs := v[key]
837 if len(vs) == 0 {
838 return ""
839 }
840 return vs[0]
841 }
842
843
844
845 func (v Values) Set(key, value string) {
846 v[key] = []string{value}
847 }
848
849
850
851 func (v Values) Add(key, value string) {
852 v[key] = append(v[key], value)
853 }
854
855
856 func (v Values) Del(key string) {
857 delete(v, key)
858 }
859
860
861
862
863
864
865
866
867
868
869 func ParseQuery(query string) (Values, error) {
870 m := make(Values)
871 err := parseQuery(m, query)
872 return m, err
873 }
874
875 func parseQuery(m Values, query string) (err error) {
876 for query != "" {
877 key := query
878 if i := strings.IndexAny(key, "&;"); i >= 0 {
879 key, query = key[:i], key[i+1:]
880 } else {
881 query = ""
882 }
883 if key == "" {
884 continue
885 }
886 value := ""
887 if i := strings.Index(key, "="); i >= 0 {
888 key, value = key[:i], key[i+1:]
889 }
890 key, err1 := QueryUnescape(key)
891 if err1 != nil {
892 if err == nil {
893 err = err1
894 }
895 continue
896 }
897 value, err1 = QueryUnescape(value)
898 if err1 != nil {
899 if err == nil {
900 err = err1
901 }
902 continue
903 }
904 m[key] = append(m[key], value)
905 }
906 return err
907 }
908
909
910
911 func (v Values) Encode() string {
912 if v == nil {
913 return ""
914 }
915 var buf strings.Builder
916 keys := make([]string, 0, len(v))
917 for k := range v {
918 keys = append(keys, k)
919 }
920 sort.Strings(keys)
921 for _, k := range keys {
922 vs := v[k]
923 keyEscaped := QueryEscape(k)
924 for _, v := range vs {
925 if buf.Len() > 0 {
926 buf.WriteByte('&')
927 }
928 buf.WriteString(keyEscaped)
929 buf.WriteByte('=')
930 buf.WriteString(QueryEscape(v))
931 }
932 }
933 return buf.String()
934 }
935
936
937
938 func resolvePath(base, ref string) string {
939 var full string
940 if ref == "" {
941 full = base
942 } else if ref[0] != '/' {
943 i := strings.LastIndex(base, "/")
944 full = base[:i+1] + ref
945 } else {
946 full = ref
947 }
948 if full == "" {
949 return ""
950 }
951 var dst []string
952 src := strings.Split(full, "/")
953 for _, elem := range src {
954 switch elem {
955 case ".":
956
957 case "..":
958 if len(dst) > 0 {
959 dst = dst[:len(dst)-1]
960 }
961 default:
962 dst = append(dst, elem)
963 }
964 }
965 if last := src[len(src)-1]; last == "." || last == ".." {
966
967 dst = append(dst, "")
968 }
969 return "/" + strings.TrimPrefix(strings.Join(dst, "/"), "/")
970 }
971
972
973
974 func (u *URL) IsAbs() bool {
975 return u.Scheme != ""
976 }
977
978
979
980
981 func (u *URL) Parse(ref string) (*URL, error) {
982 refurl, err := Parse(ref)
983 if err != nil {
984 return nil, err
985 }
986 return u.ResolveReference(refurl), nil
987 }
988
989
990
991
992
993
994
995 func (u *URL) ResolveReference(ref *URL) *URL {
996 url := *ref
997 if ref.Scheme == "" {
998 url.Scheme = u.Scheme
999 }
1000 if ref.Scheme != "" || ref.Host != "" || ref.User != nil {
1001
1002
1003
1004 url.setPath(resolvePath(ref.EscapedPath(), ""))
1005 return &url
1006 }
1007 if ref.Opaque != "" {
1008 url.User = nil
1009 url.Host = ""
1010 url.Path = ""
1011 return &url
1012 }
1013 if ref.Path == "" && ref.RawQuery == "" {
1014 url.RawQuery = u.RawQuery
1015 if ref.Fragment == "" {
1016 url.Fragment = u.Fragment
1017 }
1018 }
1019
1020 url.Host = u.Host
1021 url.User = u.User
1022 url.setPath(resolvePath(u.EscapedPath(), ref.EscapedPath()))
1023 return &url
1024 }
1025
1026
1027
1028
1029 func (u *URL) Query() Values {
1030 v, _ := ParseQuery(u.RawQuery)
1031 return v
1032 }
1033
1034
1035
1036 func (u *URL) RequestURI() string {
1037 result := u.Opaque
1038 if result == "" {
1039 result = u.EscapedPath()
1040 if result == "" {
1041 result = "/"
1042 }
1043 } else {
1044 if strings.HasPrefix(result, "//") {
1045 result = u.Scheme + ":" + result
1046 }
1047 }
1048 if u.ForceQuery || u.RawQuery != "" {
1049 result += "?" + u.RawQuery
1050 }
1051 return result
1052 }
1053
1054
1055
1056
1057
1058 func (u *URL) Hostname() string {
1059 host, _ := splitHostPort(u.Host)
1060 return host
1061 }
1062
1063
1064
1065
1066 func (u *URL) Port() string {
1067 _, port := splitHostPort(u.Host)
1068 return port
1069 }
1070
1071
1072
1073
1074 func splitHostPort(hostport string) (host, port string) {
1075 host = hostport
1076
1077 colon := strings.LastIndexByte(host, ':')
1078 if colon != -1 && validOptionalPort(host[colon:]) {
1079 host, port = host[:colon], host[colon+1:]
1080 }
1081
1082 if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
1083 host = host[1 : len(host)-1]
1084 }
1085
1086 return
1087 }
1088
1089
1090
1091
1092 func (u *URL) MarshalBinary() (text []byte, err error) {
1093 return []byte(u.String()), nil
1094 }
1095
1096 func (u *URL) UnmarshalBinary(text []byte) error {
1097 u1, err := Parse(string(text))
1098 if err != nil {
1099 return err
1100 }
1101 *u = *u1
1102 return nil
1103 }
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113 func validUserinfo(s string) bool {
1114 for _, r := range s {
1115 if 'A' <= r && r <= 'Z' {
1116 continue
1117 }
1118 if 'a' <= r && r <= 'z' {
1119 continue
1120 }
1121 if '0' <= r && r <= '9' {
1122 continue
1123 }
1124 switch r {
1125 case '-', '.', '_', ':', '~', '!', '$', '&', '\'',
1126 '(', ')', '*', '+', ',', ';', '=', '%', '@':
1127 continue
1128 default:
1129 return false
1130 }
1131 }
1132 return true
1133 }
1134
1135
1136 func stringContainsCTLByte(s string) bool {
1137 for i := 0; i < len(s); i++ {
1138 b := s[i]
1139 if b < ' ' || b == 0x7f {
1140 return true
1141 }
1142 }
1143 return false
1144 }
1145
View as plain text