Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support message headers #10

Merged
merged 24 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
78da22f
message headers supported
kwitekrac Oct 22, 2023
7548760
queue create and bind split
kwitekrac Oct 22, 2023
6642c9f
docs(release_notes): update RELEASE_NOTES.md
kwitekrac Oct 22, 2023
2456088
docs(release_notes): update RELEASE_NOTES.md
kwitekrac Oct 22, 2023
e69b42a
code formatted
kwitekrac Oct 23, 2023
2318132
docs(release_notes): update RELEASE_NOTES.md
kwitekrac Oct 23, 2023
d74ea7a
received headers type decode
kwitekrac Oct 24, 2023
335d16c
Merge branch 'support-message-headers' of https://github.com/zaphiro-…
kwitekrac Oct 24, 2023
36632e4
docs(release_notes): update RELEASE_NOTES.md
kwitekrac Oct 24, 2023
f7541d9
more types of message headers supported in consume
kwitekrac Oct 24, 2023
1611c57
Merge branch 'support-message-headers' of https://github.com/zaphiro-…
kwitekrac Oct 24, 2023
d7e5c26
publish message with headers works
kwitekrac Oct 28, 2023
7368871
docs(release_notes): update RELEASE_NOTES.md
kwitekrac Oct 28, 2023
70c1244
code restructured
kwitekrac Oct 28, 2023
d133562
Merge branch 'support-message-headers' of https://github.com/zaphiro-…
kwitekrac Oct 28, 2023
04ad862
added passive parameter exported functions
kwitekrac Oct 30, 2023
5800539
docs(release_notes): update RELEASE_NOTES.md
kwitekrac Oct 30, 2023
7fdb5f3
Update CMakeLists.txt
kdevelleZ Oct 31, 2023
79de6c8
Merge branch 'support-message-headers' of https://github.com/zaphiro-…
kdevelleZ Oct 31, 2023
c65569f
docs(release_notes): update RELEASE_NOTES.md
kdevelleZ Oct 31, 2023
011471d
Update labview_rabbitmq.c
kwitekrac Nov 2, 2023
c286d07
docs(release_notes): update RELEASE_NOTES.md
kwitekrac Nov 2, 2023
46ffb2f
fixed style issues reported in PR review
kwitekrac Nov 5, 2023
e2a9bd1
docs(release_notes): update RELEASE_NOTES.md
kwitekrac Nov 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .vscode/settings.json
kdevelleZ marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"files.associations": {
"optional": "c",
"istream": "c",
"ostream": "c",
"ratio": "c",
"system_error": "c",
"array": "c",
"functional": "c",
"tuple": "c",
"type_traits": "c",
"utility": "c",
"chrono": "c",
"algorithm": "c",
"sstream": "c",
"random": "c"
}
}
3 changes: 2 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# rabbitmq-c Release Notes

## 0.0.1-dev - 2023-08-07
## 0.0.1-dev - 2023-10-22

### Features

