utils.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package s3auth
  15. import (
  16. "encoding/hex"
  17. "net/http"
  18. "regexp"
  19. "strings"
  20. "unicode/utf8"
  21. )
  22. // if object matches reserved string, no need to encode them
  23. var reservedObjectNames = regexp.MustCompile("^[a-zA-Z0-9-_.~/]+$")
  24. // EncodePath encode the strings from UTF-8 byte representations to HTML hex escape sequences
  25. //
  26. // This is necessary since regular url.Parse() and url.Encode() functions do not support UTF-8
  27. // non english characters cannot be parsed due to the nature in which url.Encode() is written
  28. //
  29. // This function on the other hand is a direct replacement for url.Encode() technique to support
  30. // pretty much every UTF-8 character.
  31. func encodePath(pathName string) string {
  32. if reservedObjectNames.MatchString(pathName) {
  33. return pathName
  34. }
  35. var encodedPathname string
  36. for _, s := range pathName {
  37. if 'A' <= s && s <= 'Z' || 'a' <= s && s <= 'z' || '0' <= s && s <= '9' { // §2.3 Unreserved characters (mark)
  38. encodedPathname = encodedPathname + string(s)
  39. continue
  40. }
  41. switch s {
  42. case '-', '_', '.', '~', '/': // §2.3 Unreserved characters (mark)
  43. encodedPathname = encodedPathname + string(s)
  44. continue
  45. default:
  46. len := utf8.RuneLen(s)
  47. if len < 0 {
  48. // if utf8 cannot convert return the same string as is
  49. return pathName
  50. }
  51. u := make([]byte, len)
  52. utf8.EncodeRune(u, s)
  53. for _, r := range u {
  54. hex := hex.EncodeToString([]byte{r})
  55. encodedPathname = encodedPathname + "%" + strings.ToUpper(hex)
  56. }
  57. }
  58. }
  59. return encodedPathname
  60. }
  61. // getHostAddr returns host header if available, otherwise returns host from URL
  62. func getHostAddr(req http.Request) string {
  63. if req.Host != "" {
  64. return req.Host
  65. }
  66. return req.URL.Host
  67. }
  68. // Encode input URL path to URL encoded path.
  69. func encodeURL2Path(req http.Request, virtualHost bool) (path string) {
  70. if virtualHost {
  71. reqHost := getHostAddr(req)
  72. dotPos := strings.Index(reqHost, ".")
  73. if dotPos > -1 {
  74. bucketName := reqHost[:dotPos]
  75. path = "/" + bucketName
  76. path += req.URL.Path
  77. path = encodePath(path)
  78. return
  79. }
  80. }
  81. path = encodePath(req.URL.Path)
  82. return
  83. }