Skip to content

Commit 4de3a0c

Browse files
isak-jacobssonDaniel Myhrmanftor-workkillenheladagen
authored andcommitted
web-server: Fix usage of Civetweb server
Co-authored-by: Daniel Myhrman <danielmy@axis.com> Co-authored-by: Fernando Esquirio Torres <fernando.torres@axis.com> Co-authored-by: Mattias Axelsson <mattiaax@axis.com>
1 parent bae8395 commit 4de3a0c

26 files changed

Lines changed: 783 additions & 318 deletions

.github/custom-linters/lint-example-structure.sh

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,78 @@
11
#!/bin/bash
22

3-
. "$(git rev-parse --show-toplevel)"/.github/utils/util-functions.sh
3+
REPO_ROOT="$(git rev-parse --show-toplevel)"
4+
. "$REPO_ROOT"/.github/utils/util-functions.sh
5+
cd "$REPO_ROOT" || exit 1
46

57
#-------------------------------------------------------------------------------
68
# Functions
79
#-------------------------------------------------------------------------------
810

11+
workflow_exists() {
12+
local example_name="$1"
13+
[ -f ".github/workflows/${example_name}.yml" ]
14+
}
15+
16+
collect_examples_for_lint() {
17+
local collected=()
18+
19+
for dir in *; do
20+
[ -d "$dir" ] || continue
21+
22+
# If the top-level directory has a workflow, it is an example on its own.
23+
if workflow_exists "$dir"; then
24+
collected+=("$dir")
25+
continue
26+
fi
27+
28+
# Otherwise treat it as a container folder and collect child examples.
29+
local found_child_examples=no
30+
for child in "$dir"/*; do
31+
[ -d "$child" ] || continue
32+
[ -f "$child/Dockerfile" ] || continue
33+
found_child_examples=yes
34+
collected+=("$(basename "$child")")
35+
done
36+
37+
# If neither a workflow nor child examples are found, keep it as-is so
38+
# the lint can report that the directory lacks a workflow.
39+
[ "$found_child_examples" = yes ] || collected+=("$dir")
40+
done
41+
42+
if [ "${#collected[@]}" -gt 0 ]; then
43+
printf '%s\n' "${collected[@]}" | sort -u
44+
fi
45+
}
46+
947
check_examples_have_workflow_file() {
1048
local ret=0
1149
local fail_list=()
1250
local yml_array=
13-
local acap_array=
51+
local example_array=
1452

1553
print_section "Verify that all examples have a workflow .yml file"
1654

1755
# shellcheck disable=SC2207
1856
yml_array=($(ls .github/workflows))
19-
# shellcheck disable=SC2207,SC2035
20-
acap_array=($(ls -d */))
57+
example_array=()
58+
while IFS= read -r example; do
59+
[ -n "$example" ] && example_array+=("$example")
60+
done < <(collect_examples_for_lint)
2161

