Skip to content

Commit a01df86

Browse files
committed
app: http: Add hex_format option to AT#XHTTPCREQ
Add an optional hex_format parameter (5th positional argument) to AT#XHTTPCREQ. When set to 1, the response body delivered in AT#XHTTPCDATA URCs is encoded as an ASCII hex string (two chars per byte) instead of raw binary bytes. Jira: SM-337 Signed-off-by: Juha Ylinen <juha.ylinen@nordicsemi.no>
1 parent cc88c7d commit a01df86

2 files changed

Lines changed: 96 additions & 22 deletions

File tree

app/src/sm_at_httpc.c

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
LOG_MODULE_REGISTER(sm_httpc, CONFIG_SM_LOG_LEVEL);
2222

23-
#define HTTP_RECV_BUF_SIZE 2048
23+
#define HTTP_RECV_BUF_SIZE 2049 /* 2048 bytes + null terminator for string ops */
2424
#define HTTP_URL_MAX_LEN 512
2525
#define HTTP_HOST_MAX_LEN 256
2626
#define HTTP_PATH_MAX_LEN 256
@@ -82,6 +82,7 @@ struct http_request {
8282
int64_t timeout_timestamp; /* Idle timeout deadline; reset on each send/receive */
8383
struct modem_pipe *pipe; /* AT pipe that created this request */
8484
bool manual_mode; /* Manual mode: body not auto-received, host pulls chunks */
85+
bool hex_rx; /* Deliver response body as ASCII hex string */
8586
int bytes_sent; /* Response-body bytes sent to the host */
8687
bool connection_close; /* Server sent "Connection: close" header */
8788
};
@@ -481,7 +482,22 @@ static void http_send_headers_complete(struct http_request *req)
481482
req->content_length);
482483
}
483484

