amqp_table_t TableValueImpl::CopyTableInner(const amqp_table_t &table, amqp_pool_t &pool) { amqp_table_t new_table; new_table.num_entries = table.num_entries; new_table.entries = (amqp_table_entry_t*)amqp_pool_alloc(&pool, sizeof(amqp_table_entry_t)*table.num_entries); if (NULL == new_table.entries) { throw std::bad_alloc(); } for (int i = 0; i < table.num_entries; ++i) { amqp_table_entry_t *entry = &new_table.entries[i]; amqp_pool_alloc_bytes(&pool, table.entries[i].key.len, &entry->key); if (NULL == entry->key.bytes) { throw std::bad_alloc(); } memcpy(entry->key.bytes, table.entries[i].key.bytes, entry->key.len); entry->value = CopyValue(table.entries[i].value, pool); } return new_table; }
amqp_field_value_t TableValueImpl::CopyValue(const amqp_field_value_t value, amqp_pool_t &pool) { amqp_field_value_t new_value = value; switch (value.kind) { case AMQP_FIELD_KIND_UTF8: case AMQP_FIELD_KIND_BYTES: amqp_pool_alloc_bytes(&pool, value.value.bytes.len, &new_value.value.bytes); memcpy(new_value.value.bytes.bytes, value.value.bytes.bytes, value.value.bytes.len); return new_value; case AMQP_FIELD_KIND_ARRAY: { new_value.value.array.entries = (amqp_field_value_t*)amqp_pool_alloc(&pool, sizeof(amqp_field_value_t)*value.value.array.num_entries); for (int i = 0; i < value.value.array.num_entries; ++i) { new_value.value.array.entries[i] = CopyValue(value.value.array.entries[i], pool); } return new_value; } case AMQP_FIELD_KIND_TABLE: new_value.value.table = CopyTableInner(value.value.table, pool); return new_value; default: return new_value; } }
amqp_table_t TableValueImpl::CreateAmqpTableInner(const Table &table, amqp_pool_t &pool) { amqp_table_t new_table; new_table.num_entries = table.size(); new_table.entries = (amqp_table_entry_t*)amqp_pool_alloc(&pool, sizeof(amqp_table_entry_t)*table.size()); if (NULL == new_table.entries) { throw std::bad_alloc(); } amqp_table_entry_t *output_it = new_table.entries; for (Table::const_iterator it = table.begin(); it != table.end(); ++it, ++output_it) { amqp_pool_alloc_bytes(&pool, it->first.size(), &output_it->key); if (NULL == output_it->key.bytes) { throw std::bad_alloc(); } std::copy(it->first.begin(), it->first.end(), (char *)output_it->key.bytes); output_it->value = boost::apply_visitor(TableValueImpl::generate_field_value(pool), it->second.m_impl->m_value); } return new_table; }
void amqp_create_table(amqp_connection_state_t state, amqp_table_t *output, int initialSize) { output->entries = amqp_pool_alloc(&state->frame_pool, initialSize * sizeof(amqp_table_entry_t)); output->size = initialSize; output->num_entries = 0; }
amqp_table_entry_t *amqp_table_add_entry( amqp_connection_state_t state, amqp_table_t *table, amqp_bytes_t key) { amqp_table_entry_t *entry; if (table->num_entries == table->size) { int new_size = table->size * 2; amqp_table_entry_t *new_entries = amqp_pool_alloc( &state->frame_pool, new_size * sizeof(amqp_table_entry_t) ); memcpy( new_entries, table->entries, table->size * sizeof(amqp_table_entry_t) ); table->size = new_size; table->entries = new_entries; } entry = &table->entries[table->num_entries]; table->num_entries++; entry->key = key; return entry; }
amqp_field_value_t TableValueImpl::generate_field_value::operator()(const array_t &value) const { amqp_field_value_t v; v.kind = AMQP_FIELD_KIND_ARRAY; v.value.array.num_entries = value.size(); v.value.array.entries = (amqp_field_value_t*)amqp_pool_alloc(&pool, sizeof(amqp_field_value_t)*value.size()); if (NULL == v.value.array.entries) { throw std::bad_alloc(); } amqp_field_value_t *output_iterator = v.value.array.entries; for (array_t::const_iterator it = value.begin(); it != value.end(); ++it, ++output_iterator) { *output_iterator = boost::apply_visitor(generate_field_value(pool), it->m_impl->m_value); } return v; }
amqp_connection_state_t amqp_new_connection(void) { amqp_connection_state_t state = (amqp_connection_state_t) calloc(1, sizeof(struct amqp_connection_state_t_)); if (state == NULL) return NULL; init_amqp_pool(&state->frame_pool, INITIAL_FRAME_POOL_PAGE_SIZE); init_amqp_pool(&state->decoding_pool, INITIAL_DECODING_POOL_PAGE_SIZE); if (amqp_tune_connection(state, 0, INITIAL_FRAME_POOL_PAGE_SIZE, 0) != 0) goto out_nomem; state->inbound_buffer.bytes = amqp_pool_alloc(&state->frame_pool, state->inbound_buffer.len); if (state->inbound_buffer.bytes == NULL) goto out_nomem; state->state = CONNECTION_STATE_INITIAL; /* the server protocol version response is 8 bytes, which conveniently is also the minimum frame size */ state->target_size = 8; state->sockfd = -1; state->sock_inbound_buffer.len = INITIAL_INBOUND_SOCK_BUFFER_SIZE; state->sock_inbound_buffer.bytes = malloc(INITIAL_INBOUND_SOCK_BUFFER_SIZE); if (state->sock_inbound_buffer.bytes == NULL) goto out_nomem; return state; out_nomem: free(state->sock_inbound_buffer.bytes); empty_amqp_pool(&state->frame_pool); empty_amqp_pool(&state->decoding_pool); free(state); return NULL; }
void amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount, amqp_bytes_t *output) { output->len = amount; output->bytes = amqp_pool_alloc(pool, amount); }
amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state, amqp_channel_t channel, amqp_method_number_t request_id, amqp_method_number_t *expected_reply_ids, void *decoded_request_method) { int status; amqp_rpc_reply_t result; memset(&result, 0, sizeof(result)); status = amqp_send_method(state, channel, request_id, decoded_request_method); if (status < 0) { result.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; result.library_errno = -status; return result; } { amqp_frame_t frame; retry: status = wait_frame_inner(state, &frame); if (status <= 0) { result.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION; result.library_errno = -status; return result; } /* * We store the frame for later processing unless it's something * that directly affects us here, namely a method frame that is * either * - on the channel we want, and of the expected type, or * - on the channel we want, and a channel.close frame, or * - on channel zero, and a connection.close frame. */ if (!( (frame.frame_type == AMQP_FRAME_METHOD) && ( ((frame.channel == channel) && ((amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids)) || (frame.payload.method.id == AMQP_CHANNEL_CLOSE_METHOD))) || ((frame.channel == 0) && (frame.payload.method.id == AMQP_CONNECTION_CLOSE_METHOD)) ) )) { amqp_frame_t *frame_copy = amqp_pool_alloc(&state->decoding_pool, sizeof(amqp_frame_t)); amqp_link_t *link = amqp_pool_alloc(&state->decoding_pool, sizeof(amqp_link_t)); *frame_copy = frame; link->next = NULL; link->data = frame_copy; if (state->last_queued_frame == NULL) { state->first_queued_frame = link; } else { state->last_queued_frame->next = link; } state->last_queued_frame = link; goto retry; } result.reply_type = (amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids)) ? AMQP_RESPONSE_NORMAL : AMQP_RESPONSE_SERVER_EXCEPTION; result.reply = frame.payload.method; return result; } }
int amqp_decode_table(amqp_bytes_t encoded, amqp_pool_t *pool, amqp_table_t *output, int *offsetptr) { int offset = *offsetptr; uint32_t tablesize = D_32(encoded, offset); int num_entries = 0; amqp_table_entry_t *entries = malloc(INITIAL_TABLE_SIZE * sizeof(amqp_table_entry_t)); int allocated_entries = INITIAL_TABLE_SIZE; int limit; if (entries == NULL) { return -ENOMEM; } offset += 4; limit = offset + tablesize; while (offset < limit) { size_t keylen; amqp_table_entry_t *entry; keylen = D_8(encoded, offset); offset++; if (num_entries >= allocated_entries) { void *newentries; allocated_entries = allocated_entries * 2; newentries = realloc(entries, allocated_entries * sizeof(amqp_table_entry_t)); if (newentries == NULL) { free(entries); return -ENOMEM; } entries = newentries; } entry = &entries[num_entries]; entry->key.len = keylen; entry->key.bytes = D_BYTES(encoded, offset, keylen); offset += keylen; entry->kind = D_8(encoded, offset); offset++; switch (entry->kind) { case 'S': entry->value.bytes.len = D_32(encoded, offset); offset += 4; entry->value.bytes.bytes = D_BYTES(encoded, offset, entry->value.bytes.len); offset += entry->value.bytes.len; break; case 'I': entry->value.i32 = (int32_t) D_32(encoded, offset); offset += 4; break; case 'D': entry->value.decimal.decimals = D_8(encoded, offset); offset++; entry->value.decimal.value = D_32(encoded, offset); offset += 4; break; case 'T': entry->value.u64 = D_64(encoded, offset); offset += 8; break; case 'F': AMQP_CHECK_RESULT(amqp_decode_table(encoded, pool, &(entry->value.table), &offset)); break; case 't': entry->value.boolean = D_8(encoded, offset); offset += 1; break; default: return -EINVAL; } num_entries++; } output->num_entries = num_entries; output->entries = amqp_pool_alloc(pool, num_entries * sizeof(amqp_table_entry_t)); output->size = num_entries; memcpy(output->entries, entries, num_entries * sizeof(amqp_table_entry_t)); *offsetptr = offset; return 0; }
int amqp_handle_input(amqp_connection_state_t state, amqp_bytes_t received_data, amqp_frame_t *decoded_frame) { int total_bytes_consumed = 0; int bytes_consumed; /* Returning frame_type of zero indicates either insufficient input, or a complete, ignored frame was read. */ decoded_frame->frame_type = 0; read_more: if (received_data.len == 0) { return total_bytes_consumed; } if (state->state == CONNECTION_STATE_IDLE) { state->inbound_buffer.bytes = amqp_pool_alloc(&state->frame_pool, state->inbound_buffer.len); if (state->inbound_buffer.bytes == NULL) { /* state->inbound_buffer.len is always nonzero, because it corresponds to frame_max, which is not permitted to be less than AMQP_FRAME_MIN_SIZE (currently 4096 bytes). */ return -ENOMEM; } state->state = CONNECTION_STATE_WAITING_FOR_HEADER; } bytes_consumed = state->target_size - state->inbound_offset; if (received_data.len < bytes_consumed) { bytes_consumed = received_data.len; } E_BYTES(state->inbound_buffer, state->inbound_offset, bytes_consumed, received_data.bytes); state->inbound_offset += bytes_consumed; total_bytes_consumed += bytes_consumed; assert(state->inbound_offset <= state->target_size); if (state->inbound_offset < state->target_size) { return total_bytes_consumed; } switch (state->state) { case CONNECTION_STATE_WAITING_FOR_HEADER: if (D_8(state->inbound_buffer, 0) == AMQP_PSEUDOFRAME_PROTOCOL_HEADER && D_16(state->inbound_buffer, 1) == AMQP_PSEUDOFRAME_PROTOCOL_CHANNEL) { state->target_size = 8; state->state = CONNECTION_STATE_WAITING_FOR_PROTOCOL_HEADER; } else { state->target_size = D_32(state->inbound_buffer, 3) + HEADER_SIZE + FOOTER_SIZE; state->state = CONNECTION_STATE_WAITING_FOR_BODY; } /* Wind buffer forward, and try to read some body out of it. */ received_data.len -= bytes_consumed; received_data.bytes = ((char *) received_data.bytes) + bytes_consumed; goto read_more; case CONNECTION_STATE_WAITING_FOR_BODY: { int frame_type = D_8(state->inbound_buffer, 0); #if 0 printf("recving:\n"); amqp_dump(state->inbound_buffer.bytes, state->target_size); #endif /* Check frame end marker (footer) */ if (D_8(state->inbound_buffer, state->target_size - 1) != AMQP_FRAME_END) { return -EINVAL; } decoded_frame->channel = D_16(state->inbound_buffer, 1); switch (frame_type) { case AMQP_FRAME_METHOD: { amqp_bytes_t encoded; /* Four bytes of method ID before the method args. */ encoded.len = state->target_size - (HEADER_SIZE + 4 + FOOTER_SIZE); encoded.bytes = D_BYTES(state->inbound_buffer, HEADER_SIZE + 4, encoded.len); decoded_frame->frame_type = AMQP_FRAME_METHOD; decoded_frame->payload.method.id = D_32(state->inbound_buffer, HEADER_SIZE); AMQP_CHECK_RESULT(amqp_decode_method(decoded_frame->payload.method.id, &state->decoding_pool, encoded, &decoded_frame->payload.method.decoded)); break; } case AMQP_FRAME_HEADER: { amqp_bytes_t encoded; /* 12 bytes for properties header. */ encoded.len = state->target_size - (HEADER_SIZE + 12 + FOOTER_SIZE); encoded.bytes = D_BYTES(state->inbound_buffer, HEADER_SIZE + 12, encoded.len); decoded_frame->frame_type = AMQP_FRAME_HEADER; decoded_frame->payload.properties.class_id = D_16(state->inbound_buffer, HEADER_SIZE); decoded_frame->payload.properties.body_size = D_64(state->inbound_buffer, HEADER_SIZE+4); decoded_frame->payload.properties.raw = encoded; AMQP_CHECK_RESULT(amqp_decode_properties(decoded_frame->payload.properties.class_id, &state->decoding_pool, encoded, &decoded_frame->payload.properties.decoded)); break; } case AMQP_FRAME_BODY: { size_t fragment_len = state->target_size - (HEADER_SIZE + FOOTER_SIZE); decoded_frame->frame_type = AMQP_FRAME_BODY; decoded_frame->payload.body_fragment.len = fragment_len; decoded_frame->payload.body_fragment.bytes = D_BYTES(state->inbound_buffer, HEADER_SIZE, fragment_len); break; } case AMQP_FRAME_HEARTBEAT: decoded_frame->frame_type = AMQP_FRAME_HEARTBEAT; break; default: /* Ignore the frame by not changing frame_type away from 0. */ break; } return_to_idle(state); return total_bytes_consumed; } case CONNECTION_STATE_WAITING_FOR_PROTOCOL_HEADER: decoded_frame->frame_type = AMQP_PSEUDOFRAME_PROTOCOL_HEADER; decoded_frame->channel = AMQP_PSEUDOFRAME_PROTOCOL_CHANNEL; amqp_assert(D_8(state->inbound_buffer, 3) == (uint8_t) 'P', "Invalid protocol header received"); decoded_frame->payload.protocol_header.transport_high = D_8(state->inbound_buffer, 4); decoded_frame->payload.protocol_header.transport_low = D_8(state->inbound_buffer, 5); decoded_frame->payload.protocol_header.protocol_version_major = D_8(state->inbound_buffer, 6); decoded_frame->payload.protocol_header.protocol_version_minor = D_8(state->inbound_buffer, 7); return_to_idle(state); return total_bytes_consumed; default: amqp_assert(0, "Internal error: invalid amqp_connection_state_t->state %d", state->state); } }
int amqp_handle_input(amqp_connection_state_t state, amqp_bytes_t received_data, amqp_frame_t *decoded_frame) { size_t bytes_consumed; void *raw_frame; /* Returning frame_type of zero indicates either insufficient input, or a complete, ignored frame was read. */ decoded_frame->frame_type = 0; if (received_data.len == 0) return 0; if (state->state == CONNECTION_STATE_IDLE) { state->inbound_buffer.bytes = amqp_pool_alloc(&state->frame_pool, state->inbound_buffer.len); if (state->inbound_buffer.bytes == NULL) /* state->inbound_buffer.len is always nonzero, because it corresponds to frame_max, which is not permitted to be less than AMQP_FRAME_MIN_SIZE (currently 4096 bytes). */ return -ERROR_NO_MEMORY; state->state = CONNECTION_STATE_HEADER; } bytes_consumed = consume_data(state, &received_data); /* do we have target_size data yet? if not, return with the expectation that more will arrive */ if (state->inbound_offset < state->target_size) return bytes_consumed; raw_frame = state->inbound_buffer.bytes; switch (state->state) { case CONNECTION_STATE_INITIAL: /* check for a protocol header from the server */ if (memcmp(raw_frame, "AMQP", 4) == 0) { decoded_frame->frame_type = AMQP_PSEUDOFRAME_PROTOCOL_HEADER; decoded_frame->channel = 0; decoded_frame->payload.protocol_header.transport_high = amqp_d8(raw_frame, 4); decoded_frame->payload.protocol_header.transport_low = amqp_d8(raw_frame, 5); decoded_frame->payload.protocol_header.protocol_version_major = amqp_d8(raw_frame, 6); decoded_frame->payload.protocol_header.protocol_version_minor = amqp_d8(raw_frame, 7); return_to_idle(state); return bytes_consumed; } /* it's not a protocol header; fall through to process it as a regular frame header */ case CONNECTION_STATE_HEADER: /* frame length is 3 bytes in */ state->target_size = amqp_d32(raw_frame, 3) + HEADER_SIZE + FOOTER_SIZE; state->state = CONNECTION_STATE_BODY; bytes_consumed += consume_data(state, &received_data); /* do we have target_size data yet? if not, return with the expectation that more will arrive */ if (state->inbound_offset < state->target_size) return bytes_consumed; /* fall through to process body */ case CONNECTION_STATE_BODY: { amqp_bytes_t encoded; int res; /* Check frame end marker (footer) */ if (amqp_d8(raw_frame, state->target_size - 1) != AMQP_FRAME_END) return -ERROR_BAD_AMQP_DATA; decoded_frame->frame_type = amqp_d8(raw_frame, 0); decoded_frame->channel = amqp_d16(raw_frame, 1); switch (decoded_frame->frame_type) { case AMQP_FRAME_METHOD: decoded_frame->payload.method.id = amqp_d32(raw_frame, HEADER_SIZE); encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 4); encoded.len = state->target_size - HEADER_SIZE - 4 - FOOTER_SIZE; res = amqp_decode_method(decoded_frame->payload.method.id, &state->decoding_pool, encoded, &decoded_frame->payload.method.decoded); if (res < 0) return res; break; case AMQP_FRAME_HEADER: decoded_frame->payload.properties.class_id = amqp_d16(raw_frame, HEADER_SIZE); /* unused 2-byte weight field goes here */ decoded_frame->payload.properties.body_size = amqp_d64(raw_frame, HEADER_SIZE + 4); encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 12); encoded.len = state->target_size - HEADER_SIZE - 12 - FOOTER_SIZE; decoded_frame->payload.properties.raw = encoded; res = amqp_decode_properties(decoded_frame->payload.properties.class_id, &state->decoding_pool, encoded, &decoded_frame->payload.properties.decoded); if (res < 0) return res; break; case AMQP_FRAME_BODY: decoded_frame->payload.body_fragment.len = state->target_size - HEADER_SIZE - FOOTER_SIZE; decoded_frame->payload.body_fragment.bytes = amqp_offset(raw_frame, HEADER_SIZE); break; case AMQP_FRAME_HEARTBEAT: break; default: /* Ignore the frame */ decoded_frame->frame_type = 0; break; } return_to_idle(state); return bytes_consumed; } default: amqp_abort("Internal error: invalid amqp_connection_state_t->state %d", state->state); return bytes_consumed; } }