url.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // Copyright 2017 Frédéric Guillot. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package url // import "miniflux.app/url"
  5. import (
  6. "fmt"
  7. "net/url"
  8. "sort"
  9. "strings"
  10. )
  11. // IsAbsoluteURL returns true if the link is absolute.
  12. func IsAbsoluteURL(link string) bool {
  13. u, err := url.Parse(link)
  14. if err != nil {
  15. return false
  16. }
  17. return u.IsAbs()
  18. }
  19. // AbsoluteURL converts the input URL as absolute URL if necessary.
  20. func AbsoluteURL(baseURL, input string) (string, error) {
  21. if strings.HasPrefix(input, "//") {
  22. input = "https://" + input[2:]
  23. }
  24. u, err := url.Parse(input)
  25. if err != nil {
  26. return "", fmt.Errorf("unable to parse input URL: %v", err)
  27. }
  28. if u.IsAbs() {
  29. return u.String(), nil
  30. }
  31. base, err := url.Parse(baseURL)
  32. if err != nil {
  33. return "", fmt.Errorf("unable to parse base URL: %v", err)
  34. }
  35. return base.ResolveReference(u).String(), nil
  36. }
  37. // RootURL returns absolute URL without the path.
  38. func RootURL(websiteURL string) string {
  39. if strings.HasPrefix(websiteURL, "//") {
  40. websiteURL = "https://" + websiteURL[2:]
  41. }
  42. absoluteURL, err := AbsoluteURL(websiteURL, "")
  43. if err != nil {
  44. return websiteURL
  45. }
  46. u, err := url.Parse(absoluteURL)
  47. if err != nil {
  48. return absoluteURL
  49. }
  50. return u.Scheme + "://" + u.Host + "/"
  51. }
  52. // IsHTTPS returns true if the URL is using HTTPS.
  53. func IsHTTPS(websiteURL string) bool {
  54. parsedURL, err := url.Parse(websiteURL)
  55. if err != nil {
  56. return false
  57. }
  58. return strings.ToLower(parsedURL.Scheme) == "https"
  59. }
  60. // Domain returns only the domain part of the given URL.
  61. func Domain(websiteURL string) string {
  62. parsedURL, err := url.Parse(websiteURL)
  63. if err != nil {
  64. return websiteURL
  65. }
  66. return parsedURL.Host
  67. }
  68. // RequestURI returns the encoded URI to be used in HTTP requests.
  69. func RequestURI(websiteURL string) string {
  70. u, err := url.Parse(websiteURL)
  71. if err != nil {
  72. return websiteURL
  73. }
  74. queryValues := u.Query()
  75. u.RawQuery = "" // Clear RawQuery to make sure it's encoded properly.
  76. u.Fragment = "" // Clear fragment because Web browsers strip #fragment before sending the URL to a web server.
  77. var buf strings.Builder
  78. buf.WriteString(u.String())
  79. if len(queryValues) > 0 {
  80. buf.WriteByte('?')
  81. // Sort keys.
  82. keys := make([]string, 0, len(queryValues))
  83. for k := range queryValues {
  84. keys = append(keys, k)
  85. }
  86. sort.Strings(keys)
  87. i := 0
  88. for _, key := range keys {
  89. keyEscaped := url.QueryEscape(key)
  90. values := queryValues[key]
  91. for _, value := range values {
  92. if i > 0 {
  93. buf.WriteByte('&')
  94. }
  95. buf.WriteString(keyEscaped)
  96. // As opposed to the standard library, we append the = only if the value is not empty.
  97. if value != "" {
  98. buf.WriteByte('=')
  99. buf.WriteString(url.QueryEscape(value))
  100. }
  101. i++
  102. }
  103. }
  104. }
  105. return buf.String()
  106. }