• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-mcu / 2096440417

13 Oct 2025 12:38PM UTC coverage: 60.837% (-0.4%) from 61.268%
2096440417

push

gitlab-ci

danielskinstad
feat: send tier in authentication request

Ticket: MEN-8559
Changelog: Add device tier support to authentication requests. The client now
sends a 'tier' parameter in authentication requests, supporting "standard"
(default) and "micro" tiers. The tier is configurable via Kconfig or
through the client config struct before initialization, which will take
precedence over the Kconfig option.

Signed-off-by: Daniel Skinstad Drabitzius <daniel.drabitzius@northern.tech>

18 of 19 new or added lines in 2 files covered. (94.74%)

292 existing lines in 5 files now uncovered.

2456 of 4037 relevant lines covered (60.84%)

68.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.22
/src/platform/net/zephyr/http.c
1
/**
2
 * @file      http.c
3
 * @brief     Mender HTTP interface for Zephyr platform
4
 *
5
 * Copyright joelguittet and mender-mcu-client contributors
6
 * Copyright Northern.tech AS
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20

21
#include <version.h>
22
#include <zephyr/net/http/client.h>
23
#include <zephyr/kernel.h>
24
#include "api.h"
25
#include "http.h"
26
#include "log.h"
27

28
#include "net.h"
29

30
/* Zephyr versions 4.2.0 and above return int, versions below 4.2.0 return void */
31
#if (ZEPHYR_VERSION_CODE < ZEPHYR_VERSION(4, 2, 0))
32
#define HTTP_CALLBACK_RETURN_VALUE
33
#define HTTP_CALLBACK_RETURN_TYPE void
34
#else
35
#define HTTP_CALLBACK_RETURN_VALUE 0
36
#define HTTP_CALLBACK_RETURN_TYPE  int
37
#endif
38

39
/**
40
 * @brief HTTP User-Agent
41
 */
42
#define MENDER_HEADER_HTTP_USER_AGENT "User-Agent: Mender/" MENDER_CLIENT_VERSION " MCU Zephyr/" KERNEL_VERSION_STRING "\r\n"
43

44
/**
45
 * @brief Request timeout (milliseconds)
46
 */
47
#define MENDER_HTTP_REQUEST_TIMEOUT (60 * 1000)
48

49
const size_t mender_http_recv_buf_length = 512;
50

51
/**
52
 * @brief Request context
53
 */
54
typedef struct {
55
    mender_err_t (*callback)(mender_http_client_event_t, void *, size_t, void *); /**< Callback to be invoked when data are received */
56
    void        *params;                                                          /**< Callback parameters */
57
    mender_err_t ret;                                                             /**< Last callback return value */
58
} mender_http_request_context;
59

60
/**
61
 * @brief Mender HTTP configuration
62
 */
63
static mender_http_config_t http_config;
64

65
/**
66
 * @brief HTTP response callback, invoked to handle data received
67
 * @param response HTTP response structure
68
 * @param final_call Indicate final call
69
 * @param user_data User data, used to retrieve request context data
70
 * @return HTTP_CALLBACK_RETURN_VALUE, 0 if Zephyr >= 4.2.0, void otherwise
71
 */
72
static HTTP_CALLBACK_RETURN_TYPE http_response_cb(struct http_response *response, enum http_final_call final_call, void *user_data);
73

74
/**
75
 * @brief HTTP artifact response callback, invoked to handle data received
76
 * @param response HTTP response structure
77
 * @param final_call Indicate final call
78
 * @param user_data User data, used to retrieve request context data
79
 * @return HTTP_CALLBACK_RETURN_VALUE, 0 if Zephyr >= 4.2.0, void otherwise
80
 */
81
static HTTP_CALLBACK_RETURN_TYPE artifact_response_cb(struct http_response *response, enum http_final_call final_call, void *user_data);
82

