topic.go 2.8 KB

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