aws-eventstream 1.0.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0bd89e470bb670750b5db3fa159c05c47f6ae848
4
- data.tar.gz: 0e6a31068166775645f803c36f2aa27c7b7341ce
2
+ SHA256:
3
+ metadata.gz: 9f37cbd739b4d6e3bcd8bfa8e67b2a3e4f8688fe8f424746bfdebd99c4c16919
4
+ data.tar.gz: ba94156d2e21beed7afc345c7ac6bf20ea4aadf8546314fbe7941bb9df0f1c9f
5
5
  SHA512:
6
- metadata.gz: 475cdba6288c5ba3224a65d18f9b3d16feba99d2b9868043af4c01194adeeb55f686200cac511e3067465ffe8b7cfa93dbe306751f31f6ce93966d5b052f4b6c
7
- data.tar.gz: b96b77164bc0e6e1f9095de872400f9a589f912d450f6cdb0c40c3b769888f38f90be9b572b09f2715f1358ba43b71d06842d95c628108e57bdd1c5201aba3be
6
+ metadata.gz: df1d5d4f2e01bcec3776fb398134705398276da232af8b5fd2d1370addf66c222e3437d401cdcee8ca9371d8856c2f3eba3f2a9711bff8400d7e9f75569e86e7
7
+ data.tar.gz: 4a5a585db66d623d7ee7a153e5881c065422210ba7972221ce577031b84204dfa5061d84b4e2a3336a0ae92bea5c5cd7e65824f976a8e459a9db29eb2146c0ad
data/CHANGELOG.md ADDED
@@ -0,0 +1,32 @@
1
+ Unreleased Changes
2
+ ------------------
3
+
4
+ 1.1.1 (2021-03-04)
5
+ ------------------
6
+
7
+ * Issue - Include LICENSE, CHANGELOG, and VERSION files with this gem.
8
+
9
+ 1.1.0 (2020-04-08)
10
+ ------------------
11
+
12
+ * Feature - Remove internal ByteBuffer and replace with String to remove dup and string mutation.
13
+
14
+ 1.0.3 (2019-04-24)
15
+ ------------------
16
+
17
+ * Issue - Use single quotes for string where interpolation is not done.
18
+
19
+ 1.0.2 (2019-03-11)
20
+ ------------------
21
+
22
+ * Issue - public #encode_headers method
23
+
24
+ 1.0.1 (2018-06-15)
25
+ ------------------
26
+
27
+ * Issue - #decode_chunk buffers insufficient prelude message
28
+
29
+ 1.0.0 (2018-05-10)
30
+ ------------------
31
+
32
+ * Feature - Initial release of `aws-eventstream` gem.
data/LICENSE.txt ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.1
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'aws-eventstream/decoder'
2
4
  require_relative 'aws-eventstream/encoder'
3
5
 
4
- require_relative 'aws-eventstream/bytes_buffer'
5
6
  require_relative 'aws-eventstream/message'
6
7
  require_relative 'aws-eventstream/header_value'
7
8
  require_relative 'aws-eventstream/types'
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'stringio'
2
4
  require 'tempfile'
3
5
  require 'zlib'
4
6
 
5
7
  module Aws
6
- module EventStream
8
+ module EventStream
7
9
 
8
10
  # This class provides method for decoding binary inputs into
9
11
  # single or multiple messages (Aws::EventStream::Message).
@@ -28,7 +30,7 @@ module Aws
28
30
  # message_pool = decoder.decode(io)
29
31
  # message_pool.next
30
32
  # # => Aws::EventStream::Message
31
- #
33
+ #
32
34
  # * {#decode_chunk} - decodes a single message from a chunk of data,
33
35
  # returning message object followed by boolean(indicating eof status
34
36
  # of data) in an array object
@@ -63,27 +65,26 @@ module Aws
63
65
  include Enumerable
64
66
 
65
67
  ONE_MEGABYTE = 1024 * 1024
