|
15 | 15 |
|
16 | 16 | #include <pybmq_session.h> |
17 | 17 |
|
| 18 | +#include <pybmq_gilacquireguard.h> |
18 | 19 | #include <pybmq_gilreleaseguard.h> |
19 | 20 | #include <pybmq_messageutils.h> |
20 | 21 | #include <pybmq_mocksession.h> |
@@ -77,13 +78,14 @@ Session::Session( |
77 | 78 | PyObject* py_session_event_callback, |
78 | 79 | PyObject* py_message_event_callback, |
79 | 80 | PyObject* py_ack_event_callback, |
| 81 | + PyObject* fake_authn_credential_cb, |
80 | 82 | const char* broker_uri, |
81 | 83 | const char* script_name, |
82 | 84 | bmqt::CompressionAlgorithmType::Enum message_compression_type, |
83 | 85 | bsl::optional<int> num_processing_threads, |
84 | 86 | bsl::optional<int> blob_buffer_size, |
85 | 87 | bsl::optional<int> channel_high_watermark, |
86 | | - bsl::optional<bsl::pair<int, int> > event_queue_watermarks, |
| 88 | + bsl::optional<bsl::pair<int, int>> event_queue_watermarks, |
87 | 89 | const bsls::TimeInterval& stats_dump_interval, |
88 | 90 | const bsls::TimeInterval& connect_timeout, |
89 | 91 | const bsls::TimeInterval& disconnect_timeout, |
@@ -144,6 +146,74 @@ Session::Session( |
144 | 146 | event_queue_watermarks.value().second); |
145 | 147 | } |
146 | 148 |
|
| 149 | + if (fake_authn_credential_cb != nullptr && fake_authn_credential_cb != Py_None) |
| 150 | + { |
| 151 | + // Increment reference count since we're storing the Python object |
| 152 | + Py_INCREF(fake_authn_credential_cb); |
| 153 | + |
| 154 | + // Create a C++ lambda that wraps the Python callback |
| 155 | + AuthnCredentialCb cpp_callback = |
| 156 | + [fake_authn_credential_cb](bsl::ostream& error) |
| 157 | + -> bsl::optional<bmqt::AuthnCredential> { |
| 158 | + pybmq::GilAcquireGuard guard; |
| 159 | + |
| 160 | + // Call get_credential_data() method on the Python object |
| 161 | + bslma::ManagedPtr<PyObject> result = |
| 162 | + RefUtils::toManagedPtr(PyObject_CallMethod( |
| 163 | + fake_authn_credential_cb, |
| 164 | + "get_credential_data", |
| 165 | + nullptr)); |
| 166 | + |
| 167 | + if (!result) { |
| 168 | + // Python exception occurred |
| 169 | + PyErr_Print(); |
| 170 | + error << "Error calling get_credential_data()"; |
| 171 | + return bsl::optional<bmqt::AuthnCredential>(); |
| 172 | + } |
| 173 | + |
| 174 | + if (result.get() == Py_None) { |
| 175 | + return bsl::optional<bmqt::AuthnCredential>(); |
| 176 | + } |
| 177 | + |
| 178 | + // Extract tuple (mechanism, data) |
| 179 | + if (!PyTuple_Check(result.get()) || PyTuple_Size(result.get()) != 2) { |
| 180 | + error << "get_credential_data() must return (str, bytes) or None"; |
| 181 | + return bsl::optional<bmqt::AuthnCredential>(); |
| 182 | + } |
| 183 | + |
| 184 | + PyObject* mechanism_obj = PyTuple_GetItem(result.get(), 0); |
| 185 | + PyObject* data_obj = PyTuple_GetItem(result.get(), 1); |
| 186 | + |
| 187 | + if (!PyUnicode_Check(mechanism_obj) || !PyBytes_Check(data_obj)) { |
| 188 | + error << "get_credential_data() must return (str, bytes) or None"; |
| 189 | + return bsl::optional<bmqt::AuthnCredential>(); |
| 190 | + } |
| 191 | + |
| 192 | + // Convert Python str to C++ string |
| 193 | + const char* mechanism_cstr = PyUnicode_AsUTF8(mechanism_obj); |
| 194 | + bsl::string mechanism(mechanism_cstr); |
| 195 | + |
| 196 | + // Convert Python bytes to vector<char> |
| 197 | + char* data_ptr; |
| 198 | + Py_ssize_t data_len; |
| 199 | + PyBytes_AsStringAndSize(data_obj, &data_ptr, &data_len); |
| 200 | + bsl::vector<char> data(data_ptr, data_ptr + data_len); |
| 201 | + |
| 202 | + // Construct and return AuthnCredential |
| 203 | + bmqt::AuthnCredential credential; |
| 204 | + credential.setMechanism(mechanism).setData(data); |
| 205 | + |
| 206 | + // Move credential into optional (AuthnCredential is move-only) |
| 207 | + bsl::optional<bmqt::AuthnCredential> opt_credential; |
| 208 | + opt_credential.emplace(bslmf::MovableRefUtil::move(credential)); |
| 209 | + return opt_credential; |
| 210 | + }; |
| 211 | + |
| 212 | + // TODO: Uncomment when setAuthnCredentialCb is available in SessionOptions |
| 213 | + // options.setAuthnCredentialCb(cpp_callback); |
| 214 | + (void)cpp_callback; // Suppress unused variable warning |
| 215 | + } |
| 216 | + |
147 | 217 | if (stats_dump_interval != bsls::TimeInterval()) { |
148 | 218 | options.setStatsDumpInterval(stats_dump_interval); |
149 | 219 | } |
@@ -527,8 +597,8 @@ Session::post( |
527 | 597 | oss << "Failed to post message to " << queue_uri << " queue: " << post_rc; |
528 | 598 | throw GenericError(oss.str()); |
529 | 599 | } |
530 | | - // We have a successful post and the SDK now owns the `on_ack` callback object |
531 | | - // so release our reference without a DECREF. |
| 600 | + // We have a successful post and the SDK now owns the `on_ack` callback |
| 601 | + // object so release our reference without a DECREF. |
532 | 602 | managed_on_ack.release(); |
533 | 603 | } catch (const GenericError& exc) { |
534 | 604 | PyErr_SetString(d_error, exc.what()); |
|
0 commit comments