83
/**
84
 * @brief Convert mender HTTP method to Zephyr HTTP client method
85
 * @param method Mender HTTP method
86
 * @return Zephyr HTTP client method if the function succeeds, -1 otherwise
87
 */
88
static enum http_method http_method_to_zephyr_http_client_method(mender_http_method_t method);
89

90
mender_err_t
91
mender_http_init(mender_http_config_t *config) {
29✔
92

93
    assert(NULL != config);
29✔
94
    assert(NULL != config->host);
29✔
95

96
    /* Save configuration */
97
    memcpy(&http_config, config, sizeof(mender_http_config_t));
29✔
98

99
    return MENDER_OK;
29✔
100
}
101

102
/* Request built will look like this:
103
    GET https://hosted.mender.io/api/devices/v1/deployments/artifacts/{id} HTTP/1.1
104
    Host: hosted.mender.io
105
    User-Agent: Mender/2.0.0 MCU Zephyr/2.7.0
106
    Authorization: Bearer <jwt token>
107
    X-MEN-Signature: <string>
108
    Content-Type: application/json
109
*/
110
mender_err_t
111
mender_http_perform(char                *jwt,
150✔
112
                    char                *path,
113
                    mender_http_method_t method,
114
                    char                *payload,
115
                    char                *signature,
116
                    mender_err_t (*callback)(mender_http_client_event_t, void *, size_t, void *),
117
                    void *params,
118
                    int  *status) {
119

120
    assert(NULL != path);
150✔
121
    assert(NULL != callback);
150✔
122
    assert(NULL != status);
150✔
123
    mender_err_t                ret                = MENDER_FAIL;
150✔
124
    struct http_request         request            = { 0 };
150✔
125
    mender_http_request_context request_context    = { .callback = callback, .params = params, .ret = MENDER_OK };
150✔
126
    const char                 *header_fields[6]   = { NULL }; /* The list is NULL terminated; make sure the size reflects it */
150✔
127
    size_t                      header_fields_size = sizeof(header_fields) / sizeof(header_fields[0]);
150✔
128
    char                       *host               = NULL;
150✔
129
    char                       *port               = NULL;
150✔
130
    char                       *url                = NULL;
150✔
131
    int                         sock               = -1;
150✔
132
    int                         http_req_ret;
133

134
    /* Headers to be added to the request */
135
    char *host_header      = NULL;
150✔
136
    char *auth_header      = NULL;
150✔
137
    char *signature_header = NULL;
150✔
138

139
    /* Retrieve host, port and url */
140
    if (MENDER_OK != mender_net_get_host_port_url(path, http_config.host, &host, &port, &url)) {
150✔
141
        mender_log_error("Unable to retrieve host/port/url");
×
142
        goto END;
×
143
    }
144

145
    /* Configuration of the client */
146
    request.method      = http_method_to_zephyr_http_client_method(method);
150✔
147
    request.url         = url;
150✔
148
    request.host        = host;
150✔
149
    request.protocol    = "HTTP/1.1";
150✔
150
    request.payload     = payload;
150✔
151
    request.payload_len = (NULL != payload) ? strlen(payload) : 0;
150✔
152
    request.response    = http_response_cb;
150✔
153
    if (NULL == (request.recv_buf = (uint8_t *)mender_malloc(mender_http_recv_buf_length))) {
150✔
154
        mender_log_error("Unable to allocate memory");
×
155
        goto END;
×
156
    }
157
    request.recv_buf_len = mender_http_recv_buf_length;
150✔
158

159
    /* Add headers */
160

161
    if (MENDER_FAIL == header_add(header_fields, header_fields_size, MENDER_HEADER_HTTP_USER_AGENT)) {
150✔
162
        mender_log_error("Unable to add 'User-Agent' header");
×
163
        goto END;
×
164
    }
165

166
    if (NULL != jwt) {
150✔
167
        auth_header = header_alloc_and_add(header_fields, header_fields_size, "Authorization: Bearer %s\r\n", jwt);
120✔
168
        if (NULL == auth_header) {
120✔
169
            mender_log_error("Unable to add 'Authorization' header");
×
170
            goto END;
×
171
        }
172
    }
173

174
    if (NULL != signature) {
150✔
175
        signature_header = header_alloc_and_add(header_fields, header_fields_size, "X-MEN-Signature: %s\r\n", signature);
30✔
176
        if (NULL == signature_header) {
30✔
177
            mender_log_error("Unable to add 'X-MEN-Signature' header");
×
178
            goto END;
×
179
        }
180
    }
181

182
    if (NULL != payload) {
150✔
183
        if (MENDER_FAIL == header_add(header_fields, header_fields_size, "Content-Type: application/json\r\n")) {
150✔
184
            mender_log_error("Unable to add 'Content-Type' header");
×
185
            goto END;
×
186
        }
187
    }
188

189
    request.header_fields = header_fields;
150✔
190

191
    /* Connect to the server */
192
    sock = mender_net_connect(host, port);
150✔
193
    if (sock < 0) {
135✔
UNCOV
194
        mender_log_error("Unable to open HTTP client connection");
×
UNCOV
195
        goto END;
×
196
    }
197
    if (MENDER_OK != (ret = callback(MENDER_HTTP_EVENT_CONNECTED, NULL, 0, params))) {
135✔
198
        mender_log_error("An error occurred while calling 'MENDER_HTTP_EVENT_CONNECTED' callback");
×
199
        goto END;
×
200
    }
201

202
    /* Perform HTTP request */
203
    if ((http_req_ret = http_client_req(sock, &request, MENDER_HTTP_REQUEST_TIMEOUT, (void *)&request_context)) < 0) {
135✔
204
        mender_log_error("HTTP request failed: %s", strerror(-http_req_ret));
×
205
        goto END;
×
206
    }
207

208
    /* Check if an error occured during the treatment of data */
209
    if (MENDER_OK != (ret = request_context.ret)) {
135✔
210
        goto END;
×
211
    }
212

213
    /* Read HTTP status code */
214
    if (0 == request.internal.response.http_status_code) {
135✔
215
        mender_log_error("An error occurred, connection has been closed");
×
216
        callback(MENDER_HTTP_EVENT_ERROR, NULL, 0, params);
×
217
        goto END;
×
218
    } else {
219
        *status = request.internal.response.http_status_code;
135✔
220
    }
221
    if (MENDER_OK != (ret = callback(MENDER_HTTP_EVENT_DISCONNECTED, NULL, 0, params))) {
135✔
222
        mender_log_error("An error occurred while calling 'MENDER_HTTP_EVENT_DISCONNECTED' callback");
×
223
        goto END;
×
224
    }
225

226
    ret = MENDER_OK;
135✔
227

228
END:
135✔
229

230
    /* Close connection */
231
    if (sock >= 0) {
135✔
232
        mender_net_disconnect(sock);
135✔
233
    }
234

235
    /* Release memory */
236
    mender_free(host);
135✔
237
    mender_free(port);
135✔
238
    mender_free(url);
135✔
239
    mender_free(host_header);
135✔
240
    mender_free(auth_header);
135✔
241
    mender_free(signature_header);
135✔
242

243
    mender_free(request.recv_buf);
135✔
244

245
    /* Return MENDER_RETRY_ERROR if ret is MENDER_FAIL, otherwise return ret */
246
    return (MENDER_FAIL != ret) ? ret : MENDER_RETRY_ERROR;
135✔
247
}
248

