int amqp_basic_publish(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_boolean_t mandatory, amqp_boolean_t immediate, amqp_basic_properties_t const *properties, amqp_bytes_t body) { amqp_frame_t f; size_t body_offset; size_t usable_body_payload_size = state->frame_max - (HEADER_SIZE + FOOTER_SIZE); amqp_basic_publish_t m = (amqp_basic_publish_t) { .exchange = exchange, .routing_key = routing_key, .mandatory = mandatory, .immediate = immediate }; amqp_basic_properties_t default_properties; AMQP_CHECK_RESULT(amqp_send_method(state, channel, AMQP_BASIC_PUBLISH_METHOD, &m)); if (properties == NULL) { memset(&default_properties, 0, sizeof(default_properties)); properties = &default_properties; } f.frame_type = AMQP_FRAME_HEADER; f.channel = channel; f.payload.properties.class_id = AMQP_BASIC_CLASS; f.payload.properties.body_size = body.len; f.payload.properties.decoded = (void *) properties; AMQP_CHECK_RESULT(amqp_send_frame(state, &f)); body_offset = 0; while (1) { int remaining = body.len - body_offset; assert(remaining >= 0); if (remaining == 0) break; f.frame_type = AMQP_FRAME_BODY; f.channel = channel; f.payload.body_fragment.bytes = BUF_AT(body, body_offset); if (remaining >= usable_body_payload_size) { f.payload.body_fragment.len = usable_body_payload_size; } else { f.payload.body_fragment.len = remaining; } body_offset += f.payload.body_fragment.len; AMQP_CHECK_RESULT(amqp_send_frame(state, &f)); } return 0; }
int amqp_send_frame(amqp_connection_state_t state, amqp_frame_t const *frame) { amqp_bytes_t encoded; int payload_len; int separate_body; separate_body = inner_send_frame(state, frame, &encoded, &payload_len); switch (separate_body) { case 0: AMQP_CHECK_RESULT(write(state->sockfd, state->outbound_buffer.bytes, payload_len + (HEADER_SIZE + FOOTER_SIZE))); return 0; case 1: { struct iovec iov[3]; char frame_end_byte = AMQP_FRAME_END; iov[0].iov_base = state->outbound_buffer.bytes; iov[0].iov_len = HEADER_SIZE; iov[1].iov_base = encoded.bytes; iov[1].iov_len = payload_len; iov[2].iov_base = &frame_end_byte; assert(FOOTER_SIZE == 1); iov[2].iov_len = FOOTER_SIZE; AMQP_CHECK_RESULT(writev(state->sockfd, &iov[0], 3)); return 0; } default: return separate_body; } }
int amqp_send_frame_to(amqp_connection_state_t state, amqp_frame_t const *frame, amqp_output_fn_t fn, void *context) { amqp_bytes_t encoded; int payload_len; int separate_body; separate_body = inner_send_frame(state, frame, &encoded, &payload_len); switch (separate_body) { case 0: AMQP_CHECK_RESULT(fn(context, state->outbound_buffer.bytes, payload_len + (HEADER_SIZE + FOOTER_SIZE))); return 0; case 1: AMQP_CHECK_RESULT(fn(context, state->outbound_buffer.bytes, HEADER_SIZE)); AMQP_CHECK_RESULT(fn(context, encoded.bytes, payload_len)); { assert(FOOTER_SIZE == 1); char frame_end_byte = AMQP_FRAME_END; AMQP_CHECK_RESULT(fn(context, &frame_end_byte, FOOTER_SIZE)); } return 0; default: return separate_body; } }
int amqp_send_frame(amqp_connection_state_t state, amqp_frame_t const *frame) { amqp_bytes_t encoded; int payload_len; int separate_body; separate_body = inner_send_frame(state, frame, &encoded, &payload_len); switch (separate_body) { case 0: AMQP_CHECK_RESULT(write_ignore_pipe_signal(state->sockfd, state->outbound_buffer.bytes, payload_len + (HEADER_SIZE + FOOTER_SIZE))); return 0; case 1: AMQP_CHECK_RESULT(write_ignore_pipe_signal(state->sockfd, state->outbound_buffer.bytes, HEADER_SIZE)); AMQP_CHECK_RESULT(write_ignore_pipe_signal(state->sockfd, encoded.bytes, payload_len)); { unsigned char frame_end_byte = AMQP_FRAME_END; assert(FOOTER_SIZE == 1); AMQP_CHECK_RESULT(write_ignore_pipe_signal(state->sockfd, &frame_end_byte, FOOTER_SIZE)); } return 0; default: return separate_body; } }
int amqp_send_frame(amqp_connection_state_t state, amqp_frame_t const *frame) { amqp_bytes_t encoded; int payload_len; int separate_body; separate_body = inner_send_frame(state, frame, &encoded, &payload_len); switch (separate_body) { case 0: #ifdef __GNUC__ /* frgo, 2010-06-11 */ AMQP_CHECK_RESULT(write(state->sockfd, state->outbound_buffer.bytes, payload_len + (HEADER_SIZE + FOOTER_SIZE))); #else { int nRC = RABBITMQ_C_NO_ERROR; nRC = write(state->sockfd, state->outbound_buffer.bytes, payload_len + (HEADER_SIZE + FOOTER_SIZE)); if( nRC < 0 ) return nRC; } #endif return 0; case 1: { struct iovec iov[3]; char frame_end_byte = AMQP_FRAME_END; iov[0].iov_base = state->outbound_buffer.bytes; iov[0].iov_len = HEADER_SIZE; iov[1].iov_base = encoded.bytes; iov[1].iov_len = payload_len; iov[2].iov_base = &frame_end_byte; assert(FOOTER_SIZE == 1); iov[2].iov_len = FOOTER_SIZE; #ifdef __GNUC__ /* frgo, 2010-06-11 */ AMQP_CHECK_RESULT(writev(state->sockfd, &iov[0], 3)); #else { int nRC = RABBITMQ_C_NO_ERROR; nRC = writev(state->sockfd, &iov[0], 3); if( nRC < 0 ) return nRC; } #endif return 0; } default: return separate_body; } }
int amqp_encode_table(amqp_bytes_t encoded, amqp_table_t *input, int *offsetptr) { int offset = *offsetptr; int tablesize_offset = offset; int i; offset += 4; /* skip space for the size of the table to be filled in later */ for (i = 0; i < input->num_entries; i++) { amqp_table_entry_t *entry = &(input->entries[i]); E_8(encoded, offset, entry->key.len); offset++; E_BYTES(encoded, offset, entry->key.len, entry->key.bytes); offset += entry->key.len; E_8(encoded, offset, entry->kind); offset++; switch (entry->kind) { case 'S': E_32(encoded, offset, entry->value.bytes.len); offset += 4; E_BYTES(encoded, offset, entry->value.bytes.len, entry->value.bytes.bytes); offset += entry->value.bytes.len; break; case 'I': E_32(encoded, offset, (uint32_t) entry->value.i32); offset += 4; break; case 'D': E_8(encoded, offset, entry->value.decimal.decimals); offset++; E_32(encoded, offset, entry->value.decimal.value); offset += 4; break; case 'T': E_64(encoded, offset, entry->value.u64); offset += 8; break; case 'F': AMQP_CHECK_RESULT(amqp_encode_table(encoded, &(entry->value.table), &offset)); break; default: return -EINVAL; } } E_32(encoded, tablesize_offset, (offset - *offsetptr - 4)); *offsetptr = offset; return 0; }
static int amqp_login_inner(amqp_connection_state_t state, int channel_max, int frame_max, int heartbeat, amqp_sasl_method_enum sasl_method, va_list vl) { struct timeval hb; amqp_method_t method; uint32_t server_frame_max; uint16_t server_channel_max; uint16_t server_heartbeat; if(heartbeat != 0) { hb.tv_sec = 2*heartbeat; hb.tv_usec = 0; (void)setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &hb, sizeof(hb)); (void)setsockopt(state->sockfd, SOL_SOCKET, SO_SNDTIMEO, &hb, sizeof(hb)); } amqp_send_header(state); (void)AMQP_CHECK_EOF_RESULT(amqp_simple_wait_method(state, 0, AMQP_CONNECTION_START_METHOD, &method)); { amqp_connection_start_t *s = (amqp_connection_start_t *) method.decoded; if ((s->version_major != AMQP_PROTOCOL_VERSION_MAJOR) || (s->version_minor != AMQP_PROTOCOL_VERSION_MINOR)) { return -EPROTOTYPE; } /* TODO: check that our chosen SASL mechanism is in the list of acceptable mechanisms. Or even let the application choose from the list! */ } { amqp_bytes_t response_bytes = sasl_response(&state->decoding_pool, sasl_method, vl); amqp_connection_start_ok_t s = (amqp_connection_start_ok_t) { .client_properties = {.num_entries = 0, .entries = NULL}, .mechanism = sasl_method_name(sasl_method), .response = response_bytes, .locale = {.len = 5, .bytes = "en_US"} }; (void)AMQP_CHECK_RESULT(amqp_send_method(state, 0, AMQP_CONNECTION_START_OK_METHOD, &s)); }
static int wait_frame_inner(amqp_connection_state_t state, amqp_frame_t *decoded_frame) { while (1) { int result; while (amqp_data_in_buffer(state)) { amqp_bytes_t buffer; buffer.len = state->sock_inbound_limit - state->sock_inbound_offset; buffer.bytes = ((char *) state->sock_inbound_buffer.bytes) + state->sock_inbound_offset; (void)AMQP_CHECK_RESULT((result = amqp_handle_input(state, buffer, decoded_frame))); state->sock_inbound_offset += result; if (decoded_frame->frame_type != 0) { /* Complete frame was read. Return it. */ return 1; } /* Incomplete or ignored frame. Keep processing input. */ assert(result != 0); } result = eintr_safe_read(state->sockfd, state->sock_inbound_buffer.bytes, state->sock_inbound_buffer.len); if (result < 0) { mtevL(mtev_error, "Failed to read message in wait_frame_inner, size %zu\n", state->sock_inbound_buffer.len); return -errno; } if (result == 0) { /* EOF. */ return 0; } state->sock_inbound_limit = result; state->sock_inbound_offset = 0; } }
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; }
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_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_send_frame_to(amqp_connection_state_t state, amqp_frame_t const *frame, amqp_output_fn_t fn, void *context) { amqp_bytes_t encoded; int payload_len; int separate_body; separate_body = inner_send_frame(state, frame, &encoded, &payload_len); switch (separate_body) { case 0: #ifdef __GNUC__ /* frgo, 2010-06-11 */ AMQP_CHECK_RESULT(fn(context, state->outbound_buffer.bytes, payload_len + (HEADER_SIZE + FOOTER_SIZE))); #else { int nRC = RABBITMQ_C_NO_ERROR; nRC = fn(context, state->outbound_buffer.bytes, payload_len + (HEADER_SIZE + FOOTER_SIZE)); if( nRC < 0 ) return nRC; } #endif return 0; case 1: #ifdef __GNUC__ /* frgo, 2010-06-11 */ AMQP_CHECK_RESULT(fn(context, state->outbound_buffer.bytes, HEADER_SIZE)); #else { int nRC = RABBITMQ_C_NO_ERROR; nRC = fn(context, state->outbound_buffer.bytes, HEADER_SIZE); if( nRC < 0 ) return nRC; } #endif #ifdef __GNUC__ /* frgo, 2010-06-11 */ AMQP_CHECK_RESULT(fn(context, encoded.bytes, payload_len)); #else { int nRC = RABBITMQ_C_NO_ERROR; nRC = fn(context, encoded.bytes, payload_len); if( nRC < 0 ) return nRC; } #endif { assert(FOOTER_SIZE == 1); char frame_end_byte = AMQP_FRAME_END; #ifdef __GNUC__ /* frgo, 2010-06-11 */ AMQP_CHECK_RESULT(fn(context, &frame_end_byte, FOOTER_SIZE)); #else { int nRC = RABBITMQ_C_NO_ERROR; nRC = fn(context, &frame_end_byte, FOOTER_SIZE); if( nRC < 0 ) return nRC; } #endif } return 0; default: return separate_body; } }