- Support message headers (PR #10 by @kwitekrac)
- LabVIEW support (PR #8 by @kwitekrac)

### Continuous Integration
Expand Down
140 changes: 125 additions & 15 deletions labview/labview_rabbitmq.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,81 @@ MgErr copyBufferToLStrHandle(const void* buffer, int len, LStrHandle LVString)
return err;
}

// Function to split a string into multiple strings based on the ";" separator
char** splitString(char* input, int* count) {
char** tokens = NULL;
char* token = strtok(input, ";");
*count = 0;

while (token != NULL) {
tokens = (char**)realloc(tokens, ((*count) + 1) * sizeof(char*));
if (tokens == NULL) {
perror("Memory allocation error");
exit(1);
}
tokens[(*count)] = strdup(token);
(*count)++;
token = strtok(NULL, ";");
}

return tokens;
}

void getConcatenatedMessageHeaders(amqp_table_t *table, LStrHandle cheaders_key, LStrHandle cheaders_value) {
// Calculate required buffer size for concatenated headers keys and values,
// following strings will be separated by ";". Buffer will be finished with Null-terminate the C string.
// Init counters with num of elements-1 (number of separators) + 1 ('\0')
int required_buffer_size_keys = (table->num_entries-1) +1;
int required_buffer_size_values = (table->num_entries-1) +1;
for (int i = 0; i < table->num_entries; i++) {
required_buffer_size_keys += table->entries[i].key.len;
required_buffer_size_values += table->entries[i].value.value.bytes.len;
}
// Allocate memory for temp strings
char *keys = (char *)malloc(required_buffer_size_keys * sizeof(char));
char *values = (char *)malloc(required_buffer_size_values * sizeof(char));

int keys_offset=0;
int values_offset=0;
for (int i = 0; i < table->num_entries; i++) {
memcpy(keys+keys_offset, table->entries[i].key.bytes, table->entries[i].key.len);
keys_offset += table->entries[i].key.len;
keys[keys_offset]=';';
keys_offset++;
memcpy(values+values_offset, table->entries[i].value.value.bytes.bytes, table->entries[i].value.value.bytes.len);
values_offset += table->entries[i].value.value.bytes.len;
values[values_offset]=';';
values_offset++;
}
// add Null-terminate the C string
keys[keys_offset-1]='\0';
values[values_offset-1]='\0';

copyStringToLStrHandle(keys, cheaders_key);
kwitekrac marked this conversation as resolved.
Show resolved Hide resolved
copyStringToLStrHandle(values, cheaders_value);

free(keys);
free(values);
}

char* amqpBytesToString(amqp_bytes_t input) {
// Allocate memory for the C string and a null terminator
char* result = (char*)malloc(input.len + 1);

if (result == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(1);
}

// Copy the data from amqp_bytes_t to the C string
memcpy(result, input.bytes, input.len);

// Null-terminate the C string
result[input.len] = '\0';

return result;
}

LABVIEW_PUBLIC_FUNCTION
char* lv_rabbitmq_version(void) {
char* VERSION = "0.0.1";
Expand Down Expand Up @@ -196,19 +271,49 @@ int lv_amqp_login(int64_t conn_intptr, char *host, int port, int timeout_sec, ch


LABVIEW_PUBLIC_FUNCTION
int lv_amqp_basic_publish(int64_t conn_intptr, uint16_t channel, char *exchange, char *routingkey, char *messagebody, LStrHandle error_description) {
int lv_amqp_basic_publish(int64_t conn_intptr, uint16_t channel, char *exchange, char *routingkey, char *cheaders_key, char *cheaders_value, char *messagebody, LStrHandle error_description) {
chicco785 marked this conversation as resolved.
Show resolved Hide resolved
amqp_connection_state_t conn = (amqp_connection_state_t)conn_intptr;
amqp_basic_properties_t props;
props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG;
props.content_type = amqp_cstring_bytes("text/plain");
props.delivery_mode = 2; /* persistent delivery mode */
return amqp_basic_publish(conn, channel, amqp_cstring_bytes(exchange),amqp_cstring_bytes(routingkey), 0, 0, &props, amqp_cstring_bytes(messagebody));

int count, count2;
kwitekrac marked this conversation as resolved.
Show resolved Hide resolved
char** headers_key = splitString(cheaders_key, &count);
char** headers_value = splitString(cheaders_value, &count2);
if (headers_key != NULL) {
// Update flags to use custom headers
props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_HEADERS_FLAG;
// Allocate memory for custom headers
amqp_table_t *table=&props.headers;
props.headers.num_entries=count;
props.headers.entries=calloc(props.headers.num_entries, sizeof(amqp_table_entry_t));
// update headers content
for (int i = 0; i < count; i++) {
(table->entries[i]).key=amqp_cstring_bytes(headers_key[i]);
((table->entries[i]).value).kind=AMQP_FIELD_KIND_BYTES;
((table->entries[i]).value).value.bytes=amqp_cstring_bytes(headers_value[i]);
}
}

int error_code = error_code = amqp_basic_publish(conn, channel, amqp_cstring_bytes(exchange),amqp_cstring_bytes(routingkey), 0, 0, &props, amqp_cstring_bytes(messagebody));

// Dereference headers
if (headers_key != NULL) {
for (int i = 0; i < count; i++) {
free(headers_key[i]);
free(headers_value[i]);
}
free(headers_key);
free(headers_value);
}
return error_code;
//this function returns amqp_status_enum thats different from amqp_rpc_reply_t
}


LABVIEW_PUBLIC_FUNCTION
int lv_amqp_create_queue(int64_t conn_intptr, uint16_t channel, char *exchange, char *bindingkey, LStrHandle error_description) {
int lv_amqp_create_queue(int64_t conn_intptr, uint16_t channel, LStrHandle queue_name_out, LStrHandle error_description) {
amqp_connection_state_t conn = (amqp_connection_state_t)conn_intptr;
int status;
amqp_bytes_t queuename;
Expand All @@ -220,17 +325,16 @@ int lv_amqp_create_queue(int64_t conn_intptr, uint16_t channel, char *exchange,
amqp_queue_declare_ok_t *r = amqp_queue_declare(conn, channel, amqp_empty_bytes, PASSIVE, DURABLE, EXCLUSIVE, AUTO_DELETE, amqp_empty_table);

status = lv_report_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue", error_description);
if (status != 1) {
return status;
}

queuename = amqp_bytes_malloc_dup(r->queue);
if (queuename.bytes == NULL) {
copyStringToLStrHandle("Out of memory while copying queue name", error_description);
return _OUT_OF_MEMORY;
}
copyBufferToLStrHandle(r->queue.bytes, r->queue.len, queue_name_out);
return status;
}

LABVIEW_PUBLIC_FUNCTION
int lv_amqp_bind_queue(int64_t conn_intptr, uint16_t channel, char *exchange, char *queuename, char *bindingkey, LStrHandle error_description) {
amqp_connection_state_t conn = (amqp_connection_state_t)conn_intptr;
int status;

amqp_queue_bind(conn, channel, queuename, amqp_cstring_bytes(exchange), amqp_cstring_bytes(bindingkey), amqp_empty_table);
amqp_queue_bind(conn, channel, amqp_cstring_bytes(queuename), amqp_cstring_bytes(exchange), amqp_cstring_bytes(bindingkey), amqp_empty_table);
status = lv_report_amqp_error(amqp_get_rpc_reply(conn), "Binding queue", error_description);
if (status != 1) {
return status;
Expand All @@ -239,7 +343,7 @@ int lv_amqp_create_queue(int64_t conn_intptr, uint16_t channel, char *exchange,
amqp_boolean_t NO_LOCAL = 0;
kdevelleZ marked this conversation as resolved.
Show resolved Hide resolved
amqp_boolean_t NO_ACK = 1;
amqp_boolean_t EXCLUSIVE2 = 0;
amqp_basic_consume(conn, channel, queuename, amqp_empty_bytes, NO_LOCAL, NO_ACK, EXCLUSIVE2, amqp_empty_table);
amqp_basic_consume(conn, channel, amqp_cstring_bytes(queuename), amqp_empty_bytes, NO_LOCAL, NO_ACK, EXCLUSIVE2, amqp_empty_table);
/* amqp_basic_consume is used to register a consumer on the queue,
so that the broker will start delivering messages to it.*/
status = lv_report_amqp_error(amqp_get_rpc_reply(conn), "Basic consume", error_description);
Expand All @@ -248,7 +352,7 @@ int lv_amqp_create_queue(int64_t conn_intptr, uint16_t channel, char *exchange,


LABVIEW_PUBLIC_FUNCTION
int lv_amqp_consume_message(int64_t conn_intptr, int timeout_sec, LStrHandle output, LStrHandle error_description) {
int lv_amqp_consume_message(int64_t conn_intptr, int timeout_sec, LStrHandle output, LStrHandle cheaders_key, LStrHandle cheaders_value, LStrHandle error_description) {
amqp_connection_state_t conn = (amqp_connection_state_t)conn_intptr;

int status;
Expand All @@ -269,6 +373,12 @@ int lv_amqp_consume_message(int64_t conn_intptr, int timeout_sec, LStrHandle out
copyBufferToLStrHandle(envelope.message.body.bytes, envelope.message.body.len, output);
}

// Check message headers
amqp_table_t *headers = &envelope.message.properties.headers;
if (headers->num_entries > 0) {
getConcatenatedMessageHeaders(headers, cheaders_key, cheaders_value);
chicco785 marked this conversation as resolved.
Show resolved Hide resolved
}

amqp_destroy_envelope(&envelope);
return status;
}
Expand Down
8 changes: 5 additions & 3 deletions labview/labview_rabbitmq.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ int lv_amqp_exchange_declare(int64_t conn_intptr, uint16_t channel, char* exchan

int lv_amqp_login(int64_t conn_intptr, char* host, int port, int timeout_sec, char* username, char* password, LStrHandle error_description);

int lv_amqp_basic_publish(int64_t conn_intptr, uint16_t channel, char* exchange, char* routingkey, char* messagebody, LStrHandle error_description);
int lv_amqp_basic_publish(int64_t conn_intptr, uint16_t channel, char *exchange, char *routingkey, char *cheaders_key, char *cheaders_value, char *messagebody, LStrHandle error_description);

int lv_amqp_create_queue(int64_t conn_intptr, uint16_t channel, char* exchange, char* bindingkey, LStrHandle error_description);
int lv_amqp_create_queue(int64_t conn_intptr, uint16_t channel, LStrHandle queue_name_out, LStrHandle error_description);

int lv_amqp_consume_message(int64_t conn_intptr, int timeout_sec, LStrHandle output, LStrHandle error_description);
int lv_amqp_bind_queue(int64_t conn_intptr, uint16_t channel, char *exchange, char *queuename, char *bindingkey, LStrHandle error_description);

int lv_amqp_consume_message(int64_t conn_intptr, int timeout_sec, LStrHandle output, LStrHandle cheaders_key, LStrHandle cheaders_value, LStrHandle error_description);


#endif /* LABVIEW_RABBITMQ_H */