68
+ private_constant :ONE_MEGABYTE
66
69
 
67
70
  # bytes of prelude part, including 4 bytes of
68
71
  # total message length, headers length and crc checksum of prelude
69
72
  PRELUDE_LENGTH = 12
73
+ private_constant :PRELUDE_LENGTH
70
74
 
71
- # bytes of total overhead in a message, including prelude
72
- # and 4 bytes total message crc checksum
73
- OVERHEAD_LENGTH = 16
75
+ # 4 bytes message crc checksum
76
+ CRC32_LENGTH = 4
77
+ private_constant :CRC32_LENGTH
74
78
 
75
- # @options options [Boolean] format (true) When `false`
76
- # disable user-friendly formatting for message header values
79
+ # @param [Hash] options The initialization options.
80
+ # @option options [Boolean] :format (true) When `false` it
81
+ # disables user-friendly formatting for message header values
77
82
  # including timestamp and uuid etc.
78
- #
79
83
  def initialize(options = {})
80
84
  @format = options.fetch(:format, true)
81
- @message_buffer = BytesBuffer.new('')
85
+ @message_buffer = ''
82
86
  end
83
87
 
84
- # @returns [BytesBuffer]
85
- attr_reader :message_buffer
86
-
87
88
  # Decodes messages from a binary stream
88
89
  #
89
90
  # @param [IO#read] io An IO-like object
@@ -93,12 +94,12 @@ module Aws
93
94
  # @return [Enumerable<Message>, nil] Returns a new Enumerable
94
95
  # containing decoded messages if no block is given
95
96
  def decode(io, &block)
96
- io = BytesBuffer.new(io.read)
97
- return decode_io(io) unless block_given?
98
- until io.eof?
99
- # fetch message only
100
- yield(decode_message(io).first)
101
- end
97
+ raw_message = io.read
98
+ decoded_message = decode_message(raw_message)
99
+ return wrap_as_enumerator(decoded_message) unless block_given?
100
+ # fetch message only
101
+ raw_event, _eof = decoded_message
102
+ block.call(raw_event)
102
103
  end
103
104
 
104
105
  # Decodes a single message from a chunk of string
@@ -111,92 +112,81 @@ module Aws
111
112
  # and boolean pair, the boolean flag indicates whether this chunk
112
113
  # has been fully consumed, unused data is tracked at #message_buffer
113
114
  def decode_chunk(chunk = nil)
114
- @message_buffer.write(chunk) if chunk
115
- @message_buffer.rewind
115
+ @message_buffer = [@message_buffer, chunk].pack('a*a*') if chunk
116
116
  decode_message(@message_buffer)
117
117
  end
118
118
 
119
119
  private
120
120
 
121
- def decode_io(io)
122
- ::Enumerator.new {|e| e << decode_message(io) unless io.eof? }
121
+ # exposed via object.send for testing
122
+ attr_reader :message_buffer
123
+
124
+ def wrap_as_enumerator(decoded_message)
125
+ Enumerator.new do |yielder|
126
+ yielder << decoded_message
127
+ end
123
128
  end
124
129
 
125
- def decode_message(io)
130
+ def decode_message(raw_message)
131
+ # incomplete message prelude received
132
+ return [nil, true] if raw_message.bytesize < PRELUDE_LENGTH
133
+
134
+ prelude, content = raw_message.unpack("a#{PRELUDE_LENGTH}a*")
135
+
126
136
  # decode prelude
127
- total_len, headers_len, prelude_buffer = prelude(io)
137
+ total_length, header_length = decode_prelude(prelude)
128
138
 
129
139
  # incomplete message received, leave it in the buffer
130
- return [nil, true] if io.bytesize < total_len
140
+ return [nil, true] if raw_message.bytesize < total_length
131
141
 
