est_client_transport.c
Go to the documentation of this file.
1 /**
2  * @file est_client_transport.c
3  * @brief HTTP transport mechanism
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2024-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneEST Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.5.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL EST_TRACE_LEVEL
33 
34 //Dependencies
35 #include "est/est_client.h"
37 #include "debug.h"
38 
39 //Check crypto library configuration
40 #if (EST_CLIENT_SUPPORT == ENABLED)
41 
42 
43 /**
44  * @brief Send HTTP request
45  * @param[in] context Pointer to the EST client context
46  * @return Error code
47  **/
48 
50 {
51  error_t error;
52  size_t n;
53 
54  //Initialize status code
55  error = NO_ERROR;
56 
57  //Check HTTP request state
58  if(context->requestState == EST_REQ_STATE_SEND_HEADER)
59  {
60  //Send HTTP request header
61  error = httpClientWriteHeader(&context->httpClientContext);
62 
63  //Check status code
64  if(!error)
65  {
66  //Check whether the HTTP request contains a body
67  if(context->bufferLen > 0)
68  {
69  //Point to the first byte of the body
70  context->bufferPos = 0;
71 
72  //Send HTTP request body
73  context->requestState = EST_REQ_STATE_SEND_BODY;
74  }
75  else
76  {
77  //Receive HTTP response header
78  context->requestState = EST_REQ_STATE_RECEIVE_HEADER;
79  }
80  }
81  }
82  else if(context->requestState == EST_REQ_STATE_SEND_BODY)
83  {
84  //Send HTTP request body
85  if(context->bufferPos < context->bufferLen)
86  {
87  //Send more data
88  error = httpClientWriteBody(&context->httpClientContext,
89  context->buffer + context->bufferPos,
90  context->bufferLen - context->bufferPos, &n, 0);
91 
92  //Check status code
93  if(!error)
94  {
95  //Advance data pointer
96  context->bufferPos += n;
97  }
98  }
99  else
100  {
101  //Update HTTP request state
102  context->requestState = EST_REQ_STATE_RECEIVE_HEADER;
103  }
104  }
105  else if(context->requestState == EST_REQ_STATE_RECEIVE_HEADER)
106  {
107  //Receive HTTP response header
108  error = httpClientReadHeader(&context->httpClientContext);
109 
110  //Check status code
111  if(!error)
112  {
113  //Update HTTP request state
114  context->requestState = EST_REQ_STATE_PARSE_HEADER;
115  }
116  }
117  else if(context->requestState == EST_REQ_STATE_PARSE_HEADER)
118  {
119  //Parse HTTP response header
120  error = estClientParseResponseHeader(context);
121 
122  //Check status code
123  if(!error)
124  {
125  //Invalid authentication credentials?
126  if(context->statusCode == 401)
127  {
128  //Rewind to the beginning of the buffer
129  context->bufferPos = 0;
130 
131  //Update HTTP request state
132  context->requestState = EST_REQ_STATE_CLOSE_BODY;
133  }
134  else
135  {
136  //Flush the receive buffer
137  context->bufferLen = 0;
138  context->bufferPos = 0;
139 
140  //Update HTTP request state
141  context->requestState = EST_REQ_STATE_RECEIVE_BODY;
142  }
143  }
144  }
145  else if(context->requestState == EST_REQ_STATE_RECEIVE_BODY)
146  {
147  //Receive HTTP response body
148  if(context->bufferLen < EST_CLIENT_BUFFER_SIZE)
149  {
150  //Receive more data
151  error = httpClientReadBody(&context->httpClientContext,
152  context->buffer + context->bufferLen,
153  EST_CLIENT_BUFFER_SIZE - context->bufferLen, &n, 0);
154 
155  //Check status code
156  if(error == NO_ERROR)
157  {
158  //Advance data pointer
159  context->bufferLen += n;
160  }
161  else if(error == ERROR_END_OF_STREAM)
162  {
163  //The end of the response body has been reached
164  error = NO_ERROR;
165 
166  //Update HTTP request state
167  context->requestState = EST_REQ_STATE_CLOSE_BODY;
168  }
169  else
170  {
171  //Just for sanity
172  }
173  }
174  else
175  {
176  //Update HTTP request state
177  context->requestState = EST_REQ_STATE_CLOSE_BODY;
178  }
179  }
180  else if(context->requestState == EST_REQ_STATE_CLOSE_BODY)
181  {
182  //Close HTTP response body
183  error = httpClientCloseBody(&context->httpClientContext);
184 
185  //Check status code
186  if(!error)
187  {
188  //Update HTTP request state
189  context->requestState = EST_REQ_STATE_COMPLETE;
190  }
191  }
192  else
193  {
194  //Invalid state
195  error = ERROR_WRONG_STATE;
196  }
197 
198  //Return status code
199  return error;
200 }
201 
202 
203 /**
204  * @brief Format HTTP request header
205  * @param[in] context Pointer to the EST client context
206  * @param[in] method NULL-terminating string containing the HTTP method
207  * @param[in] operation NULL-terminating string containing the operation path
208  * @return Error code
209  **/
210 
212  const char_t *method, const char_t *operation)
213 {
214  error_t error;
215  HttpClientContext *httpClientContext;
217 
218  //Point to the HTTP client context
219  httpClientContext = &context->httpClientContext;
220 
221  //Create a new HTTP request
222  error = httpClientCreateRequest(httpClientContext);
223  //Any error to report?
224  if(error)
225  return error;
226 
227  //EST uses the HTTP POST and GET methods to perform the desired EST
228  //operation
229  error = httpClientSetMethod(httpClientContext, method);
230  //Any error to report?
231  if(error)
232  return error;
233 
234  //Check the length of the URI absolute path
235  if((osStrlen(operation) + osStrlen(context->pathPrefix)) > EST_CLIENT_MAX_URI_LEN)
236  return ERROR_INVALID_PATH;
237 
238  //The operation path is appended to the path-prefix to form the URI (refer
239  //to RFC 7030, section 3.2.2)
240  osStrcpy(uri, context->pathPrefix);
241  osStrcat(uri, operation);
242 
243  //Specify the URI absolute path
244  error = httpClientSetUri(httpClientContext, uri);
245  //Any error to report?
246  if(error)
247  return error;
248 
249  //A client must send a Host header field in all HTTP/1.1 requests (refer
250  //to RFC 7230, section 5.4)
251  if(context->serverPort == HTTPS_PORT)
252  {
253  //A host without any trailing port information implies the default port
254  //for the service requested
255  error = httpClientAddHeaderField(httpClientContext, "Host",
256  context->serverName);
257  }
258  else
259  {
260  //Append the port number information to the host
261  error = httpClientFormatHeaderField(httpClientContext,
262  "Host", "%s:%" PRIu16, context->serverName, context->serverPort);
263  }
264 
265  //Any error to report?
266  if(error)
267  return error;
268 
269  //Set User-Agent header field
270  error = httpClientAddHeaderField(httpClientContext, "User-Agent",
271  "Mozilla/5.0");
272  //Any error to report?
273  if(error)
274  return error;
275 
276  //Accept any media type
277  error = httpClientAddHeaderField(httpClientContext, "Accept", "*/*");
278  //Any error to report?
279  if(error)
280  return error;
281 
282  //POST request?
283  if(osStrcmp(method, "POST") == 0)
284  {
285  //The EST server can optionally also request that the EST client submit
286  //a username/password using the HTTP Basic or Digest authentication
287  //methods (refer to RFC 7030, section 2.2.3)
288  error = httpClientSetAllowedAuthModes(httpClientContext,
289  context->allowedAuthModes);
290  //Any error to report?
291  if(error)
292  return error;
293 
294  //The HTTP content-type of "application/pkcs10" is used here (refer to
295  //RFC 7030, section 4.2.1)
296  error = httpClientAddHeaderField(httpClientContext, "Content-Type",
297  "application/pkcs10");
298  //Any error to report?
299  if(error)
300  return error;
301 
302  //The format of the message is as specified in RFC 5967 with a Content-
303  //Transfer-Encoding of "base64"
304  error = httpClientAddHeaderField(httpClientContext,
305  "Content-Transfer-Encoding", "base64");
306  //Any error to report?
307  if(error)
308  return error;
309 
310  //Specify the length of the request body
311  error = httpClientSetContentLength(httpClientContext, context->bufferLen);
312  //Any error to report?
313  if(error)
314  return error;
315  }
316  else
317  {
318  //Disable HTTP authentication mechanism
319  error = httpClientSetAllowedAuthModes(httpClientContext,
321  //Any error to report?
322  if(error)
323  return error;
324 
325  //The HTTP request body is empty
326  context->bufferLen = 0;
327  }
328 
329  //Get current HTTP authentication mode
330  context->selectedAuthMode = context->httpClientContext.authParams.selectedMode;
331 
332  //Successful processing
333  return NO_ERROR;
334 }
335 
336 
337 /**
338  * @brief Parse HTTP response header
339  * @param[in] context Pointer to the EST client context
340  * @return Error code
341  **/
342 
344 {
345  size_t n;
346  char_t *p;
347  const char_t *contentType;
348 
349  //Get HTTP response status code
350  context->statusCode = httpClientGetStatus(&context->httpClientContext);
351 
352  //Get the Content-Type header field
353  contentType = httpClientGetHeaderField(&context->httpClientContext,
354  "Content-Type");
355 
356  //Content-Type header field found?
357  if(contentType != NULL)
358  {
359  //Retrieve the header field value
360  n = osStrlen(contentType);
361  //Limit the length of the string
363 
364  //Save the media type
365  osStrncpy(context->contentType, contentType, n);
366  //Properly terminate the string with a NULL character
367  context->contentType[n] = '\0';
368 
369  //Discard the parameters that may follow the type/subtype
370  osStrtok_r(context->contentType, "; \t", &p);
371  }
372  else
373  {
374  //The Content-Type header field is not present in the response
375  context->contentType[0] = '\0';
376  }
377 
378  //Successful processing
379  return NO_ERROR;
380 }
381 
382 #endif
#define EST_CLIENT_MAX_URI_LEN
Definition: est_client.h:119
HTTP transport mechanism.
error_t httpClientCloseBody(HttpClientContext *context)
Close HTTP request or response body.
Definition: http_client.c:2065
error_t httpClientSetAllowedAuthModes(HttpClientContext *context, uint_t allowedAuthModes)
Set allowed HTTP authentication modes.
Definition: http_client.c:218
error_t httpClientSetUri(HttpClientContext *context, const char_t *uri)
Set request URI.
Definition: http_client.c:513
error_t httpClientReadBody(HttpClientContext *context, void *data, size_t size, size_t *received, uint_t flags)
Read HTTP response body.
Definition: http_client.c:1699
EST client.
uint8_t p
Definition: ndp.h:300
error_t estClientParseResponseHeader(EstClientContext *context)
Parse HTTP response header.
error_t estClientSendRequest(EstClientContext *context)
Send HTTP request.
@ EST_REQ_STATE_COMPLETE
Definition: est_client.h:199
#define osStrcmp(s1, s2)
Definition: os_port.h:174
#define osStrlen(s)
Definition: os_port.h:168
@ ERROR_END_OF_STREAM
Definition: error.h:211
error_t httpClientSetContentLength(HttpClientContext *context, size_t length)
Set the length of the HTTP request body.
Definition: http_client.c:1038
error_t httpClientCreateRequest(HttpClientContext *context)
Create a new HTTP request.
Definition: http_client.c:416
#define EST_CLIENT_BUFFER_SIZE
Definition: est_client.h:105
@ ERROR_WRONG_STATE
Definition: error.h:210
#define HttpClientContext
Definition: http_client.h:198
error_t httpClientWriteHeader(HttpClientContext *context)
Write HTTP request header.
Definition: http_client.c:1065
error_t
Error codes.
Definition: error.h:43
@ EST_REQ_STATE_PARSE_HEADER
Definition: est_client.h:196
error_t httpClientFormatHeaderField(HttpClientContext *context, const char_t *name, const char_t *format,...)
Format an HTTP header field.
Definition: http_client.c:941
#define osStrtok_r(s, delim, last)
Definition: os_port.h:228
const char_t * httpClientGetHeaderField(HttpClientContext *context, const char_t *name)
Retrieve the value of the specified header field name.
Definition: http_client.c:1592
@ EST_REQ_STATE_RECEIVE_HEADER
Definition: est_client.h:195
uint_t httpClientGetStatus(HttpClientContext *context)
Retrieve the HTTP status code of the response.
Definition: http_client.c:1565
#define MIN(a, b)
Definition: os_port.h:63
@ ERROR_INVALID_PATH
Definition: error.h:147
error_t httpClientReadHeader(HttpClientContext *context)
Read HTTP response header.
Definition: http_client.c:1425
#define HTTPS_PORT
Definition: http_common.h:40
char char_t
Definition: compiler_port.h:55
error_t httpClientAddHeaderField(HttpClientContext *context, const char_t *name, const char_t *value)
Add a header field to the HTTP request.
Definition: http_client.c:859
#define osStrcat(s1, s2)
Definition: os_port.h:222
@ EST_REQ_STATE_CLOSE_BODY
Definition: est_client.h:198
@ EST_REQ_STATE_RECEIVE_BODY
Definition: est_client.h:197
@ EST_REQ_STATE_SEND_BODY
Definition: est_client.h:194
uint8_t n
error_t httpClientWriteBody(HttpClientContext *context, const void *data, size_t length, size_t *written, uint_t flags)
Write HTTP request body.
Definition: http_client.c:1188
#define osStrncpy(s1, s2, length)
Definition: os_port.h:216
#define EST_CLIENT_MAX_CONTENT_TYPE_LEN
Definition: est_client.h:126
@ EST_REQ_STATE_SEND_HEADER
Definition: est_client.h:192
error_t estClientFormatRequestHeader(EstClientContext *context, const char_t *method, const char_t *operation)
Format HTTP request header.
error_t httpClientSetMethod(HttpClientContext *context, const char_t *method)
Set HTTP request method.
Definition: http_client.c:453
@ HTTP_AUTH_MODE_NONE
Definition: http_common.h:73
#define EstClientContext
Definition: est_client.h:159
#define osStrcpy(s1, s2)
Definition: os_port.h:210
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.