249
mender_err_t
250
mender_http_artifact_download(const char *uri, mender_artifact_download_data_t *dl_data, int *status) {
12✔
251
    assert(NULL != dl_data);
12✔
252
    assert(NULL != status);
12✔
253

254
    mender_err_t        ret                = MENDER_FAIL;
12✔
255
    struct http_request request            = { 0 };
12✔
256
    mender_err_t        request_ret        = MENDER_OK;
12✔
257
    const char         *header_fields[3]   = { NULL }; /* The list is NULL terminated; make sure the size reflects it */
12✔
258
    size_t              header_fields_size = sizeof(header_fields) / sizeof(header_fields[0]);
12✔
259
    char               *host               = NULL;
12✔
260
    char               *port               = NULL;
12✔
261
    char               *url                = NULL;
12✔
262
    int                 sock               = -1;
12✔
263
    int                 http_req_ret;
264

265
    /* Headers to be added to the request */
266
    char *host_header = NULL;
12✔
267

268
    /* Retrieve host, port and url */
269
    if (MENDER_OK != mender_net_get_host_port_url(uri, http_config.host, &host, &port, &url)) {
12✔
270
        mender_log_error("Unable to retrieve host/port/url");
×
271
        goto END;
×
272
    }
273

274
    /* Configuration of the client */
275
    request.method   = http_method_to_zephyr_http_client_method(MENDER_HTTP_GET);
12✔
276
    request.url      = url;
12✔
277
    request.host     = host;
12✔
278
    request.protocol = "HTTP/1.1";
12✔
279
    request.response = artifact_response_cb;
12✔
280
    if (NULL == (request.recv_buf = (uint8_t *)mender_malloc(mender_http_recv_buf_length))) {
12✔
281
        mender_log_error("Unable to allocate memory");
×
282
        goto END;
×
283
    }
284
    request.recv_buf_len = mender_http_recv_buf_length;
12✔
285

286
    /* Add headers */
287
    host_header = header_alloc_and_add(header_fields, header_fields_size, "Host: %s\r\n", host);
12✔
288
    if (NULL == host_header) {
12✔
289
        mender_log_error("Unable to add 'Host' header");
×
290
        goto END;
×
291
    }
292
    if (MENDER_FAIL == header_add(header_fields, header_fields_size, MENDER_HEADER_HTTP_USER_AGENT)) {
12✔
293
        mender_log_error("Unable to add 'User-Agent' header");
×
294
        goto END;
×
295
    }
296
    request.header_fields = header_fields;
12✔
297

298
    /* Connect to the server */
299
    sock = mender_net_connect(host, port);
12✔
300
    if (sock < 0) {
12✔
301
        mender_log_error("Unable to open HTTP client connection");
×
302
        goto END;
×
303
    }
304
    if (MENDER_OK != (ret = dl_data->artifact_download_callback(MENDER_HTTP_EVENT_CONNECTED, NULL, 0, dl_data))) {
12✔
305
        mender_log_error("An error occurred while calling 'MENDER_HTTP_EVENT_CONNECTED' artifact callback");
×
306
        goto END;
×
307
    }
308

309
    /* Perform HTTP request */
310
    if ((http_req_ret = http_client_req(sock, &request, MENDER_HTTP_REQUEST_TIMEOUT, dl_data)) < 0) {
12✔
311
        mender_log_error("HTTP request failed: %s", strerror(-http_req_ret));
×
312
        goto END;
×
313
    }
314

315
    /* Artifact download failed*/
316
    if (MENDER_OK != dl_data->ret) {
12✔
317
        goto END;
1✔
318
    }
319

320
    /* Check if an error occured during the treatment of data */
321
    if (MENDER_OK != (ret = request_ret)) {
11✔
322
        goto END;
×
323
    }
324

325
    /* Read HTTP status code */
326
    if (0 == request.internal.response.http_status_code) {
11✔
327
        mender_log_error("An error occurred, connection has been closed");
×
328
        dl_data->artifact_download_callback(MENDER_HTTP_EVENT_ERROR, NULL, 0, dl_data);
×
329
        goto END;
×
330
    } else {
331
        *status = request.internal.response.http_status_code;
11✔
332
    }
333
    if (MENDER_OK != (ret = dl_data->artifact_download_callback(MENDER_HTTP_EVENT_DISCONNECTED, NULL, 0, dl_data))) {
11✔
334
        mender_log_error("An error occurred while calling 'MENDER_HTTP_EVENT_DISCONNECTED' artifact callback");
×
335
        goto END;
×
336
    }
337

338
    ret = MENDER_OK;
11✔
339

340
END:
12✔
341

342
    /* Close connection */
343
    if (sock >= 0) {
12✔
344
        mender_net_disconnect(sock);
12✔
345
    }
346

347
    /* Release memory */
348
    mender_free(host);
12✔
349
    mender_free(port);
12✔
350
    mender_free(url);
12✔
351
    mender_free(host_header);
12✔
352

353
    mender_free(request.recv_buf);
12✔
354

355
    /* Return MENDER_RETRY_ERROR if ret is MENDER_FAIL, otherwise return ret */
356
    return (MENDER_FAIL != ret) ? ret : MENDER_RETRY_ERROR;
12✔
357
}
358