484-
/* Send data URC followed by raw bytes */
485+
static void http_data_send_hex(struct modem_pipe *pipe, const uint8_t *buf, size_t len)
486+
{
487+
char hex_buf[257];
488+
size_t chunk = (sizeof(hex_buf) - 1) / 2;
489+
size_t done = 0;
490+
491+
while (done < len) {
492+
size_t n = MIN(chunk, len - done);
493+
size_t sz = bin2hex(buf + done, n, hex_buf, sizeof(hex_buf));
494+
495+
data_send(pipe, hex_buf, sz);
496+
done += n;
497+
}
498+
}
499+
500+
/* Send data URC followed by raw bytes or hex string */
485501
static void http_send_data(struct http_request *req, const uint8_t *data, int len)
486502
{
487503
if (len <= 0) {
@@ -490,7 +506,11 @@ static void http_send_data(struct http_request *req, const uint8_t *data, int le
490506

491507
urc_send_to(req->pipe, "\r\n#XHTTPCDATA: %d,%d,%d\r\n", req->fd, req->bytes_sent, len);
492508
req->bytes_sent += len;
493-
data_send(req->pipe, data, len);
509+
if (req->hex_rx) {
510+
http_data_send_hex(req->pipe, data, (size_t)len);
511+
} else {
512+
data_send(req->pipe, data, len);
513+
}
494514
}
495515

496516
/*
@@ -1049,7 +1069,10 @@ static int http_datamode_callback(uint8_t op, const uint8_t *data, int len, uint
10491069
return 0;
10501070
}
10511071

1052-
/* AT#XHTTPCREQ - Start an HTTP/HTTPS request (async) */
1072+
/**
1073+
* Start an asynchronous HTTP or HTTPS request on a connected AT socket.
1074+
* Returns OK immediately; headers arrive as #XHTTPCHEAD and body as #XHTTPCDATA/#XHTTPCSTAT.
1075+
*/
10531076
SM_AT_CMD_CUSTOM(xhttpcreq, "AT#XHTTPCREQ", handle_at_httpcreq);
10541077
STATIC int handle_at_httpcreq(enum at_parser_cmd_type cmd_type, struct at_parser *parser,
10551078
uint32_t param_count)
@@ -1124,7 +1147,6 @@ STATIC int handle_at_httpcreq(enum at_parser_cmd_type cmd_type, struct at_parser
11241147
return -EINVAL;
11251148
}
11261149

1127-
int body_len = 0;
11281150
int next_param_idx = 4; /* Next parameter index after method */
11291151

11301152
/* Parse optional <auto_reception> flag */
@@ -1141,22 +1163,34 @@ STATIC int handle_at_httpcreq(enum at_parser_cmd_type cmd_type, struct at_parser
11411163
/* If parse fails (string param), leave next_param_idx unchanged. */
11421164
}
11431165

1166+
/* Parse optional <format> flag */
1167+
int format = 0; /* default: binary */
1168+
1169+
if (param_count > next_param_idx) {
1170+
if (at_parser_num_get(parser, next_param_idx, &format) == 0) {
1171+
if (format != 0 && format != 1) {
1172+
http_close_request(req);
1173+
return -EINVAL;
1174+
}
1175+
next_param_idx++;
1176+
}
1177+
}
1178+
11441179
/* Parse optional body length (data mode for POST/PUT).
11451180
* The parameter slot is always consumed when an integer is present
11461181
* so that extra headers start at a consistent index regardless of method.
11471182
*/
1148-
if (param_count > next_param_idx) {
1149-
int tmp = 0;
1183+
int body_len = 0;
11501184

1151-
if (at_parser_num_get(parser, next_param_idx, &tmp) == 0) {
1185+
if (param_count > next_param_idx) {
1186+
if (at_parser_num_get(parser, next_param_idx, &body_len) == 0) {
11521187
if (method == HTTP_POST || method == HTTP_PUT) {
1153-
body_len = tmp;
11541188
if (body_len < 0) {
11551189
LOG_ERR("Invalid body_len: %d", body_len);
11561190
http_close_request(req);
11571191
return -EINVAL;
11581192
}
1159-
} else if (tmp != 0) {
1193+
} else if (body_len != 0) {
11601194
LOG_ERR("body_len must be 0 for method %d", method);
11611195
http_close_request(req);
11621196
return -EINVAL;
@@ -1227,6 +1261,8 @@ STATIC int handle_at_httpcreq(enum at_parser_cmd_type cmd_type, struct at_parser
12271261
LOG_INF("HTTP %d: Manual mode enabled", req->fd);
12281262
}
12291263

1264+
req->hex_rx = (bool)format;
1265+
12301266
/* Parse URL */
12311267
err = http_parse_url_components(url, url_len, req);
12321268
if (err) {
@@ -1276,7 +1312,7 @@ STATIC int handle_at_httpcreq(enum at_parser_cmd_type cmd_type, struct at_parser
12761312

12771313
case AT_PARSER_CMD_TYPE_TEST:
12781314
rsp_send("\r\n#XHTTPCREQ: <handle>,<url>,<method>"
1279-
"[,<auto_reception>[,<body_len>[,<header>]...]]\r\n");
1315+
"[,<auto_reception>[,<format>[,<body_len>[,<header>]...]]]\r\n");
12801316

12811317
err = 0;
12821318
break;
@@ -1330,7 +1366,11 @@ static int pull_data(int socket_fd, int pull_len)
13301366
req->timeout_timestamp = k_uptime_get() + HTTP_RESPONSE_TIMEOUT_MS;
13311367
rsp_send("\r\n#XHTTPCDATA: %d,%d,%d\r\n", req->fd, req->bytes_sent, send_len);
13321368
req->bytes_sent += send_len;
1333-
data_send(req->pipe, req->recv_buf, send_len);
1369+
if (req->hex_rx) {
1370+
http_data_send_hex(req->pipe, req->recv_buf, (size_t)send_len);
1371+
} else {
1372+
data_send(req->pipe, req->recv_buf, send_len);
1373+
}
13341374
req->recv_buf_len -= send_len;
13351375
if (req->recv_buf_len > 0) {
13361376
memmove(req->recv_buf, req->recv_buf + send_len, req->recv_buf_len);
@@ -1366,7 +1406,11 @@ static int pull_data(int socket_fd, int pull_len)
13661406
req->timeout_timestamp = k_uptime_get() + HTTP_RESPONSE_TIMEOUT_MS;
13671407
rsp_send("\r\n#XHTTPCDATA: %d,%d,%d\r\n", req->fd, req->bytes_sent, ret);
13681408
req->bytes_sent += ret;
1369-
data_send(req->pipe, req->recv_buf, ret);
1409+
if (req->hex_rx) {
1410+
http_data_send_hex(req->pipe, req->recv_buf, (size_t)ret);
1411+
} else {
1412+
data_send(req->pipe, req->recv_buf, ret);
1413+
}
13701414
last_sent = ret;
13711415

13721416
check_complete:

doc/app/at_httpc.rst

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Syntax
3535

3636
::
3737

38-
AT#XHTTPCREQ=<handle>,<url>,<method>[,<auto_reception>[,<body_len>[,<header 1>[,<header 2>[...]]]]]
38+
AT#XHTTPCREQ=<handle>,<url>,<method>[,<auto_reception>[,<format>[,<body_len>[,<header 1>[,<header 2>[...]]]]]]
3939

4040
* The ``<handle>`` parameter is an integer.
4141
It identifies the connected socket handle returned by ``AT#XSOCKET`` or ``AT#XSSOCKET``, and connected by ``AT#XCONNECT``.
@@ -61,6 +61,19 @@ Syntax
6161
Response body reception is paused after the headers are parsed and ``#XHTTPCHEAD`` is emitted.
6262
The host must then retrieve body data in chunks using ``AT#XHTTPCDATA`` commands.
6363

64+
* The ``<format>`` parameter is an optional integer.
65+
It controls the encoding used to deliver response body bytes to the host.
66+
It can accept the following values:
67+
68+
* ``0`` - Binary (default).
69+
Response body bytes are forwarded to the host as raw binary.
70+
* ``1`` - Hex string.
71+
Response body bytes are encoded as a lower-case ASCII hex string before delivery.
72+
Each raw byte becomes two hex characters.
73+
The ``<length>`` field in ``#XHTTPCDATA`` always reports the raw byte count.
74+
The actual data delivered is ``2×<length>`` ASCII hex characters.
75+
Applies to both automatic and manual reception modes.
76+
6477
* The ``<body_len>`` parameter is an optional integer.
6578
It is required as a placeholder when ``<header>`` parameters follow.
6679
It can accept the following values:
@@ -115,7 +128,9 @@ Unsolicited notification
115128

116129
#XHTTPCDATA: <handle>,<offset>,<length>
117130

118-
The notification line is terminated with ``\r\n`` and the raw body bytes follow immediately with no additional separator.
131+
The notification line is terminated with ``\r\n``.
132+
In binary mode (``<format>=0``, default), the raw body bytes follow immediately with no additional separator.
133+
In hex mode (``<format>=1``), ``2×<length>`` lower-case ASCII hex characters follow instead of raw bytes.
119134

120135
* The ``<handle>`` parameter is an integer.
121136
It identifies the socket.
@@ -192,11 +207,26 @@ HTTP GET (manual mode):
192207

193208
#XHTTPCSTAT: 0,200,261,0
194209

210+
HTTP GET with hex-encoded response body (``format=1``, automatic mode):
211+
212+
::
213+
214+
AT#XHTTPCREQ=0,<url>,0,1,1
215+
#XHTTPCREQ: 0
216+
OK
217+
218+
#XHTTPCHEAD: 0,200,12
219+
220+
#XHTTPCDATA: 0,0,12
221+
48656c6c6f20576f726c6421
222+
223+
#XHTTPCSTAT: 0,200,12,0
224+
195225
HTTP POST with JSON body and custom header:
196226

197227
::
198228

199-
AT#XHTTPCREQ=0,<url>,1,1,15,"Content-Type: application/json"
229+
AT#XHTTPCREQ=0,<url>,1,1,0,15,"Content-Type: application/json"
200230
#XHTTPCREQ: 0
201231
OK
202232
{"key":"value"}
@@ -209,13 +239,13 @@ HTTP POST with JSON body and custom header:
209239

210240
#XHTTPCSTAT: 0,200,432,0
211241

212-
HTTP GET with Range header (``body_len=0`` is required as a placeholder when headers follow a GET):
242+
HTTP GET data in binary format with Range header (``body_len=0`` is required as a placeholder when ``<header>`` parameters follow):
213243

214244
In this example the server returns ``connection_close=1``, so the host must close and reopen the socket before the next request.
215245

216246
::
217247

218-
AT#XHTTPCREQ=0,<url>,0,1,0,"Range: bytes=0-127"
248+
AT#XHTTPCREQ=0,<url>,0,1,0,0,"Range: bytes=0-127"
219249
#XHTTPCREQ: 0
220250
OK
221251

@@ -226,7 +256,7 @@ In this example the server returns ``connection_close=1``, so the host must clos
226256

227257
#XHTTPCSTAT: 0,206,128,0
228258

229-
AT#XHTTPCREQ=0,<url>,0,1,0,"Range: bytes=128-255"
259+
AT#XHTTPCREQ=0,<url>,0,1,0,0,"Range: bytes=128-255"
230260
#XHTTPCREQ: 0
231261
OK
232262

@@ -257,7 +287,7 @@ HTTP POST with chunked response (``content_length=-1``):
257287

258288
::
259289

260-
AT#XHTTPCREQ=0,<url>,1,1,1024
290+
AT#XHTTPCREQ=0,<url>,1,1,0,1024
261291
#XHTTPCREQ: 0
262292
OK
263293
<1024 bytes payload>
@@ -292,15 +322,15 @@ Response syntax
292322

293323
::
294324

295-
#XHTTPCREQ: <handle>,<url>,<method>[,<auto_reception>[,<body_len>[,<header>]...]]
325+
#XHTTPCREQ: <handle>,<url>,<method>[,<auto_reception>[,<format>[,<body_len>[,<header>]...]]]
296326

297327
Example
298328
~~~~~~~
299329

300330
::
301331

302332
AT#XHTTPCREQ=?
303-
#XHTTPCREQ: <handle>,<url>,<method>[,<auto_reception>[,<body_len>[,<header>]...]]
333+
#XHTTPCREQ: <handle>,<url>,<method>[,<auto_reception>[,<format>[,<body_len>[,<header>]...]]]
304334
OK
305335

306336
HTTP data pull #XHTTPCDATA

0 commit comments

Comments
 (0)