// Get the field's location in the buffer. Return 0 if the field does not exist. // Note that even if the field location is returned, it may contain a // QD_AMQP_NULL value (qd_field_location->tag == QD_AMQP_NULL). // static qd_field_location_t *qd_message_field_location(qd_message_t *msg, qd_message_field_t field) { qd_message_content_t *content = MSG_CONTENT(msg); qd_message_field_t section = qd_field_section(field); switch (section) { case QD_FIELD_HEADER: return qd_message_header_field(msg, field); case QD_FIELD_PROPERTIES: return qd_message_properties_field(msg, field); case QD_FIELD_DELIVERY_ANNOTATION: if (content->section_delivery_annotation.parsed || (qd_message_check(msg, QD_DEPTH_DELIVERY_ANNOTATIONS) && content->section_delivery_annotation.parsed)) return &content->section_delivery_annotation; break; case QD_FIELD_MESSAGE_ANNOTATION: if (content->section_message_annotation.parsed || (qd_message_check(msg, QD_DEPTH_MESSAGE_ANNOTATIONS) && content->section_message_annotation.parsed)) return &content->section_message_annotation; break; case QD_FIELD_APPLICATION_PROPERTIES: if (content->section_application_properties.parsed || (qd_message_check(msg, QD_DEPTH_APPLICATION_PROPERTIES) && content->section_application_properties.parsed)) return &content->section_application_properties; break; case QD_FIELD_BODY: if (content->section_body.parsed || (qd_message_check(msg, QD_DEPTH_BODY) && content->section_body.parsed)) return &content->section_body; break; case QD_FIELD_FOOTER: if (content->section_footer.parsed || (qd_message_check(msg, QD_DEPTH_ALL) && content->section_footer.parsed)) return &content->section_footer; break; default: assert(false); // TBD: add support as needed return 0; } return 0; }
static void qd_io_rx_handler(void *context, qd_message_t *msg, int link_id) { IoAdapter *self = (IoAdapter*) context; // // Parse the message through the body and exit if the message is not well formed. // if (!qd_message_check(msg, QD_DEPTH_BODY)) return; // This is called from non-python threads so we need to acquire the GIL to use python APIS. qd_python_lock_state_t lock_state = qd_python_lock(); PyObject *py_msg = PyObject_CallFunction(message_type, NULL); if (!py_msg) { qd_error_py(); qd_python_unlock(lock_state); return; } iter_to_py_attr(qd_message_field_iterator(msg, QD_FIELD_TO), py_iter_copy, py_msg, "address"); iter_to_py_attr(qd_message_field_iterator(msg, QD_FIELD_REPLY_TO), py_iter_copy, py_msg, "reply_to"); // Note: correlation ID requires _typed() iter_to_py_attr(qd_message_field_iterator_typed(msg, QD_FIELD_CORRELATION_ID), py_iter_parse, py_msg, "correlation_id"); iter_to_py_attr(qd_message_field_iterator(msg, QD_FIELD_APPLICATION_PROPERTIES), py_iter_parse, py_msg, "properties"); iter_to_py_attr(qd_message_field_iterator(msg, QD_FIELD_BODY), py_iter_parse, py_msg, "body"); PyObject *value = PyObject_CallFunction(self->handler, "Ol", py_msg, link_id); Py_DECREF(py_msg); Py_XDECREF(value); qd_error_py(); qd_python_unlock(lock_state); }
/* TODO aconway 2014-05-13: more detailed message representation. */ char* qd_message_repr(qd_message_t *msg, char* buffer, size_t len) { qd_message_check(msg, QD_DEPTH_BODY); char *begin = buffer; char *end = buffer + len - sizeof(REPR_END); /* Save space for ending */ aprintf(&begin, end, "Message{", msg); copy_field(msg, QD_FIELD_TO, INT_MAX, "to='", "'", &begin, end); copy_field(msg, QD_FIELD_REPLY_TO, INT_MAX, " reply-to='", "'", &begin, end); copy_field(msg, QD_FIELD_BODY, 16, " body='", "'", &begin, end); aprintf(&begin, end, "%s", REPR_END); /* We saved space at the beginning. */ return buffer; }
// get the field location of a field in the message properties (if it exists, // else 0). static qd_field_location_t *qd_message_properties_field(qd_message_t *msg, qd_message_field_t field) { static const intptr_t offsets[] = { // position of the field's qd_field_location_t in the message content // object (intptr_t) &((qd_message_content_t *)0)->field_message_id, (intptr_t) &((qd_message_content_t *)0)->field_user_id, (intptr_t) &((qd_message_content_t *)0)->field_to, (intptr_t) &((qd_message_content_t *)0)->field_subject, (intptr_t) &((qd_message_content_t *)0)->field_reply_to, (intptr_t) &((qd_message_content_t *)0)->field_correlation_id }; // update table above if new fields need to be accessed: assert(QD_FIELD_MESSAGE_ID <= field && field <= QD_FIELD_CORRELATION_ID); qd_message_content_t *content = MSG_CONTENT(msg); if (!content->section_message_properties.parsed) { if (!qd_message_check(msg, QD_DEPTH_PROPERTIES) || !content->section_message_properties.parsed) return 0; } if (field == QD_FIELD_PROPERTIES) return &content->section_message_properties; const int index = field - QD_FIELD_MESSAGE_ID; qd_field_location_t *const location = (qd_field_location_t *)((char *)content + offsets[index]); if (location->parsed) return location; // requested field not parsed out. Need to parse out up to the requested field: qd_buffer_t *buffer = content->section_message_properties.buffer; unsigned char *cursor = qd_buffer_base(buffer) + content->section_message_properties.offset; advance(&cursor, &buffer, content->section_message_properties.hdr_length, 0, 0); if (index >= start_list(&cursor, &buffer)) return 0; // properties list too short int position = 0; while (position < index) { qd_field_location_t *f = (qd_field_location_t *)((char *)content + offsets[position]); if (f->parsed) advance(&cursor, &buffer, f->hdr_length + f->length, 0, 0); else // parse it out if (!traverse_field(&cursor, &buffer, f)) return 0; position++; } // all fields previous to the target have now been parsed and cursor/buffer // are in the correct position, parse out the field: if (traverse_field(&cursor, &buffer, location)) return location; return 0; }
// get the field location of a field in the message header (if it exists, // else 0) static qd_field_location_t *qd_message_header_field(qd_message_t *msg, qd_message_field_t field) { qd_message_content_t *content = MSG_CONTENT(msg); if (!content->section_message_header.parsed) { if (!qd_message_check(msg, QD_DEPTH_HEADER) || !content->section_message_header.parsed) return 0; } switch (field) { case QD_FIELD_HEADER: return &content->section_message_properties; default: // TBD: add header fields as needed (see qd_message_properties_field() // as an example) assert(false); return 0; } }
void qd_message_send(qd_message_t *in_msg, qd_link_t *link, bool strip_annotations) { qd_message_pvt_t *msg = (qd_message_pvt_t*) in_msg; qd_message_content_t *content = msg->content; qd_buffer_t *buf = DEQ_HEAD(content->buffers); unsigned char *cursor; pn_link_t *pnl = qd_link_pn(link); char repr[qd_message_repr_len()]; qd_log(log_source, QD_LOG_TRACE, "Sending %s on link %s", qd_message_repr(in_msg, repr, sizeof(repr)), pn_link_name(pnl)); qd_buffer_list_t new_ma; DEQ_INIT(new_ma); if (strip_annotations || compose_message_annotations(msg, &new_ma)) { // // This is the case where the message annotations have been modified. // The message send must be divided into sections: The existing header; // the new message annotations; the rest of the existing message. // Note that the original message annotations that are still in the // buffer chain must not be sent. // // Start by making sure that we've parsed the message sections through // the message annotations // // ??? NO LONGER NECESSARY??? if (!qd_message_check(in_msg, QD_DEPTH_MESSAGE_ANNOTATIONS)) { qd_log(log_source, QD_LOG_ERROR, "Cannot send: %s", qd_error_message); return; } // // Send header if present // cursor = qd_buffer_base(buf); if (content->section_message_header.length > 0) { buf = content->section_message_header.buffer; cursor = content->section_message_header.offset + qd_buffer_base(buf); advance(&cursor, &buf, content->section_message_header.length + content->section_message_header.hdr_length, send_handler, (void*) pnl); } // // Send new message annotations // qd_buffer_t *da_buf = DEQ_HEAD(new_ma); while (da_buf) { pn_link_send(pnl, (char*) qd_buffer_base(da_buf), qd_buffer_size(da_buf)); da_buf = DEQ_NEXT(da_buf); } qd_buffer_list_free_buffers(&new_ma); // // Skip over replaced message annotations // if (content->section_message_annotation.length > 0) advance(&cursor, &buf, content->section_message_annotation.hdr_length + content->section_message_annotation.length, 0, 0); // // Send remaining partial buffer // if (buf) { size_t len = qd_buffer_size(buf) - (cursor - qd_buffer_base(buf)); advance(&cursor, &buf, len, send_handler, (void*) pnl); } // Fall through to process the remaining buffers normally // Note that 'advance' will have moved us to the next buffer in the chain. } while (buf) { pn_link_send(pnl, (char*) qd_buffer_base(buf), qd_buffer_size(buf)); buf = DEQ_NEXT(buf); } }
/** * Inbound Delivery Handler */ static void AMQP_rx_handler(void* context, qd_link_t *link, pn_delivery_t *pnd) { qd_router_t *router = (qd_router_t*) context; pn_link_t *pn_link = qd_link_pn(link); qdr_link_t *rlink = (qdr_link_t*) qd_link_get_context(link); qdr_delivery_t *delivery = 0; qd_message_t *msg; // // Receive the message into a local representation. If the returned message // pointer is NULL, we have not yet received a complete message. // // Note: In the link-routing case, consider cutting the message through. There's // no reason to wait for the whole message to be received before starting to // send it. // msg = qd_message_receive(pnd); if (!msg) return; // // Consume the delivery. // pn_link_advance(pn_link); // // If there's no router link, free the message and finish. It's likely that the link // is closing. // if (!rlink) { qd_message_free(msg); return; } // // Handle the link-routed case // if (qdr_link_is_routed(rlink)) { pn_delivery_tag_t dtag = pn_delivery_tag(pnd); delivery = qdr_link_deliver_to_routed_link(rlink, msg, pn_delivery_settled(pnd), (uint8_t*) dtag.start, dtag.size); if (delivery) { if (pn_delivery_settled(pnd)) pn_delivery_settle(pnd); else { pn_delivery_set_context(pnd, delivery); qdr_delivery_set_context(delivery, pnd); qdr_delivery_incref(delivery); } } return; } // // Determine if the incoming link is anonymous. If the link is addressed, // there are some optimizations we can take advantage of. // bool anonymous_link = qdr_link_is_anonymous(rlink); // // Determine if the user of this connection is allowed to proxy the // user_id of messages. A message user_id is proxied when the // property value differs from the authenticated user name of the connection. // If the user is not allowed to proxy the user_id then the message user_id // must be blank or it must be equal to the connection user name. // bool check_user = false; qd_connection_t *conn = qd_link_connection(link); if (conn->policy_settings) check_user = !conn->policy_settings->allowUserIdProxy; // // Validate the content of the delivery as an AMQP message. This is done partially, only // to validate that we can find the fields we need to route the message. // // If the link is anonymous, we must validate through the message properties to find the // 'to' field. If the link is not anonymous, we don't need the 'to' field as we will be // using the address from the link target. // qd_message_depth_t validation_depth = (anonymous_link || check_user) ? QD_DEPTH_PROPERTIES : QD_DEPTH_MESSAGE_ANNOTATIONS; bool valid_message = qd_message_check(msg, validation_depth); if (valid_message) { if (check_user) { // This connection must not allow proxied user_id qd_iterator_t *userid_iter = qd_message_field_iterator(msg, QD_FIELD_USER_ID); if (userid_iter) { // The user_id property has been specified if (qd_iterator_remaining(userid_iter) > 0) { // user_id property in message is not blank if (!qd_iterator_equal(userid_iter, (const unsigned char *)conn->user_id)) { // This message is rejected: attempted user proxy is disallowed qd_log(router->log_source, QD_LOG_DEBUG, "Message rejected due to user_id proxy violation. User:%s", conn->user_id); pn_link_flow(pn_link, 1); pn_delivery_update(pnd, PN_REJECTED); pn_delivery_settle(pnd); qd_message_free(msg); qd_iterator_free(userid_iter); return; } } qd_iterator_free(userid_iter); } } qd_parsed_field_t *in_ma = qd_message_message_annotations(msg); qd_bitmask_t *link_exclusions; bool strip = qdr_link_strip_annotations_in(rlink); qd_iterator_t *ingress_iter = router_annotate_message(router, in_ma, msg, &link_exclusions, strip); if (anonymous_link) { qd_iterator_t *addr_iter = 0; int phase = 0; // // If the message has delivery annotations, get the to-override field from the annotations. // if (in_ma) { qd_parsed_field_t *ma_to = qd_parse_value_by_key(in_ma, QD_MA_TO); if (ma_to) { addr_iter = qd_iterator_dup(qd_parse_raw(ma_to)); phase = qd_message_get_phase_annotation(msg); } } // // Still no destination address? Use the TO field from the message properties. // if (!addr_iter) addr_iter = qd_message_field_iterator(msg, QD_FIELD_TO); if (addr_iter) { qd_iterator_reset_view(addr_iter, ITER_VIEW_ADDRESS_HASH); if (phase > 0) qd_iterator_annotate_phase(addr_iter, '0' + (char) phase); delivery = qdr_link_deliver_to(rlink, msg, ingress_iter, addr_iter, pn_delivery_settled(pnd), link_exclusions); } } else { const char *term_addr = pn_terminus_get_address(qd_link_remote_target(link)); if (!term_addr) term_addr = pn_terminus_get_address(qd_link_source(link)); if (term_addr) { qd_composed_field_t *to_override = qd_compose_subfield(0); qd_compose_insert_string(to_override, term_addr); qd_message_set_to_override_annotation(msg, to_override); int phase = qdr_link_phase(rlink); if (phase != 0) qd_message_set_phase_annotation(msg, phase); } delivery = qdr_link_deliver(rlink, msg, ingress_iter, pn_delivery_settled(pnd), link_exclusions); } if (delivery) { if (pn_delivery_settled(pnd)) pn_delivery_settle(pnd); else { pn_delivery_set_context(pnd, delivery); qdr_delivery_set_context(delivery, pnd); qdr_delivery_incref(delivery); } } else { // // The message is now and will always be unroutable because there is no address. // pn_link_flow(pn_link, 1); pn_delivery_update(pnd, PN_REJECTED); pn_delivery_settle(pnd); qd_message_free(msg); } // // Rules for delivering messages: // // For addressed (non-anonymous) links: // to-override must be set (done in the core?) // uses qdr_link_deliver to hand over to the core // // For anonymous links: // If there's a to-override in the annotations, use that address // Or, use the 'to' field in the message properties // } else { // // Message is invalid. Reject the message and don't involve the router core. // pn_link_flow(pn_link, 1); pn_delivery_update(pnd, PN_REJECTED); pn_delivery_settle(pnd); qd_message_free(msg); } }