static int inner_send_frame(amqp_connection_state_t state, amqp_frame_t const *frame, amqp_bytes_t *encoded, int *payload_len) { int separate_body; E_8(state->outbound_buffer, 0, frame->frame_type); E_16(state->outbound_buffer, 1, frame->channel); switch (frame->frame_type) { case AMQP_FRAME_METHOD: E_32(state->outbound_buffer, HEADER_SIZE, frame->payload.method.id); encoded->len = state->outbound_buffer.len - (HEADER_SIZE + 4 + FOOTER_SIZE); encoded->bytes = D_BYTES(state->outbound_buffer, HEADER_SIZE + 4, encoded->len); *payload_len = AMQP_CHECK_RESULT(amqp_encode_method(frame->payload.method.id, frame->payload.method.decoded, *encoded)) + 4; separate_body = 0; break; case AMQP_FRAME_HEADER: E_16(state->outbound_buffer, HEADER_SIZE, frame->payload.properties.class_id); E_16(state->outbound_buffer, HEADER_SIZE+2, 0); /* "weight" */ E_64(state->outbound_buffer, HEADER_SIZE+4, frame->payload.properties.body_size); encoded->len = state->outbound_buffer.len - (HEADER_SIZE + 12 + FOOTER_SIZE); encoded->bytes = D_BYTES(state->outbound_buffer, HEADER_SIZE + 12, encoded->len); *payload_len = AMQP_CHECK_RESULT(amqp_encode_properties(frame->payload.properties.class_id, frame->payload.properties.decoded, *encoded)) + 12; separate_body = 0; break; case AMQP_FRAME_BODY: *encoded = frame->payload.body_fragment; *payload_len = encoded->len; separate_body = 1; break; case AMQP_FRAME_HEARTBEAT: *encoded = AMQP_EMPTY_BYTES; *payload_len = 0; separate_body = 0; break; default: return -EINVAL; } E_32(state->outbound_buffer, 3, *payload_len); if (!separate_body) { E_8(state->outbound_buffer, *payload_len + HEADER_SIZE, AMQP_FRAME_END); } #if 0 if (separate_body) { printf("sending body frame (header):\n"); amqp_dump(state->outbound_buffer.bytes, HEADER_SIZE); printf("sending body frame (payload):\n"); amqp_dump(encoded->bytes, *payload_len); } else { printf("sending:\n"); amqp_dump(state->outbound_buffer.bytes, *payload_len + HEADER_SIZE + FOOTER_SIZE); } #endif return separate_body; }
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); } }