/src/librabbitmq/librabbitmq/amqp_consumer.c
Line | Count | Source |
1 | | // Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors. |
2 | | // SPDX-License-Identifier: mit |
3 | | |
4 | | #include "amqp_private.h" |
5 | | #include "amqp_socket.h" |
6 | | #include "rabbitmq-c/amqp.h" |
7 | | |
8 | | #include <stdlib.h> |
9 | | #include <string.h> |
10 | | |
11 | | static int amqp_basic_properties_clone(amqp_basic_properties_t *original, |
12 | | amqp_basic_properties_t *clone, |
13 | 527 | amqp_pool_t *pool) { |
14 | 527 | memset(clone, 0, sizeof(*clone)); |
15 | 527 | clone->_flags = original->_flags; |
16 | | |
17 | 527 | #define CLONE_BYTES_POOL(original, clone, pool) \ |
18 | 954 | if (0 == original.len) { \ |
19 | 502 | clone = amqp_empty_bytes; \ |
20 | 502 | } else { \ |
21 | 452 | amqp_pool_alloc_bytes(pool, original.len, &clone); \ |
22 | 452 | if (NULL == clone.bytes) { \ |
23 | 0 | return AMQP_STATUS_NO_MEMORY; \ |
24 | 0 | } \ |
25 | 452 | memcpy(clone.bytes, original.bytes, clone.len); \ |
26 | 452 | } |
27 | | |
28 | 527 | if (clone->_flags & AMQP_BASIC_CONTENT_TYPE_FLAG) { |
29 | 57 | CLONE_BYTES_POOL(original->content_type, clone->content_type, pool) |
30 | 57 | } |
31 | | |
32 | 527 | if (clone->_flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) { |
33 | 168 | CLONE_BYTES_POOL(original->content_encoding, clone->content_encoding, pool) |
34 | 168 | } |
35 | | |
36 | 527 | if (clone->_flags & AMQP_BASIC_HEADERS_FLAG) { |
37 | 282 | int res = amqp_table_clone(&original->headers, &clone->headers, pool); |
38 | 282 | if (AMQP_STATUS_OK != res) { |
39 | 23 | return res; |
40 | 23 | } |
41 | 282 | } |
42 | | |
43 | 504 | if (clone->_flags & AMQP_BASIC_DELIVERY_MODE_FLAG) { |
44 | 82 | clone->delivery_mode = original->delivery_mode; |
45 | 82 | } |
46 | | |
47 | 504 | if (clone->_flags & AMQP_BASIC_PRIORITY_FLAG) { |
48 | 55 | clone->priority = original->priority; |
49 | 55 | } |
50 | | |
51 | 504 | if (clone->_flags & AMQP_BASIC_CORRELATION_ID_FLAG) { |
52 | 169 | CLONE_BYTES_POOL(original->correlation_id, clone->correlation_id, pool) |
53 | 169 | } |
54 | | |
55 | 504 | if (clone->_flags & AMQP_BASIC_REPLY_TO_FLAG) { |
56 | 101 | CLONE_BYTES_POOL(original->reply_to, clone->reply_to, pool) |
57 | 101 | } |
58 | | |
59 | 504 | if (clone->_flags & AMQP_BASIC_EXPIRATION_FLAG) { |
60 | 106 | CLONE_BYTES_POOL(original->expiration, clone->expiration, pool) |
61 | 106 | } |
62 | | |
63 | 504 | if (clone->_flags & AMQP_BASIC_MESSAGE_ID_FLAG) { |
64 | 78 | CLONE_BYTES_POOL(original->message_id, clone->message_id, pool) |
65 | 78 | } |
66 | | |
67 | 504 | if (clone->_flags & AMQP_BASIC_TIMESTAMP_FLAG) { |
68 | 38 | clone->timestamp = original->timestamp; |
69 | 38 | } |
70 | | |
71 | 504 | if (clone->_flags & AMQP_BASIC_TYPE_FLAG) { |
72 | 48 | CLONE_BYTES_POOL(original->type, clone->type, pool) |
73 | 48 | } |
74 | | |
75 | 504 | if (clone->_flags & AMQP_BASIC_USER_ID_FLAG) { |
76 | 87 | CLONE_BYTES_POOL(original->user_id, clone->user_id, pool) |
77 | 87 | } |
78 | | |
79 | 504 | if (clone->_flags & AMQP_BASIC_APP_ID_FLAG) { |
80 | 79 | CLONE_BYTES_POOL(original->app_id, clone->app_id, pool) |
81 | 79 | } |
82 | | |
83 | 504 | if (clone->_flags & AMQP_BASIC_CLUSTER_ID_FLAG) { |
84 | 61 | CLONE_BYTES_POOL(original->cluster_id, clone->cluster_id, pool) |
85 | 61 | } |
86 | | |
87 | 504 | return AMQP_STATUS_OK; |
88 | 504 | #undef CLONE_BYTES_POOL |
89 | 504 | } |
90 | | |
91 | 22 | void amqp_destroy_message(amqp_message_t *message) { |
92 | 22 | empty_amqp_pool(&message->pool); |
93 | 22 | amqp_bytes_free(message->body); |
94 | 22 | } |
95 | | |
96 | 0 | void amqp_destroy_envelope(amqp_envelope_t *envelope) { |
97 | 0 | amqp_destroy_message(&envelope->message); |
98 | 0 | amqp_bytes_free(envelope->routing_key); |
99 | 0 | amqp_bytes_free(envelope->exchange); |
100 | 0 | amqp_bytes_free(envelope->consumer_tag); |
101 | 0 | } |
102 | | |
103 | 0 | static int amqp_bytes_malloc_dup_failed(amqp_bytes_t bytes) { |
104 | 0 | if (bytes.len != 0 && bytes.bytes == NULL) { |
105 | 0 | return 1; |
106 | 0 | } |
107 | 0 | return 0; |
108 | 0 | } |
109 | | |
110 | | amqp_rpc_reply_t amqp_consume_message(amqp_connection_state_t state, |
111 | | amqp_envelope_t *envelope, |
112 | | const struct timeval *timeout, |
113 | 0 | AMQP_UNUSED int flags) { |
114 | 0 | int res; |
115 | 0 | amqp_frame_t frame; |
116 | 0 | amqp_basic_deliver_t *delivery_method; |
117 | 0 | amqp_rpc_reply_t ret; |
118 | |
|
119 | 0 | memset(&ret, 0, sizeof(ret)); |
120 | 0 | memset(envelope, 0, sizeof(*envelope)); |
121 | |
|
122 | 0 | res = amqp_simple_wait_frame_noblock(state, &frame, timeout); |
123 | 0 | if (AMQP_STATUS_OK != res) { |
124 | 0 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
125 | 0 | ret.library_error = res; |
126 | 0 | goto error_out1; |
127 | 0 | } |
128 | | |
129 | 0 | if (AMQP_FRAME_METHOD != frame.frame_type || |
130 | 0 | AMQP_BASIC_DELIVER_METHOD != frame.payload.method.id) { |
131 | 0 | amqp_put_back_frame(state, &frame); |
132 | 0 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
133 | 0 | ret.library_error = AMQP_STATUS_UNEXPECTED_STATE; |
134 | 0 | goto error_out1; |
135 | 0 | } |
136 | | |
137 | 0 | delivery_method = frame.payload.method.decoded; |
138 | |
|
139 | 0 | envelope->channel = frame.channel; |
140 | 0 | envelope->consumer_tag = amqp_bytes_malloc_dup(delivery_method->consumer_tag); |
141 | 0 | envelope->delivery_tag = delivery_method->delivery_tag; |
142 | 0 | envelope->redelivered = delivery_method->redelivered; |
143 | 0 | envelope->exchange = amqp_bytes_malloc_dup(delivery_method->exchange); |
144 | 0 | envelope->routing_key = amqp_bytes_malloc_dup(delivery_method->routing_key); |
145 | |
|
146 | 0 | if (amqp_bytes_malloc_dup_failed(envelope->consumer_tag) || |
147 | 0 | amqp_bytes_malloc_dup_failed(envelope->exchange) || |
148 | 0 | amqp_bytes_malloc_dup_failed(envelope->routing_key)) { |
149 | 0 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
150 | 0 | ret.library_error = AMQP_STATUS_NO_MEMORY; |
151 | 0 | goto error_out2; |
152 | 0 | } |
153 | | |
154 | 0 | ret = amqp_read_message(state, envelope->channel, &envelope->message, 0); |
155 | 0 | if (AMQP_RESPONSE_NORMAL != ret.reply_type) { |
156 | 0 | goto error_out2; |
157 | 0 | } |
158 | | |
159 | 0 | ret.reply_type = AMQP_RESPONSE_NORMAL; |
160 | 0 | return ret; |
161 | | |
162 | 0 | error_out2: |
163 | 0 | amqp_bytes_free(envelope->routing_key); |
164 | 0 | amqp_bytes_free(envelope->exchange); |
165 | 0 | amqp_bytes_free(envelope->consumer_tag); |
166 | 0 | error_out1: |
167 | 0 | return ret; |
168 | 0 | } |
169 | | |
170 | | amqp_rpc_reply_t amqp_read_message(amqp_connection_state_t state, |
171 | | amqp_channel_t channel, |
172 | | amqp_message_t *message, |
173 | 5.57k | AMQP_UNUSED int flags) { |
174 | 5.57k | amqp_frame_t frame; |
175 | 5.57k | amqp_rpc_reply_t ret; |
176 | | |
177 | 5.57k | size_t body_read; |
178 | 5.57k | char *body_read_ptr; |
179 | 5.57k | int res; |
180 | | |
181 | 5.57k | memset(&ret, 0, sizeof(ret)); |
182 | 5.57k | memset(message, 0, sizeof(*message)); |
183 | | |
184 | 5.57k | res = amqp_simple_wait_frame_on_channel(state, channel, &frame); |
185 | 5.57k | if (AMQP_STATUS_OK != res) { |
186 | 5.00k | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
187 | 5.00k | ret.library_error = res; |
188 | | |
189 | 5.00k | goto error_out1; |
190 | 5.00k | } |
191 | | |
192 | 573 | if (AMQP_FRAME_HEADER != frame.frame_type) { |
193 | 40 | if (AMQP_FRAME_METHOD == frame.frame_type && |
194 | 34 | (AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id || |
195 | 32 | AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) { |
196 | | |
197 | 3 | ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION; |
198 | 3 | ret.reply = frame.payload.method; |
199 | | |
200 | 37 | } else { |
201 | 37 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
202 | 37 | ret.library_error = AMQP_STATUS_UNEXPECTED_STATE; |
203 | | |
204 | 37 | amqp_put_back_frame(state, &frame); |
205 | 37 | } |
206 | 40 | goto error_out1; |
207 | 40 | } |
208 | | |
209 | 533 | if (AMQP_BASIC_CLASS != frame.payload.properties.class_id) { |
210 | 6 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
211 | 6 | ret.library_error = AMQP_STATUS_BAD_AMQP_DATA; |
212 | 6 | goto error_out1; |
213 | 6 | } |
214 | | |
215 | 527 | init_amqp_pool(&message->pool, 4096); |
216 | 527 | res = amqp_basic_properties_clone(frame.payload.properties.decoded, |
217 | 527 | &message->properties, &message->pool); |
218 | | |
219 | 527 | if (AMQP_STATUS_OK != res) { |
220 | 23 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
221 | 23 | ret.library_error = res; |
222 | 23 | goto error_out3; |
223 | 23 | } |
224 | | |
225 | 504 | if (0 == frame.payload.properties.body_size) { |
226 | 21 | message->body = amqp_empty_bytes; |
227 | 483 | } else { |
228 | 483 | if (SIZE_MAX < frame.payload.properties.body_size) { |
229 | 0 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
230 | 0 | ret.library_error = AMQP_STATUS_NO_MEMORY; |
231 | 0 | goto error_out1; |
232 | 0 | } |
233 | 483 | message->body = |
234 | 483 | amqp_bytes_malloc((size_t)frame.payload.properties.body_size); |
235 | 483 | if (NULL == message->body.bytes) { |
236 | 0 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
237 | 0 | ret.library_error = AMQP_STATUS_NO_MEMORY; |
238 | 0 | goto error_out1; |
239 | 0 | } |
240 | 483 | } |
241 | | |
242 | 504 | body_read = 0; |
243 | 504 | body_read_ptr = message->body.bytes; |
244 | | |
245 | 973 | while (body_read < message->body.len) { |
246 | 951 | res = amqp_simple_wait_frame_on_channel(state, channel, &frame); |
247 | 951 | if (AMQP_STATUS_OK != res) { |
248 | 446 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
249 | 446 | ret.library_error = res; |
250 | 446 | goto error_out2; |
251 | 446 | } |
252 | 505 | if (AMQP_FRAME_BODY != frame.frame_type) { |
253 | 20 | if (AMQP_FRAME_METHOD == frame.frame_type && |
254 | 17 | (AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id || |
255 | 16 | AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) { |
256 | | |
257 | 2 | ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION; |
258 | 2 | ret.reply = frame.payload.method; |
259 | 18 | } else { |
260 | 18 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
261 | 18 | ret.library_error = AMQP_STATUS_BAD_AMQP_DATA; |
262 | 18 | } |
263 | 20 | goto error_out2; |
264 | 20 | } |
265 | | |
266 | 485 | if (body_read + frame.payload.body_fragment.len > message->body.len) { |
267 | 16 | ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; |
268 | 16 | ret.library_error = AMQP_STATUS_BAD_AMQP_DATA; |
269 | 16 | goto error_out2; |
270 | 16 | } |
271 | | |
272 | 469 | memcpy(body_read_ptr, frame.payload.body_fragment.bytes, |
273 | 469 | frame.payload.body_fragment.len); |
274 | | |
275 | 469 | body_read += frame.payload.body_fragment.len; |
276 | 469 | body_read_ptr += frame.payload.body_fragment.len; |
277 | 469 | } |
278 | | |
279 | 22 | ret.reply_type = AMQP_RESPONSE_NORMAL; |
280 | 22 | return ret; |
281 | | |
282 | 482 | error_out2: |
283 | 482 | amqp_bytes_free(message->body); |
284 | 505 | error_out3: |
285 | 505 | empty_amqp_pool(&message->pool); |
286 | 5.55k | error_out1: |
287 | 5.55k | return ret; |
288 | 505 | } |