Hoernchen has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/41121?usp=email )


Change subject: smdpp: es9p pure asn1 support
......................................................................

smdpp: es9p pure asn1 support

Can be used instead of the json layer.

Change-Id: I1d824931bd6513d2320ba30df0f8193cd8352863
---
M smdpp/rsp_client.cpp
M smdpp/smdpp_Tests.ttcn
M smdpp/smdpp_Tests_Functions.cc
3 files changed, 377 insertions(+), 15 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks 
refs/changes/21/41121/1

diff --git a/smdpp/rsp_client.cpp b/smdpp/rsp_client.cpp
index d03a64e..97f5a5a 100644
--- a/smdpp/rsp_client.cpp
+++ b/smdpp/rsp_client.cpp
@@ -928,6 +928,7 @@
                std::string clientKeyPath;
                bool includeAdminProtocolHeader = false;
                bool verboseOutput = false;
+               std::string contentType = "application/json";
        };

        ResponseData postJson(const std::string& url, unsigned int port, const 
std::string& jsonData, X509_STORE* store, std::vector<X509*>& certPool,
@@ -1021,15 +1022,23 @@
        }

        struct curl_slist* headers = nullptr;
+       std::string contentTypeHeader = "Content-Type: " + config.contentType;
        if (config.useMutualTLS) {
-               headers = curl_slist_append(headers, "Content-Type: 
application/json;charset=UTF-8");
+               if (config.contentType == "application/json") {
+                       contentTypeHeader += ";charset=UTF-8";
+               }
+               headers = curl_slist_append(headers, contentTypeHeader.c_str());
                headers = curl_slist_append(headers, "Accept: 
application/json");
                if (config.includeAdminProtocolHeader) {
                        headers = curl_slist_append(headers, "X-Admin-Protocol: 
gsma/rsp/v2.5.0");
                }
        } else {
-               headers = curl_slist_append(headers, "Content-Type: 
application/json");
-               headers = curl_slist_append(headers, "Accept: 
application/json");
+               headers = curl_slist_append(headers, contentTypeHeader.c_str());
+               // For ASN.1, accept the same content type
+               std::string acceptHeader = "Accept: " + (config.contentType == 
"application/x-gsma-rsp-asn1" ? config.contentType : "application/json");
+               headers = curl_slist_append(headers, acceptHeader.c_str());
+               // Always add X-Admin-Protocol for ES9+ regardless of content 
type
+               headers = curl_slist_append(headers, "X-Admin-Protocol: 
gsma/rsp/v2.5.0");
        }

        SslCtxData ctxData = { .store = store, .certPool = &certPool, 
.verifyResult = false, .errorMessage = "" };
@@ -1040,6 +1049,7 @@
                curl_easy_setopt(curl, CURLOPT_PORT, port);
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData.c_str());
+               curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, jsonData.size()); 
// Important for binary data
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
                curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response.body);
                curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerCallback);
@@ -1049,6 +1059,10 @@
                curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
                curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);

+               // Set reasonable timeouts
+               curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
+               curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
+
                if (config.useMutualTLS) {
                        if (!config.clientCertPath.empty()) {
                                curl_easy_setopt(curl, CURLOPT_SSLCERT, 
config.clientCertPath.c_str());
@@ -1630,8 +1644,16 @@
                return sendHttpsPostUnified(endpoint, body, httpStatusCode, 
portOverride, m_useMutualTLS, m_clientCertPath, m_clientKeyPath);
        }

+       std::string sendHttpsPostWithContentType(const std::string& endpoint, 
const std::string& body, const std::string& contentType, int& httpStatusCode,
+                                                unsigned int portOverride) {
+               LOG_DEBUG("sendHttpsPostWithContentType: endpoint=" + endpoint 
+ ", contentType=" + contentType + ", bodySize=" + std::to_string(body.size()) +
+                         ", port=" + std::to_string(portOverride));
+               return sendHttpsPostUnified(endpoint, body, httpStatusCode, 
portOverride, false, "", "", contentType);
+       }
+
        std::string sendHttpsPostUnified(const std::string& endpoint, const 
std::string& body, int& httpStatusCode, unsigned int portOverride,
-                                        bool useMutualTLS = false, const 
std::string& clientCertPath = "", const std::string& clientKeyPath = "") {
+                                        bool useMutualTLS = false, const 
std::string& clientCertPath = "", const std::string& clientKeyPath = "",
+                                        const std::string& contentType = 
"application/json") {
                if (!m_httpClient) {
                        m_httpClient = std::make_unique<HttpClient>();
                }
@@ -1664,6 +1686,7 @@
                config.clientKeyPath = clientKeyPath;
                config.includeAdminProtocolHeader = useMutualTLS; // ES2+ 
requires this header
                config.verboseOutput = false;
+               config.contentType = contentType;

                LOG_DEBUG("Sending " + std::string(useMutualTLS ? "ES2+ request 
with mutual TLS" : "HTTPS request") + " to: " + endpoint +
                          " (port: " + std::to_string(portOverride) + ")");
diff --git a/smdpp/smdpp_Tests.ttcn b/smdpp/smdpp_Tests.ttcn
index bca2261..0d1eab9 100644
--- a/smdpp/smdpp_Tests.ttcn
+++ b/smdpp/smdpp_Tests.ttcn
@@ -233,6 +233,24 @@
     out integer statusCode
 ) return charstring;

+external function ext_RSPClient_sendHttpsPostWithContentType(
+    integer clientHandle,
+    charstring endpoint,
+    charstring body,
+    integer dport,
+    charstring contentType,
+    out integer statusCode
+) return charstring;
+
+external function ext_RSPClient_sendHttpsPostBinary(
+    integer clientHandle,
+    charstring endpoint,
+    octetstring body,
+    integer dport,
+    charstring contentType,
+    out integer statusCode
+) return octetstring;
+
 /* RSP Protocol Constants */
 const charstring c_oid_rspRole_dp_auth := "2.23.146.1.2.1.4";
 const charstring c_oid_rspRole_dp_pb := "2.23.146.1.2.1.5";
@@ -336,6 +354,11 @@
        var charstring g_last_es9p_request := "";
 };

+type enumerated ES9EncodingMode {
+       ES9_JSON,
+       ES9_ASN1
+}
+
 type record smdpp_ConnHdlrPars {
        charstring smdp_server_url,
        integer smdp_es9p_server_port,
@@ -352,7 +375,8 @@
        GetBppErrorInjection gbpp_err_injection optional,
        boolean cc_required optional,
        boolean use_ppk optional,
-       integer metadata_segments optional
+       integer metadata_segments optional,
+       ES9EncodingMode es9_encoding_mode optional
 };

 private function f_init_pars() runs on MTC_CT return smdpp_ConnHdlrPars {
@@ -372,7 +396,8 @@
                gbpp_err_injection := omit,
                cc_required := false,
                use_ppk := false,
-               metadata_segments := 1
+               metadata_segments := 1,
+               es9_encoding_mode := omit  /* Default to JSON mode */
        };
        return pars;
 }
@@ -920,16 +945,62 @@
 private function f_es9p_transceive_wrap(RemoteProfileProvisioningRequest 
request)
 runs on smdpp_ConnHdlr
 return DecodedRPPReponse_Wrap {
-       f_es9p_send_new(request);
+
+       var ES9EncodingMode encoding_mode := ES9_JSON;
+       if (ispresent(g_pars_smdpp.es9_encoding_mode)) {
+               encoding_mode := g_pars_smdpp.es9_encoding_mode;
+       }
+
+       if (encoding_mode == ES9_ASN1) {
+               return f_es9p_transceive_wrap_asn1(request);
+       } else {
+               f_es9p_send_new(request);
+
+               var integer http_status;
+               var charstring response_body := ext_RSPClient_sendHttpsPost(
+                       g_rsp_client_handle,
+                       g_last_es9p_endpoint,
+                       g_last_es9p_request,
+                       g_pars_smdpp.smdp_es9p_server_port,
+                       http_status
+               );
+
+               if (http_status != 200) {
+                       setverdict(fail, "HTTP error response: " & 
int2str(http_status));
+                       var DecodedRPPReponse_Wrap empty_response;
+                       return empty_response;
+               }
+
+               // Decode to wrapper type that handles both success and error
+               var DecodedRPPReponse_Wrap response := {omit, omit};
+               dec_RemoteProfileProvisioningResponse_from_JSON(response_body, 
response);
+
+               return response;
+       }
+}
+
+/* Pure ASN.1 mode transceive function */
+private function f_es9p_transceive_wrap_asn1(RemoteProfileProvisioningRequest 
request)
+runs on smdpp_ConnHdlr
+return DecodedRPPReponse_Wrap {
+       /* Encode the request as pure ASN.1 (already includes A2 tag) */
+       var octetstring asn1_request := 
enc_RemoteProfileProvisioningRequest(request);
+
+       ext_logInfo("ASN.1 request size: " & int2str(lengthof(asn1_request)) & 
" bytes");
+       ext_logInfo("ASN.1 request first 50 bytes: " & 
oct2str(substr(asn1_request, 0, 50)));
+       ext_logInfo("ASN.1 request full: " & oct2str(asn1_request));

        var integer http_status;
-       var charstring response_body := ext_RSPClient_sendHttpsPost(
+       ext_logInfo("Sending ASN.1 request to /gsma/rsp2/asn1 on port " & 
int2str(g_pars_smdpp.smdp_es9p_server_port));
+       var octetstring response_body := ext_RSPClient_sendHttpsPostBinary(
                g_rsp_client_handle,
-               g_last_es9p_endpoint,
-               g_last_es9p_request,
-        g_pars_smdpp.smdp_es9p_server_port,
+               "/gsma/rsp2/asn1",
+               asn1_request,
+               g_pars_smdpp.smdp_es9p_server_port,
+               "application/x-gsma-rsp-asn1",
                http_status
        );
+       ext_logInfo("Received HTTP status: " & int2str(http_status));

        if (http_status != 200) {
                setverdict(fail, "HTTP error response: " & 
int2str(http_status));
@@ -937,11 +1008,129 @@
                return empty_response;
        }

-       // Decode to wrapper type that handles both success and error
-       var DecodedRPPReponse_Wrap response := {omit, omit};
-       dec_RemoteProfileProvisioningResponse_from_JSON(response_body, 
response);
+       /* Decode pure ASN.1 response (already includes A3 tag) */
+       var RemoteProfileProvisioningResponse asn1_response := 
dec_RemoteProfileProvisioningResponse(response_body);

-       return response;
+       /* Convert to DecodedRPPReponse_Wrap format for compatibility */
+       var DecodedRPPReponse_Wrap wrap := {omit, omit};
+
+       /* ASN.1 errors are encoded as specific CHOICE values */
+       if (f_is_asn1_error_response(asn1_response)) {
+               ext_logInfo("ASN.1 response detected as error, converting...");
+               wrap.err := f_convert_asn1_error_to_json(asn1_response);
+               ext_logInfo("Converted error: subject=" & wrap.err.subjectCode 
& ", reason=" & wrap.err.reasonCode);
+       } else {
+               /* Success response */
+               wrap.asn1_pdu := asn1_response;
+       }
+
+       return wrap;
+}
+
+/* Helper to check if ASN.1 response is an error */
+private function f_is_asn1_error_response(RemoteProfileProvisioningResponse 
resp) return boolean {
+       if (ischosen(resp.authenticateClientResponseEs9)) {
+               ext_logInfo("Response has authenticateClientResponseEs9");
+               /* AuthenticateClientResponseEs9 is a CHOICE between 
authenticateClientOk and authenticateClientError */
+               if 
(ischosen(resp.authenticateClientResponseEs9.authenticateClientError)) {
+                       return true;
+               }
+       } else if (ischosen(resp.getBoundProfilePackageResponse)) {
+               /* GetBoundProfilePackageResponse is a CHOICE between 
getBoundProfilePackageOk and getBoundProfilePackageError */
+               if 
(ischosen(resp.getBoundProfilePackageResponse.getBoundProfilePackageError)) {
+                       return true;
+               }
+       } else if (ischosen(resp.initiateAuthenticationResponse)) {
+               /* InitiateAuthenticationResponse is a CHOICE between 
initiateAuthenticationOk and initiateAuthenticationError */
+               if 
(ischosen(resp.initiateAuthenticationResponse.initiateAuthenticationError)) {
+                       return true;
+               }
+       } else if (ischosen(resp.cancelSessionResponseEs9)) {
+               /* CancelSessionResponseEs9 is a CHOICE between cancelSessionOk 
and cancelSessionError */
+               if (ischosen(resp.cancelSessionResponseEs9.cancelSessionError)) 
{
+                       return true;
+               }
+       }
+       return false;
+}
+
+/* Helper to convert ASN.1 error to JSON error format */
+private function 
f_convert_asn1_error_to_json(RemoteProfileProvisioningResponse resp)
+return JSON_ESx_FunctionExecutionStatusCodeData {
+       var JSON_ESx_FunctionExecutionStatusCodeData err := {
+               subjectCode := "",
+               reasonCode := "",
+               subjectIdentifier := omit,
+               message_ := ""
+       };
+
+       if (ischosen(resp.authenticateClientResponseEs9)) {
+               if 
(ischosen(resp.authenticateClientResponseEs9.authenticateClientError)) {
+                       var integer errorCode := 
resp.authenticateClientResponseEs9.authenticateClientError;
+                       /* Map AuthenticateClient error codes per SGP.22 */
+                       if (errorCode == 1) {  /* eumCertificateInvalid */
+                               err.subjectCode := "8.1.1";
+                               err.reasonCode := "3.1";
+                               err.message_ := "EUM Certificate Invalid";
+                       } else if (errorCode == 2) {  /* eumCertificateExpired 
*/
+                               err.subjectCode := "8.1.1";
+                               err.reasonCode := "3.2";
+                               err.message_ := "EUM Certificate Expired";
+                       } else if (errorCode == 3) {  /* 
euiccCertificateInvalid */
+                               err.subjectCode := "8.1.2";
+                               err.reasonCode := "3.1";
+                               err.message_ := "eUICC Certificate Invalid";
+                       } else if (errorCode == 4) {  /* 
euiccCertificateExpired */
+                               err.subjectCode := "8.1.2";
+                               err.reasonCode := "3.2";
+                               err.message_ := "eUICC Certificate Expired";
+                       } else if (errorCode == 5) {  /* euiccSignatureInvalid 
*/
+                               err.subjectCode := "8.1.3";
+                               err.reasonCode := "3.1";
+                               err.message_ := "eUICC Signature Invalid";
+                       } else if (errorCode == 8) {  /* matchingIdRefused */
+                               err.subjectCode := "8.2.6";
+                               err.reasonCode := "3.1";
+                               err.message_ := "Matching ID Refused";
+                       } else if (errorCode == 127) {  /* undefinedError */
+                               err.subjectCode := "8.8.1";
+                               err.reasonCode := "5";
+                               err.message_ := "Undefined Error";
+                       }
+               }
+       } else if (ischosen(resp.getBoundProfilePackageResponse)) {
+               if 
(ischosen(resp.getBoundProfilePackageResponse.getBoundProfilePackageError)) {
+                       var integer errorCode := 
resp.getBoundProfilePackageResponse.getBoundProfilePackageError;
+                       /* Map GetBoundProfilePackage error codes */
+                       if (errorCode == 1) {  /* euiccSignatureInvalid */
+                               err.subjectCode := "8.1.3";
+                               err.reasonCode := "3.1";
+                               err.message_ := "eUICC Signature Invalid";
+                       } else if (errorCode == 2) {  /* 
confirmationCodeMissing */
+                               err.subjectCode := "8.2.7";
+                               err.reasonCode := "3.7";
+                               err.message_ := "Confirmation Code Missing";
+                       } else if (errorCode == 3) {  /* 
confirmationCodeRefused */
+                               err.subjectCode := "8.2.7";
+                               err.reasonCode := "3.8";
+                               err.message_ := "Confirmation Code Refused";
+                       } else if (errorCode == 127) {  /* undefinedError */
+                               err.subjectCode := "8.8.1";
+                               err.reasonCode := "5";
+                               err.message_ := "Undefined Error";
+                       }
+               }
+       }
+
+       /* Fallback for undefined or empty errors, tbd */
+       if (err.subjectCode == "" or err.reasonCode == "") {
+               err.subjectCode := "8.8.1";
+               err.reasonCode := "5";
+               err.message_ := "Undefined Error (Server Internal Error)";
+               ext_logInfo("Using fallback error codes for undefined error");
+       }
+
+       return err;
 }

 private function f_es9p_transceive_success(RemoteProfileProvisioningRequest 
request)
@@ -3055,6 +3244,12 @@
        ext_logInfo("=== Step 2: AuthenticateClient ===");
        var RemoteProfileProvisioningRequest authClientReq := 
f_create_authenticate_client_request();
        var RemoteProfileProvisioningResponse authClientResp := 
f_es9p_transceive_success(authClientReq);
+
+       if (not 
ischosen(authClientResp.authenticateClientResponseEs9.authenticateClientOk)) {
+               setverdict(fail, "AuthenticateClient did not return success 
response");
+               f_rsp_client_cleanup();
+               return;
+       }
        var AuthenticateClientOk authClientOk := 
authClientResp.authenticateClientResponseEs9.authenticateClientOk;

        ext_logInfo("=== Step 3: CancelSession - " & reason_name & " ===");
@@ -5638,6 +5833,81 @@
     setverdict(pass);
 }

+/* ========================================================================
+ * ASN.1 Mode Test Variants
+ * These tests run the same logic as the NIST tests but use pure ASN.1
+ * encoding instead of JSON for the ES9+ interface
+ * ======================================================================== */
+
+testcase TC_SM_DP_ES9_InitiateAuthenticationASN1_01_Nominal() runs on MTC_CT {
+    var smdpp_ConnHdlrPars pars := f_init_pars();
+    pars.es9_encoding_mode := ES9_ASN1;
+    var smdpp_ConnHdlr vc_conn;
+    f_init(testcasename());
+    vc_conn := f_start_handler(refers(f_TC_InitiateAuth_01_Nominal), pars);
+    vc_conn.done;
+    setverdict(pass);
+}
+
+testcase TC_SM_DP_ES9_AuthenticateClientASN1_01_Nominal() runs on MTC_CT {
+    var smdpp_ConnHdlrPars pars := f_init_pars();
+    pars.es9_encoding_mode := ES9_ASN1;
+    var smdpp_ConnHdlr vc_conn;
+    f_init(testcasename());
+    vc_conn := f_start_handler(refers(f_TC_AuthenticateClient_01_Nominal), 
pars);
+    vc_conn.done;
+    setverdict(pass);
+}
+
+testcase TC_SM_DP_ES9_GetBoundProfilePackageASN1_01_Nominal() runs on MTC_CT {
+    var smdpp_ConnHdlrPars pars := f_init_pars();
+    pars.es9_encoding_mode := ES9_ASN1;
+    var smdpp_ConnHdlr vc_conn;
+    f_init(testcasename());
+    vc_conn := f_start_handler(refers(f_TC_GetBoundProfilePackage_01_Nominal), 
pars);
+    vc_conn.done;
+    setverdict(pass);
+}
+
+testcase TC_SM_DP_ES9_CancelSession_After_AuthenticateClientASN1_01_Nominal() 
runs on MTC_CT {
+    var smdpp_ConnHdlrPars pars := f_init_pars();
+    pars.es9_encoding_mode := ES9_ASN1;
+    var smdpp_ConnHdlr vc_conn;
+    f_init(testcasename());
+    vc_conn := 
f_start_handler(refers(f_TC_CancelSession_After_AuthenticateClient_01_End_User_Rejection),
 pars);
+    vc_conn.done;
+    setverdict(pass);
+}
+
+testcase TC_SM_DP_ES9_HandleNotificationASN1_01_Nominal() runs on MTC_CT {
+    var smdpp_ConnHdlrPars pars := f_init_pars();
+    pars.es9_encoding_mode := ES9_ASN1;
+    var smdpp_ConnHdlr vc_conn;
+    f_init(testcasename());
+    vc_conn := f_start_handler(refers(f_TC_HandleNotification_01_Nominal), 
pars);
+    vc_conn.done;
+    setverdict(pass);
+}
+
+/* quick comparison */
+testcase TC_ES9_Mode_Comparison() runs on MTC_CT {
+    var smdpp_ConnHdlrPars pars_json := f_init_pars();
+    pars_json.es9_encoding_mode := ES9_JSON;
+    var smdpp_ConnHdlr vc_json;
+    f_init(testcasename() & "_JSON");
+    vc_json := f_start_handler(refers(f_TC_InitiateAuth_01_Nominal), 
pars_json);
+    vc_json.done;
+
+    var smdpp_ConnHdlrPars pars_asn1 := f_init_pars();
+    pars_asn1.es9_encoding_mode := ES9_ASN1;
+    var smdpp_ConnHdlr vc_asn1;
+    f_init(testcasename() & "_ASN1");
+    vc_asn1 := f_start_handler(refers(f_TC_InitiateAuth_01_Nominal), 
pars_asn1);
+    vc_asn1.done;
+
+    setverdict(pass);
+}
+
 control {
        execute(TC_rsp_complete_flow());

diff --git a/smdpp/smdpp_Tests_Functions.cc b/smdpp/smdpp_Tests_Functions.cc
index 7bd08e3..fb9b504 100644
--- a/smdpp/smdpp_Tests_Functions.cc
+++ b/smdpp/smdpp_Tests_Functions.cc
@@ -640,4 +640,73 @@
        });
 }

