1/*
2 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16#include <aws/core/utils/event/EventHeader.h>
17#include <aws/core/utils/event/EventMessage.h>
18#include <aws/core/utils/event/EventStreamEncoder.h>
19#include <aws/core/utils/logging/LogMacros.h>
20#include <aws/core/auth/AWSAuthSigner.h>
21#include <aws/common/byte_order.h>
22#include <aws/core/utils/memory/AWSMemory.h>
23
24#include <cassert>
25
26namespace Aws
27{
28 namespace Utils
29 {
30 namespace Event
31 {
32 static const char TAG[] = "EventStreamEncoder";
33
34 static void EncodeHeaders(const Aws::Utils::Event::Message& msg, aws_array_list* headers)
35 {
36 aws_array_list_init_dynamic(headers, get_aws_allocator(), msg.GetEventHeaders().size(), sizeof(aws_event_stream_header_value_pair));
37 for (auto&& header : msg.GetEventHeaders())
38 {
39 const uint8_t headerKeyLen = static_cast<uint8_t>(header.first.length());
40 switch(header.second.GetType())
41 {
42 case EventHeaderValue::EventHeaderType::BOOL_TRUE:
43 case EventHeaderValue::EventHeaderType::BOOL_FALSE:
44 aws_event_stream_add_bool_header(headers, header.first.c_str(), headerKeyLen, header.second.GetEventHeaderValueAsBoolean());
45 break;
46 case EventHeaderValue::EventHeaderType::BYTE:
47 aws_event_stream_add_bool_header(headers, header.first.c_str(), headerKeyLen, header.second.GetEventHeaderValueAsByte());
48 break;
49 case EventHeaderValue::EventHeaderType::INT16:
50 aws_event_stream_add_int16_header(headers, header.first.c_str(), headerKeyLen, header.second.GetEventHeaderValueAsInt16());
51 break;
52 case EventHeaderValue::EventHeaderType::INT32:
53 aws_event_stream_add_int32_header(headers, header.first.c_str(), headerKeyLen, header.second.GetEventHeaderValueAsInt32());
54 break;
55 case EventHeaderValue::EventHeaderType::INT64:
56 aws_event_stream_add_int64_header(headers, header.first.c_str(), headerKeyLen, header.second.GetEventHeaderValueAsInt64());
57 break;
58 case EventHeaderValue::EventHeaderType::BYTE_BUF:
59 {
60 const auto& bytes = header.second.GetEventHeaderValueAsBytebuf();
61 aws_event_stream_add_bytebuf_header(headers, header.first.c_str(), headerKeyLen, bytes.GetUnderlyingData(), static_cast<uint16_t>(bytes.GetLength()), 1 /*copy*/);
62 }
63 break;
64 case EventHeaderValue::EventHeaderType::STRING:
65 {
66 const auto& bytes = header.second.GetUnderlyingBuffer();
67 aws_event_stream_add_string_header(headers, header.first.c_str(), headerKeyLen, reinterpret_cast<char*>(bytes.GetUnderlyingData()), static_cast<uint16_t>(bytes.GetLength()), 0 /*copy*/);
68 }
69 break;
70 case EventHeaderValue::EventHeaderType::TIMESTAMP:
71 aws_event_stream_add_timestamp_header(headers, header.first.c_str(), headerKeyLen, header.second.GetEventHeaderValueAsTimestamp());
72 break;
73 case EventHeaderValue::EventHeaderType::UUID:
74 {
75 ByteBuffer uuidBytes = header.second.GetEventHeaderValueAsUuid();
76 aws_event_stream_add_uuid_header(headers, header.first.c_str(), headerKeyLen, uuidBytes.GetUnderlyingData());
77 }
78 break;
79 default:
80 AWS_LOG_ERROR(TAG, "Encountered unknown type of header.");
81 break;
82 }
83 }
84 }
85
86 EventStreamEncoder::EventStreamEncoder(Client::AWSAuthSigner* signer) : m_signer(signer)
87 {
88 }
89
90
91 Aws::Vector<unsigned char> EventStreamEncoder::EncodeAndSign(const Aws::Utils::Event::Message& msg)
92 {
93 aws_event_stream_message encoded = Encode(msg);
94 aws_event_stream_message signedMessage = Sign(&encoded);
95
96 const auto signedMessageLength = signedMessage.message_buffer ? aws_event_stream_message_total_length(&signedMessage) : 0;
97
98 Aws::Vector<unsigned char> outputBits(signedMessage.message_buffer, signedMessage.message_buffer + signedMessageLength);
99 aws_event_stream_message_clean_up(&encoded);
100 aws_event_stream_message_clean_up(&signedMessage);
101 return outputBits;
102 }
103
104 aws_event_stream_message EventStreamEncoder::Encode(const Aws::Utils::Event::Message& msg)
105 {
106 aws_array_list headers;
107 EncodeHeaders(msg, &headers);
108
109 aws_byte_buf payload;
110 payload.len = msg.GetEventPayload().size();
111 // this const_cast is OK because aws_byte_buf will only be "read from" by the following functions.
112 payload.buffer = const_cast<uint8_t*>(msg.GetEventPayload().data());
113 payload.capacity = 0;
114 payload.allocator = nullptr;
115
116 aws_event_stream_message encoded;
117 if(aws_event_stream_message_init(&encoded, get_aws_allocator(), &headers, &payload) == AWS_OP_ERR)
118 {
119 AWS_LOGSTREAM_ERROR(TAG, "Error creating event-stream message from paylaod.");
120 aws_event_stream_headers_list_cleanup(&headers);
121 // GCC 4.9.4 issues a warning with -Wextra if we simply do
122 // return {};
123 aws_event_stream_message empty{nullptr, nullptr, 0};
124 return empty;
125 }
126 aws_event_stream_headers_list_cleanup(&headers);
127 return encoded;
128 }
129
130 aws_event_stream_message EventStreamEncoder::Sign(aws_event_stream_message* msg)
131 {
132 const auto msglen = msg->message_buffer ? aws_event_stream_message_total_length(msg) : 0;
133 Event::Message signedMessage;
134 signedMessage.WriteEventPayload(msg->message_buffer, msglen);
135
136 assert(m_signer);
137 if (!m_signer->SignEventMessage(signedMessage, m_signatureSeed))
138 {
139 AWS_LOGSTREAM_ERROR(TAG, "Failed to sign event message frame.");
140 // GCC 4.9.4 issues a warning with -Wextra if we simply do
141 // return {};
142 aws_event_stream_message empty{nullptr, nullptr, 0};
143 return empty;
144 }
145
146 aws_array_list headers;
147 EncodeHeaders(signedMessage, &headers);
148
149 aws_byte_buf payload;
150 payload.len = signedMessage.GetEventPayload().size();
151 payload.buffer = signedMessage.GetEventPayload().data();
152 payload.capacity = 0;
153 payload.allocator = nullptr;
154
155 aws_event_stream_message signedmsg;
156 if(aws_event_stream_message_init(&signedmsg, get_aws_allocator(), &headers, &payload))
157 {
158 AWS_LOGSTREAM_ERROR(TAG, "Error creating event-stream message from paylaod.");
159 aws_event_stream_headers_list_cleanup(&headers);
160 // GCC 4.9.4 issues a warning with -Wextra if we simply do
161 // return {};
162 aws_event_stream_message empty{nullptr, nullptr, 0};
163 return empty;
164 }
165 aws_event_stream_headers_list_cleanup(&headers);
166 return signedmsg;
167 }
168
169 } // namespace Event
170 } // namespace Utils
171} // namespace Aws
172
173