├── .github
└── workflows
│ ├── monthly-copyright-update.yml
│ ├── nightly-documentation-update.yml
│ ├── nightly-package-update.yml
│ ├── nightly-pr-to-main.yml
│ ├── nightly-publish-main.yml
│ └── nightly-submodule-update.yml
├── .gitignore
├── .gitmodules
├── 51Degrees_module
└── ngx_http_51D_module.c
├── Makefile
├── README.md
├── ci
├── README.md
├── build-package-requirements.ps1
├── build-package.ps1
├── build-project.ps1
├── fetch-assets.ps1
├── get-next-package-version.ps1
├── install-package.ps1
├── options.json
├── publish-package.ps1
├── run-integration-tests.ps1
├── run-performance-tests.ps1
├── run-unit-tests.ps1
├── setup-environment.ps1
└── update-packages.ps1
├── docs
├── Doxyfile
└── DoxygenLayout.xml
├── examples
├── curl
│ ├── Dockerfile
│ ├── README.md
│ ├── curl.php
│ ├── curl.sh
│ ├── nginx.conf
│ └── run.sh
├── example.php
└── hash
│ ├── config.conf
│ ├── gettingStarted.conf
│ ├── jsExample
│ ├── index.html
│ └── javascript.conf
│ ├── matchMetrics.conf
│ ├── matchQuery.conf
│ └── responseHeaders
│ ├── response.html
│ └── responseHeader.conf
├── module_conf
└── hash_config
├── suppressions.txt
├── test.sh
└── tests
├── 51degrees.t
├── examples
├── config.t
├── gettingStarted.t
├── jsExample
│ ├── cdn.test.js
│ ├── package-lock.json
│ └── package.json
├── matchMetrics.t
├── matchQuery.t
└── responseHeader.t
└── performance
├── CMakeLists.txt
├── nginx.conf.template
└── runPerf.sh
/.github/workflows/monthly-copyright-update.yml:
--------------------------------------------------------------------------------
1 | name: Monthly Copyright Update
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | # * is a special character in YAML so quote this string
7 | - cron: '0 0 1 * *'
8 |
9 | jobs:
10 | # Run the common workflow on each pull request
11 | Monthly_Copyright_Update:
12 | uses: 51Degrees/common-ci/.github/workflows/monthly-copyright-update.yml@main
13 | with:
14 | repo-name: ${{ github.event.repository.name }}
15 | secrets:
16 | token: ${{ secrets.ACCESS_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/nightly-documentation-update.yml:
--------------------------------------------------------------------------------
1 | name: Nightly Documentation Update
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | # * is a special character in YAML so quote this string
7 | - cron: '30 3 * * *'
8 |
9 | jobs:
10 | # Run the common workflow on each pull request
11 | Nightly_Documentation_Update:
12 | uses: 51Degrees/common-ci/.github/workflows/nightly-documentation-update.yml@main
13 | with:
14 | repo-name: ${{ github.event.repository.name }}
15 | secrets:
16 | token: ${{ secrets.ACCESS_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/nightly-package-update.yml:
--------------------------------------------------------------------------------
1 | name: Nightly Package Update
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | # * is a special character in YAML so quote this string
7 | - cron: '0 0 * * *'
8 |
9 | jobs:
10 | Nightly_Package_Update:
11 | uses: 51Degrees/common-ci/.github/workflows/nightly-package-update.yml@main
12 | with:
13 | repo-name: ${{ github.event.repository.name }}
14 | org-name: ${{ github.event.repository.owner.login }}
15 | secrets:
16 | token: ${{ secrets.ACCESS_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/nightly-pr-to-main.yml:
--------------------------------------------------------------------------------
1 | name: Nightly PRs to Main
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | dryrun:
7 | default: false
8 | type: boolean
9 | description: Dry Run
10 | workflow_run:
11 | workflows: [Nightly Submodule Update]
12 | types: [completed]
13 |
14 | jobs:
15 | # Run the common workflow on each pull request
16 | Nightly_PR_to_Main:
17 | uses: 51Degrees/common-ci/.github/workflows/nightly-prs-to-main.yml@main
18 | with:
19 | repo-name: ${{ github.event.repository.name }}
20 | org-name: ${{ github.event.repository.owner.login }}
21 | dryrun: ${{ inputs.dryrun || false }}
22 | secrets:
23 | token: ${{ secrets.ACCESS_TOKEN }}
24 | DeviceDetection: ${{ secrets.DEVICE_DETECTION_KEY }}
25 | DeviceDetectionUrl: ${{ secrets.DEVICE_DETECTION_URL }}
26 | NginxKey: ${{ secrets.NGINX_PLUS_KEY }}
27 | NginxCert: ${{ secrets.NGINX_PLUS_CERT }}
28 | NginxJwtToken: ${{ secrets.NGINX_PLUS_JWT_TOKEN }}
29 |
--------------------------------------------------------------------------------
/.github/workflows/nightly-publish-main.yml:
--------------------------------------------------------------------------------
1 | name: Nightly Publish Main
2 |
3 | on:
4 | workflow_dispatch:
5 | workflow_run:
6 | workflows: [Nightly PRs to Main]
7 | types: [completed]
8 |
9 | jobs:
10 | # Run the common workflow to test and publish the main branch
11 | Nightly_Publish_Main:
12 | uses: 51Degrees/common-ci/.github/workflows/nightly-publish-main.yml@main
13 | with:
14 | repo-name: ${{ github.event.repository.name }}
15 | build-platform: ubuntu-latest
16 | secrets:
17 | token: ${{ secrets.ACCESS_TOKEN }}
18 | DeviceDetection: ${{ secrets.DEVICE_DETECTION_KEY }}
19 | DeviceDetectionUrl: ${{ secrets.DEVICE_DETECTION_URL }}
20 | NginxKey: ${{ secrets.NGINX_PLUS_KEY }}
21 | NginxCert: ${{ secrets.NGINX_PLUS_CERT }}
22 | NginxJwtToken: ${{ secrets.NGINX_PLUS_JWT_TOKEN }}
23 |
--------------------------------------------------------------------------------
/.github/workflows/nightly-submodule-update.yml:
--------------------------------------------------------------------------------
1 | name: Nightly Submodule Update
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | # * is a special character in YAML so quote this string
7 | - cron: '0 2 * * *'
8 |
9 | jobs:
10 | # Run the common workflow on each pull request
11 | Nightly_Submodule_Update:
12 | uses: 51Degrees/common-ci/.github/workflows/nightly-submodule-update.yml@main
13 | with:
14 | repo-name: ${{ github.event.repository.name }}
15 | secrets:
16 | token: ${{ secrets.ACCESS_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | vendor/
3 | nginx
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "device-detection-cxx"]
2 | path = device-detection-cxx
3 | url=../device-detection-cxx
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ifndef FIFTYONEDEGREES_NGINX_VERSION
2 | VERSION := 1.20.0
3 | else
4 | VERSION := $(FIFTYONEDEGREES_NGINX_VERSION)
5 | endif
6 |
7 | ifndef DONT_CLEAN_TESTS
8 | CLEANTESTS := 1
9 | else
10 | CLEANTESTS := 0
11 | endif
12 |
13 | TESTS := tests/51degrees.t
14 |
15 |
16 | $(eval $(API):;@:)
17 |
18 | ifneq (test,$(firstword $(MAKECMDGOALS)))
19 | API := hash
20 | ARGS := -std=gnu11 -Wall -Wno-unused-variable -Wno-missing-braces
21 | endif
22 |
23 |
24 | FULLPATH := $(shell pwd)
25 | ifndef FIFTYONEDEGREES_DATAFILE
26 | DATAFILE := 51Degrees-LiteV4.1.hash
27 | else
28 | DATAFILE := $(FIFTYONEDEGREES_DATAFILE)
29 | endif
30 |
31 | MODULEPATH := $(FULLPATH)/build/modules/ngx_http_51D_module.so
32 | FILEPATH := $(FULLPATH)/device-detection-cxx/device-detection-data/$(DATAFILE)
33 |
34 |
35 | ifndef STATIC_BUILD
36 | MODULE_ARG := --add-dynamic-module
37 | else
38 | MODULE_ARG := --add-module
39 | endif
40 |
41 | .PHONY hash:
42 |
43 | clean:
44 | if [ -d "51Degrees_module/src" ]; then \
45 | rm -rf 51Degrees_module/src; \
46 | fi
47 | if [ -d "build" ]; then \
48 | rm -rf build; \
49 | fi
50 | if [ -f "nginx" ]; then \
51 | rm -f nginx; \
52 | fi
53 | if [ -f "vendor/nginx-$(VERSION)/Makefile" ]; then \
54 | cd $(CURDIR)/vendor/nginx-$(VERSION) && make clean; \
55 | fi
56 | if [ "$(CLEANTESTS)" -eq "1" ]; then \
57 | if [ -d "tests/nginx-tests" ]; then \
58 | rm -r tests/nginx-tests; \
59 | fi; \
60 | fi;
61 |
62 | build: clean
63 | mkdir -p 51Degrees_module/src/hash
64 | mkdir -p 51Degrees_module/src/common-cxx
65 | exit
66 | cp module_conf/$(API)_config 51Degrees_module/config
67 | cp device-detection-cxx/src/*.c 51Degrees_module/src/
68 | cp device-detection-cxx/src/*.h 51Degrees_module/src/
69 | cp device-detection-cxx/src/hash/*.c 51Degrees_module/src/hash/
70 | cp device-detection-cxx/src/hash/*.h 51Degrees_module/src/hash/
71 | cp device-detection-cxx/src/common-cxx/*.c 51Degrees_module/src/common-cxx/
72 | cp device-detection-cxx/src/common-cxx/*.h 51Degrees_module/src/common-cxx/
73 |
74 |
75 | get-source:
76 | if [ ! -d "vendor" ]; then mkdir vendor; fi
77 | cd vendor && curl -L -O "http://nginx.org/download/nginx-$(VERSION).tar.gz"
78 | cd vendor && tar xzf "nginx-$(VERSION).tar.gz"
79 |
80 | configure: build
81 | if [ ! -d "vendor/nginx-$(VERSION)" ]; then $(MAKE) get-source; fi
82 | cd $(CURDIR)/vendor/nginx-$(VERSION) && \
83 | ./configure \
84 | --prefix=$(CURDIR)/build \
85 | --with-ld-opt="-lm -latomic $(MEM_LD_FLAGS)" \
86 | $(MODULE_ARG)=$(CURDIR)/51Degrees_module \
87 | --with-compat \
88 | --with-cc-opt="$(ARGS) $(MEM_CC_FLAGS)" \
89 | --with-debug \
90 | --sbin-path=$(CURDIR) \
91 | --conf-path="nginx.conf" \
92 | --with-http_sub_module
93 |
94 | configure-no-module: clean
95 | if [ ! -d "vendor/nginx-$(VERSION)" ]; then $(MAKE) get-source; fi
96 | cd $(CURDIR)/vendor/nginx-$(VERSION) && \
97 | ./configure \
98 | --prefix=$(CURDIR)/build \
99 | --with-compat \
100 | --with-debug \
101 | --sbin-path=$(CURDIR) \
102 | --conf-path="nginx.conf" \
103 | --with-http_sub_module
104 |
105 | install: configure
106 | cd $(CURDIR)/vendor/nginx-$(VERSION) && make install
107 | sed "/\/\*\*/,/\*\//d" examples/$(API)/gettingStarted.conf > build/nginx.conf
108 | sed -i "s!%%DAEMON_MODE%%!on!g" build/nginx.conf
109 | sed -i "s!%%MODULE_PATH%%!!g" build/nginx.conf
110 | sed -i "s!%%FILE_PATH%%!$(FILEPATH)!g" build/nginx.conf
111 | sed -i "s!%%TEST_GLOBALS%%!!g" build/nginx.conf
112 | sed -i "s!%%TEST_GLOBALS_HTTP%%!!g" build/nginx.conf
113 | echo > build/html/$(API)
114 |
115 | install-no-module: configure-no-module
116 | cd $(CURDIR)/vendor/nginx-$(VERSION) && make install
117 |
118 | module: configure
119 | cd $(CURDIR)/vendor/nginx-$(VERSION) && make modules
120 | if [ ! -d "build/modules" ]; then mkdir -p build/modules; fi
121 | cp $(CURDIR)/vendor/nginx-$(VERSION)/objs/*.so build/modules
122 |
123 | all-versions:
124 | mkdir modules
125 | $(foreach version, \
126 | 1.19.0 1.19.5 1.19.8 1.20.0, \
127 | $(MAKE) module VERSION=$(version); \
128 | mv build/modules/ngx_http_51D_module.so modules/ngx_http_51D_hash_module-$(version).so;)
129 |
130 | set-mem:
131 | $(eval MEM_CC_FLAGS := -O0 -g -fsanitize=address)
132 | $(eval MEM_LD_FLAGS := -g -fsanitize=address)
133 |
134 | mem-check: set-mem install
135 |
136 | test-prep:
137 | if [ ! -f "tests/nginx-tests/lib/Test/Nginx.pm" ]; then \
138 | rm -rf tests/nginx-tests; mkdir -p tests/nginx-tests; \
139 | curl -fL "https://github.com/nginx/nginx-tests/archive/refs/heads/master.tar.gz" \
140 | | tar -xzC tests/nginx-tests --strip-components 1; \
141 | fi;
142 |
143 | test-full:
144 | $(MAKE) test TESTS="$(TESTS) tests/nginx-tests"
145 |
146 | test-examples: test-prep
147 | ifeq (,$(wildcard $(FULLPATH)/nginx))
148 | $(error Local binary must be built first (use "make install"))
149 | else
150 | $(eval CMD := TEST_NGINX_BINARY="$(FULLPATH)/nginx" TEST_MODULE_PATH="$(FULLPATH)/build/" TEST_FILE_PATH="$(FILEPATH)" ASAN_OPTIONS=detect_odr_violation=0 LSAN_OPTIONS=suppressions=suppressions.txt prove $(FIFTYONEDEGREES_FORMATTER) -v tests/examples :: $(DATAFILE))
151 | ifdef FIFTYONEDEGREES_TEST_OUTPUT
152 | $(CMD) > $(FIFTYONEDEGREES_TEST_OUTPUT)
153 | else
154 | $(CMD)
155 | endif
156 | endif
157 |
158 | test: test-prep
159 | ifeq (,$(wildcard $(FULLPATH)/nginx))
160 | $(error Local binary must be built first (use "make install"))
161 | else
162 | $(eval CMD := TEST_NGINX_BINARY="$(FULLPATH)/nginx" TEST_NGINX_GLOBALS="load_module $(MODULEPATH);" TEST_NGINX_GLOBALS_HTTP="51D_file_path $(FILEPATH);" ASAN_OPTIONS=detect_odr_violation=0 LSAN_OPTIONS=suppressions=suppressions.txt prove $(FIFTYONEDEGREES_FORMATTER) -v $(TESTS) :: $(DATAFILE))
163 | ifdef FIFTYONEDEGREES_TEST_OUTPUT
164 | $(CMD) > $(FIFTYONEDEGREES_TEST_OUTPUT)
165 | else
166 | $(CMD)
167 | endif
168 | endif
169 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 51Degrees Device Detection Nginx Module
2 |
3 |  **Device Detection in C** Nginx module
4 |
5 | [Developer documentation](https://51degrees.com/documentation/_other_integrations__nginx.html)
6 |
7 | # Introduction
8 | This project integrates 51Degrees Device Detection V4 engine as a module to Nginx, allowing users to configure Nginx to perform device detection by setting corresponding 51Degrees directives in the Nginx configuration file. Currently only Linux platform is supported.
9 |
10 | # Getting Started
11 | Before following the quick start, make sure to have Nginx installed and have a 51Degrees data file ready to use. The Nginx executable can be installed and built as instructed in the [Build and Test](#build-and-test) section.
12 |
13 | ## Data File
14 |
15 | In order to perform device detection, you will need to use a 51Degrees data file.
16 | This repository includes a free, 'lite' file in the 'device-detection-data'
17 | sub-module that has a significantly reduced set of properties. To obtain a
18 | file with a more complete set of device properties see the
19 | [51Degrees website](https://51degrees.com/pricing).
20 | If you want to use the lite file, you will need to install [GitLFS](https://git-lfs.github.com/).
21 |
22 | For Linux:
23 | ```
24 | sudo apt-get install git-lfs
25 | git lfs install
26 | ```
27 |
28 | Then, navigate to the `device-detection-cxx/device-detection-data` directory and execute:
29 |
30 | ```
31 | git lfs pull
32 | ```
33 |
34 | ## Quick Start
35 |
36 | To quickly start using 51Degrees module with Nginx, please follow the below steps:
37 | - Install an Nginx version 1.19.0 or above.
38 | - Obtain a 51Degrees data file. A Lite version is available at [Github](https://github.com/51Degrees/device-detection-data).
39 | - Download the pre-built binaries at our [Github release page](https://github.com/51Degrees/device-detection-nginx/releases).
40 | - Choose the binary for the version of Nginx that you want to use.
41 | - Create a folder which we will use for this quick start.
42 | - In the folder:
43 | - Create a Nginx configuration file and paste the following content, replacing the path in `load_module` and `51D_file_path` with the actual path to the downloaded module and the downloaded data file.
44 | ```
45 | # Specify the module to load.
46 | load_module [path to 51Degrees module];
47 |
48 | events {
49 | }
50 |
51 | http {
52 | # Specify the data file to use
53 | 51D_file_path [path to 51Degrees data file];
54 | server {
55 | listen 8888;
56 |
57 | location /test {
58 | # Perform a detection using a single User-Agent header.
59 | # Set the request header 'x-mobile' to returned value for property 'IsMobile'.
60 | 51D_match_ua x-mobile IsMobile;
61 |
62 | # Set the response header 'x-mobile' using the request header 'x-mobile'.
63 | add_header x-mobile $http_x_mobile;
64 | }
65 | }
66 | }
67 | ```
68 | - Create a folder named `html` and an empty file `test` in that folder. This is because we are serving static content in the above config file and the root directory to serve requests is defaulted at `html`.
69 | - Create a folder named `logs` for nginx to write its log messages to.
70 | - In the quick start folder, start Nginx using the following command:
71 | ```
72 | nginx -p . -c nginx.conf
73 | ```
74 | - The `-p` flag tells Nginx to set the prefix path which Nginx will use.
75 | - The `-c` flag tells Nginx to use the config file that we just created.
76 | - Install curl if you haven't.
77 | - Fire a request and see the result being sent back in the response using the following command:
78 | ```
79 | curl -H 'User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D167 Safari/9537.53' http://localhost:8888/test -I
80 | ```
81 | - You should see the `x-mobile: True` in the response header, indicating that Nginx has successfully used 51Degrees Device Detection engine to detect that client user agent is a mobile device using the input 'User-Agent' header.
82 | - At this point you can terminate the Nginx server using:
83 | ```
84 | nginx -p . -s quit
85 | ```
86 |
87 | The above gave you a quick overview of how to use Nginx 51Degrees Device Detection V4 module. However, that used a pre-built binaries of 51Degrees module, and in some cases, it might not work on your target machine due to the differences in the build environment that was used. In those cases, you will have options to rebuild the module yourself. This is detailed in the [Build and Test](#build-and-test) section.
88 |
89 | ## Latest releases
90 | The latest releases of 51Degrees Nginx module can be found on [Github](https://github.com/51Degrees/device-detection-nginx/releases).
91 |
92 | For the supported platforms and Nginx version, please check the [tested versions page](https://51degrees.com/documentation/_info__tested_versions.html) for latest update. At the time of writing, the followings are supported:
93 | - Platform Ubuntu-18.04 and 20.04, 64bit.
94 | - Nginx version: 1.19.0, 1.19.5, 1.19.10 and 1.20.0
95 |
96 | ## API references
97 | Below is the list of directives that can be used with 51Degrees Device Detection V4 module.
98 |
99 | |Directives|
100 | |---------|
101 | |Syntax: `51D_file_path` *filename*; Default: --- Context: main Specify the data file to used for 51Degrees Device Detection V4 engine|
102 | |Syntax: `51D_drift` *drift*; Default: 51D_drift 0; Context: main Specify the drift value that a detection can allow.|
103 | |Syntax: `51D_difference` *difference*; Default: 51D_difference 0; Context: main Specify the difference value that a detection can allow.|
104 | |Syntax: `51D_allow_unmatched` *on \| off*; Default: 51D_allow_unmatched off; Context: main Specify if unmatched should be allowed.|
105 | |**DEPRECATED** Syntax: `51D_use_performance_graph` *on \| off*; Default: 51D_use_performance_graph off; Context: main Specify if performance graph should be used in detection. **DEPRECATED**: Has no effect on configuration, the data file has a single (predictive) graph that is always used.|
106 | |**DEPRECATED** Syntax: `51D_use_predictive_graph` *on \| off*; Default: 51D_use_predictive_graph on; Context: main Specify if predictive graph should be used in detection. **DEPRECATED**: Has no effect on configuration, the data file has a single graph that is always used.|
107 | |Syntax: `51D_value_separator` *separator*; Default: 51D_value_separator ','; Context: main Specify the separator to be used in the value string returned from a detection. Each value in the returned result string is correspond to a requested property.|
108 | |Syntax: `51D_match_ua` *header* *properties* \[*argument*\]; Default: --- Context: main, server, `location` (**NOTE**: This directive can be used in main, server and location blocks. Specified properties are aggregated and eventually queried in the location. *header* value is set after the query is performed and is only available within `location` block) Perform a detection using a single request header `User-Agent`. *header* specifies which request header the returned *properties* values should be stored at. *properties* is a comma separated list string. *argument* specifies if a `User-Agent` is supplied as a query argument. This will override the value in the `User-Agent` header. The *argument* is optional. If a property is not available for any reason, the value being returned for that property will be `NA` This directive was previously known as `51D_match_single` (name deprecated)|
109 | |Syntax: `51D_match_ua_client_hints` *header* *properties* \[*argument*\]; Default: --- Context: main, server, `location` (**NOTE**: This directive can be used in main, server and location blocks. Specified properties are aggregated and eventually queried in the location. *header* value is set after the query is performed and is only available within `location` block) Perform a detection using request headers `User-Agent` and `Sec-CH-UA-*`. *header* specifies which request header the returned *properties* values should be stored at. *properties* is a comma separated list string. *argument* specifies if a `User-Agent` is supplied as a query argument. This will override the value in the `User-Agent` header. The *argument* is optional. If a property is not available for any reason, the value being returned for that property will be `NA`|
110 | |Syntax: `51D_match_all` *header* *properties*; Default: --- Context: main, server, `location` (**NOTE**: This directive can be used in main, server and location blocks. Specified properties are aggregated and eventually queried in the location. *header* value is set after the query is performed and is only available within `location` block) Perform a detection using all headers, query argument and cookie from a http request. *header* specifies which request header the returned *properties* values should be stored at. *properties* is a comma separated list string. If a property is not available for any reason, the value being returned for that property will be `NA`|
111 | |Syntax: `51D_get_javascript_single` *javascript_property* \[*argument*\]; Default: --- Context: location Perform a detection using a single request header `User-Agent`. The returned value of *javascript_property* is set in the response body. This works in a similar way as CDN to serve static content. *argument* specifies if a `User-Agent` is supplied as a query argument. This will override the value in the `User-Agent` header. The *argument* is optional. If the Javascript property is not available for any reason, a Javascript block comment will be returned so that it will not cause syntax error when the client executes it. The whole response body is used for the returned content so only one of these directives can be used in a single location block. Also, since the static content does not actually exist as a static file, the nginx http core module will log an error, so it is recommended to use this directive with [log_not_found](http://nginx.org/en/docs/http/ngx_http_core_module.html#log_not_found) set to off.|
112 | |Syntax: `51D_get_javascript_all` *javascript_property*; Default: --- Context: location Perform a detection using all headers, cookie and query arguments from a http request. The returned value of the *javascript_property* is set in the response body. This works in a similar way as CDN to serve static content. If the Javascript property is not available for any reason, a Javascript block comment will be returned so that it will not cause syntax error when the client executes it. The whole response body is used for the returned content so only one of these directives can be used in a single location block. Also, since the static content does not actually exist as a static file, the nginx http core module will log an error, so it is recommended to use this directive with [log_not_found](http://nginx.org/en/docs/http/ngx_http_core_module.html#log_not_found) set to off.|
113 | |Syntax: `51D_set_resp_headers` *on \| off*; Default: 51D_set_resp_headers off Context: main, server, location Allow Client Hints to be set in response headers where it is applicable to the user agent (e.g. Chrome 89 or above) so that more evidence can be returned in subsequent requests, allowing more accurate detection. Value set in a block overwrites values set in precedent blocks (e.g. value set in `location` block will overwrite value set in `server` and `main` blocks). This will only be available from the 4.3.0 version onwards.|
114 |
115 | ## Proxy Passing
116 | When using the `proxy_pass` directive in a location block where a match directive is used, the properties selected are passed as additional HTTP headers with the name specified in the first argument of `51D_match_ua`/`51D_match_ua_client_hints`/`51D_match_all`.
117 |
118 | ## Fast-CGI
119 | Using `include fastcgi_params;` makes these additional headers available via the `$_SERVER` variable.
120 |
121 | ## Match Metrics
122 | Other than detection properties, users can request further metrics of a match in the same way. The available metrics are Drift, Difference, Method, MatchedNodes, User-Agents and DeviceId.
123 | e.g.
124 | ```
125 | 51D_match_all x-metrics Drift,Difference,Method,MatchedNodes,DeviceId,UserAgents
126 | ```
127 |
128 | ## Output Format
129 | The value of the header is set to a comma separated list of values (comma delimited is the default behaviour, but the delimiter can be set explicitly with `51D_value_separator`), these are in the same order the properties are listed in the config file. So setting a header with the line:
130 | ```
131 | 51D_match_all x-device HardwareName,BrowserName,PlatformName;
132 | ```
133 | will give a header named `x-device` with a value like `Desktop,Firefox,Ubuntu`. Alternatively, headers can be set individually like:
134 | ```
135 | 51D_match_all x-hardware HardwareName;
136 | 51D_match_all x-browser BrowserName;
137 | 51D_match_all x-platform PlatformName;
138 | ```
139 | giving three separate headers.
140 |
141 | ## Examples
142 | All examples are located in the `examples` folder:
143 | |Example|Description|
144 | |-------|-----------|
145 | |gettingStarted.conf|Shows a simple instance of how to use 51D_match_ua, 51D_match_ua_client_hints and 51D_match_all in a configuration file.|
146 | |config.conf|Shows how to configure 51Degrees detection using directives such as 51D_drift, 51D_difference, etc...|
147 | |matchQuery.conf|Shows how to perform detection using input from http request query argument|
148 | |matchMetrics.conf|Shows how to obtain other match metrics of the detection such as drift, difference, method and etc...|
149 | |responseHeaders|Shows how to enable Client Hints support to request further evidence from user agent to provide more accurate detection. This will only be available from the 4.3.0 version onwards.|
150 | |jsExample|Shows how to use 51D_get_javascript* directive to get the Javascript to obtain further evidences from the client. To run this example, start Nginx with the included `javascript.conf` set as the configuration file. Once Nginx fully start, open `index.html` in a browser to see that the screen width is now set correctly.|
151 |
152 |
153 | To run these examples, follow the instructions detailed in each example.
154 |
155 | # Build and Test (Linux)
156 | Before build and test the 51Degrees Device Detection V4 module, make sure to check out the project and all of its sub-modules.
157 |
158 | ## Fetching sub-modules
159 |
160 | This repository has sub-modules that must be fetched.
161 | If cloning for the first time, use:
162 |
163 | ```
164 | git clone --recurse-submodules https://github.com/51Degrees/device-detection-nginx.git
165 | ```
166 |
167 | If you have already cloned the repository and want to fetch the sub modules, use:
168 |
169 | ```
170 | git submodule update --init --recursive
171 | ```
172 |
173 | If you have downloaded this repository as a zip file then these sub modules need to be downloaded separately as GitHub does not include them in the archive. In particular, note that the zip download will contain links to LFS content, rather than the files themselves. As such, these need to be downloaded individually.
174 |
175 | ## Build
176 |
177 | To build 51Degrees Device Detection V4 module, the following libraries are required.
178 | - C compiler that support C11 or above.
179 | - make
180 | - zlib1g-dev
181 | - libpcre3
182 | - libpcre3-dev
183 | - libatomic
184 |
185 | To build the module only, run the following command. This will output to `ngx_http_51D_module.so` in `build/modules` directory.
186 | ```
187 | make module
188 | ```
189 |
190 |
191 | To build the module and Nginx, run the following command.
192 | ```
193 | make install
194 | ```
195 | By default this will use the latest version of Nginx specified in the Makefile. To use a specific version of Nginx, run the above command with `FIFTYONEDEGREES_NGINX_VERSION` variable. e.g.
196 | ```
197 | make install FIFTYONEDEGREES_NGINX_VERSION=[Version]
198 | ```
199 |
200 |
201 | To build and link the module statically with Nginx, use `STATIC_BUILD` variable. e.g.
202 | ```
203 | make install STATIC_BUILD=1
204 | ```
205 |
206 |
207 | The Nginx executable will be output to the project root directory. The prefix path of the Nginx will be set to `build` directory. A `nginx.conf` is also included with a simple example of `51D_match_ua`, `51D_match_ua_client_hints` and `51D_match_all` directives, set in a static content location. The build process has created the static file in the `build/html` folder so you can start Nginx and start sending requests to that static content endpoint.
208 |
209 | ## Test
210 | All tests are allocated in the `tests` folder.
211 |
212 | To be able to test the 51Degrees module the following libraries are required.
213 | - Perl 5 and prove Test::Harness
214 | - CMake 10 or above
215 | - node and jest
216 | - curl
217 | - grep
218 |
219 | Before running any test, make sure to build the project with `make install` command. The tests are designed to test the dynamic build only so `STATIC_BUILD` will not work.
220 |
221 | To run the 51Degrees tests with the default 51Degrees Lite data file included in the `device-detection-cxx\device-detection-data` directory, run the following command:
222 | ```
223 | make test
224 | ```
225 |
226 | To run the 51Degrees together with the Nginx test suite, run the following command:
227 | ```
228 | make test-full
229 | ```
230 |
231 | These do not run all the required tests as some tests requires properties that are not supported with the Lite version of the data file. To run all tests, obtain a data file with support properties JavascriptHardwareProfile,ScreenPixelsWidthJavascript and ScreenPixelsWidth. Then, use the `FIFTYONEDEGREES_DATAFILE` variable to specify the file name to run the tests with. The new data file should be placed in the `device-detection-cxx\device-detection-data` folder and should have different name to `51Degrees-LiteV4.1.hash`. If tests still fail, obtain data file with any other properties used in the tests. Below is an example of running tests with different data file:
232 | ```
233 | make [test|test-full] FIFTYONEDEGREES_DATAFILE=51Degrees-EnterpriseV4.1.hash
234 | ```
235 |
236 | ## Performance Test
237 |
238 | The project also include a performance test suite. This required `CMake 10` or above to be installed.
239 |
240 | To run the performance test, please follow the below steps:
241 | - Make sure to build the project with `make install`.
242 | - Navigate to the `tests/performance` directory.
243 | - Create a `build` folder and navigate to it.
244 | - Run:
245 | ```
246 | cmake ..
247 | cmake --build .
248 | ./runPerf.sh
249 | ```
250 | - This step will build our internal version of `Apache Benchmark` and use it for performance testing.
251 |
252 | # For Developer
253 |
254 | ## Build Options
255 | Run the following to build all current supported versions in `modules` folder:
256 | ```
257 | make all-versions
258 | ```
259 |
260 | ## Test Options
261 | There are number of test options that are recommended for development only.
262 | |Build Option|Description|
263 | |------------|-----------|
264 | |FIFTYONEDEGREES_FORMATTER|This is only available when running with `test` or `test-full` target. This allow to specify the formatter that can be use to format the test report. e.g. `make test FIFTYONEDEGREES_FORMATTER=--formatter TAP::Formatter::Junit`. This example will output a JUnit report and requires the module TAP::Formatter::JUnit to be installed. `cpan` and `cpanm` are recommended to install this module.|
265 | |FIFTYONEDEGREES_TEST_OUTPUT| This specifies the file that the test report should be written to. e.g. `make test FIFTYONEDEGREES_TEST_OUTPUT=test.xml`|
266 |
267 | ## Memory Check
268 | The project can be built with address sanitizer enabled. To do so, run:
269 | ```
270 | make mem-check
271 | ```
272 | The build options mentioned in the [Build](#build) section are also applicable to this `mem-check` rule. However, it has been observed that, in some version of Nginx such 1.19.0 or 1.20.0, the address sanitizer will report memory leak in Nginx without 51Degrees module loaded. Thus, when develop project, make sure that the error is confirmed to not be generated from the 51Degrees module.
273 |
274 | ## Stress Test
275 | The project also include a stress test. This test is inherited from V3 version of 51Degrees project. This test can be run to compared the performance against V3 version and to see how the 51Degrees module perform under heavy load. Note that, in V4 we have constructed a slightly different approach to performance testing so the output from this test is only used as a reference to compare with V3 version. For actually performance test, please follow the instructions in [Performance Test](#performance-test) section.
276 |
277 | To run the stress test, Make sure that the project is built with `make install` and the Apache Benchmark is built as described in the [Performance Test](#performance-test) section, without running `runPerf.sh` script. Then, run the `test.sh` script.
278 | ```
279 | ./test.sh
280 | ```
281 |
282 | ## Examples Test
283 | Before running the tests for the example, make sure to obtain a data file that contains all required properties in the examples.
284 |
285 | Run the example tests with the obtained data file:
286 | ```
287 | make test-examples FIFTYONEDEGREES_DATAFILE=[Obtained data file]
288 | ```
289 |
290 | ### Javascript and overrides example test
291 | The Javascript and overrides example test is designed to be run separately with Selenium using NodeJs. The test is located in `tests/examples/jsExample` directory. To run the test, follow the below steps:
292 | - Make sure to have Chrome, Firefox and Edge installed.
293 | - Download and install their drivers.
294 | - Ensure the browser versions and the drivers versions are aligned.
295 | - Navigate to the directory `tests/examples/jsExample` and run:
296 | ```
297 | npm install
298 | ```
299 | - This will installed all required package including the Selenium web driver.
300 | - Start Nginx with the `javascript.conf` as described in the file itself.
301 | - Run the test
302 | ```
303 | npm test
304 | ```
305 | - Result is output to a file called `cdn_test_results.xml`.
306 | - Stop the Nginx.
307 |
308 | ## Re-certify 51Degrees Device Detection module
309 | When a new release is made to 51Degrees Device Detection module, module maintainer will need to re-certify it the Nginx Plus for target releases.
310 |
311 | To re-certify, follow the "Partner Build, Functional Testing, and Self Certification Process" in the documentation [here](https://www.nginx.com/partners/certified-module-program-documentation/) then submit your results to Nginx to be reviewed by their engineering team.
312 |
313 | Make sure to use the latest versions of [NGINX OSS & Plus](https://docs.nginx.com/nginx/releases/) for re-certification.
314 |
315 | A NGINX Plus dev license might be required for testing.
316 |
--------------------------------------------------------------------------------
/ci/README.md:
--------------------------------------------------------------------------------
1 | # API Specific CI Approach
2 |
3 | For the general CI Approach, see [common-ci](https://github.com/51degrees/common-ci).
4 |
5 | The following secrets are required:
6 | * `ACCESS_TOKEN` - GitHub [access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#about-personal-access-tokens) for cloning repos, creating PRs, etc.
7 | * Example: `github_pat_l0ng_r4nd0m_s7r1ng`
8 |
9 | The following secrets are required to run tests:
10 | * `DEVICE_DETECTION_KEY` - [license key](https://51degrees.com/pricing) for downloading assets (TAC hashes file and TAC CSV data file)
11 | * Example: `V3RYL0NGR4ND0M57R1NG`
12 |
13 | The following secrets are required to run the full set of tests agains NGINX Plus:
14 | * `NGINX_PLUS_KEY` - Signing key used for installing NGINX Plus
15 | * `NGINX_PLUS_CERT` - NGINX Plus certificate
16 |
17 | ### Differences
18 | - There are no packages produced by this repository, so the only output from the `Nightly Publish Main` workflow is a new tag and release.
19 | - The package update step does not update dependencies from a package manager in the same way as other repos. Instead it checks the supported versions on the [NGINX Plus Releases](https://docs.nginx.com/nginx/releases/) page, and uses that to update `options.json` to ensure they are tested.
20 | - As Windows is not supported, the scripts here can be a little less strict with implementation. e.g. using `cp` rather than `Copy-Item` as we know we're in a Linux environment.
21 |
22 | ### Build Options
23 |
24 | The additional build options for this repo are:
25 | | Option | Type | Default | Purpose |
26 | | ------ | --------- | ---- | ------- |
27 | | `NginxVersion` | string | `1.21.3` | The version of the opensource NGINX code to use for building and testing. |
28 | | `MemCheck` | bool | `false` | If true the binaries are built in Debug, with memory checks enabled. |
29 | | `BuildMethod` | string | `dynamic` | Can be either `static` or `dynamic` to determine how the nginx module is built |
30 | | `FullTests` | bool | `false` | If true, then the full NGINX test suite is run. This should be set to true for the latest NGINX Plus version. |
31 |
32 | ## Prerequisites
33 |
34 | In addition to the [common prerequisites](https://github.com/51Degrees/common-ci#prerequisites), the following environment variables are required:
35 | - DEVICE_DETECTION_KEY - License key to download a data file for testing
36 | - DEVICE_DETECTION_URL - Url to download the data file from (optional)
37 | - NGINX_PLUS_KEY - Signing key used for installing NGINX Plus
38 | - NGINX_PLUT_CERT - NGINX Plus certificate
39 |
--------------------------------------------------------------------------------
/ci/build-package-requirements.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [Parameter(Mandatory=$true)]
3 | [string]$RepoName,
4 | [string]$Name,
5 | [string]$NginxVersion
6 | )
7 |
8 | $ModuleName = "ngx_http_51D_module.so"
9 | $RepoPath = [IO.Path]::Combine($pwd, $RepoName)
10 | $OutputDir = [IO.Path]::Combine($pwd, "package-files")
11 | $OutputFile = [IO.Path]::Combine($OutputDir, $ModuleName)
12 |
13 | Write-Output "Entering '$RepoPath'"
14 | Push-Location $RepoPath
15 |
16 | try {
17 |
18 | make install FIFTYONEDEGREES_NGINX_VERSION=$NginxVersion
19 |
20 | # Create a directory for binary files from which they will be uploaded
21 | # as artifacts.
22 | New-Item -Path $OutputDir -ItemType Directory -Force
23 |
24 | $InputFile = [IO.Path]::Combine($RepoPath, "build", "modules", $ModuleName)
25 |
26 | # Copy module
27 | Copy-Item -Path $InputFile -Destination $OutputFile
28 |
29 | }
30 | finally {
31 |
32 | Write-Output "Leaving '$RepoPath'"
33 | Pop-Location
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/ci/build-package.ps1:
--------------------------------------------------------------------------------
1 |
2 | param(
3 | [Parameter(Mandatory=$true)]
4 | [string]$RepoName
5 | )
6 |
7 |
8 |
9 | # Create a directory for binary files from which they will be uploaded
10 | # as artifacts.
11 | New-Item -path "package" -ItemType Directory -Force
12 |
13 | # Copy all modules
14 | Copy-Item -r "package-files/*" "package/"
15 |
--------------------------------------------------------------------------------
/ci/build-project.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [Parameter(Mandatory=$true)]
3 | [string]$RepoName,
4 | [string]$Name = "1.21.3_dynamic",
5 | [string]$NginxVersion = "1.21.3",
6 | [string]$BuildMethod = "dynamic",
7 | [bool]$MemCheck = $False
8 | )
9 |
10 | $RepoPath = [IO.Path]::Combine($pwd, $RepoName)
11 |
12 | Write-Output "Entering '$RepoPath'"
13 | Push-Location $RepoPath
14 |
15 | try {
16 |
17 | # The default command is install
18 | $InstallCommand = "install"
19 | # Set the base build arguments to be added to
20 | $BuildArgs = @("FIFTYONEDEGREES_NGINX_VERSION=$NginxVersion", "FIFTYONEDEGREES_DATAFILE=TAC-HashV41.hash")
21 |
22 | if ($BuildType -eq "static") {
23 | # Add static to the build flags if this configuration is for a static build
24 | Write-Output "Configuring build for a static module"
25 | $BuildArgs += "STATIC_BUILD=1"
26 | }
27 |
28 | if ($MemCheck -eq $True) {
29 | # If this configuration requires a memory check, use mem-check instead of install
30 | Write-Output "Configuring build memomry checks"
31 | $InstallCommand = "mem-check"
32 | }
33 |
34 | Write-Output "Building module"
35 | & make $InstallCommand $BuildArgs
36 |
37 | }
38 | finally {
39 |
40 | Write-Output "Leaving '$RepoPath'"
41 | Pop-Location
42 |
43 | }
--------------------------------------------------------------------------------
/ci/fetch-assets.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [Parameter(Mandatory=$true)]
3 | [string]$RepoName,
4 | [Parameter(Mandatory=$true)]
5 | [string]$DeviceDetection,
6 | [string]$DeviceDetectionUrl,
7 | [Parameter(Mandatory=$true)]
8 | [string]$NginxKey,
9 | [Parameter(Mandatory=$true)]
10 | [string]$NginxCert,
11 | [Parameter(Mandatory=$true)]
12 | [string]$NginxJwtToken
13 | )
14 |
15 | $RepoPath = [IO.Path]::Combine($pwd, $RepoName)
16 |
17 | # Get the TAC data file for testing
18 | Write-Output "Downloading Hash data file"
19 | ./steps/fetch-hash-assets.ps1 -RepoName $RepoName -LicenseKey $DeviceDetection -Url $DeviceDetectionUrl
20 |
21 | # And move it to the correct directory
22 | Write-Output "Moving Hash data file"
23 | Move-Item $RepoPath/TAC-HashV41.hash $RepoPath/device-detection-cxx/device-detection-data/TAC-HashV41.hash
24 |
25 | # Write the key and certificate files for NGINX Plus so it can be installed.
26 | Write-Output "Writing NGINX Plus repo key"
27 | Write-Output $NginxKey >> $RepoPath/nginx-repo.key
28 | Write-Output "Writing NGINX Plus repo certificate"
29 | Write-Output $NginxCert >> $RepoPath/nginx-repo.crt
30 | Write-Output "Writing NGINX Plus Jwt token"
31 | Write-Output $NginxJwtToken >> $RepoPath/license.jwt
32 |
33 | # Pull the evidence files for testing as they are not by default.
34 | $DataFileDir = [IO.Path]::Combine($RepoPath, "device-detection-cxx", "device-detection-data")
35 | Push-Location $DataFileDir
36 | try {
37 | Write-Output "Pulling evidence files"
38 | git lfs pull -I "*.csv"
39 | git lfs pull -I "*.yml"
40 | }
41 | finally {
42 | Pop-Location
43 | }
44 |
--------------------------------------------------------------------------------
/ci/get-next-package-version.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [Parameter(Mandatory)][string]$RepoName,
3 | [string]$VariableName = "Version"
4 | )
5 | $ErrorActionPreference = "Stop"
6 |
7 | Set-Variable -Scope Global -Name $VariableName -Value (./steps/get-next-package-version.ps1 -RepoName $RepoName)
8 |
--------------------------------------------------------------------------------
/ci/install-package.ps1:
--------------------------------------------------------------------------------
1 |
2 | param(
3 | [Parameter(Mandatory=$true)]
4 | [string]$RepoName,
5 | [string]$Name = "",
6 | [string]$NginxVersion = ""
7 | )
8 |
9 | # Combine the current working directory with the repository name
10 | $RepoPath = [IO.Path]::Combine($pwd, $RepoName)
11 |
12 | $ModuleName = "ngx_http_51D_module.so"
13 |
14 | # Define the path for downloaded artifacts
15 | $PackagePath = [IO.Path]::Combine($pwd, "package", "package_$Name", $ModuleName)
16 |
17 | # Get the local install path
18 | $ModulesPath = [IO.Path]::Combine($RepoPath, "build", "modules")
19 | $InstallPath = [IO.Path]::Combine($ModulesPath, $ModuleName)
20 |
21 | # Display the repository path and enter it
22 | Write-Output "Entering '$RepoPath'"
23 | Push-Location $RepoPath
24 |
25 | try {
26 |
27 | if ($Name -ne "") {
28 |
29 | # Install NGINX without building the module.
30 | Write-Output "Install NGINX '$NginxVersion' without 51Degrees module"
31 | make install-no-module FIFTYONEDEGREES_NGINX_VERSION=$NginxVersion
32 |
33 | # Now copy the module to the installs module directory
34 | Write-Output "Copying the 51Degrees module from '$PackagePath' to '$InstallPath'"
35 | mkdir $ModulesPath
36 | Copy-Item -Path $PackagePath -Destination $InstallPath
37 |
38 | }
39 | else {
40 |
41 | Write-Output "Not installing locally, as no NGINX version is specified."
42 |
43 | }
44 | }
45 | finally {
46 |
47 | # Leave the repository path and display it
48 | Write-Output "Leaving '$RepoPath'"
49 | Pop-Location
50 |
51 | }
52 |
53 | # Exit the script with the last exit code
54 | exit $LASTEXITCODE
55 |
--------------------------------------------------------------------------------
/ci/options.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Image": "ubuntu-latest",
4 | "Name": "ubuntu-latest_Nginx1.27.4",
5 | "NginxVersion": "1.27.4",
6 | "NginxPlusVersion": "34",
7 | "RunPerformance": true,
8 | "FullTests": true,
9 | "PackageRequirement": true
10 | },
11 | {
12 | "Image": "ubuntu-latest",
13 | "Name": "ubuntu-latest_Nginx1.27.4_MemCheck",
14 | "NginxVersion": "1.27.4",
15 | "MemCheck": true
16 | },
17 | {
18 | "Image": "ubuntu-latest",
19 | "Name": "ubuntu-latest_Nginx1.27.4_MemCheck_Static",
20 | "NginxVersion": "1.27.4",
21 | "BuildMethod": "static",
22 | "MemCheck": true
23 | },
24 | {
25 | "Image": "ubuntu-latest",
26 | "Name": "ubuntu-latest_Nginx1.27.2",
27 | "NginxVersion": "1.27.2",
28 | "NginxPlusVersion": "33",
29 | "RunPerformance": false,
30 | "FullTests": false,
31 | "PackageRequirement": true
32 | },
33 | {
34 | "Image": "ubuntu-latest",
35 | "Name": "ubuntu-latest_Nginx1.25.5",
36 | "NginxVersion": "1.25.5",
37 | "NginxPlusVersion": "32",
38 | "RunPerformance": false,
39 | "FullTests": false,
40 | "PackageRequirement": true
41 | },
42 | {
43 | "Image": "ubuntu-latest",
44 | "Name": "ubuntu-latest_Nginx1.25.3",
45 | "NginxVersion": "1.25.3",
46 | "NginxPlusVersion": "31",
47 | "RunPerformance": false,
48 | "FullTests": false,
49 | "PackageRequirement": true
50 | },
51 | {
52 | "Image": "ubuntu-latest",
53 | "Name": "ubuntu-latest_Nginx1.25.1",
54 | "NginxVersion": "1.25.1",
55 | "NginxPlusVersion": "30",
56 | "RunPerformance": false,
57 | "FullTests": false,
58 | "PackageRequirement": true
59 | },
60 | {
61 | "Image": "ubuntu-latest",
62 | "Name": "ubuntu-latest_Nginx1.23.4",
63 | "NginxVersion": "1.23.4",
64 | "NginxPlusVersion": "29",
65 | "RunPerformance": false,
66 | "FullTests": false,
67 | "PackageRequirement": true
68 | }
69 | ]
70 |
--------------------------------------------------------------------------------
/ci/publish-package.ps1:
--------------------------------------------------------------------------------
1 | Write-Output "No packages to publish, just update the tag"
2 |
--------------------------------------------------------------------------------
/ci/run-integration-tests.ps1:
--------------------------------------------------------------------------------
1 |
2 | param(
3 | [Parameter(Mandatory=$true)]
4 | [string]$RepoName,
5 | [string]$Name = "1.21.3_dynamic",
6 | [string]$NginxVersion = "1.21.3",
7 | [string]$BuildMethod = "dynamic"
8 | )
9 |
10 | $RepoPath = [IO.Path]::Combine($pwd, $RepoName)
11 |
12 | Write-Output "Entering '$RepoPath'"
13 | Push-Location $RepoPath
14 |
15 | try {
16 |
17 | # Create the output directories if they don't already exist.
18 | if ($(Test-Path -Path "test-results") -eq $False) {
19 | mkdir test-results
20 | }
21 | if ($(Test-Path -Path "test-results/integration") -eq $False) {
22 | mkdir test-results/integration
23 | }
24 |
25 | # Disable leak checks here, as they are for the unit tests.
26 | $env:ASAN_OPTIONS="detect_odr_violation=0:detect_leaks=0"
27 |
28 | Write-Output "Setting up the CDN example"
29 | # Copy the example config
30 | Copy-Item examples/hash/jsExample/javascript.conf build/nginx.conf
31 | # Set the data file
32 | sed -i "s/51Degrees-LiteV4\.1\.hash/TAC-HashV41\.hash/g" build/nginx.conf
33 | # Remove the documentation block
34 | sed -i "/\/\*\*/,/\*\//d" build/nginx.conf
35 | # Start NGINX
36 | ./nginx
37 |
38 | # Run the selenium tests
39 | $JsExamplePath = [IO.Path]::Combine($RepoPath, "tests", "examples", "jsExample")
40 | Write-Output "Entering '$JsExamplePath'"
41 | Push-Location $JsExamplePath
42 |
43 | try {
44 | Write-Output "Running CDN tests"
45 | $env:PATH="$($env:PATH):$pwd/driver"
46 | npm install
47 | npx browserslist@latest --update-db
48 | npm test
49 | }
50 | finally {
51 |
52 | Write-Output "Leaving '$JsExamplePath'"
53 | Pop-Location
54 | Write-Output "Stopping NGINX"
55 | ./nginx -s quit
56 | if ($LASTEXITCODE -ne 0 ) {
57 | Write-Error "Failed to exit Nginx"
58 | exit 1
59 | }
60 |
61 | }
62 |
63 | # Test the examples
64 | Write-Output "Testing examples"
65 | make test-examples FIFTYONEDEGREES_DATAFILE=TAC-HashV41.hash FIFTYONEDEGREES_FORMATTER='--formatter TAP::Formatter::JUnit' FIFTYONEDEGREES_TEST_OUTPUT=$RepoPath/test-results/integration/$($Name)_Examples.xml
66 |
67 | }
68 | finally {
69 |
70 | Write-Output "Leaving '$RepoPath'"
71 | Pop-Location
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/ci/run-performance-tests.ps1:
--------------------------------------------------------------------------------
1 |
2 | param(
3 | [Parameter(Mandatory=$true)]
4 | [string]$RepoName,
5 | [string]$Name = "1.21.3_dynamic",
6 | [string]$NginxVersion = "1.21.3",
7 | [string]$BuildMethod = "dynamic"
8 | )
9 |
10 | $RepoPath = [IO.Path]::Combine($pwd, $RepoName)
11 | $PerfPath = [IO.Path]::Combine($RepoPath, "tests", "performance")
12 | $PerfResultsFile = [IO.Path]::Combine($RepoPath, "test-results", "performance-summary", "results_$Name.json")
13 |
14 | Write-Output "Entering '$RepoPath'"
15 | Push-Location $RepoPath
16 |
17 | try {
18 |
19 | # Create the output directories if they don't already exist.
20 | if ($(Test-Path -Path "test-results") -eq $False) {
21 | mkdir test-results
22 | }
23 | if ($(Test-Path -Path "test-results/performance") -eq $False) {
24 | mkdir test-results/performance
25 | }
26 | if ($(Test-Path -Path "test-results/performance-summary") -eq $False) {
27 | mkdir test-results/performance-summary
28 | }
29 |
30 | }
31 | finally {
32 |
33 | Write-Output "Leaving '$RepoPath'"
34 | Pop-Location
35 |
36 | }
37 |
38 | Write-Output "Entering '$PerfPath'"
39 | Push-Location $PerfPath
40 |
41 | try {
42 | mkdir build
43 | Push-Location build
44 | try {
45 |
46 | # Build the performance tests
47 | Write-Output "Building performance test"
48 | cmake ..
49 | cmake --build .
50 |
51 | # When running the performance tests, set the data file name manually,
52 | # then unset once we're done
53 | Write-Output "Running performance test"
54 | $env:DATA_FILE_NAME="TAC-HashV41.hash"
55 | ./runPerf.sh
56 | $env:DATA_FILE_NAME=$Null
57 |
58 | # Write out the results for comparison
59 | Write-Output "Writing performance test results"
60 | $Results = Get-Content ./summary.json | ConvertFrom-Json
61 | Write-Output "{
62 | 'HigherIsBetter': {
63 | 'DetectionsPerSecond': $(1/($Results.overhead_ms / 1000))
64 | },
65 | 'LowerIsBetter': {
66 | 'MsPerDetection': $($Results.overhead_ms)
67 | }
68 | }" > $PerfResultsFile
69 |
70 | }
71 | finally {
72 |
73 | Write-Output "Leaving build"
74 | Pop-Location
75 |
76 | }
77 | }
78 | finally {
79 |
80 | Write-Output "Leaving '$PerfPath'"
81 | Pop-Location
82 |
83 | }
84 |
85 |
86 | Write-Output "Entering '$RepoPath'"
87 |
88 | Push-Location $RepoPath
89 |
90 | try {
91 |
92 | # Run the stress test script. ApacheBench will already be built from the previous test
93 | Write-Output "Run stress tests using ApacheBench"
94 | if (Test-Path -Path "tests/performance/build/ApacheBench-prefix/src/ApacheBench-build/bin/ab") {
95 | $env:DATA_FILE_NAME="TAC-HashV41.hash"
96 | ./test.sh
97 | $env:DATA_FILE_NAME=$Null
98 | }
99 | else {
100 | Write-Error 'The performance test need to be build first.'
101 | exit 1
102 | }
103 |
104 | }
105 | finally {
106 |
107 | Write-Output "Leaving '$RepoPath'"
108 | Pop-Location
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/ci/run-unit-tests.ps1:
--------------------------------------------------------------------------------
1 |
2 | param(
3 | [Parameter(Mandatory=$true)]
4 | [string]$RepoName,
5 | [string]$Name = "1.21.3_dynamic",
6 | [string]$NginxVersion = "1.21.3",
7 | [string]$BuildMethod = "dynamic",
8 | [bool]$FullTests = $False,
9 | [bool]$MemCheck = $False
10 | )
11 |
12 | $RepoPath = [IO.Path]::Combine($pwd, $RepoName)
13 |
14 | Write-Output "Entering '$RepoPath'"
15 | Push-Location $RepoPath
16 |
17 | try {
18 |
19 | # Create the output directories if they don't exist already.
20 | if ($(Test-Path -Path "test-results") -eq $False) {
21 | mkdir test-results
22 | }
23 | if ($(Test-Path -Path "test-results/unit") -eq $False) {
24 | mkdir test-results/unit
25 | }
26 |
27 | Write-Output "Running 51Degrees unit tests"
28 | make test FIFTYONEDEGREES_DATAFILE=TAC-HashV41.hash FIFTYONEDEGREES_FORMATTER='--formatter TAP::Formatter::JUnit' FIFTYONEDEGREES_TEST_OUTPUT=$RepoPath/test-results/unit/$Name.xml
29 | # Keep track of failed tests
30 | $Passed = $($LASTEXITCODE -eq 0)
31 | # Output the full results file, as some bits are not reported in GitHub
32 | cat $RepoPath/test-results/unit/$Name.xml
33 |
34 | if ($FullTests -eq $True) {
35 |
36 | # If full tests are requested, then run the full test suite (NGINX core tests, with the module enabled)
37 | Write-Output "Running full NGINX unit tests"
38 | make test-full FIFTYONEDEGREES_DATAFILE=TAC-HashV41.hash FIFTYONEDEGREES_FORMATTER='--formatter TAP::Formatter::JUnit' FIFTYONEDEGREES_TEST_OUTPUT=$RepoPath/test-results/unit/$($Name)_Full.xml
39 | $Passed = $($Passed -and $LASTEXITCODE -eq 0)
40 |
41 | # Now do the same with the NGINX Plus executable
42 | Write-Output "Running full NGINX unit tests against NGINX Plus"
43 |
44 | # For mgmt module we need to set permissions to nginx-mgmt-state
45 | sudo chmod -R a+xrw /var/lib
46 |
47 | # As we're not using the makefile, we need to set the environment variables ourselves, and add common path to license_token
48 | $env:TEST_NGINX_BINARY="/usr/sbin/nginx"
49 | $env:TEST_NGINX_GLOBALS="mgmt {license_token /etc/nginx/license.jwt;} load_module $RepoPath/build/modules/ngx_http_51D_module.so;"
50 | $env:TEST_NGINX_GLOBALS_HTTP="51D_file_path $RepoPath/device-detection-cxx/device-detection-data/TAC-HashV41.hash;"
51 | prove --formatter TAP::Formatter::JUnit -v tests/51degrees.t tests/nginx-tests :: TAC-HashV41.hash > $RepoPath/test-results/unit/$($Name)_Full_Plus.xml
52 | $Passed = $($Passed -and $LASTEXITCODE -eq 0)
53 | # Unset the environment variables for future tests.
54 | $env:TEST_NGINX_BINARY=""
55 | $env:TEST_NGINX_GLOBALS=""
56 | $env:TEST_NGINX_GLOBALS_HTTP=""
57 |
58 | }
59 |
60 | }
61 | finally {
62 |
63 | Write-Output "Leaving '$RepoPath'"
64 | Pop-Location
65 |
66 | }
67 |
68 | if ($Passed) {
69 | exit 0
70 | }
71 | else {
72 | exit 1
73 | }
74 |
--------------------------------------------------------------------------------
/ci/setup-environment.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [Parameter(Mandatory=$true)]
3 | [string]$RepoName,
4 | [string]$Name = "1.21.3 dynamic",
5 | [string]$NginxVersion = "1.21.3",
6 | [string]$NginxPlusVersion = "25",
7 | [bool]$FullTests = $False,
8 | [string]$BuildMethod = "dynamic"
9 | )
10 |
11 | $RepoPath = [IO.Path]::Combine($pwd, $RepoName)
12 | $TestsPath = [IO.Path]::Combine($RepoPath, "tests")
13 | $JsExamplePath = [IO.Path]::Combine($RepoPath, "tests", "examples", "jsExample")
14 |
15 | # Install perl libraries
16 | Write-Output 'Install perl libraries for test output formatters'
17 | # cpan will ask for auto configuration first time it is run so answer 'yes'.
18 | Write-Output y | sudo cpan
19 | sudo cpan App::cpanminus --notest
20 | sudo cpanm --force TAP::Formatter::JUnit --notest
21 |
22 | # Get the version of nginx-test to use. Where new tests have been introduced, previous versions
23 | # of NGINX do not always pass them
24 | $ParsedVersion = [System.Version]::Parse($NginxVersion)
25 | if ($ParsedVersion -le [System.Version]::Parse("1.19.5")) {
26 | $TestCommit = "6bf30e564c06b404876f0bd44ace8431b3541f24"
27 | }
28 | elseif ($ParsedVersion -le [System.Version]::Parse("1.23.2")) {
29 | $TestCommit = "3356f91a3fdae372d0946e4c89a9413f558c8017"
30 | }
31 | else {
32 | $TestCommit = $Null
33 | }
34 |
35 |
36 | # Download the nginx-tests repository
37 | Write-Output "Moving into $TestsPath"
38 | Push-Location $TestsPath
39 | try {
40 | Write-Output "Clean any existing nginx-tests folder."
41 | if (Test-Path -Path nginx-tests) {
42 | Remove-Item nginx-tests -Recurse -Force
43 | }
44 |
45 | Write-Output "Clone the nginx-tests source."
46 | git clone https://github.com/nginx/nginx-tests.git
47 |
48 | Write-Output "Moving into nginx-tests."
49 | Push-Location nginx-tests
50 |
51 | try {
52 | if ($Null -ne $TestCommit) {
53 | Write-Output "Checkout to before the breaking changes for this version."
54 | git reset --hard $TestCommit
55 | }
56 | }
57 | finally {
58 | Pop-Location
59 | }
60 | }
61 | finally {
62 | Pop-Location
63 | }
64 |
65 | # If full tests are requested, then we'll need to install NGINX Plus
66 | if ($FullTests -eq $True) {
67 | Write-Output "Uninstall existing Nginx"
68 | sudo apt-get purge nginx -y
69 |
70 | Write-Output "Create ssl directory for Nginx Plus"
71 | sudo mkdir -p /etc/ssl/nginx
72 | sudo mkdir -p /etc/nginx
73 |
74 | Write-Output "Copy the nginx-repo.* file to the created directory"
75 | sudo cp $([IO.Path]::Combine($RepoPath, "nginx-repo.key")) /etc/ssl/nginx
76 | sudo cp $([IO.Path]::Combine($RepoPath, "nginx-repo.crt")) /etc/ssl/nginx
77 | sudo cp $([IO.Path]::Combine($RepoPath, "license.jwt")) /etc/nginx
78 |
79 | Write-Output "Setting permissions for the certificates"
80 | sudo chown -R root:root /etc/ssl/nginx
81 | sudo chmod -R a+r /etc/ssl/nginx
82 | sudo chmod -R a+r /etc/nginx
83 |
84 | ls -l /etc/ssl
85 | ls -l /etc/ssl/nginx
86 | ls -l /etc/nginx
87 |
88 | Write-Output "Certificate expiration date:"
89 | openssl x509 -enddate -noout -in /etc/ssl/nginx/nginx-repo.crt
90 |
91 | Write-Output "Download and add NGINX signing key and App-protect security updates signing key:"
92 | curl -O https://nginx.org/keys/nginx_signing.key && sudo apt-key add ./nginx_signing.key
93 |
94 | Write-Output "Install apt utils"
95 | sudo apt-get install apt-transport-https lsb-release ca-certificates wget gnupg2 ubuntu-keyring
96 |
97 | Write-Output "Add Nginx Plus repository"
98 | # Add allow-insecure because there is an issue with the signing of the NGINX Plus repository.
99 | printf "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg allow-insecure=yes] https://pkgs.nginx.com/plus/ubuntu $(lsb_release -cs) nginx-plus\n" | sudo tee /etc/apt/sources.list.d/nginx-plus.list
100 |
101 | Write-Output "Download nginx-plus apt configuration files to /etc/apt/apt.conf.d"
102 | sudo wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx
103 |
104 | Write-Output "Update the repository and install Nginx Plus"
105 | sudo apt-get update
106 | $packageVersion = ((apt-cache show nginx-plus | Select-String -CaseSensitive '^Version: ') -replace 'Version: ' -clike "$NginxPlusVersion-*")[0]
107 | sudo apt-get install nginx-plus=$packageVersion -y --allow-unauthenticated
108 |
109 | Write-Output "Check if installation was successful"
110 | /usr/sbin/nginx -v 2>&1 | grep -F $NginxVersion || $(throw "Failed to install Nginx Plus $NginxVersion")
111 | }
112 |
113 | Write-Output "Install Apache dev for performance tests"
114 | sudo apt-get update -y
115 | sudo apt-get install cmake apache2-dev libapr1-dev libaprutil1-dev -y
116 |
117 | # Install any extra requirements for the module
118 | Write-Output "Installing NGINX dependencies"
119 | sudo apt-get install make zlib1g-dev libpcre3 libpcre3-dev
120 |
121 |
--------------------------------------------------------------------------------
/ci/update-packages.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [Parameter(Mandatory)][string]$RepoName,
3 | [string[]]$Images = "ubuntu-latest"
4 | )
5 | $ErrorActionPreference = "Stop"
6 |
7 | Write-Host "Fetching releases document..."
8 | $req = Invoke-WebRequest "https://raw.githubusercontent.com/nginx/documentation/refs/heads/main/content/nginx/releases.md"
9 |
10 | Write-Host "Parsing the table of supported releases..."
11 | $tableStart = $req.Content.IndexOf("| NGINX Plus Release |")
12 | $tableEnd = $req.Content.IndexOf("`n`n", $tableStart)
13 | $supportedReleases = ([regex]'(?m)^\| R(\d+)').Matches($req.Content.Substring($tableStart, $tableEnd-$tableStart)) | ForEach-Object { $_.Groups[1].Value }
14 | Write-Host "Supported NGINX Plus releases: $supportedReleases"
15 |
16 | Write-Host "Searching for corresponding Open Source versions of each release..."
17 | $releaseRegex = [regex]'(?mx) # enable multiline mode and allow comments/spaces
18 | ^\#\#\sNGINX\sPlus\sRelease\s(\d+)\s\(R\d+\) \n # \s is required to match non-breaking spaces that the document uses
19 | .* \n # discard release date since we know supported versions from the table
20 | _?Based\son\sNGINX\sOpen\sSource\s([0-9.]+)
21 | '
22 | $openSourceOf = @{}
23 | $releaseRegex.Matches($req.Content) | ForEach-Object { $openSourceOf[$_.Groups[1].Value] = $_.Groups[2].Value }
24 |
25 | Write-Host "Building options array..."
26 | [Collections.ArrayList]$options = @()
27 | foreach ($release in $supportedReleases) {
28 | $isLatest = $options.Count -lt 1
29 | foreach ($image in $Images) {
30 | [void]$options.Add([ordered]@{
31 | Image = $image
32 | Name = "$($image)_Nginx$($openSourceOf[$release])"
33 | NginxVersion = $openSourceOf[$release]
34 | NginxPlusVersion = $release
35 | RunPerformance = $IsLatest # Only run performance if this is the latest NGINX Plus version
36 | FullTests = $IsLatest # Only run the full NGINX test suite if this is the latest NGINX Plus version
37 | PackageRequirement = $True
38 | })
39 | if ($isLatest) {
40 | # Add memory check configurations if this is the latest NGINX Plus version
41 | [void]$options.Add([ordered]@{
42 | Image = $image
43 | Name = "$($image)_Nginx$($openSourceOf[$release])_MemCheck"
44 | NginxVersion = $openSourceOf[$release]
45 | MemCheck = $True
46 | })
47 | [void]$options.Add([ordered]@{
48 | Image = $image
49 | Name = "$($image)_Nginx$($openSourceOf[$release])_MemCheck_Static"
50 | NginxVersion = $openSourceOf[$release]
51 | BuildMethod = "static"
52 | MemCheck = $True
53 | })
54 | }
55 | }
56 | }
57 |
58 | Write-Host "Writing options file to '$RepoName/ci/options.json'"
59 | $Options | ConvertTo-Json > "$RepoName/ci/options.json"
60 | Get-Content "$RepoName/ci/options.json"
61 |
--------------------------------------------------------------------------------
/docs/DoxygenLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
--------------------------------------------------------------------------------
/examples/curl/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:12-slim
2 | WORKDIR /app
3 |
4 | #build dependencies
5 | RUN apt-get update && apt-get install --no-install-recommends -y git curl ca-certificates build-essential libpcre3-dev libz-dev nano && rm -rf /var/lib/apt/lists/*
6 |
7 | #cloning the current repo
8 | RUN git clone https://github.com/51Degrees/device-detection-nginx . && git submodule update --init --recursive
9 |
10 | #dev dependencies
11 | RUN curl -Lo device-detection-cxx/device-detection-data/51Degrees-LiteV4.1.hash https://github.com/51Degrees/device-detection-data/raw/main/51Degrees-LiteV4.1.hash
12 |
13 | RUN make install
14 |
15 | COPY nginx.conf /app/build/nginx.conf
16 |
17 | EXPOSE 8080
18 |
19 | CMD ["/app/nginx", "-c", "/app/build/nginx.conf"]
20 |
--------------------------------------------------------------------------------
/examples/curl/README.md:
--------------------------------------------------------------------------------
1 | # cURL Example
2 |
3 | This example demonstrates how to feed arbitrary evidence in the form of headers to an nginx module using cURL.
4 |
5 | ## Build
6 |
7 | There is a docker image with nginx and 51d module - convenient for testing on a mac.
8 |
9 | You can just run `./run.sh` from this directory or `docker` commands similar to:
10 |
11 | ```sh
12 | docker build -t 51d-nginx-curl .
13 | docker run -d -p 8080:8080 51d-nginx-curl
14 | ```
15 |
16 | ## Run
17 |
18 | Use the `curl.sh` script or just plain curl command (the evidence provided may be arbitrary):
19 | ```
20 | $ curl --head \
21 | -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.41' \
22 | -H 'Sec-CH-UA: "Not.A/Brand";v="8","Chromium";v="114","Microsoft Edge";v="114"' \
23 | -H 'Sec-CH-UA-Arch: x86' \
24 | -H 'Sec-CH-UA-Bitness: 64' \
25 | -H 'Sec-CH-UA-Full-Version: 114.0.5735.110' \
26 | -H 'Sec-CH-UA-Full-Version-List: "Not.A/Brand";v="8.0.0.0","Chromium";v="114.0.5735.110","Microsoft Edge";v="114.0.1823.41"' \
27 | -H 'Sec-CH-UA-Mobile: ?0' \
28 | -H 'Sec-CH-UA-Model: ' \
29 | -H 'Sec-CH-UA-Platform: Windows' \
30 | -H 'Sec-CH-UA-Platform-Version: 15.0.0' \
31 | http://127.0.0.1:8080/hash
32 |
33 | HTTP/1.1 200 OK
34 | Server: nginx/1.20.0
35 | Date: Wed, 16 Aug 2023 16:03:00 GMT
36 | Content-Type: text/plain
37 | Content-Length: 1
38 | Last-Modified: Wed, 16 Aug 2023 13:41:12 GMT
39 | Connection: keep-alive
40 | ETag: "64dcd1f8-1"
41 | x-device-type: NoMatch
42 | x-platform-name: Windows
43 | x-platform-version: 11.0
44 | x-browser-name: Edge (Chromium) for Windows
45 | x-browser-version: 114.0
46 | x-is-mobile: False
47 | Accept-Ranges: bytes
48 | ```
49 |
50 | To test feeding the evidence via the PHP libcurl - use `curl.php` script:
51 |
52 | ```
53 | $ php examples/curl/curl.php
54 | HTTP/1.1 200 OK
55 | Server: nginx/1.20.0
56 | Date: Wed, 16 Aug 2023 16:01:19 GMT
57 | Content-Type: text/plain
58 | Content-Length: 1
59 | Last-Modified: Wed, 16 Aug 2023 13:41:12 GMT
60 | Connection: keep-alive
61 | ETag: "64dcd1f8-1"
62 | x-device-type: NoMatch
63 | x-platform-name: Windows
64 | x-platform-version: 11.0
65 | x-browser-name: Edge (Chromium) for Windows
66 | x-browser-version: 114.0
67 | x-is-mobile: False
68 | Accept-Ranges: bytes
69 | ```
--------------------------------------------------------------------------------
/examples/curl/curl.php:
--------------------------------------------------------------------------------
1 | $value) {
5 | echo "$header: $value \n";
6 | }
7 | ?>
8 |
--------------------------------------------------------------------------------
/examples/hash/config.conf:
--------------------------------------------------------------------------------
1 | /**
2 | @example hash/config.conf
3 |
4 | This example shows how to configure the available settings for 51Degrees'
5 | on-premise device detection in Nginx. This example is available in full
6 | on [GitHub](https://github.com/51Degrees/device-detection-nginx/blob/master/examples/hash/config.conf).
7 |
8 | @include{doc} example-require-datafile.txt
9 |
10 | Make sure to include at least IsMobile, BrowserName and PlatformName properties
11 | for this to work.
12 |
13 | Before using the example, update the followings:
14 | - Remove this 'how to' guide block.
15 | - Update the %%%DAEMON_MODE%% to 'on' or 'off'.
16 | - Remove the %%%TEST_GLOBALS%%.
17 | - Update the %%%MODULE_PATH%% with the actual path.
18 | - Remove the %%%TEST_GLOBALS_HTTP%%.
19 | - Update the %%%FILE_PATH%% with the actual file path.
20 | - Replace the nginx.conf with this file or run Nginx with `-c`
21 | option pointing to this file.
22 | - Create a static file `config` in the Nginx `document_root`.
23 |
24 | In a Linux environment, once Nginx has started, run the following command:
25 | ```
26 | $ curl localhost:8080/config -I -A "Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D167 Safari/9537.53"
27 | ```
28 | Expected output:
29 | ```
30 | HTTP/1.1 200 OK
31 | ...
32 | x-device: Mobile Safari^sep^iOS^sep^PERFORMANCE
33 | x-mobile: True
34 | ...
35 | ```
36 |
37 | `NOTE`: All the lines above, this line and the end of comment block line after
38 | this line should be removed before using this example.
39 | */
40 |
41 | ## Replace the DAEMON_MODE with 'on' or 'off'. ##
42 | daemon %%DAEMON_MODE%%;
43 | worker_processes 4;
44 |
45 | ## The following line is required for testing. Remove before ##
46 | ## running with Nginx. ##
47 | %%TEST_GLOBALS%%
48 | ## Update the MODULE_PATH before running with Nginx ##
49 | load_module %%MODULE_PATH%%modules/ngx_http_51D_module.so;
50 |
51 | events {
52 | worker_connections 1024;
53 | }
54 |
55 | # // Snippet Start
56 | http {
57 | ## The following line is required for testing. Remove before ##
58 | ## running with Nginx. ##
59 | %%TEST_GLOBALS_HTTP%%
60 | ## Set the data file for the 51Degrees module to use. ##
61 | ## Update the FILE_PATH before running with Nginx. ##
62 | 51D_file_path %%FILE_PATH%%;
63 | 51D_value_separator ^sep^;
64 | 51D_drift 3;
65 | 51D_difference 5;
66 | 51D_allow_unmatched on;
67 |
68 | server {
69 | listen 127.0.0.1:8080;
70 | server_name localhost;
71 |
72 | location /config {
73 | ## Do a single User-Agent match for device information ##
74 | 51D_match_ua x-device BrowserName,PlatformName,Method;
75 |
76 | ## Do a multiple HTTP header match for IsMobile ##
77 | 51D_match_all x-mobile IsMobile;
78 |
79 | ## Add to response headers for easy viewing. ##
80 | add_header x-device $http_x_device;
81 | add_header x-mobile $http_x_mobile;
82 | }
83 | }
84 | }
85 | # // Snippet End
86 |
--------------------------------------------------------------------------------
/examples/hash/gettingStarted.conf:
--------------------------------------------------------------------------------
1 | /**
2 | @example hash/gettingStarted.conf
3 |
4 | This example shows how to quickly start using 51Degrees' on-premise device
5 | detection in Nginx. This example is available in full on [GitHub](
6 | https://github.com/51Degrees/device-detection-nginx/blob/master/examples/hash/gettingStarted.conf).
7 |
8 | @include{doc} example-require-datafile.txt
9 |
10 | Make sure to include at least IsMobile, BrowserName and PlatformName properties
11 | for this to work.
12 |
13 | Before using the example, update the followings:
14 | - Remove this 'how to' guide block.
15 | - Update the %%%DAEMON_MODE%% to 'on' or 'off'.
16 | - Remove the %%%TEST_GLOBALS%%.
17 | - Update the %%%MODULE_PATH%% with the actual path.
18 | - Remove the %%%TEST_GLOBALS_HTTP%%.
19 | - Update the %%%FILE_PATH%% with the actual file path.
20 | - Replace the nginx.conf with this file or run Nginx with `-c`
21 | option pointing to this file.
22 | - Create a static file `hash` in the Nginx `document_root`.
23 |
24 | In a Linux environment, once Nginx has started, run the following command:
25 | ```
26 | $ curl localhost:8080/hash -I -A "Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D167 Safari/9537.53"
27 | ```
28 | Expected output:
29 | ```
30 | HTTP/1.1 200 OK
31 | ...
32 | x-device: Mobile Safari,iOS
33 | x-mobile: True
34 | ...
35 | ```
36 |
37 | `NOTE`: All the lines above, this line and the end of comment block line after
38 | this line should be removed before using this example.
39 | */
40 |
41 | ## Replace DAEMON_MODE with 'on' or 'off' before running ##
42 | ## with Nginx. ##
43 | daemon %%DAEMON_MODE%%;
44 | worker_processes 4;
45 |
46 | ## The following line is required for testing. Remove before ##
47 | ## running with Nginx. ##
48 | %%TEST_GLOBALS%%
49 | ## Update MODULE_PATH before running with Nginx ##
50 | load_module %%MODULE_PATH%%modules/ngx_http_51D_module.so;
51 |
52 | events {
53 | worker_connections 1024;
54 | }
55 |
56 | # // Snippet Start
57 | http {
58 | ## The following line is required for testing. Remove before ##
59 | ## running with Nginx. ##
60 | %%TEST_GLOBALS_HTTP%%
61 | ## Set the data file for the 51Degrees module to use. ##
62 | ## Update the FILE_PATH before running with Nginx. ##
63 | 51D_file_path %%FILE_PATH%%;
64 |
65 | server {
66 | listen 127.0.0.1:8080;
67 | server_name localhost;
68 |
69 | location /hash {
70 | ## Do a single User-Agent match for device information ##
71 | 51D_match_ua x-device BrowserName,PlatformName;
72 |
73 | ## Do a User-Agent + Client-Hints match for device information ##
74 | 51D_match_ua_client_hints x-device-hinted BrowserName,PlatformName;
75 |
76 | ## Do a multiple HTTP header match for IsMobile ##
77 | 51D_match_all x-mobile IsMobile;
78 |
79 | ## Add to response headers for easy viewing. ##
80 | add_header x-device $http_x_device;
81 | add_header x-device-hinted $http_x_device_hinted;
82 | add_header x-mobile $http_x_mobile;
83 | }
84 | }
85 | }
86 | # // Snippet End
87 |
--------------------------------------------------------------------------------
/examples/hash/jsExample/index.html:
--------------------------------------------------------------------------------
1 |
2 |
164 |
171 |
172 |
173 |
174 |
--------------------------------------------------------------------------------
/examples/hash/jsExample/javascript.conf:
--------------------------------------------------------------------------------
1 | /**
2 | @example hash/jsExample/javascript.conf
3 |
4 | This example shows how to return Javascript to obtain further evidence for
5 | 51Degrees' on-premise device detection in Nginx. This example is available
6 | in full on [GitHub](https://github.com/51Degrees/device-detection-nginx/blob/master/examples/hash/jsExample/javascript.conf).
7 |
8 | @include{doc} example-require-datafile.txt
9 |
10 | Properties required for this example are not included in the free Lite file,
11 | so a data file that includes ScreenPixelsWidth and ScreenPixelsWidthJavascript
12 | need to be obtained from [configurator](https://configure.51degrees.com/).
13 |
14 | Before using the example, update the followings:
15 | - Remove this 'how to' guide block.
16 | - Update the file path specifed in `51D_file_path` with the actual file path.
17 | - Replace the nginx.conf with this file or run Nginx with `-c`
18 | option pointing to this file.
19 | - Create a static file `overrides` in the Nginx `document_root`.
20 |
21 | In a Linux environment, once Nginx has started, open the [index.html](
22 | https://github.com/51Degrees/device-detection-nginx/blob/master/examples/hash/jsExample/index.html)
23 | in a browser.
24 |
25 | The `index.html` send a request to the `/51D.js` endpoint to obtain the javascript
26 | and execute it as below:
27 | ```
28 |
29 | ```
30 | Then use the obtained screen pixels width to send back to the `/overrides` endpoint.
31 | The full details can be seen in the `index.html`.
32 |
33 | The Javascript can be seen by running the below command.
34 | ```
35 | $ curl localhost:8080/51D.js -A "Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D167 Safari/9537.53"
36 | ```
37 |
38 | Expected display after open the `index.html` in the browser is:
39 | ```
40 | Screen Pixels Width: 1920
41 | ```
42 | The number should be the screen pixels width of your machine.
43 |
44 | `NOTE`: All the lines above, this line and the end of comment block line after
45 | this line should be removed before using this example.
46 | */
47 |
48 | worker_processes 4;
49 |
50 | load_module modules/ngx_http_51D_module.so;
51 |
52 | events {
53 | worker_connections 1024;
54 | }
55 | # // Snippet Start
56 | http {
57 | ## Set the data file for the 51Degrees module to use ##
58 | 51D_file_path ./device-detection-cxx/device-detection-data/51Degrees-LiteV4.1.hash;
59 |
60 | server {
61 | listen 8888;
62 |
63 | location /overrides {
64 | ## Get screen pixels width. Use matched all so override evidence can be processed ##
65 | 51D_match_all x-screenpixelswidth ScreenPixelsWidth;
66 |
67 | ## Add to response headers for easy viewing. ##
68 | add_header Access-Control-Allow-Origin *;
69 | add_header Access-Control-Allow-Heaaders *;
70 | add_header Access-Control-Expose-Headers *;
71 | add_header x-screenpixelswidth $http_x_screenpixelswidth;
72 | }
73 |
74 | location /51D.js {
75 | ## Get the javascript from ScreenPixelsWidthJavascript property ##
76 | 51D_get_javascript_single ScreenPixelsWidthJavascript;
77 |
78 | ## Since the URL location does not actually exist, an error will be logged, ##
79 | ## disable file not found error logging ##
80 | log_not_found off;
81 | }
82 | }
83 | }
84 | # // Snippet End
85 |
--------------------------------------------------------------------------------
/examples/hash/matchMetrics.conf:
--------------------------------------------------------------------------------
1 | /**
2 | @example hash/matchMetrics.conf
3 |
4 | This example shows how to obtain further metrics for 51Degrees' on-premise
5 | device detection in Nginx. The available metris are Drift, Difference, Method,
6 | UserAgents, DeviceId and MatchedNodes. This example is available in full on
7 | [GitHub](
8 | https://github.com/51Degrees/device-detection-nginx/blob/master/examples/hash/matchMetrics.conf).
9 |
10 | @include{doc} example-require-datafile.txt
11 |
12 | Make sure to include at least IsMobile property for this to work.
13 |
14 | Before using the example, update the followings:
15 | - Remove this 'how to' guide block.
16 | - Update the %%%DAEMON_MODE%% to 'on' or 'off'.
17 | - Remove the %%%TEST_GLOBALS%%.
18 | - Update the %%%MODULE_PATH%% with the actual path.
19 | - Remove the %%%TEST_GLOBALS_HTTP%%.
20 | - Update the %%%FILE_PATH%% with the actual file path.
21 | - Replace the nginx.conf with this file or run Nginx with `-c`
22 | option pointing to this file.
23 | - Create a static file `metrics` in the Nginx `document_root`.
24 |
25 | In a Linux environment, once Nginx has started, run the following command:
26 | ```
27 | $ curl localhost:8080/metrics -I -A "Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D167 Safari/9537.53"
28 | ```
29 | Expected output:
30 | ```
31 | HTTP/1.1 200 OK
32 | ...
33 | x-metrics: 0,0,PERFORMANCE,18
34 | x-user-agents: _________.0 (iPhone_______Phone ___7_1 like ____OS X_______WebK__________2 (KHTML,______Gecko_ Ver_________Mobile______7 Safari________
35 | x-device-id: 12280-24384-24305-0
36 | ...
37 | ```
38 |
39 | `NOTE`: All the lines above, this line and the end of comment block line after
40 | this line should be removed before using this example.
41 | */
42 |
43 | ## Update DAEMON_MODE to 'on' or 'off' before running with Nginx ##
44 | daemon %%DAEMON_MODE%%;
45 | worker_processes 4;
46 |
47 | ## The following line is only for testing. Remove before running ##
48 | ## with Nginx ##
49 | %%TEST_GLOBALS%%
50 | ## Update MODULE_PATH before running with Nginx ##
51 | load_module %%MODULE_PATH%%modules/ngx_http_51D_module.so;
52 |
53 | events {
54 | worker_connections 1024;
55 | }
56 |
57 | # // Snippet Start
58 | http {
59 | ## The following line is only for testing. Remove before ##
60 | ## running with Nginx ##
61 | %%TEST_GLOBALS_HTTP%%
62 | ## Set the data file for the 51Degrees module to use ##
63 | ## Update the FILE_PATH before running with Nginx ##
64 | 51D_file_path %%FILE_PATH%%;
65 | 51D_drift 1;
66 | 51D_difference 1;
67 |
68 | server {
69 | listen 127.0.0.1:8080;
70 | server_name localhost;
71 |
72 | location /metrics {
73 | ## Get the metrics for the match using single User-Agent ##
74 | 51D_match_ua x-metrics Drift,Difference,Method,MatchedNodes;
75 |
76 | ## Get the matched user agent string ##
77 | 51D_match_ua x-user-agents UserAgents;
78 |
79 | ## Get the matched device ID ##
80 | 51D_match_ua x-device-id DeviceId;
81 |
82 | ## Add to response headers for easy viewing. ##
83 | add_header x-metrics $http_x_metrics;
84 | add_header x-user-agents $http_x_user_agents;
85 | add_header x-device-id $http_x_device_id;
86 | }
87 | }
88 | }
89 | # // Snippet End
90 |
--------------------------------------------------------------------------------
/examples/hash/matchQuery.conf:
--------------------------------------------------------------------------------
1 | /**
2 | @example hash/matchQuery.conf
3 |
4 | This example shows how query argument take precendence over header value
5 | for 51Degrees' on-premise device detection in Nginx. This example is available
6 | in full on [GitHub](
7 | https://github.com/51Degrees/device-detection-nginx/blob/master/examples/hash/matchQuery.conf).
8 |
9 | @include{doc} example-require-datafile.txt
10 |
11 | Make sure to include at least IsMobile property for this to work.
12 |
13 | Before using the example, update the followings:
14 | - Remove this 'how to' guide block.
15 | - Update the %%%DAEMON_MODE%% to 'on' or 'off'.
16 | - Remove the %%%TEST_GLOBALS%%.
17 | - Update the %%%MODULE_PATH%% with the actual path.
18 | - Remove the %%%TEST_GLOBALS_HTTP%%.
19 | - Update the %%%FILE_PATH%% with the actual file path.
20 | - Replace the nginx.conf with this file or run Nginx with `-c`
21 | option pointing to this file.
22 | - Create a static file `query` in the Nginx `document_root`.
23 |
24 | In a Linux environment, once Nginx has started, run the following command:
25 | ```
26 | curl -I "http://localhost:8080/query?User-Agent=Mozilla/5.0%20(iPhone;%20CPU%20iPhone%20OS%207_1%20like%20Mac%20OS%20X)%20AppleWebKit/537.51.2%20(KHTML,%20like%20Gecko)%20Version/7.0%20Mobile/11D167%20Safari/9537.53" -A "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0"
27 | ```
28 | The header is a desktop User-Agent while the argument is a mobile User-Agent.
29 | Expected output:
30 | ```
31 | HTTP/1.1 200 OK
32 | ...
33 | x-mobile-single: True
34 | x-mobile-all: True
35 | ...
36 | ```
37 |
38 | `NOTE`: All the lines above, this line and the end of comment block line after
39 | this line should be removed before using this example.
40 | */
41 |
42 | ## Replace DAEMON_NODE with 'on' or 'off' before running with Nginx ##
43 | daemon %%DAEMON_MODE%%;
44 | worker_processes 4;
45 |
46 | ## The following line is only for testing. Remove before ##
47 | ## running with Nginx ##
48 | %%TEST_GLOBALS%%
49 | ## Update the MODULE_PATH before running with Nginx. ##
50 | load_module %%MODULE_PATH%%modules/ngx_http_51D_module.so;
51 |
52 | events {
53 | worker_connections 1024;
54 | }
55 |
56 | # // Snippet Start
57 | http {
58 | ## The following line is only for testing. Remove before ##
59 | ## running with Nginx ##
60 | %%TEST_GLOBALS_HTTP%%
61 | ## Set the data file for the 51Degrees module to use ##
62 | ## Update the FILE_PATH before running with Nginx. ##
63 | 51D_file_path %%FILE_PATH%%;
64 |
65 | server {
66 | listen 127.0.0.1:8080;
67 | server_name localhost;
68 |
69 | location /query {
70 | ## Do a single User-Agent match for device information ##
71 | 51D_match_ua x-mobile-single IsMobile $arg_user_agent;
72 |
73 | ## Do a multiple HTTP header match for IsMobile ##
74 | 51D_match_all x-mobile-all IsMobile;
75 |
76 | ## Add to response headers for easy viewing. ##
77 | add_header x-mobile-single $http_x_mobile_single;
78 | add_header x-mobile-all $http_x_mobile_all;
79 | }
80 | }
81 | }
82 | # // Snippet End
83 |
--------------------------------------------------------------------------------
/examples/hash/responseHeaders/response.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | User-Agent Client Hints Example
4 |
5 |
6 |
7 |
User Agent Client Hints Example
8 |
Select the Use User Agent Client Hints button below, to use User Agent Client Hint headers in evidence for device detections.