1// Copyright (C) MongoDB, Inc. 2017-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package wiremessage
8
9import (
10	"fmt"
11)
12
13// ErrInvalidHeader is returned when methods are called on a malformed Header.
14var ErrInvalidHeader error = Error{Type: ErrHeader, Message: "invalid header"}
15
16// ErrHeaderTooSmall is returned when the size of the header is too small to be valid.
17var ErrHeaderTooSmall error = Error{Type: ErrHeader, Message: "the header is too small to be valid"}
18
19// ErrHeaderTooFewBytes is returned when a call to ReadHeader does not contain enough
20// bytes to be a valid header.
21var ErrHeaderTooFewBytes error = Error{Type: ErrHeader, Message: "invalid header because []byte too small"}
22
23// ErrHeaderInvalidLength is returned when the MessageLength of a header is
24// set but is not set to the correct size.
25var ErrHeaderInvalidLength error = Error{Type: ErrHeader, Message: "invalid header because MessageLength is imporperly set"}
26
27// ErrHeaderIncorrectOpCode is returned when the OpCode on a header is set but
28// is not set to the correct OpCode.
29var ErrHeaderIncorrectOpCode error = Error{Type: ErrHeader, Message: "invalid header because OpCode is improperly set"}
30
31// Header represents the header of a MongoDB wire protocol message.
32type Header struct {
33	MessageLength int32
34	RequestID     int32
35	ResponseTo    int32
36	OpCode        OpCode
37}
38
39// ReadHeader reads a header from the given slice of bytes starting at offset
40// pos.
41func ReadHeader(b []byte, pos int32) (Header, error) {
42	if len(b) < 16 {
43		return Header{}, ErrHeaderTooFewBytes
44	}
45	return Header{
46		MessageLength: readInt32(b, 0),
47		RequestID:     readInt32(b, 4),
48		ResponseTo:    readInt32(b, 8),
49		OpCode:        OpCode(readInt32(b, 12)),
50	}, nil
51}
52
53func (h Header) String() string {
54	return fmt.Sprintf(
55		`Header{MessageLength: %d, RequestID: %d, ResponseTo: %d, OpCode: %v}`,
56		h.MessageLength, h.RequestID, h.ResponseTo, h.OpCode,
57	)
58}
59
60// AppendHeader will append this header to the given slice of bytes.
61func (h Header) AppendHeader(b []byte) []byte {
62	b = appendInt32(b, h.MessageLength)
63	b = appendInt32(b, h.RequestID)
64	b = appendInt32(b, h.ResponseTo)
65	b = appendInt32(b, int32(h.OpCode))
66
67	return b
68}
69
70// SetDefaults sets the length and opcode of this header.
71func (h *Header) SetDefaults(length int, opcode OpCode) error {
72	switch h.MessageLength {
73	case int32(length):
74	case 0:
75		h.MessageLength = int32(length)
76	default:
77		return ErrHeaderInvalidLength
78	}
79	switch h.OpCode {
80	case opcode:
81	case OpCode(0):
82		h.OpCode = opcode
83	default:
84		return ErrHeaderIncorrectOpCode
85	}
86	return nil
87}
88