OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [net/] [url/] [url.go] - Blame information for rev 760

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 747 jeremybenn
// Copyright 2009 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
 
5
// Package URL parses URLs and implements query escaping.
6
// See RFC 3986.
7
package url
8
 
9
import (
10
        "errors"
11
        "strconv"
12
        "strings"
13
)
14
 
15
// Error reports an error and the operation and URL that caused it.
16
type Error struct {
17
        Op  string
18
        URL string
19
        Err error
20
}
21
 
22
func (e *Error) Error() string { return e.Op + " " + e.URL + ": " + e.Err.Error() }
23
 
24
func ishex(c byte) bool {
25
        switch {
26
        case '0' <= c && c <= '9':
27
                return true
28
        case 'a' <= c && c <= 'f':
29
                return true
30
        case 'A' <= c && c <= 'F':
31
                return true
32
        }
33
        return false
34
}
35
 
36
func unhex(c byte) byte {
37
        switch {
38
        case '0' <= c && c <= '9':
39
                return c - '0'
40
        case 'a' <= c && c <= 'f':
41
                return c - 'a' + 10
42
        case 'A' <= c && c <= 'F':
43
                return c - 'A' + 10
44
        }
45
        return 0
46
}
47
 
48
type encoding int
49
 
50
const (
51
        encodePath encoding = 1 + iota
52
        encodeUserPassword
53
        encodeQueryComponent
54
        encodeFragment
55
)
56
 
57
type EscapeError string
58
 
59
func (e EscapeError) Error() string {
60
        return "invalid URL escape " + strconv.Quote(string(e))
61
}
62
 
63
// Return true if the specified character should be escaped when
64
// appearing in a URL string, according to RFC 2396.
65
// When 'all' is true the full range of reserved characters are matched.
66
func shouldEscape(c byte, mode encoding) bool {
67
        // RFC 2396 §2.3 Unreserved characters (alphanum)
68
        if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
69
                return false
70
        }
71
        // TODO: Update the character sets after RFC 3986.
72
        switch c {
73
        case '-', '_', '.', '!', '~', '*', '\'', '(', ')': // §2.3 Unreserved characters (mark)
74
                return false
75
 
76
        case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)
77
                // Different sections of the URL allow a few of
78
                // the reserved characters to appear unescaped.
79
                switch mode {
80
                case encodePath: // §3.3
81
                        // The RFC allows : @ & = + $ but saves / ; , for assigning
82
                        // meaning to individual path segments. This package
83
                        // only manipulates the path as a whole, so we allow those
84
                        // last two as well. That leaves only ? to escape.
85
                        return c == '?'
86
 
87
                case encodeUserPassword: // §3.2.2
88
                        // The RFC allows ; : & = + $ , in userinfo, so we must escape only @ and /.
89
                        // The parsing of userinfo treats : as special so we must escape that too.
90
                        return c == '@' || c == '/' || c == ':'
91
 
92
                case encodeQueryComponent: // §3.4
93
                        // The RFC reserves (so we must escape) everything.
94
                        return true
95
 
96
                case encodeFragment: // §4.1
97
                        // The RFC text is silent but the grammar allows
98
                        // everything, so escape nothing.
99
                        return false
100
                }
101
        }
102
 
103
        // Everything else must be escaped.
104
        return true
105
}
106
 
107
// QueryUnescape does the inverse transformation of QueryEscape, converting
108
// %AB into the byte 0xAB and '+' into ' ' (space). It returns an error if
109
// any % is not followed by two hexadecimal digits.
110
func QueryUnescape(s string) (string, error) {
111
        return unescape(s, encodeQueryComponent)
112
}
113
 
114
// unescape unescapes a string; the mode specifies
115
// which section of the URL string is being unescaped.
116
func unescape(s string, mode encoding) (string, error) {
117
        // Count %, check that they're well-formed.
118
        n := 0
119
        hasPlus := false
120
        for i := 0; i < len(s); {
121
                switch s[i] {
122
                case '%':
123
                        n++
124
                        if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
125
                                s = s[i:]
126
                                if len(s) > 3 {
127
                                        s = s[0:3]
128
                                }
129
                                return "", EscapeError(s)
130
                        }
131
                        i += 3
132
                case '+':
133
                        hasPlus = mode == encodeQueryComponent
134
                        i++
135
                default:
136
                        i++
137
                }
138
        }
