mirror of
https://github.com/fhmq/hmq.git
synced 2026-04-27 03:58:33 +00:00
packet
This commit is contained in:
410
lib/message/message.go
Executable file
410
lib/message/message.go
Executable file
@@ -0,0 +1,410 @@
|
||||
// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
maxLPString uint16 = 65535
|
||||
maxFixedHeaderLength int = 5
|
||||
maxRemainingLength int32 = 268435455 // bytes, or 256 MB
|
||||
)
|
||||
|
||||
const (
|
||||
// QoS 0: At most once delivery
|
||||
// The message is delivered according to the capabilities of the underlying network.
|
||||
// No response is sent by the receiver and no retry is performed by the sender. The
|
||||
// message arrives at the receiver either once or not at all.
|
||||
QosAtMostOnce byte = iota
|
||||
|
||||
// QoS 1: At least once delivery
|
||||
// This quality of service ensures that the message arrives at the receiver at least once.
|
||||
// A QoS 1 PUBLISH Packet has a Packet Identifier in its variable header and is acknowledged
|
||||
// by a PUBACK Packet. Section 2.3.1 provides more information about Packet Identifiers.
|
||||
QosAtLeastOnce
|
||||
|
||||
// QoS 2: Exactly once delivery
|
||||
// This is the highest quality of service, for use when neither loss nor duplication of
|
||||
// messages are acceptable. There is an increased overhead associated with this quality of
|
||||
// service.
|
||||
QosExactlyOnce
|
||||
|
||||
// QosFailure is a return value for a subscription if there's a problem while subscribing
|
||||
// to a specific topic.
|
||||
QosFailure = 0x80
|
||||
)
|
||||
|
||||
// SupportedVersions is a map of the version number (0x3 or 0x4) to the version string,
|
||||
// "MQIsdp" for 0x3, and "MQTT" for 0x4.
|
||||
var SupportedVersions map[byte]string = map[byte]string{
|
||||
0x3: "MQIsdp",
|
||||
0x4: "MQTT",
|
||||
}
|
||||
|
||||
// MessageType is the type representing the MQTT packet types. In the MQTT spec,
|
||||
// MQTT control packet type is represented as a 4-bit unsigned value.
|
||||
type MessageType byte
|
||||
|
||||
// Message is an interface defined for all MQTT message types.
|
||||
type Message interface {
|
||||
// Name returns a string representation of the message type. Examples include
|
||||
// "PUBLISH", "SUBSCRIBE", and others. This is statically defined for each of
|
||||
// the message types and cannot be changed.
|
||||
Name() string
|
||||
|
||||
// Desc returns a string description of the message type. For example, a
|
||||
// CONNECT message would return "Client request to connect to Server." These
|
||||
// descriptions are statically defined (copied from the MQTT spec) and cannot
|
||||
// be changed.
|
||||
Desc() string
|
||||
|
||||
// Type returns the MessageType of the Message. The retured value should be one
|
||||
// of the constants defined for MessageType.
|
||||
Type() MessageType
|
||||
|
||||
// PacketId returns the packet ID of the Message. The retured value is 0 if
|
||||
// there's no packet ID for this message type. Otherwise non-0.
|
||||
PacketId() uint16
|
||||
|
||||
// Encode writes the message bytes into the byte array from the argument. It
|
||||
// returns the number of bytes encoded and whether there's any errors along
|
||||
// the way. If there's any errors, then the byte slice and count should be
|
||||
// considered invalid.
|
||||
Encode([]byte) (int, error)
|
||||
|
||||
// Decode reads the bytes in the byte slice from the argument. It returns the
|
||||
// total number of bytes decoded, and whether there's any errors during the
|
||||
// process. The byte slice MUST NOT be modified during the duration of this
|
||||
// message being available since the byte slice is internally stored for
|
||||
// references.
|
||||
Decode([]byte) (int, error)
|
||||
|
||||
Len() int
|
||||
}
|
||||
|
||||
const (
|
||||
// RESERVED is a reserved value and should be considered an invalid message type
|
||||
RESERVED MessageType = iota
|
||||
|
||||
// CONNECT: Client to Server. Client request to connect to Server.
|
||||
CONNECT
|
||||
|
||||
// CONNACK: Server to Client. Connect acknowledgement.
|
||||
CONNACK
|
||||
|
||||
// PUBLISH: Client to Server, or Server to Client. Publish message.
|
||||
PUBLISH
|
||||
|
||||
// PUBACK: Client to Server, or Server to Client. Publish acknowledgment for
|
||||
// QoS 1 messages.
|
||||
PUBACK
|
||||
|
||||
// PUBACK: Client to Server, or Server to Client. Publish received for QoS 2 messages.
|
||||
// Assured delivery part 1.
|
||||
PUBREC
|
||||
|
||||
// PUBREL: Client to Server, or Server to Client. Publish release for QoS 2 messages.
|
||||
// Assured delivery part 1.
|
||||
PUBREL
|
||||
|
||||
// PUBCOMP: Client to Server, or Server to Client. Publish complete for QoS 2 messages.
|
||||
// Assured delivery part 3.
|
||||
PUBCOMP
|
||||
|
||||
// SUBSCRIBE: Client to Server. Client subscribe request.
|
||||
SUBSCRIBE
|
||||
|
||||
// SUBACK: Server to Client. Subscribe acknowledgement.
|
||||
SUBACK
|
||||
|
||||
// UNSUBSCRIBE: Client to Server. Unsubscribe request.
|
||||
UNSUBSCRIBE
|
||||
|
||||
// UNSUBACK: Server to Client. Unsubscribe acknowlegment.
|
||||
UNSUBACK
|
||||
|
||||
// PINGREQ: Client to Server. PING request.
|
||||
PINGREQ
|
||||
|
||||
// PINGRESP: Server to Client. PING response.
|
||||
PINGRESP
|
||||
|
||||
// DISCONNECT: Client to Server. Client is disconnecting.
|
||||
DISCONNECT
|
||||
|
||||
// RESERVED2 is a reserved value and should be considered an invalid message type.
|
||||
RESERVED2
|
||||
)
|
||||
|
||||
func (this MessageType) String() string {
|
||||
return this.Name()
|
||||
}
|
||||
|
||||
// Name returns the name of the message type. It should correspond to one of the
|
||||
// constant values defined for MessageType. It is statically defined and cannot
|
||||
// be changed.
|
||||
func (this MessageType) Name() string {
|
||||
switch this {
|
||||
case RESERVED:
|
||||
return "RESERVED"
|
||||
case CONNECT:
|
||||
return "CONNECT"
|
||||
case CONNACK:
|
||||
return "CONNACK"
|
||||
case PUBLISH:
|
||||
return "PUBLISH"
|
||||
case PUBACK:
|
||||
return "PUBACK"
|
||||
case PUBREC:
|
||||
return "PUBREC"
|
||||
case PUBREL:
|
||||
return "PUBREL"
|
||||
case PUBCOMP:
|
||||
return "PUBCOMP"
|
||||
case SUBSCRIBE:
|
||||
return "SUBSCRIBE"
|
||||
case SUBACK:
|
||||
return "SUBACK"
|
||||
case UNSUBSCRIBE:
|
||||
return "UNSUBSCRIBE"
|
||||
case UNSUBACK:
|
||||
return "UNSUBACK"
|
||||
case PINGREQ:
|
||||
return "PINGREQ"
|
||||
case PINGRESP:
|
||||
return "PINGRESP"
|
||||
case DISCONNECT:
|
||||
return "DISCONNECT"
|
||||
case RESERVED2:
|
||||
return "RESERVED2"
|
||||
}
|
||||
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
// Desc returns the description of the message type. It is statically defined (copied
|
||||
// from MQTT spec) and cannot be changed.
|
||||
func (this MessageType) Desc() string {
|
||||
switch this {
|
||||
case RESERVED:
|
||||
return "Reserved"
|
||||
case CONNECT:
|
||||
return "Client request to connect to Server"
|
||||
case CONNACK:
|
||||
return "Connect acknowledgement"
|
||||
case PUBLISH:
|
||||
return "Publish message"
|
||||
case PUBACK:
|
||||
return "Publish acknowledgement"
|
||||
case PUBREC:
|
||||
return "Publish received (assured delivery part 1)"
|
||||
case PUBREL:
|
||||
return "Publish release (assured delivery part 2)"
|
||||
case PUBCOMP:
|
||||
return "Publish complete (assured delivery part 3)"
|
||||
case SUBSCRIBE:
|
||||
return "Client subscribe request"
|
||||
case SUBACK:
|
||||
return "Subscribe acknowledgement"
|
||||
case UNSUBSCRIBE:
|
||||
return "Unsubscribe request"
|
||||
case UNSUBACK:
|
||||
return "Unsubscribe acknowledgement"
|
||||
case PINGREQ:
|
||||
return "PING request"
|
||||
case PINGRESP:
|
||||
return "PING response"
|
||||
case DISCONNECT:
|
||||
return "Client is disconnecting"
|
||||
case RESERVED2:
|
||||
return "Reserved"
|
||||
}
|
||||
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
// DefaultFlags returns the default flag values for the message type, as defined by
|
||||
// the MQTT spec.
|
||||
func (this MessageType) DefaultFlags() byte {
|
||||
switch this {
|
||||
case RESERVED:
|
||||
return 0
|
||||
case CONNECT:
|
||||
return 0
|
||||
case CONNACK:
|
||||
return 0
|
||||
case PUBLISH:
|
||||
return 0
|
||||
case PUBACK:
|
||||
return 0
|
||||
case PUBREC:
|
||||
return 0
|
||||
case PUBREL:
|
||||
return 2
|
||||
case PUBCOMP:
|
||||
return 0
|
||||
case SUBSCRIBE:
|
||||
return 2
|
||||
case SUBACK:
|
||||
return 0
|
||||
case UNSUBSCRIBE:
|
||||
return 2
|
||||
case UNSUBACK:
|
||||
return 0
|
||||
case PINGREQ:
|
||||
return 0
|
||||
case PINGRESP:
|
||||
return 0
|
||||
case DISCONNECT:
|
||||
return 0
|
||||
case RESERVED2:
|
||||
return 0
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// New creates a new message based on the message type. It is a shortcut to call
|
||||
// one of the New*Message functions. If an error is returned then the message type
|
||||
// is invalid.
|
||||
func (this MessageType) New() (Message, error) {
|
||||
switch this {
|
||||
case CONNECT:
|
||||
return NewConnectMessage(), nil
|
||||
case CONNACK:
|
||||
return NewConnackMessage(), nil
|
||||
case PUBLISH:
|
||||
return NewPublishMessage(), nil
|
||||
case PUBACK:
|
||||
return NewPubackMessage(), nil
|
||||
case PUBREC:
|
||||
return NewPubrecMessage(), nil
|
||||
case PUBREL:
|
||||
return NewPubrelMessage(), nil
|
||||
case PUBCOMP:
|
||||
return NewPubcompMessage(), nil
|
||||
case SUBSCRIBE:
|
||||
return NewSubscribeMessage(), nil
|
||||
case SUBACK:
|
||||
return NewSubackMessage(), nil
|
||||
case UNSUBSCRIBE:
|
||||
return NewUnsubscribeMessage(), nil
|
||||
case UNSUBACK:
|
||||
return NewUnsubackMessage(), nil
|
||||
case PINGREQ:
|
||||
return NewPingreqMessage(), nil
|
||||
case PINGRESP:
|
||||
return NewPingrespMessage(), nil
|
||||
case DISCONNECT:
|
||||
return NewDisconnectMessage(), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("msgtype/NewMessage: Invalid message type %d", this)
|
||||
}
|
||||
|
||||
// Valid returns a boolean indicating whether the message type is valid or not.
|
||||
func (this MessageType) Valid() bool {
|
||||
return this > RESERVED && this < RESERVED2
|
||||
}
|
||||
|
||||
// ValidTopic checks the topic, which is a slice of bytes, to see if it's valid. Topic is
|
||||
// considered valid if it's longer than 0 bytes, and doesn't contain any wildcard characters
|
||||
// such as + and #.
|
||||
func ValidTopic(topic []byte) bool {
|
||||
return len(topic) > 0 && bytes.IndexByte(topic, '#') == -1 && bytes.IndexByte(topic, '+') == -1
|
||||
}
|
||||
|
||||
// ValidQos checks the QoS value to see if it's valid. Valid QoS are QosAtMostOnce,
|
||||
// QosAtLeastonce, and QosExactlyOnce.
|
||||
func ValidQos(qos byte) bool {
|
||||
return qos == QosAtMostOnce || qos == QosAtLeastOnce || qos == QosExactlyOnce
|
||||
}
|
||||
|
||||
// ValidVersion checks to see if the version is valid. Current supported versions include 0x3 and 0x4.
|
||||
func ValidVersion(v byte) bool {
|
||||
_, ok := SupportedVersions[v]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ValidConnackError checks to see if the error is a Connack Error or not
|
||||
func ValidConnackError(err error) bool {
|
||||
return err == ErrInvalidProtocolVersion || err == ErrIdentifierRejected ||
|
||||
err == ErrServerUnavailable || err == ErrBadUsernameOrPassword || err == ErrNotAuthorized
|
||||
}
|
||||
|
||||
func ValidConnackErrorEx(err error) (bool, ConnackCode) {
|
||||
if err == ErrInvalidProtocolVersion {
|
||||
return true, ErrInvalidProtocolVersion
|
||||
}
|
||||
if err == ErrIdentifierRejected {
|
||||
return true, ErrIdentifierRejected
|
||||
}
|
||||
if err == ErrServerUnavailable {
|
||||
return true, ErrServerUnavailable
|
||||
}
|
||||
if err == ErrBadUsernameOrPassword {
|
||||
return true, ErrBadUsernameOrPassword
|
||||
}
|
||||
if err == ErrNotAuthorized {
|
||||
return true, ErrNotAuthorized
|
||||
}
|
||||
return false, ConnectionAccepted
|
||||
|
||||
}
|
||||
|
||||
// Read length prefixed bytes
|
||||
func readLPBytes(buf []byte) ([]byte, int, error) {
|
||||
if len(buf) < 2 {
|
||||
return nil, 0, fmt.Errorf("utils/readLPBytes: Insufficient buffer size. Expecting %d, got %d.", 2, len(buf))
|
||||
}
|
||||
|
||||
n, total := 0, 0
|
||||
|
||||
n = int(binary.BigEndian.Uint16(buf))
|
||||
total += 2
|
||||
|
||||
if len(buf) < n {
|
||||
return nil, total, fmt.Errorf("utils/readLPBytes: Insufficient buffer size. Expecting %d, got %d.", n, len(buf))
|
||||
}
|
||||
|
||||
total += n
|
||||
|
||||
return buf[2:total], total, nil
|
||||
}
|
||||
|
||||
// Write length prefixed bytes
|
||||
func writeLPBytes(buf []byte, b []byte) (int, error) {
|
||||
total, n := 0, len(b)
|
||||
|
||||
if n > int(maxLPString) {
|
||||
return 0, fmt.Errorf("utils/writeLPBytes: Length (%d) greater than %d bytes.", n, maxLPString)
|
||||
}
|
||||
|
||||
if len(buf) < 2+n {
|
||||
return 0, fmt.Errorf("utils/writeLPBytes: Insufficient buffer size. Expecting %d, got %d.", 2+n, len(buf))
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf, uint16(n))
|
||||
total += 2
|
||||
|
||||
copy(buf[total:], b)
|
||||
total += n
|
||||
|
||||
return total, nil
|
||||
}
|
||||
Reference in New Issue
Block a user