359
mender_err_t
360
mender_http_exit(void) {
×
361

362
    /* Nothing to do */
363
    return MENDER_OK;
×
364
}
365

366
static HTTP_CALLBACK_RETURN_TYPE
367
http_response_cb(struct http_response *response, MENDER_ARG_UNUSED enum http_final_call final_call, void *user_data) {
218✔
368
    assert(NULL != response);
218✔
369
    assert(NULL != user_data);
218✔
370

371
    /* Retrieve request context */
372
    mender_http_request_context *request_context = (mender_http_request_context *)user_data;
218✔
373

374
    /* Check if data is available */
375
    if ((true == response->body_found) && (NULL != response->body_frag_start) && (0 != response->body_frag_len) && (MENDER_OK == request_context->ret)) {
218✔
376

377
        /* Transmit data received to the upper layer */
378
        if (MENDER_OK
125✔
379
            != (request_context->ret = request_context->callback(
125✔
380
                    MENDER_HTTP_EVENT_DATA_RECEIVED, (void *)response->body_frag_start, response->body_frag_len, request_context->params))) {
125✔
381
            mender_log_error("An error occurred, stop reading data");
×
382
        }
383
    }
384
    return HTTP_CALLBACK_RETURN_VALUE;
218✔
385
}
386

387
static HTTP_CALLBACK_RETURN_TYPE
388
artifact_response_cb(struct http_response *response, MENDER_ARG_UNUSED enum http_final_call final_call, void *user_data) {
258✔
389

390
    assert(NULL != response);
258✔
391
    assert(NULL != user_data);
258✔
392

393
    /* Retrieve request context */
394
    mender_artifact_download_data_t *dl_data = user_data;
258✔
395

396
    /* Check if data is available */
397
    if (response->body_found && (NULL != response->body_frag_start) && (0 != response->body_frag_len) && (MENDER_OK == (dl_data->ret))) {
258✔
398
        /* Transmit data received to the upper layer */
399
        dl_data->ret
400
            = dl_data->artifact_download_callback(MENDER_HTTP_EVENT_DATA_RECEIVED, (void *)response->body_frag_start, response->body_frag_len, dl_data);
248✔
401
        if (MENDER_OK != (dl_data->ret)) {
248✔
402
            mender_log_error("An error occurred, stop reading data");
1✔
403
        }
404
    }
405
    return HTTP_CALLBACK_RETURN_VALUE;
258✔
406
}
407

408
static enum http_method
409
http_method_to_zephyr_http_client_method(mender_http_method_t method) {
162✔
410

411
    /* Convert method */
412
    switch (method) {
162✔
413
        case MENDER_HTTP_GET:
12✔
414
            return HTTP_GET;
12✔
415
        case MENDER_HTTP_POST:
69✔
416
            return HTTP_POST;
69✔
417
        case MENDER_HTTP_PUT:
80✔
418
            return HTTP_PUT;
80✔
419
        case MENDER_HTTP_PATCH:
1✔
420
            return HTTP_PATCH;
1✔
421
        default:
×
422
            return -1;
×
423
    }
424
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc