Left: | ||
Right: |
OLD | NEW |
---|---|
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // Parse URLs (actually URIs, but that seems overly pedantic). | 5 // Package URL parses and escapes URLs (actually URIs). |
dsymonds
2011/08/15 09:20:23
to be more pedantic, although the objects that thi
| |
6 // RFC 3986 | 6 // See RFC 3986. |
7 | 7 package url |
8 package http | |
9 | 8 |
10 import ( | 9 import ( |
11 "os" | 10 "os" |
12 "strconv" | 11 "strconv" |
13 "strings" | 12 "strings" |
14 ) | 13 ) |
15 | 14 |
16 // URLError reports an error and the operation and URL that caused it. | 15 // Error reports an error and the operation and URL that caused it. |
17 type URLError struct { | 16 type Error struct { |
18 Op string | 17 Op string |
19 URL string | 18 URL string |
20 Error os.Error | 19 Error os.Error |
21 } | 20 } |
22 | 21 |
23 func (e *URLError) String() string { return e.Op + " " + e.URL + ": " + e.Error. String() } | 22 func (e *Error) String() string { return e.Op + " " + e.URL + ": " + e.Error.Str ing() } |
24 | 23 |
25 func ishex(c byte) bool { | 24 func ishex(c byte) bool { |
26 switch { | 25 switch { |
27 case '0' <= c && c <= '9': | 26 case '0' <= c && c <= '9': |
28 return true | 27 return true |
29 case 'a' <= c && c <= 'f': | 28 case 'a' <= c && c <= 'f': |
30 return true | 29 return true |
31 case 'A' <= c && c <= 'F': | 30 case 'A' <= c && c <= 'F': |
32 return true | 31 return true |
33 } | 32 } |
(...skipping 15 matching lines...) Expand all Loading... | |
49 type encoding int | 48 type encoding int |
50 | 49 |
51 const ( | 50 const ( |
52 encodePath encoding = 1 + iota | 51 encodePath encoding = 1 + iota |
53 encodeUserPassword | 52 encodeUserPassword |
54 encodeQueryComponent | 53 encodeQueryComponent |
55 encodeFragment | 54 encodeFragment |
56 encodeOpaque | 55 encodeOpaque |
57 ) | 56 ) |
58 | 57 |
59 type URLEscapeError string | 58 type EscapeError string |
60 | 59 |
61 func (e URLEscapeError) String() string { | 60 func (e EscapeError) String() string { |
62 return "invalid URL escape " + strconv.Quote(string(e)) | 61 return "invalid URL escape " + strconv.Quote(string(e)) |
63 } | 62 } |
64 | 63 |
65 // Return true if the specified character should be escaped when | 64 // Return true if the specified character should be escaped when |
66 // appearing in a URL string, according to RFC 2396. | 65 // appearing in a URL string, according to RFC 2396. |
67 // When 'all' is true the full range of reserved characters are matched. | 66 // When 'all' is true the full range of reserved characters are matched. |
68 func shouldEscape(c byte, mode encoding) bool { | 67 func shouldEscape(c byte, mode encoding) bool { |
69 // RFC 2396 §2.3 Unreserved characters (alphanum) | 68 // RFC 2396 §2.3 Unreserved characters (alphanum) |
70 if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { | 69 if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { |
71 return false | 70 return false |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
106 // except that the leading / must be escaped. | 105 // except that the leading / must be escaped. |
107 // (We implement that case in String.) | 106 // (We implement that case in String.) |
108 return false | 107 return false |
109 } | 108 } |
110 } | 109 } |
111 | 110 |
112 // Everything else must be escaped. | 111 // Everything else must be escaped. |
113 return true | 112 return true |
114 } | 113 } |
115 | 114 |
116 // URLUnescape unescapes a string in ``URL encoded'' form, | 115 // Unescape unescapes a string in ``URL encoded'' form, |
117 // converting %AB into the byte 0xAB and '+' into ' ' (space). | 116 // converting %AB into the byte 0xAB and '+' into ' ' (space). |
118 // It returns an error if any % is not followed | 117 // It returns an error if any % is not followed |
119 // by two hexadecimal digits. | 118 // by two hexadecimal digits. |
120 // Despite the name, this encoding applies only to individual | 119 // Despite the name, this encoding applies only to individual |
121 // components of the query portion of the URL. | 120 // components of the query portion of the URL. |
122 func URLUnescape(s string) (string, os.Error) { | 121 func Unescape(s string) (string, os.Error) { |
123 » return urlUnescape(s, encodeQueryComponent) | 122 » return unescape(s, encodeQueryComponent) |
124 } | 123 } |
125 | 124 |
126 // urlUnescape is like URLUnescape but mode specifies | 125 // unescape is like Unescape but mode specifies |
127 // which section of the URL is being unescaped. | 126 // which section of the URL is being unescaped. |
128 func urlUnescape(s string, mode encoding) (string, os.Error) { | 127 func unescape(s string, mode encoding) (string, os.Error) { |
129 // Count %, check that they're well-formed. | 128 // Count %, check that they're well-formed. |
130 n := 0 | 129 n := 0 |
131 hasPlus := false | 130 hasPlus := false |
132 for i := 0; i < len(s); { | 131 for i := 0; i < len(s); { |
133 switch s[i] { | 132 switch s[i] { |
134 case '%': | 133 case '%': |
135 n++ | 134 n++ |
136 if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { | 135 if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { |
137 s = s[i:] | 136 s = s[i:] |
138 if len(s) > 3 { | 137 if len(s) > 3 { |
139 s = s[0:3] | 138 s = s[0:3] |
140 } | 139 } |
141 » » » » return "", URLEscapeError(s) | 140 » » » » return "", EscapeError(s) |
142 } | 141 } |
143 i += 3 | 142 i += 3 |
144 case '+': | 143 case '+': |
145 hasPlus = mode == encodeQueryComponent | 144 hasPlus = mode == encodeQueryComponent |
146 i++ | 145 i++ |
147 default: | 146 default: |
148 i++ | 147 i++ |
149 } | 148 } |
150 } | 149 } |
151 | 150 |
(...skipping 19 matching lines...) Expand all Loading... | |
171 i++ | 170 i++ |
172 default: | 171 default: |
173 t[j] = s[i] | 172 t[j] = s[i] |
174 j++ | 173 j++ |
175 i++ | 174 i++ |
176 } | 175 } |
177 } | 176 } |
178 return string(t), nil | 177 return string(t), nil |
179 } | 178 } |
180 | 179 |
181 // URLEscape converts a string into ``URL encoded'' form. | 180 // Escape converts a string into ``URL encoded'' form. |
182 // Despite the name, this encoding applies only to individual | 181 // Despite the name, this encoding applies only to individual |
183 // components of the query portion of the URL. | 182 // components of the query portion of the URL. |
184 func URLEscape(s string) string { | 183 func Escape(s string) string { |
185 » return urlEscape(s, encodeQueryComponent) | 184 » return escape(s, encodeQueryComponent) |
186 } | 185 } |
187 | 186 |
188 func urlEscape(s string, mode encoding) string { | 187 // EncodedPath returns the URL's path in "URL path encoded" form. |
dsymonds
2011/08/15 09:20:23
this method seems odd all the way up here. move it
| |
188 func (u *URL) EncodedPath() string { | |
189 » return escape(u.Path, encodePath) | |
190 } | |
191 | |
192 func escape(s string, mode encoding) string { | |
189 spaceCount, hexCount := 0, 0 | 193 spaceCount, hexCount := 0, 0 |
190 for i := 0; i < len(s); i++ { | 194 for i := 0; i < len(s); i++ { |
191 c := s[i] | 195 c := s[i] |
192 if shouldEscape(c, mode) { | 196 if shouldEscape(c, mode) { |
193 if c == ' ' && mode == encodeQueryComponent { | 197 if c == ' ' && mode == encodeQueryComponent { |
194 spaceCount++ | 198 spaceCount++ |
195 } else { | 199 } else { |
196 hexCount++ | 200 hexCount++ |
197 } | 201 } |
198 } | 202 } |
(...skipping 27 matching lines...) Expand all Loading... | |
226 // as the form user or user:password and unescapes and returns | 230 // as the form user or user:password and unescapes and returns |
227 // the two halves. | 231 // the two halves. |
228 // | 232 // |
229 // This functionality should only be used with legacy web sites. | 233 // This functionality should only be used with legacy web sites. |
230 // RFC 2396 warns that interpreting Userinfo this way | 234 // RFC 2396 warns that interpreting Userinfo this way |
231 // ``is NOT RECOMMENDED, because the passing of authentication | 235 // ``is NOT RECOMMENDED, because the passing of authentication |
232 // information in clear text (such as URI) has proven to be a | 236 // information in clear text (such as URI) has proven to be a |
233 // security risk in almost every case where it has been used.'' | 237 // security risk in almost every case where it has been used.'' |
234 func UnescapeUserinfo(rawUserinfo string) (user, password string, err os.Error) { | 238 func UnescapeUserinfo(rawUserinfo string) (user, password string, err os.Error) { |
235 u, p := split(rawUserinfo, ':', true) | 239 u, p := split(rawUserinfo, ':', true) |
236 » if user, err = urlUnescape(u, encodeUserPassword); err != nil { | 240 » if user, err = unescape(u, encodeUserPassword); err != nil { |
237 return "", "", err | 241 return "", "", err |
238 } | 242 } |
239 » if password, err = urlUnescape(p, encodeUserPassword); err != nil { | 243 » if password, err = unescape(p, encodeUserPassword); err != nil { |
240 return "", "", err | 244 return "", "", err |
241 } | 245 } |
242 return | 246 return |
243 } | 247 } |
244 | 248 |
245 // EscapeUserinfo combines user and password in the form | 249 // EscapeUserinfo combines user and password in the form |
246 // user:password (or just user if password is empty) and then | 250 // user:password (or just user if password is empty) and then |
247 // escapes it for use as the URL.RawUserinfo field. | 251 // escapes it for use as the URL.RawUserinfo field. |
248 // | 252 // |
249 // This functionality should only be used with legacy web sites. | 253 // This functionality should only be used with legacy web sites. |
250 // RFC 2396 warns that interpreting Userinfo this way | 254 // RFC 2396 warns that interpreting Userinfo this way |
251 // ``is NOT RECOMMENDED, because the passing of authentication | 255 // ``is NOT RECOMMENDED, because the passing of authentication |
252 // information in clear text (such as URI) has proven to be a | 256 // information in clear text (such as URI) has proven to be a |
253 // security risk in almost every case where it has been used.'' | 257 // security risk in almost every case where it has been used.'' |
254 func EscapeUserinfo(user, password string) string { | 258 func EscapeUserinfo(user, password string) string { |
255 » raw := urlEscape(user, encodeUserPassword) | 259 » raw := escape(user, encodeUserPassword) |
256 if password != "" { | 260 if password != "" { |
257 » » raw += ":" + urlEscape(password, encodeUserPassword) | 261 » » raw += ":" + escape(password, encodeUserPassword) |
258 } | 262 } |
259 return raw | 263 return raw |
260 } | 264 } |
261 | 265 |
262 // A URL represents a parsed URL (technically, a URI reference). | 266 // A URL represents a parsed URL (technically, a URI reference). |
263 // The general form represented is: | 267 // The general form represented is: |
264 // scheme://[userinfo@]host/path[?query][#fragment] | 268 // scheme://[userinfo@]host/path[?query][#fragment] |
265 // The Raw, RawAuthority, RawPath, and RawQuery fields are in "wire format" | 269 // The Raw, RawAuthority, RawPath, and RawQuery fields are in "wire format" |
266 // (special characters must be hex-escaped if not meant to have special meaning) . | 270 // (special characters must be hex-escaped if not meant to have special meaning) . |
267 // All other fields are logical values; '+' or '%' represent themselves. | 271 // All other fields are logical values; '+' or '%' represent themselves. |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
317 if s[i] == c { | 321 if s[i] == c { |
318 if cutc { | 322 if cutc { |
319 return s[0:i], s[i+1:] | 323 return s[0:i], s[i+1:] |
320 } | 324 } |
321 return s[0:i], s[i:] | 325 return s[0:i], s[i:] |
322 } | 326 } |
323 } | 327 } |
324 return s, "" | 328 return s, "" |
325 } | 329 } |
326 | 330 |
327 // ParseURL parses rawurl into a URL structure. | 331 // ParseURL parses rawurl into a URL structure. |
dsymonds
2011/08/15 09:20:23
update name
| |
328 // The string rawurl is assumed not to have a #fragment suffix. | 332 // The string rawurl is assumed not to have a #fragment suffix. |
329 // (Web browsers strip #fragment before sending the URL to a web server.) | 333 // (Web browsers strip #fragment before sending the URL to a web server.) |
330 // The rawurl may be relative or absolute. | 334 // The rawurl may be relative or absolute. |
331 func ParseURL(rawurl string) (url *URL, err os.Error) { | 335 func Parse(rawurl string) (url *URL, err os.Error) { |
332 » return parseURL(rawurl, false) | 336 » return parse(rawurl, false) |
333 } | 337 } |
334 | 338 |
335 // ParseRequestURL parses rawurl into a URL structure. It assumes that | 339 // ParseRequest parses rawurl into a URL structure. It assumes that |
336 // rawurl was received from an HTTP request, so the rawurl is interpreted | 340 // rawurl was received from an HTTP request, so the rawurl is interpreted |
337 // only as an absolute URI or an absolute path. | 341 // only as an absolute URI or an absolute path. |
338 // The string rawurl is assumed not to have a #fragment suffix. | 342 // The string rawurl is assumed not to have a #fragment suffix. |
339 // (Web browsers strip #fragment before sending the URL to a web server.) | 343 // (Web browsers strip #fragment before sending the URL to a web server.) |
340 func ParseRequestURL(rawurl string) (url *URL, err os.Error) { | 344 func ParseRequest(rawurl string) (url *URL, err os.Error) { |
341 » return parseURL(rawurl, true) | 345 » return parse(rawurl, true) |
342 } | 346 } |
343 | 347 |
344 // parseURL parses a URL from a string in one of two contexts. If | 348 // parse parses a URL from a string in one of two contexts. If |
345 // viaRequest is true, the URL is assumed to have arrived via an HTTP request, | 349 // viaRequest is true, the URL is assumed to have arrived via an HTTP request, |
346 // in which case only absolute URLs or path-absolute relative URLs are allowed. | 350 // in which case only absolute URLs or path-absolute relative URLs are allowed. |
347 // If viaRequest is false, all forms of relative URLs are allowed. | 351 // If viaRequest is false, all forms of relative URLs are allowed. |
348 func parseURL(rawurl string, viaRequest bool) (url *URL, err os.Error) { | 352 func parse(rawurl string, viaRequest bool) (url *URL, err os.Error) { |
349 var ( | 353 var ( |
350 leadingSlash bool | 354 leadingSlash bool |
351 path string | 355 path string |
352 ) | 356 ) |
353 | 357 |
354 if rawurl == "" { | 358 if rawurl == "" { |
355 err = os.NewError("empty url") | 359 err = os.NewError("empty url") |
356 goto Error | 360 goto Error |
357 } | 361 } |
358 url = new(URL) | 362 url = new(URL) |
359 url.Raw = rawurl | 363 url.Raw = rawurl |
360 | 364 |
361 // Split off possible leading "http:", "mailto:", etc. | 365 // Split off possible leading "http:", "mailto:", etc. |
362 // Cannot contain escaped characters. | 366 // Cannot contain escaped characters. |
363 if url.Scheme, path, err = getscheme(rawurl); err != nil { | 367 if url.Scheme, path, err = getscheme(rawurl); err != nil { |
364 goto Error | 368 goto Error |
365 } | 369 } |
366 leadingSlash = strings.HasPrefix(path, "/") | 370 leadingSlash = strings.HasPrefix(path, "/") |
367 | 371 |
368 if url.Scheme != "" && !leadingSlash { | 372 if url.Scheme != "" && !leadingSlash { |
369 // RFC 2396: | 373 // RFC 2396: |
370 // Absolute URI (has scheme) with non-rooted path | 374 // Absolute URI (has scheme) with non-rooted path |
371 // is uninterpreted. It doesn't even have a ?query. | 375 // is uninterpreted. It doesn't even have a ?query. |
372 // This is the case that handles mailto:name@example.com. | 376 // This is the case that handles mailto:name@example.com. |
373 url.RawPath = path | 377 url.RawPath = path |
374 | 378 |
375 » » if url.Path, err = urlUnescape(path, encodeOpaque); err != nil { | 379 » » if url.Path, err = unescape(path, encodeOpaque); err != nil { |
376 goto Error | 380 goto Error |
377 } | 381 } |
378 url.OpaquePath = true | 382 url.OpaquePath = true |
379 } else { | 383 } else { |
380 if viaRequest && !leadingSlash { | 384 if viaRequest && !leadingSlash { |
381 err = os.NewError("invalid URI for request") | 385 err = os.NewError("invalid URI for request") |
382 goto Error | 386 goto Error |
383 } | 387 } |
384 | 388 |
385 // Split off query before parsing path further. | 389 // Split off query before parsing path further. |
(...skipping 24 matching lines...) Expand all Loading... | |
410 // instead. Clients that wish to use RawAuthority will have to | 414 // instead. Clients that wish to use RawAuthority will have to |
411 // interpret it themselves: RFC 2396 does not define the meaning . | 415 // interpret it themselves: RFC 2396 does not define the meaning . |
412 | 416 |
413 if strings.Contains(rawHost, "%") { | 417 if strings.Contains(rawHost, "%") { |
414 // Host cannot contain escaped characters. | 418 // Host cannot contain escaped characters. |
415 err = os.NewError("hexadecimal escape in host") | 419 err = os.NewError("hexadecimal escape in host") |
416 goto Error | 420 goto Error |
417 } | 421 } |
418 url.Host = rawHost | 422 url.Host = rawHost |
419 | 423 |
420 » » if url.Path, err = urlUnescape(path, encodePath); err != nil { | 424 » » if url.Path, err = unescape(path, encodePath); err != nil { |
421 goto Error | 425 goto Error |
422 } | 426 } |
423 } | 427 } |
424 return url, nil | 428 return url, nil |
425 | 429 |
426 Error: | 430 Error: |
427 » return nil, &URLError{"parse", rawurl, err} | 431 » return nil, &Error{"parse", rawurl, err} |
428 | 432 |
429 } | 433 } |
430 | 434 |
431 // ParseURLReference is like ParseURL but allows a trailing #fragment. | 435 // ParseWithReference is like Parse but allows a trailing #fragment. |
432 func ParseURLReference(rawurlref string) (url *URL, err os.Error) { | 436 func ParseWithReference(rawurlref string) (url *URL, err os.Error) { |
433 // Cut off #frag. | 437 // Cut off #frag. |
434 rawurl, frag := split(rawurlref, '#', false) | 438 rawurl, frag := split(rawurlref, '#', false) |
435 » if url, err = ParseURL(rawurl); err != nil { | 439 » if url, err = Parse(rawurl); err != nil { |
436 return nil, err | 440 return nil, err |
437 } | 441 } |
438 url.Raw += frag | 442 url.Raw += frag |
439 url.RawPath += frag | 443 url.RawPath += frag |
440 if len(frag) > 1 { | 444 if len(frag) > 1 { |
441 frag = frag[1:] | 445 frag = frag[1:] |
442 » » if url.Fragment, err = urlUnescape(frag, encodeFragment); err != nil { | 446 » » if url.Fragment, err = unescape(frag, encodeFragment); err != ni l { |
443 » » » return nil, &URLError{"parse", rawurl, err} | 447 » » » return nil, &Error{"parse", rawurl, err} |
444 } | 448 } |
445 } | 449 } |
446 return url, nil | 450 return url, nil |
447 } | 451 } |
448 | 452 |
449 // String reassembles url into a valid URL string. | 453 // String reassembles url into a valid URL string. |
450 // | 454 // |
451 // There are redundant fields stored in the URL structure: | 455 // There are redundant fields stored in the URL structure: |
452 // the String method consults Scheme, Path, Host, RawUserinfo, | 456 // the String method consults Scheme, Path, Host, RawUserinfo, |
453 // RawQuery, and Fragment, but not Raw, RawPath or RawAuthority. | 457 // RawQuery, and Fragment, but not Raw, RawPath or RawAuthority. |
(...skipping 13 matching lines...) Expand all Loading... | |
467 result += info + "@" | 471 result += info + "@" |
468 } | 472 } |
469 result += url.Host | 473 result += url.Host |
470 } | 474 } |
471 if url.OpaquePath { | 475 if url.OpaquePath { |
472 path := url.Path | 476 path := url.Path |
473 if strings.HasPrefix(path, "/") { | 477 if strings.HasPrefix(path, "/") { |
474 result += "%2f" | 478 result += "%2f" |
475 path = path[1:] | 479 path = path[1:] |
476 } | 480 } |
477 » » result += urlEscape(path, encodeOpaque) | 481 » » result += escape(path, encodeOpaque) |
478 } else { | 482 } else { |
479 » » result += urlEscape(url.Path, encodePath) | 483 » » result += escape(url.Path, encodePath) |
480 } | 484 } |
481 if url.RawQuery != "" { | 485 if url.RawQuery != "" { |
482 result += "?" + url.RawQuery | 486 result += "?" + url.RawQuery |
483 } | 487 } |
484 if url.Fragment != "" { | 488 if url.Fragment != "" { |
485 » » result += "#" + urlEscape(url.Fragment, encodeFragment) | 489 » » result += "#" + escape(url.Fragment, encodeFragment) |
486 } | 490 } |
487 return result | 491 return result |
488 } | 492 } |
489 | 493 |
494 // Values maps a string key to a list of values. | |
495 // It is typically used for query parameters and form values. | |
496 // Unlike in the Header map, the keys in a Values map | |
dsymonds
2011/08/15 09:20:23
s/Header/http.Header/
| |
497 // are case-sensitive. | |
498 type Values map[string][]string | |
499 | |
500 // Get gets the first value associated with the given key. | |
501 // If there are no values associated with the key, Get returns | |
502 // the empty string. To access multiple values, use the map | |
503 // directly. | |
504 func (v Values) Get(key string) string { | |
505 if v == nil { | |
506 return "" | |
507 } | |
508 vs, ok := v[key] | |
509 if !ok || len(vs) == 0 { | |
510 return "" | |
511 } | |
512 return vs[0] | |
513 } | |
514 | |
515 // Set sets the key to value. It replaces any existing | |
516 // values. | |
517 func (v Values) Set(key, value string) { | |
518 v[key] = []string{value} | |
519 } | |
520 | |
521 // Add adds the key to value. It appends to any existing | |
522 // values associated with key. | |
523 func (v Values) Add(key, value string) { | |
524 v[key] = append(v[key], value) | |
525 } | |
526 | |
527 // Del deletes the values associated with key. | |
528 func (v Values) Del(key string) { | |
529 v[key] = nil, false | |
530 } | |
531 | |
532 // ParseQuery parses the URL-encoded query string and returns | |
533 // a map listing the values specified for each key. | |
534 // ParseQuery always returns a non-nil map containing all the | |
535 // valid query parameters found; err describes the first decoding error | |
536 // encountered, if any. | |
537 func ParseQuery(query string) (m Values, err os.Error) { | |
538 m = make(Values) | |
539 err = parseQuery(m, query) | |
540 return | |
541 } | |
542 | |
543 func parseQuery(m Values, query string) (err os.Error) { | |
544 for _, kv := range strings.Split(query, "&") { | |
545 if len(kv) == 0 { | |
546 continue | |
547 } | |
548 kvPair := strings.SplitN(kv, "=", 2) | |
549 | |
550 var key, value string | |
551 var e os.Error | |
552 key, e = Unescape(kvPair[0]) | |
553 if e == nil && len(kvPair) > 1 { | |
554 value, e = Unescape(kvPair[1]) | |
555 } | |
556 if e != nil { | |
557 err = e | |
558 continue | |
559 } | |
560 m[key] = append(m[key], value) | |
561 } | |
562 return err | |
563 } | |
564 | |
490 // Encode encodes the values into ``URL encoded'' form. | 565 // Encode encodes the values into ``URL encoded'' form. |
491 // e.g. "foo=bar&bar=baz" | 566 // e.g. "foo=bar&bar=baz" |
492 func (v Values) Encode() string { | 567 func (v Values) Encode() string { |
493 if v == nil { | 568 if v == nil { |
494 return "" | 569 return "" |
495 } | 570 } |
496 parts := make([]string, 0, len(v)) // will be large enough for most uses | 571 parts := make([]string, 0, len(v)) // will be large enough for most uses |
497 for k, vs := range v { | 572 for k, vs := range v { |
498 » » prefix := URLEscape(k) + "=" | 573 » » prefix := Escape(k) + "=" |
499 for _, v := range vs { | 574 for _, v := range vs { |
500 » » » parts = append(parts, prefix+URLEscape(v)) | 575 » » » parts = append(parts, prefix+Escape(v)) |
501 } | 576 } |
502 } | 577 } |
503 return strings.Join(parts, "&") | 578 return strings.Join(parts, "&") |
504 } | 579 } |
505 | 580 |
506 // resolvePath applies special path segments from refs and applies | 581 // resolvePath applies special path segments from refs and applies |
507 // them to base, per RFC 2396. | 582 // them to base, per RFC 2396. |
508 func resolvePath(basepath string, refpath string) string { | 583 func resolvePath(basepath string, refpath string) string { |
509 base := strings.Split(basepath, "/") | 584 base := strings.Split(basepath, "/") |
510 refs := strings.Split(refpath, "/") | 585 refs := strings.Split(refpath, "/") |
(...skipping 20 matching lines...) Expand all Loading... | |
531 } | 606 } |
532 } | 607 } |
533 return strings.Join(base, "/") | 608 return strings.Join(base, "/") |
534 } | 609 } |
535 | 610 |
536 // IsAbs returns true if the URL is absolute. | 611 // IsAbs returns true if the URL is absolute. |
537 func (url *URL) IsAbs() bool { | 612 func (url *URL) IsAbs() bool { |
538 return url.Scheme != "" | 613 return url.Scheme != "" |
539 } | 614 } |
540 | 615 |
541 // ParseURL parses a URL in the context of a base URL. The URL in ref | 616 // ParseURL parses a URL in the context of a base URL. The URL in ref |
dsymonds
2011/08/15 09:20:23
update name
| |
542 // may be relative or absolute. ParseURL returns nil, err on parse | 617 // may be relative or absolute. ParseURL returns nil, err on parse |
543 // failure, otherwise its return value is the same as ResolveReference. | 618 // failure, otherwise its return value is the same as ResolveReference. |
544 func (base *URL) ParseURL(ref string) (*URL, os.Error) { | 619 func (base *URL) Parse(ref string) (*URL, os.Error) { |
545 » refurl, err := ParseURL(ref) | 620 » refurl, err := Parse(ref) |
546 if err != nil { | 621 if err != nil { |
547 return nil, err | 622 return nil, err |
548 } | 623 } |
549 return base.ResolveReference(refurl), nil | 624 return base.ResolveReference(refurl), nil |
550 } | 625 } |
551 | 626 |
552 // ResolveReference resolves a URI reference to an absolute URI from | 627 // ResolveReference resolves a URI reference to an absolute URI from |
553 // an absolute base URI, per RFC 2396 Section 5.2. The URI reference | 628 // an absolute base URI, per RFC 2396 Section 5.2. The URI reference |
554 // may be relative or absolute. ResolveReference always returns a new | 629 // may be relative or absolute. ResolveReference always returns a new |
555 // URL instance, even if the returned URL is identical to either the | 630 // URL instance, even if the returned URL is identical to either the |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
597 } | 672 } |
598 url.Raw = url.String() | 673 url.Raw = url.String() |
599 return url | 674 return url |
600 } | 675 } |
601 | 676 |
602 // Query parses RawQuery and returns the corresponding values. | 677 // Query parses RawQuery and returns the corresponding values. |
603 func (u *URL) Query() Values { | 678 func (u *URL) Query() Values { |
604 v, _ := ParseQuery(u.RawQuery) | 679 v, _ := ParseQuery(u.RawQuery) |
605 return v | 680 return v |
606 } | 681 } |
OLD | NEW |