132
- # decode headers and payload
133
- headers, payload = context(io, total_len, headers_len, prelude_buffer)
134
-
135
- # track extra message data in the buffer if exists
136
- # for #decode_chunk, io is @message_buffer
137
- if eof = io.eof?
138
- @message_buffer.clear!
139
- else
140
- @message_buffer = BytesBuffer.new(@message_buffer.read)
142
+ content, checksum, remaining = content.unpack("a#{total_length - PRELUDE_LENGTH - CRC32_LENGTH}Na*")
143
+ unless Zlib.crc32([prelude, content].pack('a*a*')) == checksum
144
+ raise Errors::MessageChecksumError
141
145
  end
142
146
 
143
- [Message.new(headers: headers, payload: payload), eof]
147
+ # decode headers and payload
148
+ headers, payload = decode_context(content, header_length)
149
+
150
+ @message_buffer = remaining
151
+
152
+ [Message.new(headers: headers, payload: payload), remaining.empty?]
144
153
  end
145
154
 
146
- def prelude(io)
147
- # buffer prelude into bytes buffer
155
+ def decode_prelude(prelude)
148
156
  # prelude contains length of message and headers,
149
157
  # followed with CRC checksum of itself
150
- buffer = BytesBuffer.new(io.read(PRELUDE_LENGTH))
151
-
152
- # prelude checksum takes last 4 bytes
153
- checksum = Zlib.crc32(buffer.read(PRELUDE_LENGTH - 4))
154
- unless checksum == unpack_uint32(buffer)
155
- raise Errors::PreludeChecksumError
156
- end
157
-
158
- buffer.rewind
159
- total_len, headers_len, _ = buffer.read.unpack("N*")
160
- [total_len, headers_len, buffer]
158
+ content, checksum = prelude.unpack("a#{PRELUDE_LENGTH - CRC32_LENGTH}N")
159
+ raise Errors::PreludeChecksumError unless Zlib.crc32(content) == checksum
160
+ content.unpack('N*')
161
161
  end
162
162
 
163
- def context(io, total_len, headers_len, prelude_buffer)
164
- # buffer rest of the message except prelude length
165
- # including context and total message checksum
166
- buffer = BytesBuffer.new(io.read(total_len - PRELUDE_LENGTH))
167
- context_len = total_len - OVERHEAD_LENGTH
168
-
169
- prelude_buffer.rewind
170
- checksum = Zlib.crc32(prelude_buffer.read << buffer.read(context_len))
171
- unless checksum == unpack_uint32(buffer)
172
- raise Errors::MessageChecksumError
173
- end
174
-
175
- buffer.rewind
163
+ def decode_context(content, header_length)
164
+ encoded_header, encoded_payload = content.unpack("a#{header_length}a*")
176
165
  [
177
- extract_headers(BytesBuffer.new(buffer.read(headers_len))),
178
- extract_payload(BytesBuffer.new(buffer.read(context_len - headers_len)))
166
+ extract_headers(encoded_header),
167
+ extract_payload(encoded_payload)
179
168
  ]
180
169
  end
181
170
 
182
171
  def extract_headers(buffer)
172
+ scanner = buffer
183
173
  headers = {}
184
- until buffer.eof?
174
+ until scanner.bytesize == 0
185
175
  # header key
186
- key_len = unpack_uint8(buffer)
187
- key = buffer.read(key_len)
176
+ key_length, scanner = scanner.unpack('Ca*')
177
+ key, scanner = scanner.unpack("a#{key_length}a*")
188
178
 
189
179
  # header value
190
- value_type = Types.types[unpack_uint8(buffer)]
191
- unpack_pattern, value_len, _ = Types.pattern[value_type]
192
- if !!unpack_pattern == unpack_pattern
180
+ type_index, scanner = scanner.unpack('Ca*')
181
+ value_type = Types.types[type_index]
182
+ unpack_pattern, value_length = Types.pattern[value_type]
183
+ value = if !!unpack_pattern == unpack_pattern
193
184
  # boolean types won't have value specified