22-
for acap in "${acap_array[@]%?}"; do
62+
for example in "${example_array[@]}"; do
2363
local found_workflow_file=no
2464
for yml in "${yml_array[@]}"; do
25-
[ "$acap.yml" = "$yml" ] && {
65+
[ "$example.yml" = "$yml" ] && {
2666
found_workflow_file=yes
2767
break
2868
}
2969
done
30-
[ "$found_workflow_file" = yes ] || fail_list+=("$acap")
70+
[ "$found_workflow_file" = yes ] || fail_list+=("$example")
3171
done
3272

3373
if [ "${#fail_list[@]}" -ne 0 ]; then
34-
print_line "Application directories"
35-
print_list_no_split "${acap_array[@]%?}"
74+
print_line "Example directories"
75+
print_list_no_split "${example_array[@]}"
3676
print_line "YML files"
3777
print_list_no_split "${yml_array[@]}"
3878
print_line "ERROR: Applications that dont't have .yml file under .github/workflows:"
@@ -47,16 +87,13 @@ check_examples_have_workflow_file() {
4787

4888
check_examples_have_top_readme_entry() {
4989
local ret=0
50-
local acap_list=
90+
local example_list=
5191
local readme_examples=
5292
local unique=
5393

5494
print_section "Verify that all examples have an entry in top-README"
5595

56-
# Find directories that do not start with dot
57-
acap_list="$(find . -maxdepth 1 -type d | sed 's|^\./||' |
58-
grep -v '^[.]' |
59-
sort)"
96+
example_list="$(collect_examples_for_lint)"
6097

6198
# Find lines starting with '* ['
6299
# The grep removes anything but '[*]', then remove entries with space
@@ -67,7 +104,7 @@ check_examples_have_top_readme_entry() {
67104
grep -oe '[0-9a-z-]*')"
68105

69106
print_line "# Control that all example directories have an entry in README"
70-
unique=$(printf '%s\n%s' "${acap_list}" "${readme_examples}" | sort | uniq -u)
107+
unique=$(printf '%s\n%s' "${example_list}" "${readme_examples}" | sort | uniq -u)
71108
if [ "$unique" ]; then
72109
print_line "ERROR: Mismatch between example directories and entries in README.md."
73110
print_line " The following items are only found in one of the two:"

.github/workflows/web-server-using-fastcgi.yml renamed to .github/workflows/http-requests-using-fastcgi.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
name: Build web-server-using-fastcgi application
1+
name: Build http-requests-using-fastcgi application
22
on:
33
workflow_dispatch:
44
push:
55
paths:
6-
- 'web-server-using-fastcgi/**'
7-
- '!web-server-using-fastcgi/README.md'
8-
- '.github/workflows/web-server-using-fastcgi.yml'
6+
- 'web-server/http-requests-using-fastcgi/**'
7+
- '!web-server/http-requests-using-fastcgi/README.md'
8+
- '.github/workflows/http-requests-using-fastcgi.yml'
99
jobs:
1010
test-app:
1111
name: Test app
@@ -16,7 +16,7 @@ jobs:
1616
arch: ["armv7hf", "aarch64"]
1717
env:
1818
EXREPO: acap-native-examples
19-
EXNAME: web-server-using-fastcgi
19+
EXNAME: web-server/http-requests-using-fastcgi
2020
steps:
2121
- uses: actions/checkout@v6
2222
- uses: docker/setup-buildx-action@v4

.github/workflows/web-server.yml renamed to .github/workflows/reverse-proxy-using-fixed-port.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
name: Build web-server application
1+
name: Build reverse-proxy-using-fixed-port application
22
on:
33
workflow_dispatch:
44
push:
55
paths:
6-
- 'web-server/**'
7-
- '!web-server/README.md'
8-
- '.github/workflows/web-server.yml'
6+
- 'web-server/reverse-proxy-using-fixed-port/**'
7+
- '!web-server/reverse-proxy-using-fixed-port/README.md'
8+
- '.github/workflows/reverse-proxy-using-fixed-port.yml'
99
jobs:
1010
test-app:
1111
name: Test app
@@ -16,7 +16,7 @@ jobs:
1616
arch: ["armv7hf", "aarch64"]
1717
env:
1818
EXREPO: acap-native-examples
19-
EXNAME: web-server
19+
EXNAME: web-server/reverse-proxy-using-fixed-port
2020
steps:
2121
- uses: actions/checkout@v6
2222
- uses: docker/setup-buildx-action@v4

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,12 @@ The examples are organized into logical groups to help you find the most relevan
8383

8484
- [curl-openssl](./curl-openssl/)
8585
- An example that use cURL and OpenSSL libraries to retrieve a file securely from an external server.
86-
- [web-server](./web-server/)
87-
- An example in C that serves HTTP requests by setting up the Axis device web server in a reverse proxy configuration and route to a custom web server running in the ACAP application.
88-
- [web-server-using-fastcgi](./web-server-using-fastcgi/)
89-
- An example in C and explains how to build an ACAP application that can handle HTTP requests sent to the Axis device, using the device's own web server.
86+
87+
- Web server examples:
88+
- [reverse-proxy-using-fixed-port](./web-server/reverse-proxy-using-fixed-port/)
89+
- An example in C that serves HTTP requests by setting up the Axis device web server in a reverse proxy configuration and routing to a custom web server running in the ACAP application.
90+
- [http-requests-using-fastcgi](./web-server/http-requests-using-fastcgi/)
91+
- An example in C and explains how to build an ACAP application that can handle HTTP requests sent to the Axis device, using the device's own web server.
9092

9193
### Event handling
9294

web-server/README.md

Lines changed: 10 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -1,194 +1,18 @@
11
*Copyright (C) 2021, Axis Communications AB, Lund, Sweden. All Rights Reserved.*
22

3-
# Serve HTTP requests through reverse proxy
3+
# Web server examples introduction
44

5-
This example demonstrates how to setup the Axis device web server (Apache) in a
6-
[Reverse Proxy](https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html)
7-
configuration, where HTTP requests to the application are routed to a web server
8-
[CivetWeb](https://github.com/civetweb/civetweb) running inside the ACAP application
9-
and acting as a CGI.
5+
This folder contains ACAP Native SDK examples that show two ways to expose HTTP endpoints and web content on an Axis device.
106

11-
> [!NOTE]
12-
> The web server runs on the port specified in the application,
13-
> however there is a possibility that the port already is in use.
14-
> For more information on common ports used by Axis devices, see the
15-
> [commonly used network ports](https://help.axis.com/en-us/axis-os-knowledge-base#commonly-used-network-ports)
16-
> in the AXIS OS knowledge base. To change the port of the application,
17-
> both the [`web_server_rev_proxy.c`](./app/web_server_rev_proxy.c) and [`manifest.json`](./app/manifest.json] file have to be updated before
18-
> building the application.
7+
## Available examples
198

20-
The advantage of a webserver proxy is that when porting existing code to your
21-
ACAP application, its request handling can remain largely unmodified. This eases
22-
the task of sharing code between platforms. The webserver proxy method enforces
23-
a URL routing scheme as follows:
9+
1. **Reverse proxy using fixed port**
10+
The reverse proxy example using CivetWeb demonstrates how to setup the Axis device web server (Apache) in a Reverse Proxy configuration, where HTTP requests to the application are routed to a web server CivetWeb running inside the ACAP application and acting as a CGI. See [reverse-proxy-using-fixed-port](./reverse-proxy-using-fixed-port/).
2411

25-
`http://<AXIS_DEVICE_IP>/local/<appName>/<apiPath>`
12+
2. **HTTP requests using FastCGI**
13+
The HTTP request example using FastCGI explains how to build an ACAP application that can handle HTTP requests sent to the Axis device. The application uses FastCGI to handle the request and response, and uriparser to parse the received query parameters. See [http-requests-using-fastcgi](./http-requests-using-fastcgi/).
2614

27-
With `<appName>` and `<apiPath>` as defined in the manifest.
15+
## Which one to use
2816

29-
Note that this example shows the reverse proxy concept using CivetWeb, but you are
30-
free to use any webserver of your choice.
31-
32-
## Alternative approach
33-
34-
Another example that serves HTTP requests is
35-
[web-server-using-fastcgi](../web-server-using-fastcgi), where the Axis device
36-
web server and the supported ACAP API
37-
[FastCGI](https://developer.axis.com/acap/api/native-sdk-api/#fastcgi)
38-
are used.
39-
40-
## Reverse proxy configuration in Apache server
41-
42-
A reverse proxy configuration provides a flexible way for an ACAP application
43-
to expose an external API through the Apache Server in AXIS OS and internally
44-
route the requests to a web server running in the ACAP application.
45-
46-
The Apache server is configured using the `manifest.json` file in an ACAP
47-
application. In `manifest.json` under `configuration`, it is possible to specify
48-
a `settingPage` and a `reverseProxy` where the latter will connect the CivetWeb
49-
server to the Apache server.
50-
51-
Prior to manifest 1.5.0, reverse proxy was only supported through the
52-
postinstall script. The manifest based method is more strict on URLs in order to
53-
avoid name clashes that could occur in the old mechanism. When upgrading, your
54-
URLs will change to the format shown in
55-
[Serve HTTP requests through reverse proxy](#serve-http-requests-through-reverse-proxy).
56-
57-
The web server running in the ACAP application can also be exposed directly to
58-
the network by allowing external access to the port in the network
59-
configuration for the device. There are disadvantages with exposing Web
60-
Server directly to the network such as non standard ports and no reuse of
61-
authentication, TLS and other features that comes with Apache Server.
62-
63-
## CivetWeb web server
64-
65-
CivetWeb is an embeddable C web server for Linux. It is a great solution
66-
for running a web server on embedded Linux. Apart from being a
67-
HTTP server, it has a C API which can be extended as desired. The CivetWeb Web
68-
Server [documentation](https://github.com/civetweb/civetweb/) describes the
69-
configuration in detail. CivetWeb is open source, and will contain different
70-
licenses depending on the features you build it with. Please see
71-
[CivetWeb's repository](https://github.com/civetweb/civetweb/) for more information.
72-
73-
## Getting started
74-
75-
These instructions will guide you on how to execute the code. Below is the
76-
structure used in the example:
77-
78-
```sh
79-
web-server
80-
├── app
81-
│ ├── LICENSE
82-
│ └── manifest.json
83-
├── Dockerfile
84-
└── README.md
85-
```
86-
87-
- **app/LICENSE** - Lists open source licensed source code in the application.
88-
- **app/manifest.json** - Defines the application and its configuration.
89-
- **Dockerfile** - Builds an Axis container image and the specified example.
90-
- **README.md** - Step by step instructions on how to run the example.
91-
92-
## Limitations
93-
94-
- Apache Reverse Proxy can not translate content with absolute addresses (i.e.
95-
/image.png) in the HTML page. Use only relative content (i.e. image.png or
96-
../image.png). See [how to handle relative URLs correctly with a reverse proxy](https://serverfault.com/questions/561892/how-to-handle-relative-urls-correctly-with-a-reverse-proxy)
97-
for more information.
98-
99-
### How to run the code
100-
101-
Below is the step by step instructions on how to execute the program. So
102-
basically starting with the generation of the .eap file to running it on a
103-
device.
104-
105-
#### Build the application
106-
107-
Standing in your working directory run the following commands:
108-
109-
> [!NOTE]
110-
>
111-
> Depending on the network your local build machine is connected to,
112-
you may need to add proxy
113-
> settings for Docker. See
114-
> [Proxy in build time](https://developer.axis.com/acap/develop/proxy/#proxy-in-build-time).
115-
116-
```sh
117-
docker build --platform=linux/amd64 --tag <APP_IMAGE> --build-arg ARCH=<ARCH> .
118-
```
119-
120-
- `<APP_IMAGE>` is the name to tag the image with, e.g., `web-server:1.0`
121-
- `<ARCH>` is the SDK architecture, `armv7hf` or `aarch64`.
122-
123-
Copy the result from the container image to a local directory `build`:
124-
125-
```sh
126-
docker cp $(docker create --platform=linux/amd64 <APP_IMAGE>):/opt/app ./build
127-
```
128-
129-
The `build` directory contains the build artifacts, where the ACAP application
130-
is found with suffix `.eap`, depending on which SDK architecture that was
131-
chosen, one of these files should be found:
132-
133-
- `web_server_rev_proxy_1_0_0_aarch64.eap`
134-
- `web_server_rev_proxy_1_0_0_armv7hf.eap`
135-
136-
> [!NOTE]
137-
>
138-
> For detailed information on how to build, install, and run ACAP applications, refer to the official ACAP documentation: [Build, install, and run](https://developer.axis.com/acap/develop/build-install-run/).
139-
140-
#### Install and start the application
141-
142-
Browse to the application page of the Axis device:
143-
144-
```sh
145-
http://<AXIS_DEVICE_IP>/index.html#apps
146-
```
147-
148-
1. Click on the tab **Apps** in the device GUI
149-
2. Enable **Allow unsigned apps** toggle
150-
3. Click **(+ Add app)** button to upload the application file
151-
4. Select the newly built application package, depending on architecture:
152-
153-
- `web_server_rev_proxy_1_0_0_aarch64.eap`
154-
- `web_server_rev_proxy_1_0_0_armv7hf.eap`
155-
156-
5. Click **Install**
157-
6. Run the application by enabling the **Start** switch
158-
159-
#### The expected output
160-
161-
A user can make a HTTP request to the application API using e.g. cURL
162-
163-
```sh
164-
curl -u <USER>:<PASSWORD> --anyauth http://<AXIS_DEVICE_IP>/local/web_server_rev_proxy/my_web_server
165-
```
166-
167-
Where the expected output is
168-
169-
```sh
170-
<html>
171-
<head><link rel="stylesheet" href="style.css"/></head>
172-
<title>
173-
ACAP Web Server Example
174-
</title>
175-
<body>
176-
<h1>ACAP Web Server Example</h1>
177-
Welcome to the web server example, this server is based on the
178-
<a href="https://github.com/civetweb/civetweb">CivetWeb</a> C library.
179-
</body>
180-
</html>
181-
```
182-
183-
To view the rendered web page, click on the `Open` button of the application
184-
from the `Apps` tab of the device GUI. Alternatively, browse directly to
185-
`http://<AXIS_DEVICE_IP>/local/web_server_rev_proxy/my_web_server`.
186-
187-
The application log can be found by either
188-
189-
- Browse to `http://<AXIS_DEVICE_IP>/axis-cgi/admin/systemlog.cgi?appname=web_server_rev_proxy`.
190-
- Browse to the application page and click the `App log`.
191-
192-
## License
193-
194-
**[Apache License 2.0](../LICENSE)**
17+
- Choose the **reverse proxy** example when you want an embedded web server with flexible routing and easy reuse of existing server-side C code.
18+
- Choose the **FastCGI** example when you want tighter integration with Apache and a native ACAP web serving flow.

web-server/app/html/index.html

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)