139
 
140
        if n == 0 && !hasPlus {
141
                return s, nil
142
        }
143
 
144
        t := make([]byte, len(s)-2*n)
145
        j := 0
146
        for i := 0; i < len(s); {
147
                switch s[i] {
148
                case '%':
149
                        t[j] = unhex(s[i+1])<<4 | unhex(s[i+2])
150
                        j++
151
                        i += 3
152
                case '+':
153
                        if mode == encodeQueryComponent {
154
                                t[j] = ' '
155
                        } else {
156
                                t[j] = '+'
157
                        }
158
                        j++
159
                        i++
160
                default:
161
                        t[j] = s[i]
162
                        j++
163
                        i++
164
                }
165
        }
166
        return string(t), nil
167
}
168
 
169
// QueryEscape escapes the string so it can be safely placed
170
// inside a URL query.
171
func QueryEscape(s string) string {
172
        return escape(s, encodeQueryComponent)
173
}
174
 
175
func escape(s string, mode encoding) string {
176
        spaceCount, hexCount := 0, 0
177
        for i := 0; i < len(s); i++ {
178
                c := s[i]
179
                if shouldEscape(c, mode) {
180
                        if c == ' ' && mode == encodeQueryComponent {
181
                                spaceCount++
182
                        } else {
183
                                hexCount++
184
                        }
185
                }
186
        }
187
 
188
        if spaceCount == 0 && hexCount == 0 {
189
                return s
190
        }
191
 
192
        t := make([]byte, len(s)+2*hexCount)
193
        j := 0
194
        for i := 0; i < len(s); i++ {
195
                switch c := s[i]; {
196
                case c == ' ' && mode == encodeQueryComponent:
197
                        t[j] = '+'
198
                        j++
199
                case shouldEscape(c, mode):
200
                        t[j] = '%'
201
                        t[j+1] = "0123456789ABCDEF"[c>>4]
202
                        t[j+2] = "0123456789ABCDEF"[c&15]
203
                        j += 3
204
                default:
205
                        t[j] = s[i]
206
                        j++
207
                }
208
        }
209
        return string(t)
210
}
211
 
212
// A URL represents a parsed URL (technically, a URI reference).
213
// The general form represented is:
214
//
215
//      scheme://[userinfo@]host/path[?query][#fragment]
216
//
217
// URLs that do not start with a slash after the scheme are interpreted as:
218
//
219
//      scheme:opaque[?query][#fragment]
220
//
221
type URL struct {
222
        Scheme   string
223
        Opaque   string    // encoded opaque data
224
        User     *Userinfo // username and password information
225
        Host     string
226
        Path     string
227
        RawQuery string // encoded query values, without '?'
228
        Fragment string // fragment for references, without '#'
229
}
230
 
231
// User returns a Userinfo containing the provided username
232
// and no password set.
233
func User(username string) *Userinfo {
234
        return &Userinfo{username, "", false}
235
}
236
 
237
// UserPassword returns a Userinfo containing the provided username
238
// and password.
239
// This functionality should only be used with legacy web sites.
240
// RFC 2396 warns that interpreting Userinfo this way
241
// ``is NOT RECOMMENDED, because the passing of authentication
242
// information in clear text (such as URI) has proven to be a
243
// security risk in almost every case where it has been used.''
244
func UserPassword(username, password string) *Userinfo {
245
        return &Userinfo{username, password, true}
246
}
247
 
248
// The Userinfo type is an immutable encapsulation of username and
249
// password details for a URL. An existing Userinfo value is guaranteed
250
// to have a username set (potentially empty, as allowed by RFC 2396),
251
// and optionally a password.
252
type Userinfo struct {
253
        username    string
254
        password    string
255
        passwordSet bool
256
}
257
 
258
// Username returns the username.
259
func (u *Userinfo) Username() string {
260
        return u.username
261
}
262
 
263
// Password returns the password in case it is set, and whether it is set.
264
func (u *Userinfo) Password() (string, bool) {
265
        if u.passwordSet {
266
                return u.password, true
267
        }
268
        return "", false
269
}
270
 
271
// String returns the encoded userinfo information in the standard form
272
// of "username[:password]".
273
func (u *Userinfo) String() string {
274
        s := escape(u.username, encodeUserPassword)
275
        if u.passwordSet {
276
                s += ":" + escape(u.password, encodeUserPassword)
277
        }
278
        return s
279
}
280
 