+CHARSTRING ext__RSPClient__sendHttpsPostWithContentType(const INTEGER& 
clientHandle, const CHARSTRING& endpoint,
+                                                const CHARSTRING& body, const 
INTEGER& port, const CHARSTRING& contentType, INTEGER& statusCode) {
+       statusCode = INTEGER(0);
+
+       return with_client(clientHandle, 
"ext__RSPClient__sendHttpsPostWithContentType", CHARSTRING(""), [&](RSPClient* 
client) {
+               int httpStatus = 0;
+               int dstport = static_cast<int>(port);
+
+               std::string response = client->sendHttpsPostWithContentType(
+                       charstring_to_string(endpoint),
+                       charstring_to_string(body),
+                       charstring_to_string(contentType),
+                       httpStatus,
+                       dstport
+               );
+
+               statusCode = INTEGER(httpStatus);
+               return string_to_charstring(response);
+       });
+}
+
+OCTETSTRING ext__RSPClient__sendHttpsPostBinary(const INTEGER& clientHandle, 
const CHARSTRING& endpoint,
+                                                const OCTETSTRING& body, const 
INTEGER& port, const CHARSTRING& contentType, INTEGER& statusCode) {
+       statusCode = INTEGER(0);
+
+       std::string endpointStr = charstring_to_string(endpoint);
+       std::string contentTypeStr = charstring_to_string(contentType);
+       int dstport = static_cast<int>(port);
+
+       LOG_INFO("ext__RSPClient__sendHttpsPostBinary: endpoint=" + endpointStr 
+
+                ", port=" + std::to_string(dstport) +
+                ", contentType=" + contentTypeStr +
+                ", bodySize=" + std::to_string(body.lengthof()));
+
+       return with_client(clientHandle, "ext__RSPClient__sendHttpsPostBinary", 
OCTETSTRING(), [&](RSPClient* client) {
+               int httpStatus = 0;
+
+               std::string binaryBody;
+               binaryBody.reserve(body.lengthof());
+               for (int i = 0; i < body.lengthof(); i++) {
+                       
binaryBody.push_back(static_cast<char>(body[i].get_octet()));
+               }
+
+               LOG_INFO("Calling sendHttpsPostWithContentType...");
+
+               std::string response = client->sendHttpsPostWithContentType(
+                       endpointStr,
+                       binaryBody,
+                       contentTypeStr,
+                       httpStatus,
+                       dstport
+               );
+
+               LOG_INFO("sendHttpsPostWithContentType returned, status=" + 
std::to_string(httpStatus) +
+                        ", responseSize=" + std::to_string(response.size()));
+
+               if (httpStatus == 0 || response.empty()) {
+                       LOG_ERROR("HTTP request failed with status " + 
std::to_string(httpStatus));
+                       statusCode = INTEGER(0);
+                       return OCTETSTRING();  // Return empty octetstring on 
failure
+               }
+
+               OCTETSTRING result(response.size(), (const unsigned 
char*)response.data());
+
+               statusCode = INTEGER(httpStatus);
+               return result;
+       });
+}
+
 } // namespace smdpp__Tests
\ No newline at end of file

--
To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/41121?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: I1d824931bd6513d2320ba30df0f8193cd8352863
Gerrit-Change-Number: 41121
Gerrit-PatchSet: 1
Gerrit-Owner: Hoernchen <ew...@sysmocom.de>

Reply via email to