uri.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Source code and contact info at http://github.com/streadway/amqp
  5. package amqp
  6. import (
  7. "errors"
  8. "net"
  9. "net/url"
  10. "strconv"
  11. "strings"
  12. )
  13. var errURIScheme = errors.New("AMQP scheme must be either 'amqp://' or 'amqps://'")
  14. var errURIWhitespace = errors.New("URI must not contain whitespace")
  15. var schemePorts = map[string]int{
  16. "amqp": 5672,
  17. "amqps": 5671,
  18. }
  19. var defaultURI = URI{
  20. Scheme: "amqp",
  21. Host: "localhost",
  22. Port: 5672,
  23. Username: "guest",
  24. Password: "guest",
  25. Vhost: "/",
  26. }
  27. // URI represents a parsed AMQP URI string.
  28. type URI struct {
  29. Scheme string
  30. Host string
  31. Port int
  32. Username string
  33. Password string
  34. Vhost string
  35. }
  36. // ParseURI attempts to parse the given AMQP URI according to the spec.
  37. // See http://www.rabbitmq.com/uri-spec.html.
  38. //
  39. // Default values for the fields are:
  40. //
  41. // Scheme: amqp
  42. // Host: localhost
  43. // Port: 5672
  44. // Username: guest
  45. // Password: guest
  46. // Vhost: /
  47. //
  48. func ParseURI(uri string) (URI, error) {
  49. builder := defaultURI
  50. if strings.Contains(uri, " ") == true {
  51. return builder, errURIWhitespace
  52. }
  53. u, err := url.Parse(uri)
  54. if err != nil {
  55. return builder, err
  56. }
  57. defaultPort, okScheme := schemePorts[u.Scheme]
  58. if okScheme {
  59. builder.Scheme = u.Scheme
  60. } else {
  61. return builder, errURIScheme
  62. }
  63. host := u.Hostname()
  64. port := u.Port()
  65. if host != "" {
  66. builder.Host = host
  67. }
  68. if port != "" {
  69. port32, err := strconv.ParseInt(port, 10, 32)
  70. if err != nil {
  71. return builder, err
  72. }
  73. builder.Port = int(port32)
  74. } else {
  75. builder.Port = defaultPort
  76. }
  77. if u.User != nil {
  78. builder.Username = u.User.Username()
  79. if password, ok := u.User.Password(); ok {
  80. builder.Password = password
  81. }
  82. }
  83. if u.Path != "" {
  84. if strings.HasPrefix(u.Path, "/") {
  85. if u.Host == "" && strings.HasPrefix(u.Path, "///") {
  86. // net/url doesn't handle local context authorities and leaves that up
  87. // to the scheme handler. In our case, we translate amqp:/// into the
  88. // default host and whatever the vhost should be
  89. if len(u.Path) > 3 {
  90. builder.Vhost = u.Path[3:]
  91. }
  92. } else if len(u.Path) > 1 {
  93. builder.Vhost = u.Path[1:]
  94. }
  95. } else {
  96. builder.Vhost = u.Path
  97. }
  98. }
  99. return builder, nil
  100. }
  101. // PlainAuth returns a PlainAuth structure based on the parsed URI's
  102. // Username and Password fields.
  103. func (uri URI) PlainAuth() *PlainAuth {
  104. return &PlainAuth{
  105. Username: uri.Username,
  106. Password: uri.Password,
  107. }
  108. }
  109. // AMQPlainAuth returns a PlainAuth structure based on the parsed URI's
  110. // Username and Password fields.
  111. func (uri URI) AMQPlainAuth() *AMQPlainAuth {
  112. return &AMQPlainAuth{
  113. Username: uri.Username,
  114. Password: uri.Password,
  115. }
  116. }
  117. func (uri URI) String() string {
  118. authority, err := url.Parse("")
  119. if err != nil {
  120. return err.Error()
  121. }
  122. authority.Scheme = uri.Scheme
  123. if uri.Username != defaultURI.Username || uri.Password != defaultURI.Password {
  124. authority.User = url.User(uri.Username)
  125. if uri.Password != defaultURI.Password {
  126. authority.User = url.UserPassword(uri.Username, uri.Password)
  127. }
  128. }
  129. authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port))
  130. if defaultPort, found := schemePorts[uri.Scheme]; !found || defaultPort != uri.Port {
  131. authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port))
  132. } else {
  133. // JoinHostPort() automatically add brackets to the host if it's
  134. // an IPv6 address.
  135. //
  136. // If not port is specified, JoinHostPort() return an IP address in the
  137. // form of "[::1]:", so we use TrimSuffix() to remove the extra ":".
  138. authority.Host = strings.TrimSuffix(net.JoinHostPort(uri.Host, ""), ":")
  139. }
  140. if uri.Vhost != defaultURI.Vhost {
  141. // Make sure net/url does not double escape, e.g.
  142. // "%2F" does not become "%252F".
  143. authority.Path = uri.Vhost
  144. authority.RawPath = url.QueryEscape(uri.Vhost)
  145. } else {
  146. authority.Path = "/"
  147. }
  148. return authority.String()
  149. }