194
- value = unpack_pattern
185
+ unpack_pattern
195
186
  else
196
- value_len = unpack_uint16(buffer) unless value_len
197
- value = unpack_pattern ?
198
- buffer.read(value_len).unpack(unpack_pattern)[0] :
199
- buffer.read(value_len)
187
+ value_length, scanner = scanner.unpack('S>a*') unless value_length
188
+ unpacked_value, scanner = scanner.unpack("#{unpack_pattern || "a#{value_length}"}a*")
189
+ unpacked_value
200
190
  end
201
191
 
202
192
  headers[key] = HeaderValue.new(
@@ -208,40 +198,23 @@ module Aws
208
198
  headers
209
199
  end
210
200
 
211
- def extract_payload(buffer)
212
- buffer.bytesize <= ONE_MEGABYTE ?
213
- payload_stringio(buffer) :
214
- payload_tempfile(buffer)
201
+ def extract_payload(encoded)
202
+ encoded.bytesize <= ONE_MEGABYTE ?
203
+ payload_stringio(encoded) :
204
+ payload_tempfile(encoded)
215
205
  end
216
206
 
217
- def payload_stringio(buffer)
218
- StringIO.new(buffer.read)
207
+ def payload_stringio(encoded)
208
+ StringIO.new(encoded)
219
209
  end
220
210
 
221
- def payload_tempfile(buffer)
211
+ def payload_tempfile(encoded)
222
212
  payload = Tempfile.new
223
213
  payload.binmode
224
- until buffer.eof?
225
- payload.write(buffer.read(ONE_MEGABYTE))
226
- end
214
+ payload.write(encoded)
227
215
  payload.rewind
228
216
  payload
229
217
  end
230
-
231
- # overhead decode helpers
232
-
233
- def unpack_uint32(buffer)
234
- buffer.read(4).unpack("N")[0]
235
- end
236
-
237
- def unpack_uint16(buffer)
238
- buffer.read(2).unpack("S>")[0]
239
- end
240
-
241
- def unpack_uint8(buffer)
242
- buffer.readbyte.unpack("C")[0]
243
- end
244
218
  end
245
-
246
219
  end
247
220
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zlib'
2
4
 
3
5
  module Aws
4
- module EventStream
6
+ module EventStream
5
7
 
6
8
  # This class provides #encode method for encoding
7
9
  # Aws::EventStream::Message into binary.
@@ -39,6 +41,12 @@ module Aws
39
41
  # and 4 bytes total message crc checksum
40
42
  OVERHEAD_LENGTH = 16
41
43
 
44
+ # Maximum header length allowed (after encode) 128kb
45
+ MAX_HEADERS_LENGTH = 131072
46
+
47
+ # Maximum payload length allowed (after encode) 16mb
48
+ MAX_PAYLOAD_LENGTH = 16777216
49
+
42
50
  # Encodes Aws::EventStream::Message to output IO when
43
51
  # provided, else return the encoded binary string
44
52
  #
@@ -53,7 +61,7 @@ module Aws
53
61
  # will be returned. Else, encoded binary string is
54
62
  # returned.
55
63
  def encode(message, io = nil)
56
- encoded = encode_message(message).read
64
+ encoded = encode_message(message)
57
65
  if io
58
66
  io.write(encoded)
59
67
  io.close
@@ -62,75 +70,73 @@ module Aws
62
70
  end
63
71
  end
64
72
 
65
- private
66
-
73
+ # Encodes an Aws::EventStream::Message
74
+ # into String
75
+ #
76
+ # @param [Aws::EventStream::Message] message
77
+ #
78
+ # @return [String]
67
79
  def encode_message(message)
68
80
  # create context buffer with encode headers
69
- ctx_buffer = encode_headers(message)
70
- headers_len = ctx_buffer.bytesize
81
+ encoded_header = encode_headers(message)
82
+ header_length = encoded_header.bytesize
71
83
  # encode payload
72
- ctx_buffer << message.payload.read
73
- total_len = ctx_buffer.bytesize + OVERHEAD_LENGTH
84
+ if message.payload.length > MAX_PAYLOAD_LENGTH
85
+ raise Aws::EventStream::Errors::EventPayloadLengthExceedError.new
86
+ end
87
+ encoded_payload = message.payload.read
88
+ total_length = header_length + encoded_payload.bytesize + OVERHEAD_LENGTH
74
89
 
75
90
  # create message buffer with prelude section
76
- buffer = prelude(total_len, headers_len)
91
+ encoded_prelude = encode_prelude(total_length, header_length)
77
92
 
78
93
  # append message context (headers, payload)
79
- buffer << ctx_buffer.read
94
+ encoded_content = [
95
+ encoded_prelude,
96
+ encoded_header,
97
+ encoded_payload,
98
+ ].pack('a*a*a*')
80
99
  # append message checksum
81
- buffer << pack_uint32(Zlib.crc32(buffer.read))
82
-
83
- # write buffered message to io
84
- buffer.rewind
85
- buffer
100
+ message_checksum = Zlib.crc32(encoded_content)
101
+ [encoded_content, message_checksum].pack('a*N')
86
102
  end
87
103
 
88
- def encode_headers(msg)
89
- buffer = BytesBuffer.new('')
90
- msg.headers.each do |k, v|
91
- # header key
92
- buffer << pack_uint8(k.bytesize)
93
- buffer << k
104
+ # Encodes headers part of an Aws::EventStream::Message
105
+ # into String
106
+ #
107
+ # @param [Aws::EventStream::Message] message
108
+ #
109
+ # @return [String]
110
+ def encode_headers(message)
111
+ header_entries = message.headers.map do |key, value|
112
+ encoded_key = [key.bytesize, key].pack('Ca*')
94
113
 
95
114
  # header value
96
- pattern, val_len, idx = Types.pattern[v.type]
97
- buffer << pack_uint8(idx)
115
+ pattern, value_length, type_index = Types.pattern[value.type]
116
+ encoded_value = [type_index].pack('C')
98
117
  # boolean types doesn't need to specify value
99
- next if !!pattern == pattern
100
- buffer << pack_uint16(v.value.bytesize) unless val_len
101
- pattern ? buffer << [v.value].pack(pattern) :
102
- buffer << v.value
118
+ next [encoded_key, encoded_value].pack('a*a*') if !!pattern == pattern
119
+ encoded_value = [encoded_value, value.value.bytesize].pack('a*S>') unless value_length
120
+
121
+ [
122
+ encoded_key,
123
+ encoded_value,
124
+ pattern ? [value.value].pack(pattern) : value.value,
125
+ ].pack('a*a*a*')
126
+ end
127
+ header_entries.join.tap do |encoded_header|
128
+ break encoded_header if encoded_header.bytesize <= MAX_HEADERS_LENGTH
129
+ raise Aws::EventStream::Errors::EventHeadersLengthExceedError.new
103
130
  end
104
- buffer
105
- end
106
-
107
- def prelude(total_len, headers_len)
108
- BytesBuffer.new(pack_uint32([
109
- total_len,
110
- headers_len,
111
- Zlib.crc32(pack_uint32([total_len, headers_len]))
112
- ]))
113
- end
114
-
115
- # overhead encode helpers
116
-
117
- def pack_uint8(val)
118
- [val].pack("C")
119
131
  end
120
132
 
121
- def pack_uint16(val)
122
- [val].pack("S>")
123
- end
133
+ private
124
134
 
125
- def pack_uint32(val)
126
- if val.respond_to?(:each)
127
- val.pack("N*")
128
- else
129
- [val].pack("N")
130
- end
135
+ def encode_prelude(total_length, headers_length)
136
+ prelude_body = [total_length, headers_length].pack('NN')
137
+ checksum = Zlib.crc32(prelude_body)
138
+ [prelude_body, checksum].pack('a*N')
131
139
  end
132
-
133
140
  end
134
-
135
141
  end
136
142
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module EventStream
3
5
  module Errors
@@ -6,7 +8,7 @@ module Aws
6
8
  class ReadBytesExceedLengthError < RuntimeError
7
9
  def initialize(target_byte, total_len)
8
10
  msg = "Attempting reading bytes to offset #{target_byte} exceeds"\
9
- " buffer length of #{total_len}"
11
+ " buffer length of #{total_len}"
10
12
  super(msg)
11
13
  end
12
14
  end
@@ -14,19 +16,31 @@ module Aws
14
16
  # Raise when insufficient bytes of a message is received
15
17
  class IncompleteMessageError < RuntimeError
16
18
  def initialize(*args)
17
- super("Not enough bytes for event message")
19
+ super('Not enough bytes for event message')
18
20
  end
19
21
  end
20
22
 
21
23
  class PreludeChecksumError < RuntimeError
22
24
  def initialize(*args)
23
- super("Prelude checksum mismatch")
25
+ super('Prelude checksum mismatch')
24
26
  end
25
27
  end
26
28
 
27
29
  class MessageChecksumError < RuntimeError
28
30
  def initialize(*args)
29
- super("Message checksum mismatch")
31
+ super('Message checksum mismatch')
32
+ end
33
+ end
34
+
35
+ class EventPayloadLengthExceedError < RuntimeError
36
+ def initialize(*args)
37
+ super("Payload length of a message should be under 16mb.")
38
+ end
39
+ end
40
+
41
+ class EventHeadersLengthExceedError < RuntimeError
42
+ def initialize(*args)
43
+ super("Encoded headers length of a message should be under 128kb.")
30
44
  end
31
45
  end
32
46
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module EventStream
3
5
 
@@ -20,8 +22,8 @@ module Aws
20
22
 
21
23
  def format_value(value)
22
24
  case @type
23
- when "timestamp" then format_timestamp(value)
24
- when "uuid" then format_uuid(value)
25
+ when 'timestamp' then format_timestamp(value)
26
+ when 'uuid' then format_uuid(value)
25
27
  else
26
28
  value
27
29
  end
@@ -32,7 +34,7 @@ module Aws
32
34
  # For user-friendly uuid representation,
33
35
  # format binary bytes into uuid string format
34
36
  uuid_pattern = [ [ 3, 2, 1, 0 ], [ 5, 4 ], [ 7, 6 ], [ 8, 9 ], 10..15 ]
35
- uuid_pattern.map {|p| p.map {|n| "%02x" % bytes.to_a[n] }.join }.join("-")
37
+ uuid_pattern.map {|p| p.map {|n| "%02x" % bytes.to_a[n] }.join }.join('-')
36
38
  end
37
39
 
38
40
  def format_timestamp(value)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module EventStream
3
5
  class Message
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module EventStream
3
5
 
@@ -6,32 +8,32 @@ module Aws
6
8
 
7
9
  def self.types
8
10
  [
9
- "bool_true",
10
- "bool_false",
11
- "byte",
12
- "short",
13
- "integer",
14
- "long",
15
- "bytes",
16
- "string",
17
- "timestamp",
18
- "uuid"
11
+ 'bool_true',
12
+ 'bool_false',
13
+ 'byte',
14
+ 'short',
15
+ 'integer',
16
+ 'long',
17
+ 'bytes',
18
+ 'string',
19
+ 'timestamp',
20
+ 'uuid'
19
21
  ]
20
22
  end
21
23
 
22
24
  # pack/unpack pattern, byte size, type idx
23
25
  def self.pattern
24
26
  {
25
- "bool_true" => [true, 0, 0],
26
- "bool_false" => [false, 0, 1],
27
- "byte" => ["c", 1, 2],
28
- "short" => ["s>", 2, 3],
29
- "integer" => ["l>", 4, 4],
30
- "long" => ["q>", 8, 5],
31
- "bytes" => [nil, nil, 6],
32
- "string" => [nil, nil, 7],
33
- "timestamp" => ["q>", 8, 8],
34
- "uuid" => [nil, 16, 9]
27
+ 'bool_true' => [true, 0, 0],
28
+ 'bool_false' => [false, 0, 1],
29
+ 'byte' => ["c", 1, 2],
30
+ 'short' => ["s>", 2, 3],
31
+ 'integer' => ["l>", 4, 4],
32
+ 'long' => ["q>", 8, 5],
33
+ 'bytes' => [nil, nil, 6],
34
+ 'string' => [nil, nil, 7],
35
+ 'timestamp' => ["q>", 8, 8],
36
+ 'uuid' => [nil, 16, 9]
35
37
  }
36
38
  end
37
39
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-eventstream
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amazon Web Services
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-10 00:00:00.000000000 Z
11
+ date: 2021-03-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Amazon Web Services event stream library. Decodes and encodes binary
14
14
  stream under `vnd.amazon.event-stream` content-type
@@ -17,15 +17,17 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - CHANGELOG.md
21
+ - LICENSE.txt
22
+ - VERSION
20
23
  - lib/aws-eventstream.rb
21
- - lib/aws-eventstream/bytes_buffer.rb
22
24
  - lib/aws-eventstream/decoder.rb
23
25
  - lib/aws-eventstream/encoder.rb
24
26
  - lib/aws-eventstream/errors.rb
25
27
  - lib/aws-eventstream/header_value.rb
26
28
  - lib/aws-eventstream/message.rb
27
29
  - lib/aws-eventstream/types.rb
28
- homepage: http://github.com/aws/aws-sdk-ruby
30
+ homepage: https://github.com/aws/aws-sdk-ruby
29
31
  licenses:
30
32
  - Apache-2.0
31
33
  metadata:
@@ -47,7 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
49
  version: '0'
48
50
  requirements: []
49
51
  rubyforge_project:
50
- rubygems_version: 2.5.2.3
52
+ rubygems_version: 2.7.6.2
51
53
  signing_key:
52
54
  specification_version: 4
53
55
  summary: AWS Event Stream Library
@@ -1,66 +0,0 @@
1
- module Aws
2
- module EventStream
3
-
4
- # @api private
5
- class BytesBuffer
6
-
7
- # This Util class is for Decoder/Encoder usage only
8
- # Not for public common bytes buffer usage
9
- def initialize(data)
10
- @data = data
11
- @pos = 0
12
- end
13
-
14
- def read(len = nil, offset = 0)
15
- return '' if len == 0 || bytesize == 0
16
- unless eof?
17
- start_byte = @pos + offset
18
- end_byte = len ?
19
- start_byte + len - 1 :
20
- bytesize - 1
21
-
22
- error = Errors::ReadBytesExceedLengthError.new(end_byte, bytesize)
23
- raise error if end_byte >= bytesize
24
-
25
- @pos = end_byte + 1
26
- @data[start_byte..end_byte]
27
- end
28
- end
29
-
30
- def readbyte
31
- unless eof?
32
- @pos += 1
33
- @data[@pos - 1]
34
- end
35
- end
36
-
37
- def write(bytes)
38
- @data <<= bytes
39
- bytes.bytesize
40
- end
41
- alias_method :<<, :write
42
-
43
- def rewind
44
- @pos = 0
45
- end
46
-
47
- def eof?
48
- @pos == bytesize
49
- end
50
-
51
- def bytesize
52
- @data.bytesize
53
- end
54
-
55
- def tell
56
- @pos
57
- end
58
-
59
- def clear!
60
- @data = ''
61
- @pos = 0
62
- end
63
- end
64
-
65
- end
66
- end