281
// Maybe rawurl is of the form scheme:path.
282
// (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*)
283
// If so, return scheme, path; else return "", rawurl.
284
func getscheme(rawurl string) (scheme, path string, err error) {
285
        for i := 0; i < len(rawurl); i++ {
286
                c := rawurl[i]
287
                switch {
288
                case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
289
                // do nothing
290
                case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
291
                        if i == 0 {
292
                                return "", rawurl, nil
293
                        }
294
                case c == ':':
295
                        if i == 0 {
296
                                return "", "", errors.New("missing protocol scheme")
297
                        }
298
                        return rawurl[0:i], rawurl[i+1:], nil
299
                default:
300
                        // we have encountered an invalid character,
301
                        // so there is no valid scheme
302
                        return "", rawurl, nil
303
                }
304
        }
305
        return "", rawurl, nil
306
}
307
 
308
// Maybe s is of the form t c u.
309
// If so, return t, c u (or t, u if cutc == true).
310
// If not, return s, "".
311
func split(s string, c byte, cutc bool) (string, string) {
312
        for i := 0; i < len(s); i++ {
313
                if s[i] == c {
314
                        if cutc {
315
                                return s[0:i], s[i+1:]
316
                        }
317
                        return s[0:i], s[i:]
318
                }
319
        }
320
        return s, ""
321
}
322
 
323
// Parse parses rawurl into a URL structure.
324
// The string rawurl is assumed not to have a #fragment suffix.
325
// (Web browsers strip #fragment before sending the URL to a web server.)
326
// The rawurl may be relative or absolute.
327
func Parse(rawurl string) (url *URL, err error) {
328
        return parse(rawurl, false)
329
}
330
 
331
// ParseRequest parses rawurl into a URL structure.  It assumes that
332
// rawurl was received from an HTTP request, so the rawurl is interpreted
333
// only as an absolute URI or an absolute path.
334
// The string rawurl is assumed not to have a #fragment suffix.
335
// (Web browsers strip #fragment before sending the URL to a web server.)
336
func ParseRequest(rawurl string) (url *URL, err error) {
337
        return parse(rawurl, true)
338
}
339
 
340
// parse parses a URL from a string in one of two contexts.  If
341
// viaRequest is true, the URL is assumed to have arrived via an HTTP request,
342
// in which case only absolute URLs or path-absolute relative URLs are allowed.
343
// If viaRequest is false, all forms of relative URLs are allowed.
344
func parse(rawurl string, viaRequest bool) (url *URL, err error) {
345
        var rest string
346
 
347
        if rawurl == "" {
348
                err = errors.New("empty url")
349
                goto Error
350
        }
351
        url = new(URL)
352
 
353
        // Split off possible leading "http:", "mailto:", etc.
354
        // Cannot contain escaped characters.
355
        if url.Scheme, rest, err = getscheme(rawurl); err != nil {
356
                goto Error
357
        }
358
 
359
        rest, url.RawQuery = split(rest, '?', true)
360
 
361
        if !strings.HasPrefix(rest, "/") {
362
                if url.Scheme != "" {
363
                        // We consider rootless paths per RFC 3986 as opaque.
364
                        url.Opaque = rest
365
                        return url, nil
366
                }
367
                if viaRequest {
368
                        err = errors.New("invalid URI for request")
369
                        goto Error
370
                }
371
        }
372
 
373
        if (url.Scheme != "" || !viaRequest) && strings.HasPrefix(rest, "//") && !strings.HasPrefix(rest, "///") {
374
                var authority string
375
                authority, rest = split(rest[2:], '/', false)
376
                url.User, url.Host, err = parseAuthority(authority)
377
                if err != nil {
378
                        goto Error
379
                }
380
                if strings.Contains(url.Host, "%") {
381
                        err = errors.New("hexadecimal escape in host")
382
                        goto Error
383
                }
384
        }
385
        if url.Path, err = unescape(rest, encodePath); err != nil {
386
                goto Error
387
        }
388
        return url, nil
389
 
390
Error:
391
        return nil, &Error{"parse", rawurl, err}
392
}
393
 
