uri.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. "fmt"
  9. "net/url"
  10. "strconv"
  11. "strings"
  12. )
  13. var errURIScheme = errors.New("AMQP scheme must be either 'amqp://' or 'amqps://'")
  14. var schemePorts = map[string]int{
  15. "amqp": 5672,
  16. "amqps": 5671,
  17. }
  18. var defaultURI = URI{
  19. Scheme: "amqp",
  20. Host: "localhost",
  21. Port: 5672,
  22. Username: "guest",
  23. Password: "guest",
  24. Vhost: "/",
  25. }
  26. // URI represents a parsed AMQP URI string.
  27. type URI struct {
  28. Scheme string
  29. Host string
  30. Port int
  31. Username string
  32. Password string
  33. Vhost string
  34. }
  35. // ParseURI attempts to parse the given AMQP URI according to the spec.
  36. // See http://www.rabbitmq.com/uri-spec.html.
  37. //
  38. // Default values for the fields are:
  39. //
  40. // Scheme: amqp
  41. // Host: localhost
  42. // Port: 5672
  43. // Username: guest
  44. // Password: guest
  45. // Vhost: /
  46. //
  47. func ParseURI(uri string) (URI, error) {
  48. me := defaultURI
  49. u, err := url.Parse(uri)
  50. if err != nil {
  51. return me, err
  52. }
  53. defaultPort, okScheme := schemePorts[u.Scheme]
  54. if okScheme {
  55. me.Scheme = u.Scheme
  56. } else {
  57. return me, errURIScheme
  58. }
  59. host, port := splitHostPort(u.Host)
  60. if host != "" {
  61. me.Host = host
  62. }
  63. if port != "" {
  64. port32, err := strconv.ParseInt(port, 10, 32)
  65. if err != nil {
  66. return me, err
  67. }
  68. me.Port = int(port32)
  69. } else {
  70. me.Port = defaultPort
  71. }
  72. if u.User != nil {
  73. me.Username = u.User.Username()
  74. if password, ok := u.User.Password(); ok {
  75. me.Password = password
  76. }
  77. }
  78. if u.Path != "" {
  79. if strings.HasPrefix(u.Path, "/") {
  80. if u.Host == "" && strings.HasPrefix(u.Path, "///") {
  81. // net/url doesn't handle local context authorities and leaves that up
  82. // to the scheme handler. In our case, we translate amqp:/// into the
  83. // default host and whatever the vhost should be
  84. if len(u.Path) > 3 {
  85. me.Vhost = u.Path[3:]
  86. }
  87. } else if len(u.Path) > 1 {
  88. me.Vhost = u.Path[1:]
  89. }
  90. } else {
  91. me.Vhost = u.Path
  92. }
  93. }
  94. return me, nil
  95. }
  96. // Splits host:port, host, [ho:st]:port, or [ho:st]. Unlike net.SplitHostPort
  97. // which splits :port, host:port or [host]:port
  98. //
  99. // Handles hosts that have colons that are in brackets like [::1]:http
  100. func splitHostPort(addr string) (host, port string) {
  101. i := strings.LastIndex(addr, ":")
  102. if i >= 0 {
  103. host, port = addr[:i], addr[i+1:]
  104. if len(port) > 0 && port[len(port)-1] == ']' && addr[0] == '[' {
  105. // we've split on an inner colon, the port was missing outside of the
  106. // brackets so use the full addr. We could assert that host should not
  107. // contain any colons here
  108. host, port = addr, ""
  109. }
  110. } else {
  111. host = addr
  112. }
  113. return
  114. }
  115. // PlainAuth returns a PlainAuth structure based on the parsed URI's
  116. // Username and Password fields.
  117. func (me URI) PlainAuth() *PlainAuth {
  118. return &PlainAuth{
  119. Username: me.Username,
  120. Password: me.Password,
  121. }
  122. }
  123. func (me URI) String() string {
  124. var authority string
  125. if me.Username != defaultURI.Username || me.Password != defaultURI.Password {
  126. authority += me.Username
  127. if me.Password != defaultURI.Password {
  128. authority += ":" + me.Password
  129. }
  130. authority += "@"
  131. }
  132. authority += me.Host
  133. if defaultPort, found := schemePorts[me.Scheme]; !found || defaultPort != me.Port {
  134. authority += ":" + strconv.FormatInt(int64(me.Port), 10)
  135. }
  136. var vhost string
  137. if me.Vhost != defaultURI.Vhost {
  138. vhost = me.Vhost
  139. }
  140. return fmt.Sprintf("%s://%s/%s", me.Scheme, authority, url.QueryEscape(vhost))
  141. }