topic.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. /*
  2. * Copyright (c) 2014 IBM Corp.
  3. *
  4. * All rights reserved. This program and the accompanying materials
  5. * are made available under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution, and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Seth Hoenig
  11. * Allan Stockdill-Mander
  12. * Mike Robertson
  13. */
  14. package mqtt
  15. import (
  16. "errors"
  17. "strings"
  18. )
  19. // ErrInvalidQos is the error returned when an packet is to be sent
  20. // with an invalid Qos value
  21. var ErrInvalidQos = errors.New("invalid QoS")
  22. // ErrInvalidTopicEmptyString is the error returned when a topic string
  23. // is passed in that is 0 length
  24. var ErrInvalidTopicEmptyString = errors.New("invalid Topic; empty string")
  25. // ErrInvalidTopicMultilevel is the error returned when a topic string
  26. // is passed in that has the multi level wildcard in any position but
  27. // the last
  28. var ErrInvalidTopicMultilevel = errors.New("invalid Topic; multi-level wildcard must be last level")
  29. // Topic Names and Topic Filters
  30. // The MQTT v3.1.1 spec clarifies a number of ambiguities with regard
  31. // to the validity of Topic strings.
  32. // - A Topic must be between 1 and 65535 bytes.
  33. // - A Topic is case sensitive.
  34. // - A Topic may contain whitespace.
  35. // - A Topic containing a leading forward slash is different than a Topic without.
  36. // - A Topic may be "/" (two levels, both empty string).
  37. // - A Topic must be UTF-8 encoded.
  38. // - A Topic may contain any number of levels.
  39. // - A Topic may contain an empty level (two forward slashes in a row).
  40. // - A TopicName may not contain a wildcard.
  41. // - A TopicFilter may only have a # (multi-level) wildcard as the last level.
  42. // - A TopicFilter may contain any number of + (single-level) wildcards.
  43. // - A TopicFilter with a # will match the absence of a level
  44. // Example: a subscription to "foo/#" will match messages published to "foo".
  45. func validateSubscribeMap(subs map[string]byte) ([]string, []byte, error) {
  46. if len(subs) == 0 {
  47. return nil, nil, errors.New("invalid subscription; subscribe map must not be empty")
  48. }
  49. var topics []string
  50. var qoss []byte
  51. for topic, qos := range subs {
  52. if err := validateTopicAndQos(topic, qos); err != nil {
  53. return nil, nil, err
  54. }
  55. topics = append(topics, topic)
  56. qoss = append(qoss, qos)
  57. }
  58. return topics, qoss, nil
  59. }
  60. func validateTopicAndQos(topic string, qos byte) error {
  61. if len(topic) == 0 {
  62. return ErrInvalidTopicEmptyString
  63. }
  64. levels := strings.Split(topic, "/")
  65. for i, level := range levels {
  66. if level == "#" && i != len(levels)-1 {
  67. return ErrInvalidTopicMultilevel
  68. }
  69. }
  70. if qos > 2 {
  71. return ErrInvalidQos
  72. }
  73. return nil
  74. }