394
func parseAuthority(authority string) (user *Userinfo, host string, err error) {
395
        if strings.Index(authority, "@") < 0 {
396
                host = authority
397
                return
398
        }
399
        userinfo, host := split(authority, '@', true)
400
        if strings.Index(userinfo, ":") < 0 {
401
                if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil {
402
                        return
403
                }
404
                user = User(userinfo)
405
        } else {
406
                username, password := split(userinfo, ':', true)
407
                if username, err = unescape(username, encodeUserPassword); err != nil {
408
                        return
409
                }
410
                if password, err = unescape(password, encodeUserPassword); err != nil {
411
                        return
412
                }
413
                user = UserPassword(username, password)
414
        }
415
        return
416
}
417
 
418
// ParseWithReference is like Parse but allows a trailing #fragment.
419
func ParseWithReference(rawurlref string) (url *URL, err error) {
420
        // Cut off #frag
421
        rawurl, frag := split(rawurlref, '#', true)
422
        if url, err = Parse(rawurl); err != nil {
423
                return nil, err
424
        }
425
        if frag == "" {
426
                return url, nil
427
        }
428
        if url.Fragment, err = unescape(frag, encodeFragment); err != nil {
429
                return nil, &Error{"parse", rawurlref, err}
430
        }
431
        return url, nil
432
}
433
 
434
// String reassembles the URL into a valid URL string.
435
func (u *URL) String() string {
436
        // TODO: Rewrite to use bytes.Buffer
437
        result := ""
438
        if u.Scheme != "" {
439
                result += u.Scheme + ":"
440
        }
441
        if u.Opaque != "" {
442
                result += u.Opaque
443
        } else {
444
                if u.Host != "" || u.User != nil {
445
                        result += "//"
446
                        if u := u.User; u != nil {
447
                                result += u.String() + "@"
448
                        }
449
                        result += u.Host
450
                }
451
                result += escape(u.Path, encodePath)
452
        }
453
        if u.RawQuery != "" {
454
                result += "?" + u.RawQuery
455
        }
456
        if u.Fragment != "" {
457
                result += "#" + escape(u.Fragment, encodeFragment)
458
        }
459
        return result
460
}
461
 
462
// Values maps a string key to a list of values.
463
// It is typically used for query parameters and form values.
464
// Unlike in the http.Header map, the keys in a Values map
465
// are case-sensitive.
466
type Values map[string][]string
467
 
468
// Get gets the first value associated with the given key.
469
// If there are no values associated with the key, Get returns
470
// the empty string. To access multiple values, use the map
471
// directly.
472
func (v Values) Get(key string) string {
473
        if v == nil {
474
                return ""
475
        }
476
        vs, ok := v[key]
477
        if !ok || len(vs) == 0 {
478
                return ""
479
        }
480
        return vs[0]
481
}
482
 
483
// Set sets the key to value. It replaces any existing
484
// values.
485
func (v Values) Set(key, value string) {
486
        v[key] = []string{value}
487
}
488
 
489
// Add adds the key to value. It appends to any existing
490
// values associated with key.
491
func (v Values) Add(key, value string) {
492
        v[key] = append(v[key], value)
493
}
494
 
495
// Del deletes the values associated with key.
496
func (v Values) Del(key string) {
497
        delete(v, key)
498
}
499
 
500
// ParseQuery parses the URL-encoded query string and returns
501
// a map listing the values specified for each key.
502
// ParseQuery always returns a non-nil map containing all the
503
// valid query parameters found; err describes the first decoding error
504
// encountered, if any.
505
func ParseQuery(query string) (m Values, err error) {
506
        m = make(Values)
507
        err = parseQuery(m, query)
508
        return
509
}
510
 
511
func parseQuery(m Values, query string) (err error) {
512
        for query != "" {
513
                key := query
514
                if i := strings.IndexAny(key, "&;"); i >= 0 {
515
                        key, query = key[:i], key[i+1:]
516
                } else {
517
                        query = ""
518
                }
519
                if key == "" {
520
                        continue
521
                }
522
                value := ""
523
                if i := strings.Index(key, "="); i >= 0 {
524
                        key, value = key[:i], key[i+1:]
525
                }
526
                key, err1 := QueryUnescape(key)
527
                if err1 != nil {
528
                        err = err1
529
                        continue
530
                }
531
                value, err1 = QueryUnescape(value)
532
                if err1 != nil {
533
                        err = err1
534
                        continue
535
                }
536
                m[key] = append(m[key], value)
537
        }
538
        return err
539
}
540
 
