/** * Link Detached Handler */ static int AMQP_link_detach_handler(void* context, qd_link_t *link, qd_detach_type_t dt) { qdr_link_t *rlink = (qdr_link_t*) qd_link_get_context(link); pn_condition_t *cond = qd_link_pn(link) ? pn_link_remote_condition(qd_link_pn(link)) : 0; if (rlink) { qdr_error_t *error = qdr_error_from_pn(cond); qdr_link_detach(rlink, dt, error); // // This is the last event for this link that we will send into the core. Remove the // core linkage. Note that the core->qd linkage is still in place. // qd_link_set_context(link, 0); // // If the link was lost (due to connection drop), or the linkage from the core // object is already gone, finish disconnecting the linkage and free the qd_link // because the core will silently free its own resources. // if (dt == QD_LOST || qdr_link_get_context(rlink) == 0) { qdr_link_set_context(rlink, 0); qd_link_free(link); } } return 0; }
static void CORE_link_detach(void *context, qdr_link_t *link, qdr_error_t *error, bool first, bool close) { qd_link_t *qlink = (qd_link_t*) qdr_link_get_context(link); if (!qlink) return; pn_link_t *pn_link = qd_link_pn(qlink); if (!pn_link) return; if (error) { pn_condition_t *cond = pn_link_condition(pn_link); qdr_error_copy(error, cond); } if (close) qd_link_close(qlink); else qd_link_detach(qlink); // // This is the last event for this link that we are going to send into Proton. // Remove the core->proton linkage. Note that the proton->core linkage may still // be intact and needed. // qdr_link_set_context(link, 0); // // If this is the second detach, free the qd_link // if (!first) qd_link_free(qlink); }
static void CORE_link_first_attach(void *context, qdr_connection_t *conn, qdr_link_t *link, qdr_terminus_t *source, qdr_terminus_t *target) { qd_router_t *router = (qd_router_t*) context; qd_connection_t *qconn = (qd_connection_t*) qdr_connection_get_context(conn); // // Create a new link to be attached // qd_link_t *qlink = qd_link(router->node, qconn, qdr_link_direction(link), qdr_link_name(link)); // // Copy the source and target termini to the link // qdr_terminus_copy(source, qd_link_source(qlink)); qdr_terminus_copy(target, qd_link_target(qlink)); // // Associate the qd_link and the qdr_link to each other // qdr_link_set_context(link, qlink); qd_link_set_context(qlink, link); // // Open (attach) the link // pn_link_open(qd_link_pn(qlink)); }
static void CORE_link_drained(void *context, qdr_link_t *link) { qd_link_t *qlink = (qd_link_t*) qdr_link_get_context(link); if (!qlink) return; pn_link_t *plink = qd_link_pn(qlink); if (plink) pn_link_drained(plink); }
static void CORE_link_offer(void *context, qdr_link_t *link, int delivery_count) { qd_link_t *qlink = (qd_link_t*) qdr_link_get_context(link); if (!qlink) return; pn_link_t *plink = qd_link_pn(qlink); if (plink) pn_link_offered(plink, delivery_count); }
static void CORE_link_flow(void *context, qdr_link_t *link, int credit) { qd_link_t *qlink = (qd_link_t*) qdr_link_get_context(link); if (!qlink) return; pn_link_t *plink = qd_link_pn(qlink); if (plink) pn_link_flow(plink, credit); }
static void CORE_link_drain(void *context, qdr_link_t *link, bool mode) { qd_link_t *qlink = (qd_link_t*) qdr_link_get_context(link); if (!qlink) return; pn_link_t *plink = qd_link_pn(qlink); if (plink) { if (pn_link_is_receiver(plink)) pn_link_set_drain(plink, mode); } }
/** * Handler for flow events on links */ static int AMQP_link_flow_handler(void* context, qd_link_t *link) { qd_router_t *router = (qd_router_t*) context; qdr_link_t *rlink = (qdr_link_t*) qd_link_get_context(link); pn_link_t *pnlink = qd_link_pn(link); if (!rlink) return 0; qdr_link_flow(router->router_core, rlink, pn_link_remote_credit(pnlink), pn_link_get_drain(pnlink)); return 0; }
/** * New Outgoing Link Handler */ static int AMQP_outgoing_link_handler(void* context, qd_link_t *link) { qd_connection_t *conn = qd_link_connection(link); qdr_connection_t *qdr_conn = (qdr_connection_t*) qd_connection_get_context(conn); qdr_link_t *qdr_link = qdr_link_first_attach(qdr_conn, QD_OUTGOING, qdr_terminus(qd_link_remote_source(link)), qdr_terminus(qd_link_remote_target(link)), pn_link_name(qd_link_pn(link))); qdr_link_set_context(qdr_link, link); qd_link_set_context(link, qdr_link); return 0; }
static void CORE_link_push(void *context, qdr_link_t *link) { qd_router_t *router = (qd_router_t*) context; qd_link_t *qlink = (qd_link_t*) qdr_link_get_context(link); if (!qlink) return; pn_link_t *plink = qd_link_pn(qlink); if (plink) { int link_credit = pn_link_credit(plink); qdr_link_process_deliveries(router->router_core, link, link_credit); } }
static void CORE_link_second_attach(void *context, qdr_link_t *link, qdr_terminus_t *source, qdr_terminus_t *target) { qd_link_t *qlink = (qd_link_t*) qdr_link_get_context(link); if (!qlink) return; qdr_terminus_copy(source, qd_link_source(qlink)); qdr_terminus_copy(target, qd_link_target(qlink)); // // Open (attach) the link // pn_link_open(qd_link_pn(qlink)); }
static void CORE_link_deliver(void *context, qdr_link_t *link, qdr_delivery_t *dlv, bool settled) { qd_router_t *router = (qd_router_t*) context; qd_link_t *qlink = (qd_link_t*) qdr_link_get_context(link); if (!qlink) return; pn_link_t *plink = qd_link_pn(qlink); if (!plink) return; const char *tag; int tag_length; qdr_delivery_tag(dlv, &tag, &tag_length); pn_delivery(plink, pn_dtag(tag, tag_length)); pn_delivery_t *pdlv = pn_link_current(plink); // // If the remote send settle mode is set to 'settled', we should settle the delivery on behalf of the receiver. // bool remote_snd_settled = qd_link_remote_snd_settle_mode(qlink) == PN_SND_SETTLED; if (!settled && !remote_snd_settled) { pn_delivery_set_context(pdlv, dlv); qdr_delivery_set_context(dlv, pdlv); qdr_delivery_incref(dlv); } qd_message_send(qdr_delivery_message(dlv), qlink, qdr_link_strip_annotations_out(link)); if (!settled && remote_snd_settled) // Tell the core that the delivery has been accepted and settled, since we are settling on behalf of the receiver qdr_delivery_update_disposition(router->router_core, dlv, PN_ACCEPTED, true, false); if (settled || remote_snd_settled) pn_delivery_settle(pdlv); pn_link_advance(plink); }
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); } }