Part 1
요약·해설
HTTP/1.1·HTTP/2 service, linker setup, wildcard path, static·dynamic·WebSocket resource와 callback 소유권을 설명합니다.
HTTP · HTTPS
Static · Dynamic · WebSocket
HTTP/1.1 · HTTP/2
Part 2
접을 수 있는 영어 원문 전체
영어 원문 전체 펼치기
7F305AC6C41CA156629828B4032E6411D6D934EE07AAF31C3C1F2342BB5E906E
.. _http_server_interface:
HTTP Server
###########
.. contents::
:local:
:depth: 2
Overview
********
Zephyr provides an HTTP server library, which allows to register HTTP services
and HTTP resources associated with those services. The server creates a listening
socket for every registered service, and handles incoming client connections.
It's possible to communicate over a plain TCP socket (HTTP) or a TLS socket (HTTPS).
Both, HTTP/1.1 (RFC 2616) and HTTP/2 (RFC 9113) protocol versions are supported.
The server operation is generally transparent for the application, running in a
background thread. The application can control the server activity with
respective API functions.
Certain resource types (for example dynamic resource) provide resource-specific
application callbacks, allowing the server to interact with the application (for
instance provide resource content, or process request payload).
Currently, the following resource types are supported:
* Static resources - content defined compile-time, cannot be modified at runtime
(:c:enumerator:`HTTP_RESOURCE_TYPE_STATIC`).
* Dynamic resources - content provided at runtime by respective application
callback (:c:enumerator:`HTTP_RESOURCE_TYPE_DYNAMIC`).
* Websocket resources - allowing to establish Websocket connections with the
server (:c:enumerator:`HTTP_RESOURCE_TYPE_WEBSOCKET`).
Zephyr provides a sample demonstrating HTTP(s) server operation and various
resource types usage. See :zephyr:code-sample:`sockets-http-server` for more
information.
Server Setup
************
A few prerequisites are needed in order to enable HTTP server functionality in
the application.
First of all, the HTTP server has to be enabled in applications configuration file
with :kconfig:option:`CONFIG_HTTP_SERVER` Kconfig option:
.. code-block:: cfg
:caption: ``prj.conf``
CONFIG_HTTP_SERVER=y
All HTTP services and HTTP resources are placed in a dedicated linker section.
The linker section for services is predefined locally, however the application
is responsible for defining linker sections for resources associated with
respective services. Linker section names for resources should be prefixed with
``http_resource_desc_``, appended with the service name.
Linker sections for resources should be defined in a linker file. For example,
for a service named ``my_service``, the linker section shall be defined as follows:
.. code-block:: c
:caption: ``sections-rom.ld``
#include <zephyr/linker/iterable_sections.h>
ITERABLE_SECTION_ROM(http_resource_desc_my_service, Z_LINK_ITERABLE_SUBALIGN)
Finally, the linker file and linker section have to be added to your application
using CMake:
.. code-block:: cmake
:caption: ``CMakeLists.txt``
zephyr_linker_sources(SECTIONS sections-rom.ld)
zephyr_linker_section(NAME http_resource_desc_my_service
KVMA RAM_REGION GROUP RODATA_REGION
SUBALIGN Z_LINK_ITERABLE_SUBALIGN)
.. note::
You need to define a separate linker section for each HTTP service
registered in the system.
Sample Usage
************
Services
========
The application needs to define an HTTP service (or multiple services), with
the same name as used for the linker section with :c:macro:`HTTP_SERVICE_DEFINE`
macro:
.. code-block:: c
#include <zephyr/net/http/service.h>
static uint16_t http_service_port = 80;
HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL);
Alternatively, an HTTPS service can be defined with
:c:macro:`HTTPS_SERVICE_DEFINE`:
.. code-block:: c
#include <zephyr/net/http/service.h>
#include <zephyr/net/tls_credentials.h>
#define HTTP_SERVER_CERTIFICATE_TAG 1
static uint16_t https_service_port = 443;
static const sec_tag_t sec_tag_list[] = {
HTTP_SERVER_CERTIFICATE_TAG,
};
HTTPS_SERVICE_DEFINE(my_service, "0.0.0.0", &https_service_port, 1, 10,
NULL, sec_tag_list, sizeof(sec_tag_list));
.. note::
HTTPS services rely on TLS credentials being registered in the system.
See :ref:`sockets_tls_credentials_subsys` for information on how to
configure TLS credentials in the system.
Once HTTP(s) service is defined, resources can be registered for it with
:c:macro:`HTTP_RESOURCE_DEFINE` macro.
Application can enable resource wildcard support by enabling
:kconfig:option:`CONFIG_HTTP_SERVER_RESOURCE_WILDCARD` option. When this
option is set, then it is possible to match several incoming HTTP requests
with just one resource handler. The `fnmatch()
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html>`__
POSIX API function is used to match the pattern in the URL paths.
Example:
.. code-block:: c
HTTP_RESOURCE_DEFINE(my_resource, my_service, "/foo*", &resource_detail);
This would match all URLs that start with a string ``foo``. See
`POSIX.2 chapter 2.13
<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13>`__
for pattern matching syntax description.
Static resources
================
Static resource content is defined build-time and is immutable. The following
example shows how gzip compressed webpage can be defined as a static resource
in the application:
.. code-block:: c
static const uint8_t index_html_gz[] = {
#include "index.html.gz.inc"
};
struct http_resource_detail_static index_html_gz_resource_detail = {
.common = {
.type = HTTP_RESOURCE_TYPE_STATIC,
.bitmask_of_supported_http_methods = BIT(HTTP_GET),
.content_encoding = "gzip",
},
.static_data = index_html_gz,
.static_data_len = sizeof(index_html_gz),
};
HTTP_RESOURCE_DEFINE(index_html_gz_resource, my_service, "/",
&index_html_gz_resource_detail);
The resource content and content encoding is application specific. For the above
example, a gzip compressed webpage can be generated during build, by adding the
following code to the application's ``CMakeLists.txt`` file:
.. code-block:: cmake
:caption: ``CMakeLists.txt``
set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/)
set(source_file_index src/index.html)
generate_inc_file_for_target(app ${source_file_index} ${gen_dir}/index.html.gz.inc --gzip)
where ``src/index.html`` is the location of the webpage to be compressed.
Dynamic resources
=================
For dynamic resource, a resource callback is registered to exchange data between
the server and the application. The application defines a resource buffer used
to pass the request payload data from the server, and to provide response payload
to the server. The following example code shows how to register a dynamic resource
with a simple resource handler, which echoes received data back to the client:
.. code-block:: c
static uint8_t recv_buffer[1024];
static int dyn_handler(struct http_client_ctx *client,
enum http_data_status status, uint8_t *buffer,
size_t len, void *user_data)
{
#define MAX_TEMP_PRINT_LEN 32
static char print_str[MAX_TEMP_PRINT_LEN];
enum http_method method = client->method;
static size_t processed;
__ASSERT_NO_MSG(buffer != NULL);
if (status == HTTP_SERVER_DATA_ABORTED) {
LOG_DBG("Transaction aborted after %zd bytes.", processed);
processed = 0;
return 0;
}
processed += len;
snprintf(print_str, sizeof(print_str), "%s received (%zd bytes)",
http_method_str(method), len);
LOG_HEXDUMP_DBG(buffer, len, print_str);
if (status == HTTP_SERVER_DATA_FINAL) {
LOG_DBG("All data received (%zd bytes).", processed);
processed = 0;
}
/* This will echo data back to client as the buffer and recv_buffer
* point to same area.
*/
return len;
}
struct http_resource_detail_dynamic dyn_resource_detail = {
.common = {
.type = HTTP_RESOURCE_TYPE_DYNAMIC,
.bitmask_of_supported_http_methods =
BIT(HTTP_GET) | BIT(HTTP_POST),
},
.cb = dyn_handler,
.data_buffer = recv_buffer,
.data_buffer_len = sizeof(recv_buffer),
.user_data = NULL,
};
HTTP_RESOURCE_DEFINE(dyn_resource, my_service, "/dynamic",
&dyn_resource_detail);
The resource callback may be called multiple times for a single request, hence
the application should be able to keep track of the received data progress.
The ``status`` field informs the application about the progress in passing
request payload from the server to the application. As long as the status
reports :c:enumerator:`HTTP_SERVER_DATA_MORE`, the application should expect
more data to be provided in a consecutive callback calls.
Once all request payload has been passed to the application, the server reports
:c:enumerator:`HTTP_SERVER_DATA_FINAL` status. In case of communication errors
during request processing (for example client closed the connection before
complete payload has been received), the server reports
:c:enumerator:`HTTP_SERVER_DATA_ABORTED`. Either of the two events indicate that
the application shall reset any progress recorded for the resource, and await
a new request to come. The server guarantees that the resource can only be
accessed by single client at a time.
The resource callback returns the number of bytes to be replied in the response
payload to the server (provided in the resource data buffer). In case there is
no more data to be included in the response, the callback should return 0.
The server will call the resource callback until it provided all request data
to the application, and the application reports there is no more data to include
in the reply.
Websocket resources
===================
Websocket resources register an application callback, which is called when a
Websocket connection upgrade takes place. The callback is provided with a socket
descriptor corresponding to the underlying TCP/TLS connection. Once called,
the application takes full control over the socket, i. e. is responsible to
release it when done.
.. code-block:: c
static int ws_socket;
static uint8_t ws_recv_buffer[1024];
int ws_setup(int sock, void *user_data)
{
ws_socket = sock;
return 0;
}
struct http_resource_detail_websocket ws_resource_detail = {
.common = {
.type = HTTP_RESOURCE_TYPE_WEBSOCKET,
/* We need HTTP/1.1 Get method for upgrading */
.bitmask_of_supported_http_methods = BIT(HTTP_GET),
},
.cb = ws_setup,
.data_buffer = ws_recv_buffer,
.data_buffer_len = sizeof(ws_recv_buffer),
.user_data = NULL, /* Fill this for any user specific data */
};
HTTP_RESOURCE_DEFINE(ws_resource, my_service, "/", &ws_resource_detail);
The above minimalistic example shows how to register a Websocket resource with
a simple callback, used only to store the socket descriptor provided. Further
processing of the Websocket connection is application-specific, hence outside
of scope of this guide. See :zephyr:code-sample:`sockets-http-server` for an
example Websocket-based echo service implementation.
API Reference
*************
.. doxygengroup:: http_service
.. doxygengroup:: http_server
Part 3
한국어 전문 번역
HTTP Server
Overview
Zephyr는 HTTP service와 각 service에 연결된 HTTP resource를 등록할 수 있는 HTTP server library를 제공합니다. Server는 등록된 service마다 listening socket을 만들고 들어오는 client connection을 처리합니다. Plain TCP socket을 사용한 HTTP와 TLS socket을 사용한 HTTPS로 통신할 수 있으며 HTTP/1.1(RFC 2616)과 HTTP/2(RFC 9113)를 모두 지원합니다.
Server는 일반적으로 application에 투명하게 background thread에서 동작합니다. Application은 해당 API 함수로 server activity를 제어할 수 있습니다.
Dynamic resource 같은 일부 resource type은 resource 전용 application callback을 제공합니다. 이를 통해 server가 application과 상호작용해 resource content를 받거나 request payload를 처리할 수 있습니다.
현재 지원하는 resource type은 다음과 같습니다.
HTTP_RESOURCE_TYPE_STATIC: compile time에 content가 정해지며 runtime에는 수정할 수 없는 static resource입니다.HTTP_RESOURCE_TYPE_DYNAMIC: 해당 application callback이 runtime에 content를 제공하는 dynamic resource입니다.HTTP_RESOURCE_TYPE_WEBSOCKET: server와 WebSocket connection을 설정할 수 있는 WebSocket resource입니다.
sockets-http-server sample은 HTTP/HTTPS server 동작과 여러 resource type의 사용법을 보여줍니다.
Server Setup
Application에서 HTTP server 기능을 활성화하려면 몇 가지 준비가 필요합니다.
prj.conf에서 CONFIG_HTTP_SERVER Kconfig option을 활성화합니다.
CONFIG_HTTP_SERVER=y모든 HTTP service와 HTTP resource는 전용 linker section에 배치됩니다. Service linker section은 library 내부에 미리 정의돼 있지만, application은 각 service에 연결할 resource linker section을 정의해야 합니다. Resource section 이름은 http_resource_desc_ prefix 뒤에 service 이름을 붙여 만듭니다.
my_service라는 service의 resource linker section은 linker file에 다음처럼 정의합니다.
#include <zephyr/linker/iterable_sections.h>
ITERABLE_SECTION_ROM(http_resource_desc_my_service, Z_LINK_ITERABLE_SUBALIGN)마지막으로 CMake를 사용해 linker file과 linker section을 application에 추가합니다.
zephyr_linker_sources(SECTIONS sections-rom.ld)
zephyr_linker_section(NAME http_resource_desc_my_service
KVMA RAM_REGION GROUP RODATA_REGION
SUBALIGN Z_LINK_ITERABLE_SUBALIGN)Sample Usage
Services
Application은 linker section에 사용한 이름과 같은 HTTP service 하나 또는 여러 개를 HTTP_SERVICE_DEFINE macro로 정의해야 합니다.
#include <zephyr/net/http/service.h>
static uint16_t http_service_port = 80;
HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL);HTTPS_SERVICE_DEFINE로 HTTPS service를 정의할 수도 있습니다.
#include <zephyr/net/http/service.h>
#include <zephyr/net/tls_credentials.h>
#define HTTP_SERVER_CERTIFICATE_TAG 1
static uint16_t https_service_port = 443;
static const sec_tag_t sec_tag_list[] = {
HTTP_SERVER_CERTIFICATE_TAG,
};
HTTPS_SERVICE_DEFINE(my_service, "0.0.0.0", &https_service_port, 1, 10,
NULL, sec_tag_list, sizeof(sec_tag_list));HTTP 또는 HTTPS service를 정의한 뒤 HTTP_RESOURCE_DEFINE macro로 해당 service에 resource를 등록할 수 있습니다.
CONFIG_HTTP_SERVER_RESOURCE_WILDCARD option을 활성화하면 resource wildcard를 사용할 수 있습니다. 하나의 resource handler가 여러 incoming HTTP request와 일치할 수 있으며, URL path의 pattern matching에는 POSIX fnmatch()를 사용합니다.
예를 들면 다음과 같습니다.
HTTP_RESOURCE_DEFINE(my_resource, my_service, "/foo*", &resource_detail);/foo* pattern은 foo 문자열로 시작하는 모든 URL과 일치합니다. Pattern syntax는 POSIX.2 chapter 2.13을 참고합니다.
Static resources
Static resource content는 build time에 정의되고 변경할 수 없습니다. 다음 예는 gzip으로 압축한 web page를 application의 static resource로 정의합니다.
static const uint8_t index_html_gz[] = {
#include "index.html.gz.inc"
};
struct http_resource_detail_static index_html_gz_resource_detail = {
.common = {
.type = HTTP_RESOURCE_TYPE_STATIC,
.bitmask_of_supported_http_methods = BIT(HTTP_GET),
.content_encoding = "gzip",
},
.static_data = index_html_gz,
.static_data_len = sizeof(index_html_gz),
};
HTTP_RESOURCE_DEFINE(index_html_gz_resource, my_service, "/",
&index_html_gz_resource_detail);HTTP_RESOURCE_TYPE_STATIC과 지원 method HTTP_GET, content encoding gzip을 지정하고 build에서 생성한 byte array와 길이를 제공합니다. HTTP_RESOURCE_DEFINE는 이 resource를 my_service의 root path /에 등록합니다.
Resource content와 content encoding은 application이 정합니다. 위 예에서 gzip compressed web page는 application의 CMakeLists.txt에 다음 code를 추가해 build 중 생성할 수 있습니다.
set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/)
set(source_file_index src/index.html)
generate_inc_file_for_target(app ${source_file_index} ${gen_dir}/index.html.gz.inc --gzip)src/index.html은 압축할 web page의 위치입니다.
Dynamic resources
Dynamic resource는 server와 application 사이에서 data를 교환하는 resource callback을 등록합니다. Application은 server가 request payload를 전달하고 application이 response payload를 제공할 때 함께 쓰는 resource buffer를 정의합니다. 다음은 받은 data를 client에 그대로 돌려주는 단순 handler를 가진 dynamic resource 등록 예입니다.
static uint8_t recv_buffer[1024];
static int dyn_handler(struct http_client_ctx *client,
enum http_data_status status, uint8_t *buffer,
size_t len, void *user_data)
{
#define MAX_TEMP_PRINT_LEN 32
static char print_str[MAX_TEMP_PRINT_LEN];
enum http_method method = client->method;
static size_t processed;
__ASSERT_NO_MSG(buffer != NULL);
if (status == HTTP_SERVER_DATA_ABORTED) {
LOG_DBG("Transaction aborted after %zd bytes.", processed);
processed = 0;
return 0;
}
processed += len;
snprintf(print_str, sizeof(print_str), "%s received (%zd bytes)",
http_method_str(method), len);
LOG_HEXDUMP_DBG(buffer, len, print_str);
if (status == HTTP_SERVER_DATA_FINAL) {
LOG_DBG("All data received (%zd bytes).", processed);
processed = 0;
}
/* This will echo data back to client as the buffer and recv_buffer
* point to same area.
*/
return len;
}
struct http_resource_detail_dynamic dyn_resource_detail = {
.common = {
.type = HTTP_RESOURCE_TYPE_DYNAMIC,
.bitmask_of_supported_http_methods =
BIT(HTTP_GET) | BIT(HTTP_POST),
},
.cb = dyn_handler,
.data_buffer = recv_buffer,
.data_buffer_len = sizeof(recv_buffer),
.user_data = NULL,
};
HTTP_RESOURCE_DEFINE(dyn_resource, my_service, "/dynamic",
&dyn_resource_detail);dyn_handler()는 request method와 data status를 확인하고 받은 byte를 누적합니다. 예제의 callback buffer와 recv_buffer가 같은 영역을 가리키므로 받은 data 길이를 반환하면 그대로 response payload가 됩니다. Resource는 GET과 POST method를 지원하며 /dynamic path에 등록됩니다.
하나의 request를 처리하는 동안 resource callback이 여러 번 호출될 수 있으므로 application은 받은 data의 진행 상태를 추적할 수 있어야 합니다.
status field는 server가 application으로 request payload를 넘기는 진행 상황을 알려줍니다. HTTP_SERVER_DATA_MORE이면 뒤의 callback에서 data가 더 전달됩니다. 모든 payload가 전달되면 HTTP_SERVER_DATA_FINAL을 보고합니다. 처리 중 통신 오류가 나면, 예를 들어 전체 payload를 받기 전에 client가 connection을 닫으면 HTTP_SERVER_DATA_ABORTED를 보고합니다.
HTTP_SERVER_DATA_FINAL과 HTTP_SERVER_DATA_ABORTED 모두 application이 resource에 기록한 진행 상태를 reset하고 새 request를 기다려야 한다는 뜻입니다. Server는 한 시점에 client 하나만 해당 resource에 접근하도록 보장합니다.
Resource callback은 resource data buffer에 놓은 response payload 가운데 server가 reply에 넣어야 할 byte 수를 반환합니다. Response에 더 넣을 data가 없으면 0을 반환해야 합니다.
Server는 request data를 application에 모두 제공하고 application이 reply에 더 넣을 data가 없다고 보고할 때까지 resource callback을 호출합니다.
Websocket resources
WebSocket resource는 WebSocket connection upgrade가 일어날 때 호출할 application callback을 등록합니다. Callback은 underlying TCP/TLS connection에 해당하는 socket descriptor를 받습니다. Callback이 호출된 뒤에는 application이 socket을 완전히 소유하며 작업이 끝나면 socket을 해제할 책임도 집니다.
static int ws_socket;
static uint8_t ws_recv_buffer[1024];
int ws_setup(int sock, void *user_data)
{
ws_socket = sock;
return 0;
}
struct http_resource_detail_websocket ws_resource_detail = {
.common = {
.type = HTTP_RESOURCE_TYPE_WEBSOCKET,
/* We need HTTP/1.1 Get method for upgrading */
.bitmask_of_supported_http_methods = BIT(HTTP_GET),
},
.cb = ws_setup,
.data_buffer = ws_recv_buffer,
.data_buffer_len = sizeof(ws_recv_buffer),
.user_data = NULL, /* Fill this for any user specific data */
};
HTTP_RESOURCE_DEFINE(ws_resource, my_service, "/", &ws_resource_detail);ws_setup()은 전달받은 socket descriptor를 저장합니다. Upgrade에는 HTTP/1.1 GET method가 필요하므로 resource가 HTTP_GET을 지원하도록 설정합니다.
이 최소 예제는 제공된 socket descriptor를 저장하는 단순 callback으로 WebSocket resource를 등록하는 방법만 보여줍니다. 이후 WebSocket connection 처리는 application마다 달라 이 문서 범위를 벗어납니다. WebSocket echo service 구현은 sockets-http-server sample에서 확인할 수 있습니다.
API Reference
Doxygen group http_service와 http_server가 API reference를 제공합니다.
Source
출처
영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.