541
// Encode encodes the values into ``URL encoded'' form.
542
// e.g. "foo=bar&bar=baz"
543
func (v Values) Encode() string {
544
        if v == nil {
545
                return ""
546
        }
547
        parts := make([]string, 0, len(v)) // will be large enough for most uses
548
        for k, vs := range v {
549
                prefix := QueryEscape(k) + "="
550
                for _, v := range vs {
551
                        parts = append(parts, prefix+QueryEscape(v))
552
                }
553
        }
554
        return strings.Join(parts, "&")
555
}
556
 
557
// resolvePath applies special path segments from refs and applies
558
// them to base, per RFC 2396.
559
func resolvePath(basepath string, refpath string) string {
560
        base := strings.Split(basepath, "/")
561
        refs := strings.Split(refpath, "/")
562
        if len(base) == 0 {
563
                base = []string{""}
564
        }
565
        for idx, ref := range refs {
566
                switch {
567
                case ref == ".":
568
                        base[len(base)-1] = ""
569
                case ref == "..":
570
                        newLen := len(base) - 1
571
                        if newLen < 1 {
572
                                newLen = 1
573
                        }
574
                        base = base[0:newLen]
575
                        base[len(base)-1] = ""
576
                default:
577
                        if idx == 0 || base[len(base)-1] == "" {
578
                                base[len(base)-1] = ref
579
                        } else {
580
                                base = append(base, ref)
581
                        }
582
                }
583
        }
584
        return strings.Join(base, "/")
585
}
586
 
587
// IsAbs returns true if the URL is absolute.
588
func (u *URL) IsAbs() bool {
589
        return u.Scheme != ""
590
}
591
 
592
// Parse parses a URL in the context of a base URL.  The URL in ref
593
// may be relative or absolute.  Parse returns nil, err on parse
594
// failure, otherwise its return value is the same as ResolveReference.
595
func (base *URL) Parse(ref string) (*URL, error) {
596
        refurl, err := Parse(ref)
597
        if err != nil {
598
                return nil, err
599
        }
600
        return base.ResolveReference(refurl), nil
601
}
602
 
603
// ResolveReference resolves a URI reference to an absolute URI from
604
// an absolute base URI, per RFC 2396 Section 5.2.  The URI reference
605
// may be relative or absolute.  ResolveReference always returns a new
606
// URL instance, even if the returned URL is identical to either the
607
// base or reference. If ref is an absolute URL, then ResolveReference
608
// ignores base and returns a copy of ref.
609
func (base *URL) ResolveReference(ref *URL) *URL {
610
        if ref.IsAbs() {
611
                url := *ref
612
                return &url
613
        }
614
        // relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
615
        url := *base
616
        url.RawQuery = ref.RawQuery
617
        url.Fragment = ref.Fragment
618
        if ref.Opaque != "" {
619
                url.Opaque = ref.Opaque
620
                url.User = nil
621
                url.Host = ""
622
                url.Path = ""
623
                return &url
624
        }
625
        if ref.Host != "" || ref.User != nil {
626
                // The "net_path" case.
627
                url.Host = ref.Host
628
                url.User = ref.User
629
        }
630
        if strings.HasPrefix(ref.Path, "/") {
631
                // The "abs_path" case.
632
                url.Path = ref.Path
633
        } else {
634
                // The "rel_path" case.
635
                path := resolvePath(base.Path, ref.Path)
636
                if !strings.HasPrefix(path, "/") {
637
                        path = "/" + path
638
                }
639
                url.Path = path
640
        }
641
        return &url
642
}
643
 
644
// Query parses RawQuery and returns the corresponding values.
645
func (u *URL) Query() Values {
646
        v, _ := ParseQuery(u.RawQuery)
647
        return v
648
}
649
 
650
// RequestURI returns the encoded path?query or opaque?query
651
// string that would be used in an HTTP request for u.
652
func (u *URL) RequestURI() string {
653
        result := u.Opaque
654
        if result == "" {
655
                result = escape(u.Path, encodePath)
656
                if result == "" {
657
                        result = "/"
658
                }
659
        }
660
        if u.RawQuery != "" {
661
                result += "?" + u.RawQuery
662
        }
663
        return result
664
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.