├── .chglog ├── CHANGELOG.tpl.md ├── RELNOTES.tmpl └── config.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ ├── feature_request.yaml │ └── submit_question.yaml └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── config ├── share ├── status.compress.html └── status.template.html ├── src ├── ngx_http_vhost_traffic_status_control.c ├── ngx_http_vhost_traffic_status_control.h ├── ngx_http_vhost_traffic_status_display.c ├── ngx_http_vhost_traffic_status_display.h ├── ngx_http_vhost_traffic_status_display_json.c ├── ngx_http_vhost_traffic_status_display_json.h ├── ngx_http_vhost_traffic_status_display_prometheus.c ├── ngx_http_vhost_traffic_status_display_prometheus.h ├── ngx_http_vhost_traffic_status_dump.c ├── ngx_http_vhost_traffic_status_dump.h ├── ngx_http_vhost_traffic_status_filter.c ├── ngx_http_vhost_traffic_status_filter.h ├── ngx_http_vhost_traffic_status_limit.c ├── ngx_http_vhost_traffic_status_limit.h ├── ngx_http_vhost_traffic_status_module.c ├── ngx_http_vhost_traffic_status_module.h ├── ngx_http_vhost_traffic_status_module_html.h ├── ngx_http_vhost_traffic_status_node.c ├── ngx_http_vhost_traffic_status_node.h ├── ngx_http_vhost_traffic_status_set.c ├── ngx_http_vhost_traffic_status_set.h ├── ngx_http_vhost_traffic_status_shm.c ├── ngx_http_vhost_traffic_status_shm.h ├── ngx_http_vhost_traffic_status_string.c ├── ngx_http_vhost_traffic_status_string.h ├── ngx_http_vhost_traffic_status_variables.c └── ngx_http_vhost_traffic_status_variables.h ├── t ├── 000.display_html.t ├── 001.display_json.t ├── 002.check_json_syntax.t ├── 003.filter_by_host.t ├── 004.filter_by_set_key.t ├── 005.filter_check_duplicate.t ├── 006.control_status_fully.t ├── 007.control_status_group.t ├── 008.control_status_zone.t ├── 009.control_reset_fully.t ├── 010.control_reset_group.t ├── 011.control_reset_zone.t ├── 012.control_delete_fully.t ├── 013.control_delete_group.t ├── 014.control_delete_zone.t ├── 015.vts_variables_by_lua.t ├── 016.limit_traffic_by_lua.t ├── 017.limit_traffic.t ├── 018.limit_traffic_by_set_key.t ├── 019.limit_traffic_check_duplicate.t ├── 020.display_sum_key.t ├── 021.set_by_filter.t ├── 022.display_prometheus.t ├── 023.histogram_buckets.t └── 024.upstream_check.t └── util ├── fileToHex.pl ├── tplToBuffer.sh └── tplToDefine.sh /.chglog/CHANGELOG.tpl.md: -------------------------------------------------------------------------------- 1 | {{ if .Versions -}} 2 | 3 | ## [Unreleased] 4 | 5 | {{ if .Unreleased.CommitGroups -}} 6 | {{ range .Unreleased.CommitGroups -}} 7 | ### {{ .Title }} 8 | {{ range .Commits -}} 9 | - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} 10 | {{ end }} 11 | {{ end -}} 12 | {{ end -}} 13 | {{ end -}} 14 | 15 | {{ range .Versions }} 16 | ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} 17 | {{ range .CommitGroups -}} 18 | ### {{ .Title }} 19 | {{ range .Commits -}} 20 | - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} 21 | {{ end }} 22 | {{ end -}} 23 | 24 | {{- if .NoteGroups -}} 25 | {{ range .NoteGroups -}} 26 | ### {{ .Title }} 27 | {{ range .Notes }} 28 | {{ .Body }} 29 | {{ end }} 30 | {{ end -}} 31 | {{ end -}} 32 | {{ end -}} 33 | 34 | {{- if .Versions }} 35 | [Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD 36 | {{ range .Versions -}} 37 | {{ if .Tag.Previous -}} 38 | [{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} 39 | {{ end -}} 40 | {{ end -}} 41 | {{ end -}} 42 | -------------------------------------------------------------------------------- /.chglog/RELNOTES.tmpl: -------------------------------------------------------------------------------- 1 | {{ range .Versions -}} 2 | Release {{ .Tag.Name}} 3 | 4 | Release {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} 5 | {{ range .CommitGroups -}} 6 | ** {{ .Title }} 7 | {{ range .Commits -}} 8 | - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} 9 | {{ end }} 10 | {{ end -}} 11 | 12 | {{- if .NoteGroups -}} 13 | {{ range .NoteGroups -}} 14 | ** {{ .Title }} 15 | {{ range .Notes }} 16 | {{ .Body }} 17 | {{ end }} 18 | {{ end -}} 19 | {{ end -}} 20 | {{ end -}} 21 | -------------------------------------------------------------------------------- /.chglog/config.yml: -------------------------------------------------------------------------------- 1 | style: github 2 | template: CHANGELOG.tpl.md 3 | info: 4 | title: CHANGELOG 5 | repository_url: https://github.com/vozlt/nginx-module-vts 6 | options: 7 | commits: 8 | # filters: 9 | # Type: 10 | # - feat 11 | # - fix 12 | # - perf 13 | # - refactor 14 | commit_groups: 15 | # title_maps: 16 | # feat: Features 17 | # fix: Bug Fixes 18 | # perf: Performance Improvements 19 | # refactor: Code Refactoring 20 | header: 21 | pattern: "^(\\w*)\\:\\s(.*)$" 22 | pattern_maps: 23 | - Type 24 | - Subject 25 | notes: 26 | keywords: 27 | - BREAKING CHANGE -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report incorrect behavior in this module 3 | title: "BUG: " 4 | labels: [bug] 5 | 6 | body: 7 | - type: checkboxes 8 | id: checks 9 | attributes: 10 | label: module version checks 11 | options: 12 | - label: > 13 | I have checked that this issue has not already been reported. 14 | required: true 15 | - type: textarea 16 | id: problem 17 | attributes: 18 | label: Issue Description 19 | description: > 20 | Please provide a description of the issue shown in the reproducible example. 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: expected-behavior 25 | attributes: 26 | label: Expected Behavior 27 | description: > 28 | Please describe or show a code example of the expected behavior. 29 | validations: 30 | required: true 31 | - type: textarea 32 | id: version 33 | attributes: 34 | label: Installed Versions 35 | placeholder: > 36 | % sudo /usr/local/nginx/sbin/nginx -V 37 | nginx version: nginx/1.27.0 38 | built by gcc 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04) 39 | configure arguments: --add-module=../nginx-module-vts 40 | description: > 41 | Please paste the output of ``nginx -V`` 42 | value: > 43 |
44 | 45 | 46 | Replace this line with the output of nginx -V 47 | 48 | 49 |
50 | validations: 51 | required: true 52 | - type: textarea 53 | id: conf 54 | attributes: 55 | label: Reproducible Example nginx.conf 56 | description: > 57 | Please paste the reproducible nginx.conf 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this module 3 | title: "ENH: " 4 | labels: [enhancement] 5 | 6 | body: 7 | - type: checkboxes 8 | id: checks 9 | attributes: 10 | label: Feature Type 11 | description: Please check what type of feature request you would like to propose. 12 | options: 13 | - label: > 14 | Adding new functionality to this module 15 | - label: > 16 | Changing existing functionality in this module 17 | - label: > 18 | Removing existing functionality in this module 19 | - type: textarea 20 | id: description 21 | attributes: 22 | label: Problem Description 23 | description: > 24 | Please describe what problem the feature would solve, e.g. "I wish I could use this module to ..." 25 | placeholder: > 26 | I wish I could use this module... 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: feature 31 | attributes: 32 | label: Feature Description 33 | description: > 34 | Please describe how the new feature would be implemented, using psudocode if relevant. 35 | validations: 36 | required: true 37 | - type: textarea 38 | id: context 39 | attributes: 40 | label: Additional Context 41 | description: > 42 | Please provide any relevant GitHub issues, code examples or references that help describe and support the feature request. 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/submit_question.yaml: -------------------------------------------------------------------------------- 1 | name: Submit Question 2 | description: Ask a general question about this module 3 | title: "QST: " 4 | labels: [question] 5 | 6 | body: 7 | - type: textarea 8 | id: question 9 | attributes: 10 | label: Question about this module 11 | - type: textarea 12 | id: version 13 | attributes: 14 | label: Installed Versions 15 | placeholder: > 16 | % sudo /usr/local/nginx/sbin/nginx -V 17 | nginx version: nginx/1.27.0 18 | built by gcc 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04) 19 | configure arguments: --add-module=../nginx-module-vts 20 | description: > 21 | Please paste the output of ``nginx -V`` 22 | value: > 23 |
24 | 25 | 26 | Replace this line with the output of nginx -V 27 | 28 | 29 |
30 | - type: textarea 31 | id: conf 32 | attributes: 33 | label: Reproducible Example nginx.conf 34 | description: > 35 | Please paste the reproducible nginx.conf 36 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | name: 'build' 12 | runs-on: ubuntu-24.04 13 | steps: 14 | - name: 'checkout' 15 | uses: actions/checkout@v3 16 | with: 17 | path: nginx-module-vts 18 | - name: 'checkout nginx' 19 | uses: actions/checkout@v3 20 | with: 21 | repository: nginx/nginx 22 | path: nginx 23 | - name: 'checkout freenginx' 24 | uses: actions/checkout@v3 25 | with: 26 | repository: freenginx/nginx 27 | path: freenginx 28 | - name: 'checkout luajit2' 29 | uses: actions/checkout@v3 30 | with: 31 | repository: openresty/luajit2 32 | path: luajit2 33 | - name: 'checkout ngx_devel_kit' 34 | uses: actions/checkout@v3 35 | with: 36 | repository: vision5/ngx_devel_kit 37 | path: ngx_devel_kit 38 | - name: 'checkout lua-nginx-module' 39 | uses: actions/checkout@v3 40 | with: 41 | repository: openresty/lua-nginx-module 42 | path: lua-nginx-module 43 | - name: 'checkout lua-resty-core' 44 | uses: actions/checkout@v3 45 | with: 46 | repository: openresty/lua-resty-core 47 | path: lua-resty-core 48 | - name: 'checkout lua-resty-lrucache' 49 | uses: actions/checkout@v3 50 | with: 51 | repository: openresty/lua-resty-lrucache 52 | path: lua-resty-lrucache 53 | - name: 'build luajit2' 54 | working-directory: luajit2 55 | run: | 56 | make 57 | sudo make install 58 | sudo mkdir /usr/local/share/lua/5.1/resty 59 | - name: 'link resty lib' 60 | working-directory: /usr/local/share/lua/5.1/resty 61 | run: | 62 | sudo ln -s /home/runner/work/nginx-module-vts/nginx-module-vts/lua-resty-core/lib/resty/core core 63 | sudo ln -s /home/runner/work/nginx-module-vts/nginx-module-vts/lua-resty-core/lib/resty/core.lua core.lua 64 | sudo ln -s /home/runner/work/nginx-module-vts/nginx-module-vts/lua-resty-lrucache/lib/resty/lrucache lrucache 65 | sudo ln -s /home/runner/work/nginx-module-vts/nginx-module-vts/lua-resty-lrucache/lib/resty/lrucache.lua lrucache.lua 66 | - name: 'prepare nginx_upstream_check' 67 | uses: actions/checkout@v3 68 | with: 69 | repository: yaoweibin/nginx_upstream_check_module 70 | path: nginx_upstream_check 71 | 72 | - name: 'patch upstream_check' 73 | working-directory: nginx 74 | run: | 75 | patch -p1 < /home/runner/work/nginx-module-vts/nginx-module-vts/nginx_upstream_check/check_1.20.1+.patch 76 | - name: 'patch upstream_check for freenginx' 77 | working-directory: freenginx 78 | run: | 79 | patch -p1 < /home/runner/work/nginx-module-vts/nginx-module-vts/nginx_upstream_check/check_1.20.1+.patch 80 | - name: 'build nginx' 81 | working-directory: nginx 82 | run: | 83 | ./auto/configure --with-ld-opt="-Wl,-rpath,/usr/local/lib" --add-module=/home/runner/work/nginx-module-vts/nginx-module-vts/ngx_devel_kit --add-module=/home/runner/work/nginx-module-vts/nginx-module-vts/lua-nginx-module --add-module=/home/runner/work/nginx-module-vts/nginx-module-vts/nginx-module-vts --add-module=/home/runner/work/nginx-module-vts/nginx-module-vts/nginx_upstream_check 84 | make 85 | sudo make install 86 | /usr/local/nginx/sbin/nginx -V 87 | env: 88 | LUAJIT_LIB: /usr/local/lib 89 | LUAJIT_INC: /usr/local/include/luajit-2.1 90 | - name: 'build freenginx' 91 | working-directory: freenginx 92 | run: | 93 | ./auto/configure --prefix=/usr/local/freenginx --with-ld-opt="-Wl,-rpath,/usr/local/lib" --add-module=/home/runner/work/nginx-module-vts/nginx-module-vts/ngx_devel_kit --add-module=/home/runner/work/nginx-module-vts/nginx-module-vts/lua-nginx-module --add-module=/home/runner/work/nginx-module-vts/nginx-module-vts/nginx-module-vts --add-module=/home/runner/work/nginx-module-vts/nginx-module-vts/nginx_upstream_check 94 | make 95 | sudo make install 96 | /usr/local/freenginx/sbin/nginx -V 97 | env: 98 | LUAJIT_LIB: /usr/local/lib 99 | LUAJIT_INC: /usr/local/include/luajit-2.1 100 | - name: 'prepare cpanm' 101 | run: | 102 | sudo apt install -y cpanminus 103 | sudo cpanm --notest Test::Nginx::Socket > build.log 2>&1 || (cat build.log && exit 1) 104 | - name: 'prepare promtool' 105 | run: | 106 | sudo apt-get update && sudo apt-get install -y curl 107 | curl -L $(curl -s https://api.github.com/repos/prometheus/prometheus/releases/latest | grep browser_download_url | grep linux-amd64 | cut -d '"' -f 4) -o prometheus-latest.tar.gz 108 | tar xvf prometheus-latest.tar.gz 109 | sudo mv prometheus-*/promtool /usr/local/bin 110 | - name: 'test' 111 | working-directory: nginx-module-vts 112 | run: | 113 | echo "/usr/local/nginx/sbin/" >> $GITHUB_PATH 114 | sudo PATH=/usr/local/nginx/sbin:$PATH prove -r t 115 | - name: 'test upstream check' 116 | working-directory: nginx-module-vts 117 | run: | 118 | echo "/usr/local/nginx/sbin/" >> $GITHUB_PATH 119 | sudo TEST_UPSTREAM_CHECK=1 TEST_NGINX_SLEEP=1 PATH=/usr/local/nginx/sbin:$PATH prove t/024.upstream_check.t 120 | - name: 'test freenginx' 121 | working-directory: nginx-module-vts 122 | run: | 123 | echo "/usr/local/freenginx/sbin/" >> $GITHUB_PATH 124 | sudo PATH=/usr/local/freenginx/sbin:$PATH prove -r t 125 | - name: 'test upstream check for freenginx' 126 | working-directory: nginx-module-vts 127 | run: | 128 | echo "/usr/local/freenginx/sbin/" >> $GITHUB_PATH 129 | sudo TEST_UPSTREAM_CHECK=1 TEST_NGINX_SLEEP=1 PATH=/usr/local/freenginx/sbin:$PATH prove t/024.upstream_check.t 130 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | t/servroot 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [Unreleased] 3 | 4 | 5 | ## [v0.2.4] - 2025-03-12 6 | ### Fix 7 | - escape uri in module side 8 | 9 | 10 | ## [v0.2.3] - 2025-01-01 11 | ### Ci 12 | - support freenginx 13 | 14 | ### Docs 15 | - Fix README 16 | 17 | 18 | ## [v0.2.2] - 2023-05-26 19 | ### Bugfix 20 | - fixed issues/228 Change the reffered source of upstream_states in shm_add_upstream() 21 | - fixed issues/248 Shared memory (lock|unlock) is set when using the ngx_http_vhost_traffic_status_display_get_size() function 22 | 23 | ### Bugfix 24 | - Add shmtx unlock 25 | 26 | ### Chore 27 | - add cpanm --notest in CI 28 | 29 | ### Test 30 | - Fix upstream check test properly 31 | - Add upstream check test 32 | 33 | 34 | ## [v0.2.1] - 2022-09-17 35 | ### Bugfix 36 | - use trimmed serverZones name 37 | - improved the accuracy of total(*) statistics by nginx-module-sts/pull/10 38 | 39 | ### Chore 40 | - Add CI badge in README ([#245](https://github.com/vozlt/nginx-module-vts/issues/245)) 41 | 42 | ### Compatibility 43 | - fixed an issues/232 with compile errors in gcc 11.3 44 | 45 | ### Debug 46 | - added ngx_log_error() when ngx_http_vhost_traffic_status_node_position_key() failed for issues/212 47 | 48 | ### Docs 49 | - Fixed README 50 | 51 | ### Test 52 | - build without -Wno-stringop-overread ([#243](https://github.com/vozlt/nginx-module-vts/issues/243)) 53 | - Add test for prometheus syntax 54 | - Add test for display prometheus 55 | 56 | 57 | ## [v0.2.0] - 2022-09-06 58 | ### Bugfix 59 | - fixed for PR[#238](https://github.com/vozlt/nginx-module-vts/issues/238) 60 | - fixed for PR[#238](https://github.com/vozlt/nginx-module-vts/issues/238) 61 | - fixed issues/204 that syntax error has occured 62 | - rollback to 549cc4d 63 | - fixed issues/137, issues/98 that maxSize in cacheZones is displayed incorrectly 64 | - fixed issues/174 that XSS vulnerability in the html page Feature: added moduleVersion field in format/json 65 | - added escape strings for filter names in JSON 66 | - fixed the sum value of histogram in upstream metrics 67 | - fixed to display all A records of server without zone directive in the upstream block. 68 | 69 | ### Chore 70 | - Change module version. ([#241](https://github.com/vozlt/nginx-module-vts/issues/241)) 71 | - Use git-chglog 72 | 73 | ### Comment 74 | - added moduleVersion 75 | - added additional information about cacheZones 76 | - added tested versions 77 | - added a diagram for the order of module directives 78 | 79 | ### Compatibility 80 | - fixed ngx_http_vhost_traffic_status_display_get_upstream_nelts() to calculate all A records of server. 81 | 82 | ### Docs 83 | - Fix README 84 | 85 | ### Docs 86 | - fix simple typo, destory -> destroy 87 | 88 | ### Fix 89 | - limit the r->uri search scope to avoid overflow 90 | 91 | ### Prometheus 92 | - fix nginx_vts_filter_requests_total labels 93 | - remove request "total" metrics 94 | 95 | ### Refactor 96 | - changed version 97 | - changed spacing 98 | - changed spacing 99 | - changed if statement from merged pull/145 100 | 101 | ### Test 102 | - describe how to test and fix failed test case 103 | 104 | 105 | ## [v0.1.18] - 2018-06-22 106 | ### Bugfix 107 | - fixed issues/130 that nginx_vts_main_connections metrics mixed 108 | - fixed for issues/129 that worker process 4589 exited on signal 11 109 | 110 | ### Tag 111 | - v0.1.18 112 | 113 | 114 | ## [v0.1.17] - 2018-06-20 115 | ### Comment 116 | - added overCounts object explanation 117 | - added additional explanation of vhost_traffic_status_zone 118 | 119 | ### Compatibility 120 | - added "#if (NGX_HTTP_CACHE)" for the issues/122 121 | 122 | ### Delete 123 | - a.diff 124 | 125 | ### Feature 126 | - added TiB unit in format/html for the issues/111 127 | - added vhost_traffic_status_filter_max_node directive to limit the size of filters 128 | - added the histogram type of request processing time in format/json 129 | - added vhost_traffic_status_histogram_buckets directive to set the histogram type of request processing time in format/prometheus 130 | - added support for implementing format/prometheus 131 | - added request_time_counter, response_time_counter section to support accumulated request processing time for pull/67, issues/73 132 | 133 | ### Tag 134 | - v0.1.17 135 | 136 | 137 | ## [v0.1.16] - 2018-05-21 138 | ### Compatibility 139 | - fixed ngx_current_msec that changed in nginx-1.13.10 for the issues/121 140 | 141 | ### Fix 142 | - nginx will crash at vts module when configure file has no http block 143 | - nginx will crash at vts module when configure file has no http block 144 | 145 | ### Tag 146 | - v0.1.16 147 | 148 | 149 | ## [v0.1.15] - 2017-06-20 150 | ### Bugfix 151 | - fixed issues/79 that does not exited at "worker process is shutting down" 152 | - fixed issues/79 that does not exited at "worker process is shutting down" 153 | 154 | ### Comment 155 | - fixed to be compatible with version 0.27-gfm 156 | 157 | ### Compatibility 158 | - fixed goto label location for the issues/77 159 | - fixed some issues for the nginx-module-sts/issues/1 160 | - fixed "#define" macro to char array for the nginx-module-sts/issues/1 161 | 162 | ### Feature 163 | - changed ngx_http_vhost_traffic_status_node_time_queue_merge() 164 | - added vhost_traffic_status_dump to maintain statistics data permanently 165 | - added period parameter in vhost_traffic_status_average_method directive to change the average value after the elapse of time 166 | 167 | ### Fix 168 | - it is actually aam 169 | 170 | ### Tag 171 | - v0.1.15 172 | 173 | 174 | ## [v0.1.14] - 2017-03-21 175 | ### Comment 176 | - added the use cases & fixed vhost_traffic_status_bypass_(limit|stats) usage 177 | 178 | ### Compatibility 179 | - added segfault prevent routine for the issues/75 180 | 181 | ### Feature 182 | - added shared memory section to support shared memory information 183 | - added vhost_traffic_status_average_method to support for selecting an average formula 184 | - added sharedZones in JSON to support shared memory information 185 | 186 | 187 | ## [v0.1.13] - 2017-03-07 188 | ### Bugfix 189 | - fixed issues/(71|72) worker process exited on signal 11 190 | 191 | ### Comment 192 | - added nginx-vts-exporter & nginx-module-sysguard 193 | - added stream status modules 194 | - added modules nginx-module-sts and nginx-module-stream-sts 195 | 196 | ### Compatibility 197 | - added "#if (NGX_HTTP_CACHE)" 198 | 199 | ### Feature 200 | - added vhost_traffic_status_set_by_filter to support stats values access Feature: added "::main" in control to get only default status values 201 | - added vhost_traffic_status_display_sum_key for issues/61 202 | - added vhost_traffic_status_display_sum_key for issues/61 203 | 204 | ### Refactor 205 | - javascript tidy 206 | 207 | 208 | ## [v0.1.12] - 2017-02-08 209 | ### Feature 210 | - added hostname section for issues/37 211 | - added request_time section for issues/(43|57) 212 | - added request_time section for issues/(43|57) 213 | - added request_time section for issues/(43|57) 214 | 215 | ### Refactor 216 | - divided the source code 217 | 218 | 219 | ## [v0.1.11] - 2016-11-09 220 | ### Bugfix 221 | - fixed issues/56 that worker process exited on signal 11 if running control query without group argument or nonexistent group 222 | - fixed issues/52 that worker process exited on signal 11 223 | - fixed issues/6 that occured error(handler::shm_add_upstream() failed) when using fastcgi_pass $variables 224 | - fixed issues/45 that occurred segfault when balancer_by_lua breaks 225 | 226 | ### Compatibility 227 | - changed for issues/49 that occured errors when using compile with clang -Werror,-Wtautological-pointer-compare in osx os. 228 | - changed for issues/47 that occured errors when using compile with -Werror(Make all warnings into errors). The number returned by ngx_http_vhost_traffic_status_max_integer() consist of string without the suffix ULL(unsigned long long int). 229 | 230 | ### Tag 231 | - v0.1.11 232 | - v0.1.10 233 | 234 | 235 | ## [v0.1.10] - 2016-03-24 236 | ### Bugfix 237 | - initialize a variable(filter->filter_name.flushes) for issues/35 that worker process exited on signal 11 238 | 239 | ### Compatibility 240 | - added dynamic module build option for --add-dynamic-module in nginx 1.9.11 241 | 242 | 243 | ## [v0.1.9] - 2016-03-01 244 | ### Bugfix 245 | - initialize a variable(filter->filter_name.value.len) for issues/33 that occurred segfault when running "nginx -s reload" 246 | 247 | ### Exception 248 | - return NGX_CONF_ERROR if failed ngx_http_vhost_traffic_status_filter_unique() 249 | 250 | ### Feature 251 | - added vhost_traffic_status_display_jsonp to support JSONP 252 | - added vhost_traffic_status_display_jsonp to support JSONP 253 | 254 | ### Refactor 255 | - changed function names from ngx_vhost_* to ngx_http_vhost_* 256 | 257 | 258 | ## [v0.1.8] - 2015-12-15 259 | ### Feature 260 | - added support for implementing the feature that upstream peers use shared memory.(upstream zone directive) 261 | 262 | 263 | ## [v0.1.7] - 2015-12-11 264 | ### Bugfix 265 | - fixed issues/28 that can't use control functionality if location has more than a segment 266 | 267 | ### Comment 268 | - fixed spelling 269 | 270 | ### Compatibility 271 | - changed for issues/27 that error occurred(comparison of integers of different signs) 272 | 273 | ### Feature 274 | - added support for implementing traffic limit. 275 | 276 | 277 | ## [v0.1.6] - 2015-11-25 278 | ### Feature 279 | - added support for implementing variables for current traffic status values. It is starting with a $vts_*. 280 | 281 | 282 | ## [v0.1.5] - 2015-11-23 283 | ### Bugfix 284 | - fixed to work escape_json in ngx_http_vhost_traffic_status_display_set_filter_node() 285 | 286 | ### Compatibility 287 | - changed for issues/27 that ngx_vhost_traffic_status_group_to_string() macro is an error when using -Wstring-plus-int at clang compiler. 288 | 289 | 290 | ## [v0.1.4] - 2015-11-04 291 | ### Comment 292 | - fixed spelling 293 | - fixed spelling 294 | - fixed spelling 295 | 296 | ### Compatibility 297 | - fixed unused variables 298 | 299 | ### Feature 300 | - added vhost_traffic_status_filter to globally enable or disable the filter features. Feature: fixed vhost_traffic_status_filter_by_host to globally enable or disable. Feature: fixed vhost_traffic_status_filter_by_set_key to calculate user defined individual stats. Basically, country flags image is built-in in HTML. Feature: added vhost_traffic_status_filter_check_duplicate for deduplication of vhost_traffic_status_filter_by_set_key. Feature: added update interval in HTML. 301 | 302 | 303 | ## [v0.1.3] - 2015-10-21 304 | ### Bugfix 305 | - stats for cached responses with error_page directive do not create a cache file 306 | 307 | ### Feature 308 | - added vhost_traffic_status_filter_by_host, vhost_traffic_status_filter_by_set_key directive to set the dynamically keys 309 | 310 | ### Tag 311 | - v0.1.2 312 | 313 | 314 | ## [v0.1.2] - 2015-09-03 315 | ### Bugfix 316 | - added cache variable's lock routine in ngx_http_vhost_traffic_status_shm_add_cache() for issues/19 317 | 318 | ### Comment 319 | - added donation button 320 | - added uptime calculation 321 | - added the customizing 322 | - added the customizing 323 | - added the caveats 324 | 325 | ### Compatibility 326 | - added overflow handling routines of variables. It deals with the overflow of both 32bit and 64bit variables but I think that not be useful in 64bit variable(Max:16EB) at this moment. 327 | 328 | 329 | ## [v0.1.1] - 2015-05-28 330 | ### Feature 331 | - cache status support when using the proxy_cache directive 332 | 333 | 334 | ## v0.1.0 - 2015-05-28 335 | ### Bugfix 336 | - added the uscf found routine in ngx_http_vhost_traffic_status_shm_add_upstream() for issues/6 337 | - added default server_name "_" in ngx_http_vhost_traffic_status_shm_add_server(), if the server_name directive is not defined 338 | - added ngx_escape_json() in ngx_http_vhost_traffic_status_display_set_server() for the regular expressions names of server_name directive 339 | - added compare upstream hash. It does not updated upstream peers status so I have fixed it. 340 | - removed a reference(*shm_zone) in ngx_http_vhost_traffic_status_ctx_t. 341 | - changed the obtaining ms of phase for the keeping reference. 342 | - changed a reference from vtsn->stat_upstream.rtms to vtscf->vtsn_upstream->stat_upstream.rtms. It is referred to as non-existent reference after kept a reference. 343 | 344 | ### Comment 345 | - added the table of contents 346 | 347 | ### Compatibility 348 | - added response_time for the nginx v1.9.1(http://hg.nginx.org/nginx/rev/59fc60585f1e) 349 | - changed the position of nginx.h to avoid compile error on windows at v1.7.12 350 | - added ngx_http_vhost_traffic_status_escape_json() for less than 1.7.9 351 | 352 | ### Refactor 353 | - changed NGX_CONF_UNSET to 0 for uint32_t 354 | - changed uptime output from issue(pull/4#issuecomment-77839027) 355 | - added ngx_log_error() in ngx_http_vhost_traffic_status_handler() 356 | - changed length of key 357 | - changed from (ngx_atomic_t) to (ngx_atomic_uint_t) in the ngx_vhost_traffic_status_node_init() and ngx_vhost_traffic_status_node_set() for compile compatibility 358 | - added type casting(ngx_atomic_t) in the ngx_vhost_traffic_status_node_init() and ngx_vhost_traffic_status_node_set() 359 | 360 | 361 | [Unreleased]: https://github.com/vozlt/nginx-module-vts/compare/v0.2.4...HEAD 362 | [v0.2.4]: https://github.com/vozlt/nginx-module-vts/compare/v0.2.3...v0.2.4 363 | [v0.2.3]: https://github.com/vozlt/nginx-module-vts/compare/v0.2.2...v0.2.3 364 | [v0.2.2]: https://github.com/vozlt/nginx-module-vts/compare/v0.2.1...v0.2.2 365 | [v0.2.1]: https://github.com/vozlt/nginx-module-vts/compare/v0.2.0...v0.2.1 366 | [v0.2.0]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.18...v0.2.0 367 | [v0.1.18]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.17...v0.1.18 368 | [v0.1.17]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.16...v0.1.17 369 | [v0.1.16]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.15...v0.1.16 370 | [v0.1.15]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.14...v0.1.15 371 | [v0.1.14]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.13...v0.1.14 372 | [v0.1.13]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.12...v0.1.13 373 | [v0.1.12]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.11...v0.1.12 374 | [v0.1.11]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.10...v0.1.11 375 | [v0.1.10]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.9...v0.1.10 376 | [v0.1.9]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.8...v0.1.9 377 | [v0.1.8]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.7...v0.1.8 378 | [v0.1.7]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.6...v0.1.7 379 | [v0.1.6]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.5...v0.1.6 380 | [v0.1.5]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.4...v0.1.5 381 | [v0.1.4]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.3...v0.1.4 382 | [v0.1.3]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.2...v0.1.3 383 | [v0.1.2]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.1...v0.1.2 384 | [v0.1.1]: https://github.com/vozlt/nginx-module-vts/compare/v0.1.0...v0.1.1 385 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015, YoungJoo.Kim 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_vhost_traffic_status_module 2 | have=NGX_STAT_STUB . auto/have 3 | 4 | HTTP_VTS_SRCS=" \ 5 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_module.c \ 6 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_variables.c \ 7 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_string.c \ 8 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_shm.c \ 9 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_node.c \ 10 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_filter.c \ 11 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_control.c \ 12 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_limit.c \ 13 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_display.c \ 14 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_display_json.c \ 15 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_display_prometheus.c \ 16 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_set.c \ 17 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_dump.c \ 18 | " 19 | 20 | HTTP_VTS_DEPS=" \ 21 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_module.h \ 22 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_variables.h \ 23 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_string.h \ 24 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_shm.h \ 25 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_node.h \ 26 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_filter.h \ 27 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_control.h \ 28 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_limit.h \ 29 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_display.h \ 30 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_display_json.h \ 31 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_display_prometheus.h \ 32 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_set.h \ 33 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_dump.h \ 34 | $ngx_addon_dir/src/ngx_http_vhost_traffic_status_module_html.h \ 35 | " 36 | if test -n "$ngx_module_link"; then 37 | ngx_module_type=HTTP 38 | ngx_module_name=$ngx_addon_name 39 | ngx_module_srcs="$HTTP_VTS_SRCS" 40 | ngx_module_deps="$HTTP_VTS_DEPS" 41 | 42 | . auto/module 43 | else 44 | HTTP_MODULES="$HTTP_MODULES $ngx_addon_name" 45 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $HTTP_VTS_SRCS" 46 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $HTTP_VTS_DEPS" 47 | fi 48 | 49 | # vi:set ft=sh ts=4 sw=4 et fdm=marker: 50 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_control.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_CONTROL_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_CONTROL_H_INCLUDED_ 9 | 10 | 11 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_CONTROL_CMD_NONE 0 12 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_CONTROL_CMD_STATUS 1 13 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_CONTROL_CMD_DELETE 2 14 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_CONTROL_CMD_RESET 3 15 | 16 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_CONTROL_RANGE_NONE 0 17 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_CONTROL_RANGE_ALL 1 18 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_CONTROL_RANGE_GROUP 2 19 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_CONTROL_RANGE_ZONE 3 20 | 21 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_CONTROL "{" \ 22 | "\"processingReturn\":%s," \ 23 | "\"processingCommandString\":\"%V\"," \ 24 | "\"processingGroupString\":\"%V\"," \ 25 | "\"processingZoneString\":\"%V\"," \ 26 | "\"processingCounts\":%ui" \ 27 | "}" 28 | 29 | 30 | typedef struct { 31 | ngx_rbtree_node_t *node; 32 | } ngx_http_vhost_traffic_status_delete_t; 33 | 34 | 35 | typedef struct { 36 | ngx_http_request_t *r; 37 | ngx_uint_t command; 38 | ngx_int_t group; 39 | ngx_str_t *zone; 40 | ngx_str_t *arg_cmd; 41 | ngx_str_t *arg_group; 42 | ngx_str_t *arg_zone; 43 | ngx_str_t *arg_name; 44 | ngx_uint_t range; 45 | ngx_uint_t count; 46 | u_char **buf; 47 | } ngx_http_vhost_traffic_status_control_t; 48 | 49 | 50 | void ngx_http_vhost_traffic_status_node_control_range_set( 51 | ngx_http_vhost_traffic_status_control_t *control); 52 | void ngx_http_vhost_traffic_status_node_status( 53 | ngx_http_vhost_traffic_status_control_t *control); 54 | void ngx_http_vhost_traffic_status_node_delete( 55 | ngx_http_vhost_traffic_status_control_t *control); 56 | void ngx_http_vhost_traffic_status_node_reset( 57 | ngx_http_vhost_traffic_status_control_t *control); 58 | 59 | void ngx_http_vhost_traffic_status_node_upstream_lookup( 60 | ngx_http_vhost_traffic_status_control_t *control, 61 | ngx_http_upstream_server_t *us); 62 | 63 | #endif /* _NGX_HTTP_VTS_CONTROL_H_INCLUDED_ */ 64 | 65 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 66 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_display.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_DISPLAY_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_DISPLAY_H_INCLUDED_ 9 | 10 | 11 | ngx_int_t ngx_http_vhost_traffic_status_display_get_upstream_nelts( 12 | ngx_http_request_t *r); 13 | ngx_int_t ngx_http_vhost_traffic_status_display_get_size( 14 | ngx_http_request_t *r, ngx_int_t format); 15 | 16 | u_char *ngx_http_vhost_traffic_status_display_get_time_queue( 17 | ngx_http_request_t *r, 18 | ngx_http_vhost_traffic_status_node_time_queue_t *q, 19 | ngx_uint_t offset); 20 | u_char *ngx_http_vhost_traffic_status_display_get_time_queue_times( 21 | ngx_http_request_t *r, 22 | ngx_http_vhost_traffic_status_node_time_queue_t *q); 23 | u_char *ngx_http_vhost_traffic_status_display_get_time_queue_msecs( 24 | ngx_http_request_t *r, 25 | ngx_http_vhost_traffic_status_node_time_queue_t *q); 26 | 27 | u_char *ngx_http_vhost_traffic_status_display_get_histogram_bucket( 28 | ngx_http_request_t *r, 29 | ngx_http_vhost_traffic_status_node_histogram_bucket_t *b, 30 | ngx_uint_t offset, const char *fmt); 31 | u_char *ngx_http_vhost_traffic_status_display_get_histogram_bucket_msecs( 32 | ngx_http_request_t *r, 33 | ngx_http_vhost_traffic_status_node_histogram_bucket_t *b); 34 | u_char *ngx_http_vhost_traffic_status_display_get_histogram_bucket_counters( 35 | ngx_http_request_t *r, 36 | ngx_http_vhost_traffic_status_node_histogram_bucket_t *q); 37 | 38 | 39 | 40 | char *ngx_http_vhost_traffic_status_display(ngx_conf_t *cf, 41 | ngx_command_t *cmd, void *conf); 42 | 43 | 44 | #endif /* _NGX_HTTP_VTS_DISPLAY_H_INCLUDED_ */ 45 | 46 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 47 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_display_json.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_DISPLAY_JSON_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_DISPLAY_JSON_H_INCLUDED_ 9 | 10 | 11 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_S "{" 12 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_OBJECT_S "\"%V\":{" 13 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_ARRAY_S "\"%V\":[" 14 | 15 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_ARRAY_E "]" 16 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_OBJECT_E "}" 17 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_E "}" 18 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_NEXT "," 19 | 20 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_MAIN "\"hostName\":\"%V\"," \ 21 | "\"moduleVersion\":\"%s\"," \ 22 | "\"nginxVersion\":\"%s\"," \ 23 | "\"loadMsec\":%M," \ 24 | "\"nowMsec\":%M," \ 25 | "\"connections\":{" \ 26 | "\"active\":%uA," \ 27 | "\"reading\":%uA," \ 28 | "\"writing\":%uA," \ 29 | "\"waiting\":%uA," \ 30 | "\"accepted\":%uA," \ 31 | "\"handled\":%uA," \ 32 | "\"requests\":%uA" \ 33 | "}," \ 34 | "\"sharedZones\":{" \ 35 | "\"name\":\"%V\"," \ 36 | "\"maxSize\":%ui," \ 37 | "\"usedSize\":%ui," \ 38 | "\"usedNode\":%ui" \ 39 | "}," 40 | 41 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_S "\"serverZones\":{" 42 | 43 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_START "\"%V\":{" \ 44 | "\"requestCounter\":%uA," \ 45 | "\"inBytes\":%uA," \ 46 | "\"outBytes\":%uA," 47 | 48 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_MIDDLE "\"responses\":{" \ 49 | "\"1xx\":%uA," \ 50 | "\"2xx\":%uA," \ 51 | "\"3xx\":%uA," \ 52 | "\"4xx\":%uA," \ 53 | "\"5xx\":%uA" 54 | 55 | #if (NGX_HTTP_CACHE) 56 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_END ",\"miss\":%uA," \ 57 | "\"bypass\":%uA," \ 58 | "\"expired\":%uA," \ 59 | "\"stale\":%uA," \ 60 | "\"updating\":%uA," \ 61 | "\"revalidated\":%uA," \ 62 | "\"hit\":%uA," \ 63 | "\"scarce\":%uA" \ 64 | "}," \ 65 | "\"requestMsecCounter\":%uA," \ 66 | "\"requestMsec\":%M," \ 67 | "\"requestMsecs\":{" \ 68 | "\"times\":[%s]," \ 69 | "\"msecs\":[%s]" \ 70 | "}," \ 71 | "\"requestBuckets\":{" \ 72 | "\"msecs\":[%s]," \ 73 | "\"counters\":[%s]" \ 74 | "}," \ 75 | "\"overCounts\":{" \ 76 | "\"maxIntegerSize\":%s," \ 77 | "\"requestCounter\":%uA," \ 78 | "\"inBytes\":%uA," \ 79 | "\"outBytes\":%uA," \ 80 | "\"1xx\":%uA," \ 81 | "\"2xx\":%uA," \ 82 | "\"3xx\":%uA," \ 83 | "\"4xx\":%uA," \ 84 | "\"5xx\":%uA," \ 85 | "\"miss\":%uA," \ 86 | "\"bypass\":%uA," \ 87 | "\"expired\":%uA," \ 88 | "\"stale\":%uA," \ 89 | "\"updating\":%uA," \ 90 | "\"revalidated\":%uA," \ 91 | "\"hit\":%uA," \ 92 | "\"scarce\":%uA," \ 93 | "\"requestMsecCounter\":%uA" \ 94 | "}" \ 95 | "}," 96 | #else 97 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_END "}," \ 98 | "\"requestMsecCounter\":%uA," \ 99 | "\"requestMsec\":%M," \ 100 | "\"requestMsecs\":{" \ 101 | "\"times\":[%s]," \ 102 | "\"msecs\":[%s]" \ 103 | "}," \ 104 | "\"requestBuckets\":{" \ 105 | "\"msecs\":[%s]," \ 106 | "\"counters\":[%s]" \ 107 | "}," \ 108 | "\"overCounts\":{" \ 109 | "\"maxIntegerSize\":%s," \ 110 | "\"requestCounter\":%uA," \ 111 | "\"inBytes\":%uA," \ 112 | "\"outBytes\":%uA," \ 113 | "\"1xx\":%uA," \ 114 | "\"2xx\":%uA," \ 115 | "\"3xx\":%uA," \ 116 | "\"4xx\":%uA," \ 117 | "\"5xx\":%uA," \ 118 | "\"requestMsecCounter\":%uA" \ 119 | "}" \ 120 | "}," 121 | #endif 122 | 123 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_STATUS_CODE_START "\"statusCodes\":{" 124 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_STATUS_CODE ",\"%uA\":%uA" 125 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_OTHER_STATUS_CODE "\"other\":%uA" 126 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_SERVER_STATUS_CODE_END "}, " 127 | 128 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_FILTER_S "\"filterZones\":{" 129 | 130 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_UPSTREAM_S "\"upstreamZones\":{" 131 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_UPSTREAM "{\"server\":\"%V\"," \ 132 | "\"requestCounter\":%uA," \ 133 | "\"inBytes\":%uA," \ 134 | "\"outBytes\":%uA," \ 135 | "\"responses\":{" \ 136 | "\"1xx\":%uA," \ 137 | "\"2xx\":%uA," \ 138 | "\"3xx\":%uA," \ 139 | "\"4xx\":%uA," \ 140 | "\"5xx\":%uA" \ 141 | "}," \ 142 | "\"requestMsecCounter\":%uA," \ 143 | "\"requestMsec\":%M," \ 144 | "\"requestMsecs\":{" \ 145 | "\"times\":[%s]," \ 146 | "\"msecs\":[%s]" \ 147 | "}," \ 148 | "\"requestBuckets\":{" \ 149 | "\"msecs\":[%s]," \ 150 | "\"counters\":[%s]" \ 151 | "}," \ 152 | "\"responseMsecCounter\":%uA," \ 153 | "\"responseMsec\":%M," \ 154 | "\"responseMsecs\":{" \ 155 | "\"times\":[%s]," \ 156 | "\"msecs\":[%s]" \ 157 | "}," \ 158 | "\"responseBuckets\":{" \ 159 | "\"msecs\":[%s]," \ 160 | "\"counters\":[%s]" \ 161 | "}," \ 162 | "\"weight\":%ui," \ 163 | "\"maxFails\":%ui," \ 164 | "\"failTimeout\":%T," \ 165 | "\"backup\":%s," \ 166 | "\"down\":%s," \ 167 | "\"overCounts\":{" \ 168 | "\"maxIntegerSize\":%s," \ 169 | "\"requestCounter\":%uA," \ 170 | "\"inBytes\":%uA," \ 171 | "\"outBytes\":%uA," \ 172 | "\"1xx\":%uA," \ 173 | "\"2xx\":%uA," \ 174 | "\"3xx\":%uA," \ 175 | "\"4xx\":%uA," \ 176 | "\"5xx\":%uA," \ 177 | "\"requestMsecCounter\":%uA," \ 178 | "\"responseMsecCounter\":%uA" \ 179 | "}" \ 180 | "}," 181 | 182 | #if (NGX_HTTP_CACHE) 183 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_CACHE_S "\"cacheZones\":{" 184 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_JSON_FMT_CACHE "\"%V\":{" \ 185 | "\"maxSize\":%uA," \ 186 | "\"usedSize\":%uA," \ 187 | "\"inBytes\":%uA," \ 188 | "\"outBytes\":%uA," \ 189 | "\"responses\":{" \ 190 | "\"miss\":%uA," \ 191 | "\"bypass\":%uA," \ 192 | "\"expired\":%uA," \ 193 | "\"stale\":%uA," \ 194 | "\"updating\":%uA," \ 195 | "\"revalidated\":%uA," \ 196 | "\"hit\":%uA," \ 197 | "\"scarce\":%uA" \ 198 | "}," \ 199 | "\"overCounts\":{" \ 200 | "\"maxIntegerSize\":%s," \ 201 | "\"inBytes\":%uA," \ 202 | "\"outBytes\":%uA," \ 203 | "\"miss\":%uA," \ 204 | "\"bypass\":%uA," \ 205 | "\"expired\":%uA," \ 206 | "\"stale\":%uA," \ 207 | "\"updating\":%uA," \ 208 | "\"revalidated\":%uA," \ 209 | "\"hit\":%uA," \ 210 | "\"scarce\":%uA" \ 211 | "}" \ 212 | "}," 213 | #endif 214 | 215 | 216 | u_char *ngx_http_vhost_traffic_status_display_set_main( 217 | ngx_http_request_t *r, u_char *buf); 218 | u_char *ngx_http_vhost_traffic_status_display_set_server_node( 219 | ngx_http_request_t *r, 220 | u_char *buf, ngx_str_t *key, 221 | ngx_http_vhost_traffic_status_node_t *vtsn); 222 | u_char *ngx_http_vhost_traffic_status_display_set_server( 223 | ngx_http_request_t *r, u_char *buf, 224 | ngx_rbtree_node_t *node); 225 | u_char *ngx_http_vhost_traffic_status_display_set_filter_node( 226 | ngx_http_request_t *r, u_char *buf, 227 | ngx_http_vhost_traffic_status_node_t *vtsn); 228 | u_char *ngx_http_vhost_traffic_status_display_set_filter( 229 | ngx_http_request_t *r, u_char *buf, 230 | ngx_rbtree_node_t *node); 231 | u_char *ngx_http_vhost_traffic_status_display_set_upstream_node( 232 | ngx_http_request_t *r, u_char *buf, 233 | ngx_http_upstream_server_t *us, 234 | #if nginx_version > 1007001 235 | ngx_http_vhost_traffic_status_node_t *vtsn 236 | #else 237 | ngx_http_vhost_traffic_status_node_t *vtsn, ngx_str_t *name 238 | #endif 239 | ); 240 | u_char *ngx_http_vhost_traffic_status_display_set_upstream_alone( 241 | ngx_http_request_t *r, u_char *buf, ngx_rbtree_node_t *node); 242 | u_char *ngx_http_vhost_traffic_status_display_set_upstream_group( 243 | ngx_http_request_t *r, u_char *buf); 244 | 245 | #if (NGX_HTTP_CACHE) 246 | u_char *ngx_http_vhost_traffic_status_display_set_cache_node( 247 | ngx_http_request_t *r, u_char *buf, 248 | ngx_http_vhost_traffic_status_node_t *vtsn); 249 | u_char *ngx_http_vhost_traffic_status_display_set_cache( 250 | ngx_http_request_t *r, u_char *buf, 251 | ngx_rbtree_node_t *node); 252 | #endif 253 | 254 | u_char *ngx_http_vhost_traffic_status_display_set(ngx_http_request_t *r, 255 | u_char *buf); 256 | 257 | void ngx_http_vhost_traffic_status_display_encode_uri( 258 | ngx_http_request_t *r, ngx_str_t *uri); 259 | 260 | #endif /* _NGX_HTTP_VTS_DISPLAY_JSON_H_INCLUDED_ */ 261 | 262 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 263 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_dump.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #include "ngx_http_vhost_traffic_status_module.h" 8 | #include "ngx_http_vhost_traffic_status_dump.h" 9 | 10 | 11 | static u_char NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_PAD[] = { 0x53, 0x54, 0x56 }; 12 | 13 | 14 | static ssize_t ngx_http_vhost_traffic_status_dump_header_read(ngx_file_t *file, 15 | ngx_http_vhost_traffic_status_dump_header_t *file_header); 16 | static ssize_t ngx_http_vhost_traffic_status_dump_header_write(ngx_event_t *ev, 17 | ngx_file_t *file); 18 | static void ngx_http_vhost_traffic_status_dump_node_write(ngx_event_t *ev, 19 | ngx_file_t *file, ngx_rbtree_node_t *node); 20 | static ngx_int_t ngx_http_vhost_traffic_status_dump_update_valid(ngx_event_t *ev); 21 | static ngx_int_t ngx_http_vhost_traffic_status_dump_restore_add_node(ngx_event_t *ev, 22 | ngx_http_vhost_traffic_status_node_t *ovtsn, ngx_str_t *key); 23 | 24 | 25 | void 26 | ngx_http_vhost_traffic_status_file_lock(ngx_file_t *file) 27 | { 28 | ngx_err_t err = ngx_lock_fd(file->fd); 29 | 30 | if (err == 0) { 31 | return; 32 | } 33 | 34 | ngx_log_error(NGX_LOG_ALERT, file->log, err, 35 | ngx_lock_fd_n " \"%s\" failed", file->name.data); 36 | } 37 | 38 | 39 | void 40 | ngx_http_vhost_traffic_status_file_unlock(ngx_file_t *file) 41 | { 42 | ngx_err_t err = ngx_unlock_fd(file->fd); 43 | 44 | if (err == 0) { 45 | return; 46 | } 47 | 48 | ngx_log_error(NGX_LOG_ALERT, file->log, err, 49 | ngx_unlock_fd_n " \"%s\" failed", file->name.data); 50 | } 51 | 52 | 53 | void 54 | ngx_http_vhost_traffic_status_file_close(ngx_file_t *file) 55 | { 56 | if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { 57 | ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno, 58 | ngx_close_file_n " \"%s\" failed", file->name.data); 59 | } 60 | } 61 | 62 | 63 | static ssize_t 64 | ngx_http_vhost_traffic_status_dump_header_read(ngx_file_t *file, 65 | ngx_http_vhost_traffic_status_dump_header_t *file_header) 66 | { 67 | ssize_t n; 68 | 69 | ngx_memzero(file_header, sizeof(ngx_http_vhost_traffic_status_dump_header_t)); 70 | 71 | n = ngx_read_file(file, (u_char *) file_header, 72 | sizeof(ngx_http_vhost_traffic_status_dump_header_t), 0); 73 | 74 | return n; 75 | } 76 | 77 | 78 | static ssize_t 79 | ngx_http_vhost_traffic_status_dump_header_write(ngx_event_t *ev, ngx_file_t *file) 80 | { 81 | size_t len; 82 | ssize_t n; 83 | u_char *p; 84 | ngx_http_vhost_traffic_status_ctx_t *ctx; 85 | ngx_http_vhost_traffic_status_dump_header_t file_header; 86 | 87 | ctx = ev->data; 88 | 89 | ngx_memzero(&file_header, sizeof(ngx_http_vhost_traffic_status_dump_header_t)); 90 | 91 | len = (ctx->shm_name.len >= NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_HEADER_NAME_SIZE) 92 | ? NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_HEADER_NAME_SIZE - 1 93 | : ctx->shm_name.len; 94 | 95 | p = file_header.name; 96 | p = ngx_cpymem(p, ctx->shm_name.data, len); 97 | file_header.time = ngx_http_vhost_traffic_status_current_msec(); 98 | file_header.version = nginx_version; 99 | 100 | n = ngx_write_fd(file->fd, &file_header, sizeof(ngx_http_vhost_traffic_status_dump_header_t)); 101 | 102 | return n; 103 | } 104 | 105 | 106 | static void 107 | ngx_http_vhost_traffic_status_dump_node_write(ngx_event_t *ev, ngx_file_t *file, 108 | ngx_rbtree_node_t *node) 109 | { 110 | ngx_http_vhost_traffic_status_ctx_t *ctx; 111 | ngx_http_vhost_traffic_status_node_t *volatile vtsn; 112 | 113 | ctx = ev->data; 114 | 115 | if (node != ctx->rbtree->sentinel) { 116 | vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color; 117 | 118 | (void) ngx_write_fd(file->fd, vtsn, sizeof(ngx_http_vhost_traffic_status_node_t)); 119 | (void) ngx_write_fd(file->fd, vtsn->data, vtsn->len); 120 | (void) ngx_write_fd(file->fd, NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_PAD, 121 | sizeof(NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_PAD)); 122 | 123 | ngx_http_vhost_traffic_status_dump_node_write(ev, file, node->left); 124 | ngx_http_vhost_traffic_status_dump_node_write(ev, file, node->right); 125 | } 126 | } 127 | 128 | 129 | static ngx_int_t 130 | ngx_http_vhost_traffic_status_dump_update_valid(ngx_event_t *ev) 131 | { 132 | size_t len; 133 | ssize_t n; 134 | ngx_fd_t fd; 135 | ngx_int_t rc; 136 | ngx_msec_t current_msec; 137 | ngx_file_t file; 138 | ngx_http_vhost_traffic_status_ctx_t *ctx; 139 | ngx_http_vhost_traffic_status_dump_header_t file_header; 140 | 141 | ctx = ev->data; 142 | 143 | fd = ngx_open_file(ctx->dump_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); 144 | if (fd == NGX_INVALID_FILE) { 145 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, ngx_errno, 146 | ngx_open_file_n " \"%s\" failed", ctx->dump_file.data); 147 | return NGX_OK; 148 | } 149 | 150 | file.fd = fd; 151 | file.name = ctx->dump_file; 152 | file.log = ev->log; 153 | 154 | n = ngx_http_vhost_traffic_status_dump_header_read(&file, &file_header); 155 | 156 | ngx_http_vhost_traffic_status_file_close(&file); 157 | 158 | if (n != sizeof(ngx_http_vhost_traffic_status_dump_header_t)) { 159 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, 160 | "dump_update_valid::dump_header_read() size:%z failed", n); 161 | return NGX_OK; 162 | } 163 | 164 | len = (ctx->shm_name.len >= NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_HEADER_NAME_SIZE) 165 | ? NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_HEADER_NAME_SIZE - 1 166 | : ctx->shm_name.len; 167 | 168 | if (ngx_strncmp(ctx->shm_name.data, file_header.name, len) != 0) { 169 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, 170 | "dump_update_valid::dump_header_read() name[%z]:\"%s\" failed", 171 | len, file_header.name); 172 | return NGX_OK; 173 | } 174 | 175 | current_msec = ngx_http_vhost_traffic_status_current_msec(); 176 | 177 | rc = ((current_msec - file_header.time) > ctx->dump_period) ? NGX_OK : NGX_ERROR; 178 | 179 | return rc; 180 | } 181 | 182 | 183 | ngx_int_t 184 | ngx_http_vhost_traffic_status_dump_execute(ngx_event_t *ev) 185 | { 186 | u_char *name; 187 | ssize_t n; 188 | ngx_fd_t fd; 189 | ngx_file_t file; 190 | ngx_http_vhost_traffic_status_ctx_t *ctx; 191 | 192 | ctx = ev->data; 193 | 194 | name = ctx->dump_file.data; 195 | 196 | fd = ngx_open_file(name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); 197 | 198 | if (fd == NGX_INVALID_FILE) { 199 | ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, 200 | ngx_open_file_n " \"%s\" failed", name); 201 | return NGX_ERROR; 202 | } 203 | 204 | file.fd = fd; 205 | file.name = ctx->dump_file; 206 | file.log = ev->log; 207 | 208 | ngx_http_vhost_traffic_status_file_lock(&file); 209 | 210 | n = ngx_http_vhost_traffic_status_dump_header_write(ev, &file); 211 | if (n != sizeof(ngx_http_vhost_traffic_status_dump_header_t)) { 212 | ngx_log_error(NGX_LOG_ALERT, ev->log, 0, 213 | "dump_execute::dump_header_write() failed"); 214 | 215 | ngx_http_vhost_traffic_status_file_unlock(&file); 216 | ngx_http_vhost_traffic_status_file_close(&file); 217 | 218 | return NGX_ERROR; 219 | } 220 | 221 | ngx_http_vhost_traffic_status_dump_node_write(ev, &file, ctx->rbtree->root); 222 | 223 | ngx_http_vhost_traffic_status_file_unlock(&file); 224 | ngx_http_vhost_traffic_status_file_close(&file); 225 | 226 | return NGX_OK; 227 | } 228 | 229 | 230 | void 231 | ngx_http_vhost_traffic_status_dump_handler(ngx_event_t *ev) 232 | { 233 | ngx_int_t rc; 234 | 235 | if (ngx_exiting) { 236 | return; 237 | } 238 | 239 | rc = ngx_http_vhost_traffic_status_dump_update_valid(ev); 240 | if (rc != NGX_OK) { 241 | goto invalid; 242 | } 243 | 244 | rc = ngx_http_vhost_traffic_status_dump_execute(ev); 245 | if (rc != NGX_OK) { 246 | ngx_log_error(NGX_LOG_ALERT, ev->log, 0, 247 | "dump_handler::dump_header_execute() failed"); 248 | } 249 | 250 | invalid: 251 | 252 | ngx_add_timer(ev, 1000); 253 | } 254 | 255 | 256 | static ngx_int_t 257 | ngx_http_vhost_traffic_status_dump_restore_add_node(ngx_event_t *ev, 258 | ngx_http_vhost_traffic_status_node_t *ovtsn, ngx_str_t *key) 259 | { 260 | size_t size; 261 | uint32_t hash; 262 | ngx_slab_pool_t *shpool; 263 | ngx_rbtree_node_t *node; 264 | ngx_http_vhost_traffic_status_ctx_t *ctx; 265 | ngx_http_vhost_traffic_status_node_t *vtsn; 266 | 267 | ctx = ev->data; 268 | 269 | if (key->len == 0) { 270 | return NGX_ERROR; 271 | } 272 | 273 | shpool = (ngx_slab_pool_t *) ctx->shm_zone->shm.addr; 274 | 275 | ngx_shmtx_lock(&shpool->mutex); 276 | 277 | /* find node */ 278 | hash = ngx_crc32_short(key->data, key->len); 279 | 280 | node = ngx_http_vhost_traffic_status_node_lookup(ctx->rbtree, key, hash); 281 | 282 | /* copy node */ 283 | if (node == NULL) { 284 | size = offsetof(ngx_rbtree_node_t, color) 285 | + offsetof(ngx_http_vhost_traffic_status_node_t, data) 286 | + key->len; 287 | 288 | node = ngx_slab_alloc_locked(shpool, size); 289 | if (node == NULL) { 290 | ngx_log_error(NGX_LOG_ALERT, ev->log, 0, 291 | "dump_restore_add_node::ngx_slab_alloc_locked() failed"); 292 | 293 | ngx_shmtx_unlock(&shpool->mutex); 294 | return NGX_ERROR; 295 | } 296 | 297 | vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color; 298 | 299 | node->key = hash; 300 | 301 | *vtsn = *ovtsn; 302 | 303 | ngx_memcpy(vtsn->data, key->data, key->len); 304 | 305 | ngx_rbtree_insert(ctx->rbtree, node); 306 | } 307 | 308 | ngx_shmtx_unlock(&shpool->mutex); 309 | 310 | return NGX_OK; 311 | } 312 | 313 | 314 | void 315 | ngx_http_vhost_traffic_status_dump_restore(ngx_event_t *ev) 316 | { 317 | off_t offset; 318 | size_t len; 319 | ssize_t n; 320 | u_char *buf, *pad; 321 | ngx_fd_t fd; 322 | ngx_str_t key; 323 | ngx_int_t rc; 324 | ngx_file_t file; 325 | ngx_http_vhost_traffic_status_ctx_t *ctx; 326 | ngx_http_vhost_traffic_status_node_t vtsn; 327 | ngx_http_vhost_traffic_status_dump_header_t file_header; 328 | 329 | ctx = ev->data; 330 | 331 | fd = ngx_open_file(ctx->dump_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); 332 | if (fd == NGX_INVALID_FILE) { 333 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, ngx_errno, 334 | ngx_open_file_n " \"%s\" failed", ctx->dump_file.data); 335 | return; 336 | } 337 | 338 | file.fd = fd; 339 | file.name = ctx->dump_file; 340 | file.log = ev->log; 341 | 342 | n = ngx_http_vhost_traffic_status_dump_header_read(&file, &file_header); 343 | 344 | if (n != sizeof(ngx_http_vhost_traffic_status_dump_header_t)) { 345 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, 346 | "dump_restore::dump_header_read() size:%z failed", n); 347 | ngx_http_vhost_traffic_status_file_close(&file); 348 | return; 349 | } 350 | 351 | len = (ctx->shm_name.len >= NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_HEADER_NAME_SIZE) 352 | ? NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_HEADER_NAME_SIZE - 1 353 | : ctx->shm_name.len; 354 | 355 | if (ngx_strncmp(ctx->shm_name.data, file_header.name, len) != 0) { 356 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, 357 | "dump_restore::dump_header_read() name[%z]:\"%s\" failed", 358 | len, file_header.name); 359 | ngx_http_vhost_traffic_status_file_close(&file); 360 | return; 361 | } 362 | 363 | buf = ngx_pcalloc(ngx_cycle->pool, NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_BUF_SIZE); 364 | pad = ngx_pcalloc(ngx_cycle->pool, sizeof(NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_PAD)); 365 | if (buf == NULL || pad == NULL) { 366 | ngx_log_error(NGX_LOG_ALERT, ev->log, 0, 367 | "dump_restore::ngx_pcalloc() failed"); 368 | ngx_http_vhost_traffic_status_file_close(&file); 369 | return; 370 | } 371 | 372 | offset = n; 373 | 374 | for ( ;; ) { 375 | ngx_memzero(buf, NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_BUF_SIZE); 376 | 377 | /* read: node */ 378 | n = ngx_read_file(&file, (u_char *) &vtsn, 379 | sizeof(ngx_http_vhost_traffic_status_node_t), offset); 380 | 381 | if (n == NGX_ERROR || n == 0 382 | || n != sizeof(ngx_http_vhost_traffic_status_node_t)) { 383 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, 384 | "dump_restore::ngx_read_file() node size:%z failed", n); 385 | break; 386 | } 387 | 388 | if (vtsn.len > NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_BUF_SIZE) { 389 | offset += vtsn.len + sizeof(NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_PAD); 390 | continue; 391 | } 392 | 393 | /* read: data */ 394 | offset += n; 395 | n = ngx_read_file(&file, buf, vtsn.len, offset); 396 | if (n >= 0 && vtsn.len <= SSIZE_MAX && n != (ssize_t) vtsn.len) { 397 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, 398 | "dump_restore::ngx_read_file() read:%z, data:%z failed", 399 | n, vtsn.len); 400 | break; 401 | } 402 | 403 | /* read: pad */ 404 | offset += n; 405 | n = ngx_read_file(&file, pad, 406 | sizeof(NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_PAD), 407 | offset); 408 | if (n == NGX_ERROR || n == 0 409 | || n != sizeof(NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_PAD)) 410 | { 411 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, 412 | "dump_restore::ngx_read_file() pad size:%z failed", n); 413 | break; 414 | } 415 | 416 | if (ngx_memcmp(NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_PAD, pad, n) != 0) { 417 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, 418 | "dump_restore::ngx_read_file() pad does not match"); 419 | break; 420 | } 421 | 422 | /* push: node */ 423 | key.len = vtsn.len; 424 | key.data = buf; 425 | 426 | rc = ngx_http_vhost_traffic_status_dump_restore_add_node(ev, &vtsn, &key); 427 | if (rc != NGX_OK) { 428 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, 429 | "dump_restore::dump_restore_add_node() failed"); 430 | break; 431 | } 432 | 433 | offset += n; 434 | } 435 | 436 | ngx_http_vhost_traffic_status_file_close(&file); 437 | } 438 | 439 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 440 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_dump.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_DUMP_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_DUMP_H_INCLUDED_ 9 | 10 | 11 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_HEADER_NAME_SIZE 128 12 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_DATA_BUF_SIZE 1024 13 | 14 | 15 | typedef struct { 16 | u_char name[NGX_HTTP_VHOST_TRAFFIC_STATUS_DUMP_HEADER_NAME_SIZE]; 17 | ngx_msec_t time; 18 | ngx_uint_t version; 19 | } ngx_http_vhost_traffic_status_dump_header_t; 20 | 21 | 22 | void ngx_http_vhost_traffic_status_file_lock(ngx_file_t *file); 23 | void ngx_http_vhost_traffic_status_file_unlock(ngx_file_t *file); 24 | void ngx_http_vhost_traffic_status_file_close(ngx_file_t *file); 25 | 26 | ngx_int_t ngx_http_vhost_traffic_status_dump_execute(ngx_event_t *ev); 27 | void ngx_http_vhost_traffic_status_dump_handler(ngx_event_t *ev); 28 | void ngx_http_vhost_traffic_status_dump_restore(ngx_event_t *ev); 29 | 30 | 31 | #endif /* _NGX_HTTP_VTS_DUMP_H_INCLUDED_ */ 32 | 33 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 34 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_filter.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #include "ngx_http_vhost_traffic_status_module.h" 8 | #include "ngx_http_vhost_traffic_status_filter.h" 9 | 10 | 11 | int ngx_libc_cdecl 12 | ngx_http_traffic_status_filter_cmp_hashs(const void *one, const void *two) 13 | { 14 | ngx_http_vhost_traffic_status_filter_uniq_t *first = 15 | (ngx_http_vhost_traffic_status_filter_uniq_t *) one; 16 | ngx_http_vhost_traffic_status_filter_uniq_t *second = 17 | (ngx_http_vhost_traffic_status_filter_uniq_t *) two; 18 | 19 | return (first->hash - second->hash); 20 | } 21 | 22 | 23 | int ngx_libc_cdecl 24 | ngx_http_traffic_status_filter_cmp_keys(const void *one, const void *two) 25 | { 26 | ngx_http_vhost_traffic_status_filter_key_t *first = 27 | (ngx_http_vhost_traffic_status_filter_key_t *) one; 28 | ngx_http_vhost_traffic_status_filter_key_t *second = 29 | (ngx_http_vhost_traffic_status_filter_key_t *) two; 30 | 31 | return (int) ngx_strcmp(first->key.data, second->key.data); 32 | } 33 | 34 | 35 | ngx_int_t 36 | ngx_http_vhost_traffic_status_filter_unique(ngx_pool_t *pool, ngx_array_t **keys) 37 | { 38 | uint32_t hash; 39 | u_char *p; 40 | ngx_str_t key; 41 | ngx_uint_t i, n; 42 | ngx_array_t *uniqs, *filter_keys; 43 | ngx_http_vhost_traffic_status_filter_t *filter, *filters; 44 | ngx_http_vhost_traffic_status_filter_uniq_t *filter_uniqs; 45 | 46 | if (*keys == NULL) { 47 | return NGX_OK; 48 | } 49 | 50 | uniqs = ngx_array_create(pool, 1, 51 | sizeof(ngx_http_vhost_traffic_status_filter_uniq_t)); 52 | if (uniqs == NULL) { 53 | return NGX_ERROR; 54 | } 55 | 56 | /* init array */ 57 | filter_keys = NULL; 58 | filter_uniqs = NULL; 59 | 60 | filters = (*keys)->elts; 61 | n = (*keys)->nelts; 62 | 63 | for (i = 0; i < n; i++) { 64 | key.len = filters[i].filter_key.value.len 65 | + filters[i].filter_name.value.len; 66 | key.data = ngx_pcalloc(pool, key.len); 67 | if (key.data == NULL) { 68 | return NGX_ERROR; 69 | } 70 | 71 | p = key.data; 72 | p = ngx_cpymem(p, filters[i].filter_key.value.data, 73 | filters[i].filter_key.value.len); 74 | ngx_memcpy(p, filters[i].filter_name.value.data, 75 | filters[i].filter_name.value.len); 76 | hash = ngx_crc32_short(key.data, key.len); 77 | 78 | filter_uniqs = ngx_array_push(uniqs); 79 | if (filter_uniqs == NULL) { 80 | return NGX_ERROR; 81 | } 82 | 83 | filter_uniqs->hash = hash; 84 | filter_uniqs->index = i; 85 | 86 | if (p != NULL) { 87 | ngx_pfree(pool, key.data); 88 | } 89 | } 90 | 91 | filter_uniqs = uniqs->elts; 92 | n = uniqs->nelts; 93 | 94 | ngx_qsort(filter_uniqs, (size_t) n, 95 | sizeof(ngx_http_vhost_traffic_status_filter_uniq_t), 96 | ngx_http_traffic_status_filter_cmp_hashs); 97 | 98 | hash = 0; 99 | for (i = 0; i < n; i++) { 100 | if (filter_uniqs[i].hash == hash) { 101 | continue; 102 | } 103 | 104 | hash = filter_uniqs[i].hash; 105 | 106 | if (filter_keys == NULL) { 107 | filter_keys = ngx_array_create(pool, 1, 108 | sizeof(ngx_http_vhost_traffic_status_filter_t)); 109 | if (filter_keys == NULL) { 110 | return NGX_ERROR; 111 | } 112 | } 113 | 114 | filter = ngx_array_push(filter_keys); 115 | if (filter == NULL) { 116 | return NGX_ERROR; 117 | } 118 | 119 | ngx_memcpy(filter, &filters[filter_uniqs[i].index], 120 | sizeof(ngx_http_vhost_traffic_status_filter_t)); 121 | 122 | } 123 | 124 | if ((*keys)->nelts != filter_keys->nelts) { 125 | *keys = filter_keys; 126 | } 127 | 128 | return NGX_OK; 129 | } 130 | 131 | 132 | ngx_int_t 133 | ngx_http_vhost_traffic_status_filter_get_keys(ngx_http_request_t *r, 134 | ngx_array_t **filter_keys, ngx_rbtree_node_t *node) 135 | { 136 | ngx_int_t rc; 137 | ngx_str_t key; 138 | ngx_http_vhost_traffic_status_ctx_t *ctx; 139 | ngx_http_vhost_traffic_status_node_t *vtsn; 140 | ngx_http_vhost_traffic_status_filter_key_t *keys; 141 | 142 | ctx = ngx_http_get_module_main_conf(r, ngx_http_vhost_traffic_status_module); 143 | 144 | if (node != ctx->rbtree->sentinel) { 145 | vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color; 146 | 147 | if (vtsn->stat_upstream.type == NGX_HTTP_VHOST_TRAFFIC_STATUS_UPSTREAM_FG) { 148 | key.data = vtsn->data; 149 | key.len = vtsn->len; 150 | 151 | rc = ngx_http_vhost_traffic_status_node_position_key(&key, 1); 152 | if (rc != NGX_OK) { 153 | goto next; 154 | } 155 | 156 | if (*filter_keys == NULL) { 157 | *filter_keys = ngx_array_create(r->pool, 1, 158 | sizeof(ngx_http_vhost_traffic_status_filter_key_t)); 159 | 160 | if (*filter_keys == NULL) { 161 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 162 | "filter_get_keys::ngx_array_create() failed"); 163 | return NGX_ERROR; 164 | } 165 | } 166 | 167 | keys = ngx_array_push(*filter_keys); 168 | if (keys == NULL) { 169 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 170 | "filter_get_keys::ngx_array_push() failed"); 171 | return NGX_ERROR; 172 | } 173 | 174 | keys->key.len = key.len; 175 | /* 1 byte for terminating '\0' for ngx_strcmp() */ 176 | keys->key.data = ngx_pcalloc(r->pool, key.len + 1); 177 | if (keys->key.data == NULL) { 178 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 179 | "filter_get_keys::ngx_pcalloc() failed"); 180 | } 181 | 182 | ngx_memcpy(keys->key.data, key.data, key.len); 183 | } 184 | next: 185 | rc = ngx_http_vhost_traffic_status_filter_get_keys(r, filter_keys, node->left); 186 | if (rc != NGX_OK) { 187 | return rc; 188 | } 189 | 190 | rc = ngx_http_vhost_traffic_status_filter_get_keys(r, filter_keys, node->right); 191 | if (rc != NGX_OK) { 192 | return rc; 193 | } 194 | } 195 | 196 | return NGX_OK; 197 | } 198 | 199 | 200 | ngx_int_t 201 | ngx_http_vhost_traffic_status_filter_get_nodes(ngx_http_request_t *r, 202 | ngx_array_t **filter_nodes, ngx_str_t *name, 203 | ngx_rbtree_node_t *node) 204 | { 205 | ngx_int_t rc; 206 | ngx_str_t key; 207 | ngx_http_vhost_traffic_status_ctx_t *ctx; 208 | ngx_http_vhost_traffic_status_node_t *vtsn; 209 | ngx_http_vhost_traffic_status_filter_node_t *nodes; 210 | 211 | ctx = ngx_http_get_module_main_conf(r, ngx_http_vhost_traffic_status_module); 212 | 213 | if (node != ctx->rbtree->sentinel) { 214 | vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color; 215 | 216 | if (vtsn->stat_upstream.type == NGX_HTTP_VHOST_TRAFFIC_STATUS_UPSTREAM_FG) { 217 | key.data = vtsn->data; 218 | key.len = vtsn->len; 219 | 220 | rc = ngx_http_vhost_traffic_status_node_position_key(&key, 1); 221 | if (rc != NGX_OK) { 222 | goto next; 223 | } 224 | 225 | if (name->len != key.len) { 226 | goto next; 227 | } 228 | 229 | if (ngx_strncmp(name->data, key.data, key.len) != 0) { 230 | goto next; 231 | } 232 | 233 | if (*filter_nodes == NULL) { 234 | *filter_nodes = ngx_array_create(r->pool, 1, 235 | sizeof(ngx_http_vhost_traffic_status_filter_node_t)); 236 | 237 | if (*filter_nodes == NULL) { 238 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 239 | "filter_get_nodes::ngx_array_create() failed"); 240 | return NGX_ERROR; 241 | } 242 | } 243 | 244 | nodes = ngx_array_push(*filter_nodes); 245 | if (nodes == NULL) { 246 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 247 | "filter_get_nodes::ngx_array_push() failed"); 248 | return NGX_ERROR; 249 | } 250 | 251 | nodes->node = vtsn; 252 | } 253 | next: 254 | rc = ngx_http_vhost_traffic_status_filter_get_nodes(r, filter_nodes, name, node->left); 255 | if (rc != NGX_OK) { 256 | return rc; 257 | } 258 | 259 | rc = ngx_http_vhost_traffic_status_filter_get_nodes(r, filter_nodes, name, node->right); 260 | if (rc != NGX_OK) { 261 | return rc; 262 | } 263 | } 264 | 265 | return NGX_OK; 266 | } 267 | 268 | 269 | ngx_int_t 270 | ngx_http_vhost_traffic_status_filter_max_node_match(ngx_http_request_t *r, 271 | ngx_str_t *filter) 272 | { 273 | ngx_uint_t i, n; 274 | ngx_http_vhost_traffic_status_ctx_t *ctx; 275 | ngx_http_vhost_traffic_status_filter_match_t *matches; 276 | 277 | ctx = ngx_http_get_module_main_conf(r, ngx_http_vhost_traffic_status_module); 278 | 279 | if (ctx->filter_max_node_matches == NULL) { 280 | return NGX_OK; 281 | } 282 | 283 | matches = ctx->filter_max_node_matches->elts; 284 | n = ctx->filter_max_node_matches->nelts; 285 | 286 | /* disabled */ 287 | if (n == 0) { 288 | return NGX_OK; 289 | } 290 | 291 | for (i = 0; i < n; i++) { 292 | if (ngx_strncmp(filter->data, matches[i].match.data, matches[i].match.len) == 0) { 293 | return NGX_OK; 294 | } 295 | } 296 | 297 | return NGX_ERROR; 298 | } 299 | 300 | 301 | char * 302 | ngx_http_vhost_traffic_status_filter_by_set_key(ngx_conf_t *cf, ngx_command_t *cmd, 303 | void *conf) 304 | { 305 | ngx_http_vhost_traffic_status_loc_conf_t *vtscf = conf; 306 | 307 | ngx_str_t *value, name; 308 | ngx_array_t *filter_keys; 309 | ngx_http_compile_complex_value_t ccv; 310 | ngx_http_vhost_traffic_status_ctx_t *ctx; 311 | ngx_http_vhost_traffic_status_filter_t *filter; 312 | 313 | ctx = ngx_http_conf_get_module_main_conf(cf, ngx_http_vhost_traffic_status_module); 314 | if (ctx == NULL) { 315 | return NGX_CONF_ERROR; 316 | } 317 | 318 | value = cf->args->elts; 319 | if (value[1].len == 0) { 320 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty key pattern"); 321 | return NGX_CONF_ERROR; 322 | } 323 | 324 | filter_keys = (cf->cmd_type == NGX_HTTP_MAIN_CONF) ? ctx->filter_keys : vtscf->filter_keys; 325 | if (filter_keys == NULL) { 326 | filter_keys = ngx_array_create(cf->pool, 1, 327 | sizeof(ngx_http_vhost_traffic_status_filter_t)); 328 | if (filter_keys == NULL) { 329 | return NGX_CONF_ERROR; 330 | } 331 | } 332 | 333 | filter = ngx_array_push(filter_keys); 334 | if (filter == NULL) { 335 | return NGX_CONF_ERROR; 336 | } 337 | 338 | /* first argument process */ 339 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 340 | 341 | ccv.cf = cf; 342 | ccv.value = &value[1]; 343 | ccv.complex_value = &filter->filter_key; 344 | 345 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 346 | return NGX_CONF_ERROR; 347 | } 348 | 349 | /* second argument process */ 350 | if (cf->args->nelts == 3) { 351 | name = value[2]; 352 | 353 | } else { 354 | ngx_str_set(&name, ""); 355 | } 356 | 357 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 358 | 359 | ccv.cf = cf; 360 | ccv.value = &name; 361 | ccv.complex_value = &filter->filter_name; 362 | 363 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 364 | return NGX_CONF_ERROR; 365 | } 366 | 367 | if (cf->cmd_type == NGX_HTTP_MAIN_CONF) { 368 | ctx->filter_keys = filter_keys; 369 | 370 | } else { 371 | vtscf->filter_keys = filter_keys; 372 | } 373 | 374 | return NGX_CONF_OK; 375 | } 376 | 377 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 378 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_filter.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_FILTER_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_FILTER_H_INCLUDED_ 9 | 10 | 11 | typedef struct { 12 | ngx_http_complex_value_t filter_key; 13 | ngx_http_complex_value_t filter_name; 14 | } ngx_http_vhost_traffic_status_filter_t; 15 | 16 | 17 | typedef struct { 18 | ngx_str_t key; 19 | } ngx_http_vhost_traffic_status_filter_key_t; 20 | 21 | 22 | typedef struct { 23 | uint32_t hash; 24 | ngx_uint_t index; 25 | } ngx_http_vhost_traffic_status_filter_uniq_t; 26 | 27 | 28 | typedef struct { 29 | ngx_http_vhost_traffic_status_node_t *node; 30 | } ngx_http_vhost_traffic_status_filter_node_t; 31 | 32 | 33 | typedef struct { 34 | ngx_str_t match; 35 | } ngx_http_vhost_traffic_status_filter_match_t; 36 | 37 | 38 | int ngx_libc_cdecl ngx_http_traffic_status_filter_cmp_hashs( 39 | const void *one, const void *two); 40 | int ngx_libc_cdecl ngx_http_traffic_status_filter_cmp_keys( 41 | const void *one, const void *two); 42 | ngx_int_t ngx_http_vhost_traffic_status_filter_unique( 43 | ngx_pool_t *pool, ngx_array_t **keys); 44 | ngx_int_t ngx_http_vhost_traffic_status_filter_get_keys( 45 | ngx_http_request_t *r, ngx_array_t **filter_keys, 46 | ngx_rbtree_node_t *node); 47 | ngx_int_t ngx_http_vhost_traffic_status_filter_get_nodes( 48 | ngx_http_request_t *r, ngx_array_t **filter_nodes, 49 | ngx_str_t *name, ngx_rbtree_node_t *node); 50 | ngx_int_t ngx_http_vhost_traffic_status_filter_max_node_match( 51 | ngx_http_request_t *r, ngx_str_t *filter); 52 | 53 | char *ngx_http_vhost_traffic_status_filter_by_set_key(ngx_conf_t *cf, 54 | ngx_command_t *cmd, void *conf); 55 | 56 | 57 | #endif /* _NGX_HTTP_VTS_FILTER_H_INCLUDED_ */ 58 | 59 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 60 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_limit.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #include "ngx_http_vhost_traffic_status_module.h" 8 | #include "ngx_http_vhost_traffic_status_filter.h" 9 | #include "ngx_http_vhost_traffic_status_limit.h" 10 | 11 | 12 | ngx_int_t 13 | ngx_http_vhost_traffic_status_limit_handler(ngx_http_request_t *r) 14 | { 15 | ngx_int_t rc; 16 | ngx_http_vhost_traffic_status_ctx_t *ctx; 17 | ngx_http_vhost_traffic_status_loc_conf_t *vtscf; 18 | 19 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 20 | "http vts limit handler"); 21 | 22 | ctx = ngx_http_get_module_main_conf(r, ngx_http_vhost_traffic_status_module); 23 | 24 | vtscf = ngx_http_get_module_loc_conf(r, ngx_http_vhost_traffic_status_module); 25 | 26 | if (!ctx->enable || !vtscf->limit || vtscf->bypass_limit) { 27 | return NGX_DECLINED; 28 | } 29 | 30 | /* limit traffic of server */ 31 | rc = ngx_http_vhost_traffic_status_limit_handler_traffic(r, ctx->limit_traffics); 32 | if (rc != NGX_DECLINED) { 33 | return rc; 34 | } 35 | 36 | rc = ngx_http_vhost_traffic_status_limit_handler_traffic(r, vtscf->limit_traffics); 37 | if (rc != NGX_DECLINED) { 38 | return rc; 39 | } 40 | 41 | /* limit traffic of filter */ 42 | rc = ngx_http_vhost_traffic_status_limit_handler_traffic(r, ctx->limit_filter_traffics); 43 | if (rc != NGX_DECLINED) { 44 | return rc; 45 | } 46 | 47 | rc = ngx_http_vhost_traffic_status_limit_handler_traffic(r, vtscf->limit_filter_traffics); 48 | if (rc != NGX_DECLINED) { 49 | return rc; 50 | } 51 | 52 | return NGX_DECLINED; 53 | } 54 | 55 | 56 | ngx_int_t 57 | ngx_http_vhost_traffic_status_limit_handler_traffic(ngx_http_request_t *r, 58 | ngx_array_t *traffics) 59 | { 60 | unsigned type; 61 | ngx_str_t variable, key, dst; 62 | ngx_int_t rc; 63 | ngx_uint_t i, n; 64 | ngx_atomic_t traffic_used; 65 | ngx_slab_pool_t *shpool; 66 | ngx_rbtree_node_t *node; 67 | ngx_http_vhost_traffic_status_node_t *vtsn; 68 | ngx_http_vhost_traffic_status_limit_t *limits; 69 | ngx_http_vhost_traffic_status_loc_conf_t *vtscf; 70 | 71 | vtscf = ngx_http_get_module_loc_conf(r, ngx_http_vhost_traffic_status_module); 72 | 73 | rc = NGX_DECLINED; 74 | 75 | if (traffics == NULL) { 76 | return rc; 77 | } 78 | 79 | shpool = (ngx_slab_pool_t *) vtscf->shm_zone->shm.addr; 80 | 81 | ngx_shmtx_lock(&shpool->mutex); 82 | 83 | limits = traffics->elts; 84 | n = traffics->nelts; 85 | 86 | for (i = 0; i < n; i++) { 87 | if (limits[i].variable.value.len <= 0) { 88 | continue; 89 | } 90 | 91 | /* init */ 92 | traffic_used = 0; 93 | variable.len = 0; 94 | key.len = 0; 95 | dst.len = 0; 96 | type = limits[i].type; 97 | 98 | if (ngx_http_complex_value(r, &limits[i].variable, &variable) != NGX_OK) { 99 | goto done; 100 | } 101 | 102 | if (variable.len == 0) { 103 | continue; 104 | } 105 | 106 | /* traffic of filter */ 107 | if (limits[i].key.value.len > 0) { 108 | if (ngx_http_complex_value(r, &limits[i].key, &key) != NGX_OK) { 109 | goto done; 110 | } 111 | 112 | if (key.len == 0) { 113 | continue; 114 | } 115 | 116 | node = ngx_http_vhost_traffic_status_find_node(r, &key, type, 0); 117 | 118 | if (node == NULL) { 119 | continue; 120 | } 121 | 122 | vtscf->node_caches[type] = node; 123 | 124 | vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color; 125 | 126 | traffic_used = (ngx_atomic_t) ngx_http_vhost_traffic_status_node_member(vtsn, &variable); 127 | 128 | /* traffic of server */ 129 | } else { 130 | ngx_http_vhost_traffic_status_find_name(r, &dst); 131 | 132 | if (ngx_http_vhost_traffic_status_node_generate_key(r->pool, &key, &dst, type) 133 | != NGX_OK || key.len == 0) 134 | { 135 | goto done; 136 | } 137 | 138 | node = ngx_http_vhost_traffic_status_find_node(r, &key, type, 0); 139 | 140 | if (node == NULL) { 141 | continue; 142 | } 143 | 144 | vtscf->node_caches[type] = node; 145 | 146 | vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color; 147 | 148 | traffic_used = (ngx_atomic_t) ngx_http_vhost_traffic_status_node_member(vtsn, &variable); 149 | } 150 | 151 | if (traffic_used > limits[i].size) { 152 | rc = limits[i].code; 153 | goto done; 154 | } 155 | } 156 | 157 | done: 158 | 159 | ngx_shmtx_unlock(&shpool->mutex); 160 | 161 | return rc; 162 | } 163 | 164 | 165 | char * 166 | ngx_http_vhost_traffic_status_limit_traffic(ngx_conf_t *cf, ngx_command_t *cmd, 167 | void *conf) 168 | { 169 | ngx_http_vhost_traffic_status_loc_conf_t *vtscf = conf; 170 | 171 | u_char *p; 172 | off_t size; 173 | ngx_str_t *value, s; 174 | ngx_array_t *limit_traffics; 175 | ngx_http_compile_complex_value_t ccv; 176 | ngx_http_vhost_traffic_status_ctx_t *ctx; 177 | ngx_http_vhost_traffic_status_limit_t *traffic; 178 | 179 | ctx = ngx_http_conf_get_module_main_conf(cf, ngx_http_vhost_traffic_status_module); 180 | if (ctx == NULL) { 181 | return NGX_CONF_ERROR; 182 | } 183 | 184 | value = cf->args->elts; 185 | if (value[1].len == 0) { 186 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "limit_traffic() empty value pattern"); 187 | return NGX_CONF_ERROR; 188 | } 189 | 190 | if (value[1].len > 5 && ngx_strstrn(value[1].data, "$vts_", 5 - 1)) { 191 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "limit_traffic() $vts_* is not allowed here"); 192 | return NGX_CONF_ERROR; 193 | } 194 | 195 | p = (u_char *) ngx_strchr(value[1].data, ':'); 196 | if (p == NULL) { 197 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "limit_traffic() empty size pattern"); 198 | return NGX_CONF_ERROR; 199 | } 200 | 201 | s.data = p + 1; 202 | s.len = value[1].data + value[1].len - s.data; 203 | 204 | size = ngx_parse_offset(&s); 205 | if (size == NGX_ERROR) { 206 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 207 | "limit_traffic() invalid limit size \"%V\"", &value[1]); 208 | return NGX_CONF_ERROR; 209 | } 210 | 211 | limit_traffics = (cf->cmd_type == NGX_HTTP_MAIN_CONF) 212 | ? ctx->limit_traffics 213 | : vtscf->limit_traffics; 214 | if (limit_traffics == NULL) { 215 | limit_traffics = ngx_array_create(cf->pool, 1, 216 | sizeof(ngx_http_vhost_traffic_status_limit_t)); 217 | if (limit_traffics == NULL) { 218 | return NGX_CONF_ERROR; 219 | } 220 | } 221 | 222 | traffic = ngx_array_push(limit_traffics); 223 | if (traffic == NULL) { 224 | return NGX_CONF_ERROR; 225 | } 226 | 227 | value[1].len = p - value[1].data; 228 | 229 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 230 | 231 | ccv.cf = cf; 232 | ccv.value = &value[1]; 233 | ccv.complex_value = &traffic->variable; 234 | 235 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 236 | return NGX_CONF_ERROR; 237 | } 238 | 239 | traffic->size = (ngx_atomic_t) size; 240 | 241 | traffic->code = (cf->args->nelts == 3) 242 | ? (ngx_uint_t) ngx_atoi(value[2].data, value[2].len) 243 | : NGX_HTTP_SERVICE_UNAVAILABLE; 244 | 245 | traffic->type = NGX_HTTP_VHOST_TRAFFIC_STATUS_UPSTREAM_NO; 246 | 247 | traffic->key.value.len = 0; 248 | 249 | if (cf->cmd_type == NGX_HTTP_MAIN_CONF) { 250 | ctx->limit_traffics = limit_traffics; 251 | 252 | } else { 253 | vtscf->limit_traffics = limit_traffics; 254 | } 255 | 256 | return NGX_CONF_OK; 257 | } 258 | 259 | 260 | char * 261 | ngx_http_vhost_traffic_status_limit_traffic_by_set_key(ngx_conf_t *cf, ngx_command_t *cmd, 262 | void *conf) 263 | { 264 | ngx_http_vhost_traffic_status_loc_conf_t *vtscf = conf; 265 | 266 | u_char *p; 267 | off_t size; 268 | ngx_str_t *value, s, alpha; 269 | ngx_array_t *limit_traffics; 270 | ngx_http_compile_complex_value_t ccv; 271 | ngx_http_vhost_traffic_status_ctx_t *ctx; 272 | ngx_http_vhost_traffic_status_limit_t *traffic; 273 | 274 | ctx = ngx_http_conf_get_module_main_conf(cf, ngx_http_vhost_traffic_status_module); 275 | if (ctx == NULL) { 276 | return NGX_CONF_ERROR; 277 | } 278 | 279 | value = cf->args->elts; 280 | if (value[1].len == 0) { 281 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "limit_traffic_by_set_key() empty key pattern"); 282 | return NGX_CONF_ERROR; 283 | } 284 | 285 | if (value[2].len == 0) { 286 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "limit_traffic_by_set_key() empty value pattern"); 287 | return NGX_CONF_ERROR; 288 | } 289 | 290 | if (value[2].len > 5 && ngx_strstrn(value[2].data, "$vts_", 5 - 1)) { 291 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 292 | "limit_traffic_by_set_key() $vts_* is not allowed here"); 293 | return NGX_CONF_ERROR; 294 | } 295 | 296 | p = (u_char *) ngx_strchr(value[2].data, ':'); 297 | if (p == NULL) { 298 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "limit_traffic_by_set_key() empty size pattern"); 299 | return NGX_CONF_ERROR; 300 | } 301 | 302 | s.data = p + 1; 303 | s.len = value[2].data + value[2].len - s.data; 304 | 305 | size = ngx_parse_offset(&s); 306 | if (size == NGX_ERROR) { 307 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 308 | "limit_traffic_by_set_key() invalid limit size \"%V\"", &value[2]); 309 | return NGX_CONF_ERROR; 310 | } 311 | 312 | limit_traffics = (cf->cmd_type == NGX_HTTP_MAIN_CONF) 313 | ? ctx->limit_filter_traffics 314 | : vtscf->limit_filter_traffics; 315 | if (limit_traffics == NULL) { 316 | limit_traffics = ngx_array_create(cf->pool, 1, 317 | sizeof(ngx_http_vhost_traffic_status_limit_t)); 318 | if (limit_traffics == NULL) { 319 | return NGX_CONF_ERROR; 320 | } 321 | } 322 | 323 | traffic = ngx_array_push(limit_traffics); 324 | if (traffic == NULL) { 325 | return NGX_CONF_ERROR; 326 | } 327 | 328 | /* set key to be limited */ 329 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 330 | 331 | (void) ngx_http_vhost_traffic_status_replace_chrc(&value[1], '@', 332 | NGX_HTTP_VHOST_TRAFFIC_STATUS_KEY_SEPARATOR); 333 | ngx_str_set(&alpha, "[:alpha:]"); 334 | if (ngx_http_vhost_traffic_status_replace_strc(&value[1], &alpha, '@') != NGX_OK) { 335 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 336 | "limit_traffic_by_set_key()::replace_strc() failed"); 337 | } 338 | 339 | ccv.cf = cf; 340 | ccv.value = &value[1]; 341 | ccv.complex_value = &traffic->key; 342 | 343 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 344 | return NGX_CONF_ERROR; 345 | } 346 | 347 | /* set member to be limited */ 348 | value[2].len = p - value[2].data; 349 | 350 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 351 | 352 | ccv.cf = cf; 353 | ccv.value = &value[2]; 354 | ccv.complex_value = &traffic->variable; 355 | 356 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 357 | return NGX_CONF_ERROR; 358 | } 359 | 360 | traffic->size = (ngx_atomic_t) size; 361 | 362 | traffic->code = (cf->args->nelts == 4) 363 | ? (ngx_uint_t) ngx_atoi(value[3].data, value[3].len) 364 | : NGX_HTTP_SERVICE_UNAVAILABLE; 365 | 366 | traffic->type = ngx_http_vhost_traffic_status_string_to_group(value[1].data); 367 | 368 | if (cf->cmd_type == NGX_HTTP_MAIN_CONF) { 369 | ctx->limit_filter_traffics = limit_traffics; 370 | 371 | } else { 372 | vtscf->limit_filter_traffics = limit_traffics; 373 | } 374 | 375 | return NGX_CONF_OK; 376 | } 377 | 378 | 379 | ngx_int_t 380 | ngx_http_vhost_traffic_status_limit_traffic_unique(ngx_pool_t *pool, ngx_array_t **keys) 381 | { 382 | uint32_t hash; 383 | u_char *p; 384 | ngx_str_t key; 385 | ngx_uint_t i, n; 386 | ngx_array_t *uniqs, *traffic_keys; 387 | ngx_http_vhost_traffic_status_limit_t *traffic, *traffics; 388 | ngx_http_vhost_traffic_status_filter_uniq_t *traffic_uniqs; 389 | 390 | if (*keys == NULL) { 391 | return NGX_OK; 392 | } 393 | 394 | uniqs = ngx_array_create(pool, 1, 395 | sizeof(ngx_http_vhost_traffic_status_filter_uniq_t)); 396 | if (uniqs == NULL) { 397 | return NGX_ERROR; 398 | } 399 | 400 | /* init array */ 401 | traffic_keys = NULL; 402 | traffic_uniqs = NULL; 403 | 404 | traffics = (*keys)->elts; 405 | n = (*keys)->nelts; 406 | 407 | for (i = 0; i < n; i++) { 408 | key.len = traffics[i].key.value.len 409 | + traffics[i].variable.value.len; 410 | key.data = ngx_pcalloc(pool, key.len); 411 | if (key.data == NULL) { 412 | return NGX_ERROR; 413 | } 414 | 415 | p = key.data; 416 | p = ngx_cpymem(p, traffics[i].key.value.data, 417 | traffics[i].key.value.len); 418 | ngx_memcpy(p, traffics[i].variable.value.data, 419 | traffics[i].variable.value.len); 420 | hash = ngx_crc32_short(key.data, key.len); 421 | 422 | traffic_uniqs = ngx_array_push(uniqs); 423 | if (traffic_uniqs == NULL) { 424 | return NGX_ERROR; 425 | } 426 | 427 | traffic_uniqs->hash = hash; 428 | traffic_uniqs->index = i; 429 | 430 | if (p != NULL) { 431 | ngx_pfree(pool, key.data); 432 | } 433 | } 434 | 435 | traffic_uniqs = uniqs->elts; 436 | n = uniqs->nelts; 437 | 438 | ngx_qsort(traffic_uniqs, (size_t) n, 439 | sizeof(ngx_http_vhost_traffic_status_filter_uniq_t), 440 | ngx_http_traffic_status_filter_cmp_hashs); 441 | 442 | hash = 0; 443 | for (i = 0; i < n; i++) { 444 | if (traffic_uniqs[i].hash == hash) { 445 | continue; 446 | } 447 | 448 | hash = traffic_uniqs[i].hash; 449 | 450 | if (traffic_keys == NULL) { 451 | traffic_keys = ngx_array_create(pool, 1, 452 | sizeof(ngx_http_vhost_traffic_status_limit_t)); 453 | if (traffic_keys == NULL) { 454 | return NGX_ERROR; 455 | } 456 | } 457 | 458 | traffic = ngx_array_push(traffic_keys); 459 | if (traffic == NULL) { 460 | return NGX_ERROR; 461 | } 462 | 463 | ngx_memcpy(traffic, &traffics[traffic_uniqs[i].index], 464 | sizeof(ngx_http_vhost_traffic_status_limit_t)); 465 | 466 | } 467 | 468 | if ((*keys)->nelts != traffic_keys->nelts) { 469 | *keys = traffic_keys; 470 | } 471 | 472 | return NGX_OK; 473 | } 474 | 475 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 476 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_limit.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_LIMIT_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_LIMIT_H_INCLUDED_ 9 | 10 | 11 | typedef struct { 12 | ngx_http_complex_value_t key; 13 | ngx_http_complex_value_t variable; 14 | ngx_atomic_t size; 15 | ngx_uint_t code; 16 | unsigned type; /* unsigned type:5 */ 17 | } ngx_http_vhost_traffic_status_limit_t; 18 | 19 | 20 | ngx_int_t ngx_http_vhost_traffic_status_limit_handler(ngx_http_request_t *r); 21 | ngx_int_t ngx_http_vhost_traffic_status_limit_handler_traffic(ngx_http_request_t *r, 22 | ngx_array_t *traffics); 23 | 24 | ngx_int_t ngx_http_vhost_traffic_status_limit_traffic_unique( 25 | ngx_pool_t *pool, ngx_array_t **keys); 26 | char *ngx_http_vhost_traffic_status_limit_traffic(ngx_conf_t *cf, 27 | ngx_command_t *cmd, void *conf); 28 | char *ngx_http_vhost_traffic_status_limit_traffic_by_set_key(ngx_conf_t *cf, 29 | ngx_command_t *cmd, void *conf); 30 | 31 | 32 | #endif /* _NGX_HTTP_VTS_LIMIT_H_INCLUDED_ */ 33 | 34 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 35 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_node.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_NODE_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_NODE_H_INCLUDED_ 9 | 10 | 11 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_DEFAULT_QUEUE_LEN 64 12 | #define NGX_HTTP_VHOST_TRAFFIC_STATUS_DEFAULT_BUCKET_LEN 32 13 | 14 | typedef struct { 15 | ngx_msec_t time; 16 | ngx_msec_int_t msec; 17 | } ngx_http_vhost_traffic_status_node_time_t; 18 | 19 | 20 | typedef struct { 21 | ngx_http_vhost_traffic_status_node_time_t times[NGX_HTTP_VHOST_TRAFFIC_STATUS_DEFAULT_QUEUE_LEN]; 22 | ngx_int_t front; 23 | ngx_int_t rear; 24 | ngx_int_t len; 25 | } ngx_http_vhost_traffic_status_node_time_queue_t; 26 | 27 | 28 | typedef struct { 29 | ngx_msec_int_t msec; 30 | ngx_atomic_t counter; 31 | } ngx_http_vhost_traffic_status_node_histogram_t; 32 | 33 | 34 | typedef struct { 35 | ngx_http_vhost_traffic_status_node_histogram_t buckets[NGX_HTTP_VHOST_TRAFFIC_STATUS_DEFAULT_BUCKET_LEN]; 36 | ngx_int_t len; 37 | } ngx_http_vhost_traffic_status_node_histogram_bucket_t; 38 | 39 | 40 | typedef struct { 41 | /* unsigned type:5 */ 42 | unsigned type; 43 | ngx_atomic_t response_time_counter; 44 | ngx_msec_t response_time; 45 | ngx_http_vhost_traffic_status_node_time_queue_t response_times; 46 | ngx_http_vhost_traffic_status_node_histogram_bucket_t response_buckets; 47 | } ngx_http_vhost_traffic_status_node_upstream_t; 48 | 49 | 50 | typedef struct { 51 | u_char color; 52 | ngx_atomic_t stat_request_counter; 53 | ngx_atomic_t stat_in_bytes; 54 | ngx_atomic_t stat_out_bytes; 55 | ngx_atomic_t stat_1xx_counter; 56 | ngx_atomic_t stat_2xx_counter; 57 | ngx_atomic_t stat_3xx_counter; 58 | ngx_atomic_t stat_4xx_counter; 59 | ngx_atomic_t stat_5xx_counter; 60 | uint stat_status_code_length; 61 | ngx_atomic_t *stat_status_code_counter; 62 | 63 | ngx_atomic_t stat_request_time_counter; 64 | ngx_msec_t stat_request_time; 65 | ngx_http_vhost_traffic_status_node_time_queue_t stat_request_times; 66 | ngx_http_vhost_traffic_status_node_histogram_bucket_t stat_request_buckets; 67 | 68 | /* deals with the overflow of variables */ 69 | ngx_atomic_t stat_request_counter_oc; 70 | ngx_atomic_t stat_in_bytes_oc; 71 | ngx_atomic_t stat_out_bytes_oc; 72 | ngx_atomic_t stat_1xx_counter_oc; 73 | ngx_atomic_t stat_2xx_counter_oc; 74 | ngx_atomic_t stat_3xx_counter_oc; 75 | ngx_atomic_t stat_4xx_counter_oc; 76 | ngx_atomic_t stat_5xx_counter_oc; 77 | ngx_atomic_t stat_request_time_counter_oc; 78 | ngx_atomic_t stat_response_time_counter_oc; 79 | 80 | #if (NGX_HTTP_CACHE) 81 | ngx_atomic_t stat_cache_max_size; 82 | ngx_atomic_t stat_cache_used_size; 83 | ngx_atomic_t stat_cache_miss_counter; 84 | ngx_atomic_t stat_cache_bypass_counter; 85 | ngx_atomic_t stat_cache_expired_counter; 86 | ngx_atomic_t stat_cache_stale_counter; 87 | ngx_atomic_t stat_cache_updating_counter; 88 | ngx_atomic_t stat_cache_revalidated_counter; 89 | ngx_atomic_t stat_cache_hit_counter; 90 | ngx_atomic_t stat_cache_scarce_counter; 91 | 92 | /* deals with the overflow of variables */ 93 | ngx_atomic_t stat_cache_miss_counter_oc; 94 | ngx_atomic_t stat_cache_bypass_counter_oc; 95 | ngx_atomic_t stat_cache_expired_counter_oc; 96 | ngx_atomic_t stat_cache_stale_counter_oc; 97 | ngx_atomic_t stat_cache_updating_counter_oc; 98 | ngx_atomic_t stat_cache_revalidated_counter_oc; 99 | ngx_atomic_t stat_cache_hit_counter_oc; 100 | ngx_atomic_t stat_cache_scarce_counter_oc; 101 | #endif 102 | 103 | ngx_http_vhost_traffic_status_node_upstream_t stat_upstream; 104 | size_t len; 105 | ngx_uint_t ignore_status; 106 | u_char data[1]; 107 | } ngx_http_vhost_traffic_status_node_t; 108 | 109 | 110 | ngx_int_t ngx_http_vhost_traffic_status_node_generate_key(ngx_pool_t *pool, 111 | ngx_str_t *buf, ngx_str_t *dst, unsigned type); 112 | ngx_int_t ngx_http_vhost_traffic_status_node_position_key(ngx_str_t *buf, 113 | size_t pos); 114 | 115 | ngx_rbtree_node_t *ngx_http_vhost_traffic_status_node_lookup( 116 | ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash); 117 | void ngx_http_vhost_traffic_status_node_zero( 118 | ngx_http_vhost_traffic_status_node_t *vtsn); 119 | void ngx_http_vhost_traffic_status_node_init(ngx_http_request_t *r, 120 | ngx_http_vhost_traffic_status_node_t *vtsn, ngx_int_t status_code_slot); 121 | void ngx_http_vhost_traffic_status_node_set(ngx_http_request_t *r, 122 | ngx_http_vhost_traffic_status_node_t *vtsn, ngx_int_t status_code_slot); 123 | void ngx_http_vhost_traffic_status_node_update(ngx_http_request_t *r, 124 | ngx_http_vhost_traffic_status_node_t *vtsn, ngx_msec_int_t ms, ngx_int_t status_code_slot); 125 | 126 | void ngx_http_vhost_traffic_status_node_time_queue_zero( 127 | ngx_http_vhost_traffic_status_node_time_queue_t *q); 128 | void ngx_http_vhost_traffic_status_node_time_queue_init( 129 | ngx_http_vhost_traffic_status_node_time_queue_t *q); 130 | void ngx_http_vhost_traffic_status_node_time_queue_insert( 131 | ngx_http_vhost_traffic_status_node_time_queue_t *q, 132 | ngx_msec_int_t x); 133 | ngx_int_t ngx_http_vhost_traffic_status_node_time_queue_push( 134 | ngx_http_vhost_traffic_status_node_time_queue_t *q, 135 | ngx_msec_int_t x); 136 | ngx_int_t ngx_http_vhost_traffic_status_node_time_queue_pop( 137 | ngx_http_vhost_traffic_status_node_time_queue_t *q, 138 | ngx_http_vhost_traffic_status_node_time_t *x); 139 | ngx_int_t ngx_http_vhost_traffic_status_node_time_queue_rear( 140 | ngx_http_vhost_traffic_status_node_time_queue_t *q); 141 | 142 | ngx_msec_t ngx_http_vhost_traffic_status_node_time_queue_average( 143 | ngx_http_vhost_traffic_status_node_time_queue_t *q, 144 | ngx_int_t method, ngx_msec_t period); 145 | ngx_msec_t ngx_http_vhost_traffic_status_node_time_queue_amm( 146 | ngx_http_vhost_traffic_status_node_time_queue_t *q, 147 | ngx_msec_t period); 148 | ngx_msec_t ngx_http_vhost_traffic_status_node_time_queue_wma( 149 | ngx_http_vhost_traffic_status_node_time_queue_t *q, 150 | ngx_msec_t period); 151 | void ngx_http_vhost_traffic_status_node_time_queue_merge( 152 | ngx_http_vhost_traffic_status_node_time_queue_t *a, 153 | ngx_http_vhost_traffic_status_node_time_queue_t *b, 154 | ngx_msec_t period); 155 | void ngx_http_vhost_traffic_status_status_code_merge(ngx_atomic_t *dst, ngx_atomic_t *src, ngx_uint_t n); 156 | 157 | void ngx_http_vhost_traffic_status_node_histogram_bucket_init( 158 | ngx_http_request_t *r, 159 | ngx_http_vhost_traffic_status_node_histogram_bucket_t *b); 160 | void ngx_http_vhost_traffic_status_node_histogram_observe( 161 | ngx_http_vhost_traffic_status_node_histogram_bucket_t *b, 162 | ngx_msec_int_t x); 163 | 164 | void ngx_http_vhost_traffic_status_find_name(ngx_http_request_t *r, 165 | ngx_str_t *buf); 166 | ngx_rbtree_node_t *ngx_http_vhost_traffic_status_find_node(ngx_http_request_t *r, 167 | ngx_str_t *key, unsigned type, uint32_t key_hash); 168 | 169 | ngx_rbtree_node_t *ngx_http_vhost_traffic_status_find_lru(ngx_http_request_t *r); 170 | ngx_rbtree_node_t *ngx_http_vhost_traffic_status_find_lru_node(ngx_http_request_t *r, 171 | ngx_rbtree_node_t *a, ngx_rbtree_node_t *b); 172 | ngx_rbtree_node_t *ngx_http_vhost_traffic_status_find_lru_node_cmp(ngx_http_request_t *r, 173 | ngx_rbtree_node_t *a, ngx_rbtree_node_t *b); 174 | 175 | ngx_int_t ngx_http_vhost_traffic_status_node_member_cmp(ngx_str_t *member, const char *name); 176 | ngx_atomic_uint_t ngx_http_vhost_traffic_status_node_member( 177 | ngx_http_vhost_traffic_status_node_t *vtsn, 178 | ngx_str_t *member); 179 | 180 | 181 | #endif /* _NGX_HTTP_VTS_NODE_H_INCLUDED_ */ 182 | 183 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 184 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_set.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_SET_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_SET_H_INCLUDED_ 9 | 10 | 11 | typedef struct { 12 | ngx_int_t index; 13 | ngx_http_complex_value_t value; 14 | ngx_http_set_variable_pt set_handler; 15 | } ngx_http_vhost_traffic_status_filter_variable_t; 16 | 17 | 18 | ngx_int_t ngx_http_vhost_traffic_status_set_handler(ngx_http_request_t *r); 19 | char *ngx_http_vhost_traffic_status_set_by_filter(ngx_conf_t *cf, 20 | ngx_command_t *cmd, void *conf); 21 | 22 | 23 | #endif /* _NGX_HTTP_VTS_SET_H_INCLUDED_ */ 24 | 25 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 26 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_shm.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_SHM_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_SHM_H_INCLUDED_ 9 | 10 | 11 | typedef struct { 12 | ngx_str_t *name; 13 | ngx_uint_t max_size; 14 | ngx_uint_t used_size; 15 | ngx_uint_t used_node; 16 | 17 | ngx_uint_t filter_used_size; 18 | ngx_uint_t filter_used_node; 19 | } ngx_http_vhost_traffic_status_shm_info_t; 20 | 21 | 22 | ngx_int_t ngx_http_vhost_traffic_status_shm_add_server(ngx_http_request_t *r); 23 | ngx_int_t ngx_http_vhost_traffic_status_shm_add_filter(ngx_http_request_t *r); 24 | ngx_int_t ngx_http_vhost_traffic_status_shm_add_upstream(ngx_http_request_t *r); 25 | ngx_uint_t ngx_http_vhost_traffic_status_find_status_code_slot(ngx_uint_t status, ngx_array_t *status_codes); 26 | 27 | #if (NGX_HTTP_CACHE) 28 | ngx_int_t ngx_http_vhost_traffic_status_shm_add_cache(ngx_http_request_t *r); 29 | #endif 30 | 31 | void ngx_http_vhost_traffic_status_shm_info_node(ngx_http_request_t *r, 32 | ngx_http_vhost_traffic_status_shm_info_t *shm_info, ngx_rbtree_node_t *node); 33 | void ngx_http_vhost_traffic_status_shm_info(ngx_http_request_t *r, 34 | ngx_http_vhost_traffic_status_shm_info_t *shm_info); 35 | 36 | 37 | #endif /* _NGX_HTTP_VTS_SHM_H_INCLUDED_ */ 38 | 39 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 40 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_string.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "ngx_http_vhost_traffic_status_string.h" 12 | 13 | 14 | #if !defined(nginx_version) || nginx_version < 1007009 15 | 16 | /* from src/core/ngx_string.c in v1.7.9 */ 17 | uintptr_t 18 | ngx_http_vhost_traffic_status_escape_json(u_char *dst, u_char *src, size_t size) 19 | { 20 | u_char ch; 21 | ngx_uint_t len; 22 | 23 | if (dst == NULL) { 24 | len = 0; 25 | 26 | while (size) { 27 | ch = *src++; 28 | 29 | if (ch == '\\' || ch == '"') { 30 | len++; 31 | 32 | } else if (ch <= 0x1f) { 33 | len += sizeof("\\u001F") - 2; 34 | } 35 | 36 | size--; 37 | } 38 | 39 | return (uintptr_t) len; 40 | } 41 | 42 | while (size) { 43 | ch = *src++; 44 | 45 | if (ch > 0x1f) { 46 | 47 | if (ch == '\\' || ch == '"') { 48 | *dst++ = '\\'; 49 | } 50 | 51 | *dst++ = ch; 52 | 53 | } else { 54 | *dst++ = '\\'; *dst++ = 'u'; *dst++ = '0'; *dst++ = '0'; 55 | *dst++ = '0' + (ch >> 4); 56 | 57 | ch &= 0xf; 58 | 59 | *dst++ = (ch < 10) ? ('0' + ch) : ('A' + ch - 10); 60 | } 61 | 62 | size--; 63 | } 64 | 65 | return (uintptr_t) dst; 66 | } 67 | 68 | #endif 69 | 70 | 71 | ngx_int_t 72 | ngx_http_vhost_traffic_status_escape_json_pool(ngx_pool_t *pool, 73 | ngx_str_t *buf, ngx_str_t *dst) 74 | { 75 | u_char *p; 76 | 77 | buf->len = dst->len * 6; 78 | buf->data = ngx_pcalloc(pool, buf->len); 79 | if (buf->data == NULL) { 80 | *buf = *dst; 81 | return NGX_ERROR; 82 | } 83 | 84 | p = buf->data; 85 | 86 | #if !defined(nginx_version) || nginx_version < 1007009 87 | p = (u_char *) ngx_http_vhost_traffic_status_escape_json(p, dst->data, dst->len); 88 | #else 89 | p = (u_char *) ngx_escape_json(p, dst->data, dst->len); 90 | #endif 91 | 92 | buf->len = ngx_strlen(buf->data); 93 | 94 | return NGX_OK; 95 | } 96 | 97 | 98 | ngx_int_t 99 | ngx_http_vhost_traffic_status_copy_str(ngx_pool_t *pool, 100 | ngx_str_t *buf, ngx_str_t *dst) 101 | { 102 | u_char *p; 103 | 104 | buf->len = dst->len; 105 | buf->data = ngx_pcalloc(pool, dst->len + 1); /* 1 byte for terminating '\0' */ 106 | if (buf->data == NULL) { 107 | return NGX_ERROR; 108 | } 109 | 110 | p = buf->data; 111 | 112 | ngx_memcpy(p, dst->data, dst->len); 113 | 114 | return NGX_OK; 115 | } 116 | 117 | 118 | ngx_int_t 119 | ngx_http_vhost_traffic_status_replace_chrc(ngx_str_t *buf, 120 | u_char in, u_char to) 121 | { 122 | size_t len; 123 | u_char *p; 124 | 125 | p = buf->data; 126 | 127 | len = buf->len; 128 | 129 | while(len--) { 130 | if (*p == in) { 131 | *p = to; 132 | } 133 | p++; 134 | } 135 | 136 | return NGX_OK; 137 | } 138 | 139 | 140 | ngx_int_t 141 | ngx_http_vhost_traffic_status_replace_strc(ngx_str_t *buf, 142 | ngx_str_t *dst, u_char c) 143 | { 144 | size_t n, len; 145 | u_char *p, *o; 146 | p = o = buf->data; 147 | n = 0; 148 | 149 | /* we need the buf's last '\0' for ngx_strstrn() */ 150 | if (*(buf->data + buf->len) != 0) { 151 | return NGX_ERROR; 152 | } 153 | 154 | while ((p = ngx_strstrn(p, (char *) dst->data, dst->len - 1)) != NULL) { 155 | n++; 156 | len = buf->len - (p - o) - (n * dst->len) + n - 1; 157 | *p++ = c; 158 | ngx_memmove(p, p + dst->len - 1, len); 159 | } 160 | 161 | if (n > 0) { 162 | buf->len = buf->len - (n * dst->len) + n; 163 | } 164 | *(buf->data + buf->len) = '\0'; 165 | 166 | return NGX_OK; 167 | } 168 | 169 | 170 | ngx_int_t 171 | ngx_http_vhost_traffic_status_escape_prometheus(ngx_pool_t *pool, ngx_str_t *buf, u_char *p, size_t n) 172 | { 173 | u_char c, *pa, *pb, *last, *char_end; 174 | size_t size; 175 | u_char HEX_MAP[] = "0123456789ABCDEF"; 176 | 177 | last = p + n; 178 | pa = p; 179 | size = 0; 180 | 181 | /* Find the first character that needs to be escaped */ 182 | while (pa < last) { 183 | if (isascii(*pa)) { 184 | if (*pa == '"' || *pa == '\\' || *pa == '\n') { 185 | break; 186 | } else { 187 | pa++; 188 | } 189 | } else { 190 | char_end = pa; 191 | if (*pa >= 0xf8 || ngx_utf8_decode(&char_end, last - pa) > 0x10ffff) { 192 | break; 193 | } else { 194 | pa = char_end; 195 | } 196 | } 197 | } 198 | 199 | if (pa == last) { 200 | // no escapes required - return the original string 201 | buf->data = p; 202 | buf->len = n; 203 | return NGX_OK; 204 | } 205 | 206 | size = pa - p; 207 | 208 | /* Allocate enough space for the unescaped prefix and worst case for remainder */ 209 | buf->data = ngx_pcalloc(pool, size + (n - size) * 5); 210 | if (buf->data == NULL) { 211 | /* 212 | Return the unescaped string up to the first special character 213 | in case the caller does not handle the error. 214 | */ 215 | buf->data = p; 216 | buf->len = size; 217 | return NGX_ERROR; 218 | } 219 | 220 | /* Copy `size` unescaped characters to start of destination. */ 221 | pb = ngx_copy(buf->data, p, size); 222 | 223 | /* Individually copy remaining characters to destination, escaping as necessary */ 224 | while (pa < last) { 225 | if (isascii(*pa)) { 226 | if (*pa == '"' || *pa == '\\') { 227 | *pb++ = '\\'; 228 | *pb++ = *pa++; 229 | size += 2; 230 | } else if (*pa == '\n') { 231 | *pb++ = '\\'; 232 | *pb++ = 'n'; 233 | pa++; 234 | size += 2; 235 | } else { 236 | *pb++ = *pa++; 237 | size++; 238 | } 239 | } else { 240 | char_end = pa; 241 | if (*pa >= 0xf8 || ngx_utf8_decode(&char_end, last - pa) > 0x10ffff) { 242 | /* invalid UTF-8 - escape single char to allow resynchronization */ 243 | c = *pa++; 244 | /* two slashes are required to be valid encoding for prometheus*/ 245 | *pb++ = '\\'; 246 | *pb++ = '\\'; 247 | *pb++ = 'x'; 248 | *pb++ = HEX_MAP[c >> 4]; 249 | *pb++ = HEX_MAP[c & 0x0f]; 250 | size += 5; 251 | } else { 252 | while (pa < char_end) { 253 | *pb++ = *pa++; 254 | size++; 255 | } 256 | } 257 | } 258 | } 259 | 260 | buf->len = size; 261 | return NGX_OK; 262 | } 263 | 264 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 265 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_string.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_STRING_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_STRING_H_INCLUDED_ 9 | 10 | 11 | #if !defined(nginx_version) || nginx_version < 1007009 12 | uintptr_t ngx_http_vhost_traffic_status_escape_json(u_char *dst, u_char *src, size_t size); 13 | #endif 14 | ngx_int_t ngx_http_vhost_traffic_status_escape_json_pool(ngx_pool_t *pool, 15 | ngx_str_t *buf, ngx_str_t *dst); 16 | ngx_int_t ngx_http_vhost_traffic_status_copy_str(ngx_pool_t *pool, 17 | ngx_str_t *buf, ngx_str_t *dst); 18 | ngx_int_t ngx_http_vhost_traffic_status_replace_chrc(ngx_str_t *buf, 19 | u_char in, u_char to); 20 | ngx_int_t ngx_http_vhost_traffic_status_replace_strc(ngx_str_t *buf, 21 | ngx_str_t *dst, u_char c); 22 | ngx_int_t ngx_http_vhost_traffic_status_escape_prometheus(ngx_pool_t *pool, ngx_str_t *buf, 23 | u_char *p, size_t n); 24 | 25 | #endif /* _NGX_HTTP_VTS_STRING_H_INCLUDED_ */ 26 | 27 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 28 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_variables.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #include "ngx_http_vhost_traffic_status_module.h" 8 | #include "ngx_http_vhost_traffic_status_variables.h" 9 | 10 | 11 | static ngx_http_variable_t ngx_http_vhost_traffic_status_vars[] = { 12 | 13 | { ngx_string("vts_request_counter"), NULL, 14 | ngx_http_vhost_traffic_status_node_variable, 15 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_request_counter), 16 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 17 | 18 | { ngx_string("vts_in_bytes"), NULL, 19 | ngx_http_vhost_traffic_status_node_variable, 20 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_in_bytes), 21 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 22 | 23 | { ngx_string("vts_out_bytes"), NULL, 24 | ngx_http_vhost_traffic_status_node_variable, 25 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_out_bytes), 26 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 27 | 28 | { ngx_string("vts_1xx_counter"), NULL, 29 | ngx_http_vhost_traffic_status_node_variable, 30 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_1xx_counter), 31 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 32 | 33 | { ngx_string("vts_2xx_counter"), NULL, 34 | ngx_http_vhost_traffic_status_node_variable, 35 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_2xx_counter), 36 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 37 | 38 | { ngx_string("vts_3xx_counter"), NULL, 39 | ngx_http_vhost_traffic_status_node_variable, 40 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_3xx_counter), 41 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 42 | 43 | { ngx_string("vts_4xx_counter"), NULL, 44 | ngx_http_vhost_traffic_status_node_variable, 45 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_4xx_counter), 46 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 47 | 48 | { ngx_string("vts_5xx_counter"), NULL, 49 | ngx_http_vhost_traffic_status_node_variable, 50 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_5xx_counter), 51 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 52 | 53 | { ngx_string("vts_request_time_counter"), NULL, 54 | ngx_http_vhost_traffic_status_node_variable, 55 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_request_time_counter), 56 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 57 | 58 | { ngx_string("vts_request_time"), NULL, 59 | ngx_http_vhost_traffic_status_node_variable, 60 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_request_time), 61 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 62 | 63 | #if (NGX_HTTP_CACHE) 64 | { ngx_string("vts_cache_miss_counter"), NULL, 65 | ngx_http_vhost_traffic_status_node_variable, 66 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_cache_miss_counter), 67 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 68 | 69 | { ngx_string("vts_cache_bypass_counter"), NULL, 70 | ngx_http_vhost_traffic_status_node_variable, 71 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_cache_bypass_counter), 72 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 73 | 74 | { ngx_string("vts_cache_expired_counter"), NULL, 75 | ngx_http_vhost_traffic_status_node_variable, 76 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_cache_expired_counter), 77 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 78 | 79 | { ngx_string("vts_cache_stale_counter"), NULL, 80 | ngx_http_vhost_traffic_status_node_variable, 81 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_cache_stale_counter), 82 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 83 | 84 | { ngx_string("vts_cache_updating_counter"), NULL, 85 | ngx_http_vhost_traffic_status_node_variable, 86 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_cache_updating_counter), 87 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 88 | 89 | { ngx_string("vts_cache_revalidated_counter"), NULL, 90 | ngx_http_vhost_traffic_status_node_variable, 91 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_cache_revalidated_counter), 92 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 93 | 94 | { ngx_string("vts_cache_hit_counter"), NULL, 95 | ngx_http_vhost_traffic_status_node_variable, 96 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_cache_hit_counter), 97 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 98 | 99 | { ngx_string("vts_cache_scarce_counter"), NULL, 100 | ngx_http_vhost_traffic_status_node_variable, 101 | offsetof(ngx_http_vhost_traffic_status_node_t, stat_cache_scarce_counter), 102 | NGX_HTTP_VAR_NOCACHEABLE, 0 }, 103 | #endif 104 | 105 | { ngx_null_string, NULL, NULL, 0, 0, 0 } 106 | }; 107 | 108 | 109 | ngx_int_t 110 | ngx_http_vhost_traffic_status_node_variable(ngx_http_request_t *r, 111 | ngx_http_variable_value_t *v, uintptr_t data) 112 | { 113 | u_char *p; 114 | unsigned type; 115 | ngx_int_t rc; 116 | ngx_str_t key, dst; 117 | ngx_slab_pool_t *shpool; 118 | ngx_rbtree_node_t *node; 119 | ngx_http_vhost_traffic_status_node_t *vtsn; 120 | ngx_http_vhost_traffic_status_loc_conf_t *vtscf; 121 | 122 | vtscf = ngx_http_get_module_loc_conf(r, ngx_http_vhost_traffic_status_module); 123 | 124 | ngx_http_vhost_traffic_status_find_name(r, &dst); 125 | 126 | type = NGX_HTTP_VHOST_TRAFFIC_STATUS_UPSTREAM_NO; 127 | 128 | rc = ngx_http_vhost_traffic_status_node_generate_key(r->pool, &key, &dst, type); 129 | if (rc != NGX_OK) { 130 | return NGX_ERROR; 131 | } 132 | 133 | if (key.len == 0) { 134 | return NGX_ERROR; 135 | } 136 | 137 | shpool = (ngx_slab_pool_t *) vtscf->shm_zone->shm.addr; 138 | 139 | ngx_shmtx_lock(&shpool->mutex); 140 | 141 | node = ngx_http_vhost_traffic_status_find_node(r, &key, type, 0); 142 | 143 | if (node == NULL) { 144 | goto not_found; 145 | } 146 | 147 | p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN); 148 | if (p == NULL) { 149 | goto not_found; 150 | } 151 | 152 | vtsn = (ngx_http_vhost_traffic_status_node_t *) &node->color; 153 | 154 | v->len = ngx_sprintf(p, "%uA", *((ngx_atomic_t *) ((char *) vtsn + data))) - p; 155 | v->valid = 1; 156 | v->no_cacheable = 0; 157 | v->not_found = 0; 158 | v->data = p; 159 | 160 | goto done; 161 | 162 | not_found: 163 | 164 | v->not_found = 1; 165 | 166 | done: 167 | 168 | vtscf->node_caches[type] = node; 169 | 170 | ngx_shmtx_unlock(&shpool->mutex); 171 | 172 | return NGX_OK; 173 | } 174 | 175 | 176 | ngx_int_t 177 | ngx_http_vhost_traffic_status_add_variables(ngx_conf_t *cf) 178 | { 179 | ngx_http_variable_t *var, *v; 180 | 181 | for (v = ngx_http_vhost_traffic_status_vars; v->name.len; v++) { 182 | var = ngx_http_add_variable(cf, &v->name, v->flags); 183 | if (var == NULL) { 184 | return NGX_ERROR; 185 | } 186 | 187 | var->get_handler = v->get_handler; 188 | var->data = v->data; 189 | } 190 | 191 | return NGX_OK; 192 | } 193 | 194 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 195 | -------------------------------------------------------------------------------- /src/ngx_http_vhost_traffic_status_variables.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) YoungJoo Kim (vozlt) 4 | */ 5 | 6 | 7 | #ifndef _NGX_HTTP_VTS_VARIABLES_H_INCLUDED_ 8 | #define _NGX_HTTP_VTS_VARIABLES_H_INCLUDED_ 9 | 10 | 11 | ngx_int_t ngx_http_vhost_traffic_status_node_variable(ngx_http_request_t *r, 12 | ngx_http_variable_value_t *v, uintptr_t data); 13 | ngx_int_t ngx_http_vhost_traffic_status_add_variables(ngx_conf_t *cf); 14 | 15 | 16 | #endif /* _NGX_HTTP_VTS_VARIABLES_H_INCLUDED_ */ 17 | 18 | /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ 19 | -------------------------------------------------------------------------------- /t/000.display_html.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each(2) * blocks() * 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/format/html 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format html; 18 | } 19 | --- request 20 | GET /status/format/html 21 | --- response_headers_like 22 | Content-Type: text/html 23 | 24 | 25 | 26 | === TEST 2: /status 27 | --- http_config 28 | vhost_traffic_status_zone; 29 | --- config 30 | location /status { 31 | vhost_traffic_status_display; 32 | vhost_traffic_status_display_format html; 33 | } 34 | --- request 35 | GET /status 36 | --- response_headers_like 37 | Content-Type: text/html 38 | -------------------------------------------------------------------------------- /t/001.display_json.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each(2) * blocks() * 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/format/json 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | } 19 | --- request 20 | GET /status/format/json 21 | --- response_headers_like 22 | Content-Type: application/json 23 | 24 | 25 | 26 | === TEST 2: /status 27 | --- http_config 28 | vhost_traffic_status_zone; 29 | --- config 30 | location /status { 31 | vhost_traffic_status_display; 32 | vhost_traffic_status_display_format json; 33 | } 34 | --- request 35 | GET /status 36 | --- response_headers_like 37 | Content-Type: application/json 38 | -------------------------------------------------------------------------------- /t/002.check_json_syntax.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | add_response_body_check( 6 | sub { 7 | my ($block, $body, $req_idx, $repeated_req_idx, $dry_run) = @_; 8 | system("echo '$body' | python -m json.tool > /dev/null") == 0 or 9 | bail_out "JSON Syntax error($body)"; 10 | } 11 | ); 12 | 13 | plan tests => repeat_each() * blocks() * 24; 14 | no_shuffle(); 15 | run_tests(); 16 | 17 | __DATA__ 18 | 19 | === TEST 1: check_json_syntax 20 | --- http_config 21 | vhost_traffic_status_zone; 22 | proxy_cache_path /tmp/cache_one levels=1:2 keys_zone=cache_one:2m inactive=1m max_size=4m; 23 | proxy_cache_path /tmp/cache_two levels=1:2 keys_zone=cache_two:2m inactive=1m max_size=4m; 24 | upstream backend { 25 | server 127.0.0.1; 26 | } 27 | server { 28 | server_name _; 29 | vhost_traffic_status_filter_by_host on; 30 | } 31 | --- config 32 | location /status { 33 | vhost_traffic_status_display; 34 | vhost_traffic_status_display_format json; 35 | access_log off; 36 | } 37 | location /one { 38 | proxy_set_header Host one.example.org; 39 | proxy_pass http://backend; 40 | } 41 | location /two { 42 | proxy_set_header Host two.example.org; 43 | proxy_pass http://backend; 44 | } 45 | location ~ ^/storage/(.+)/.*$ { 46 | set $volume $1; 47 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 48 | } 49 | location /alone { 50 | proxy_pass http://localhost:1981; 51 | } 52 | location /cache_one { 53 | proxy_cache cache_one; 54 | proxy_cache_valid 200 10s; 55 | proxy_set_header Host backend; 56 | proxy_pass http://backend; 57 | } 58 | location /cache_two { 59 | proxy_cache cache_two; 60 | proxy_cache_valid 200 10s; 61 | proxy_set_header Host backend; 62 | proxy_pass http://backend; 63 | } 64 | --- tcp_listen: 1981 65 | --- tcp_reply eval 66 | "HTTP/1.1 200 OK\r\n\r\n{\"upstream\@alone\":\"OK\"}" 67 | --- user_files eval 68 | [ 69 | ['one/file.txt' => '{"one.example.org":"OK"}'], 70 | ['two/file.txt' => '{"two.example.org":"OK"}'], 71 | ['storage/vol0/file.txt' => '{"vol0":"OK"}'], 72 | ['storage/vol1/file.txt' => '{"vol1":"OK"}'], 73 | ['cache_one/file.txt' => '{"cache_one":"OK"}'], 74 | ['cache_two/file.txt' => '{"cache_two":"OK"}'] 75 | ] 76 | --- request eval 77 | [ 78 | 'GET /status/format/json', 79 | 'GET /one/file.txt', 80 | 'GET /two/file.txt', 81 | 'GET /status/format/json', 82 | 'GET /storage/vol0/file.txt', 83 | 'GET /storage/vol1/file.txt', 84 | 'GET /status/format/json', 85 | 'GET /alone/file.txt', 86 | 'GET /status/format/json', 87 | 'GET /cache_one/file.txt', 88 | 'GET /cache_two/file.txt', 89 | 'GET /status/format/json' 90 | ] 91 | --- response_body_like eval 92 | [ 93 | 'nginxVersion', 94 | 'OK', 95 | 'OK', 96 | '(one|two).example.org', 97 | 'OK', 98 | 'OK', 99 | 'filterZones.*(vol0|vol1)', 100 | 'OK', 101 | '::nogroups', 102 | 'OK', 103 | 'OK', 104 | 'cacheZone' 105 | ] 106 | -------------------------------------------------------------------------------- /t/003.filter_by_host.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 8; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: vhost_traffic_status_filter_by_host on 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | upstream backend { 15 | server 127.0.0.1; 16 | } 17 | server { 18 | server_name _; 19 | vhost_traffic_status_filter_by_host on; 20 | } 21 | --- config 22 | location /status { 23 | vhost_traffic_status_display; 24 | vhost_traffic_status_display_format json; 25 | access_log off; 26 | } 27 | location /one { 28 | proxy_set_header Host one.example.org; 29 | proxy_pass http://backend; 30 | } 31 | location /two { 32 | proxy_set_header Host two.example.org; 33 | proxy_pass http://backend; 34 | } 35 | --- user_files eval 36 | [ 37 | ['one/file.txt' => 'one.example.org:OK'], 38 | ['two/file.txt' => 'two.example.org:OK'] 39 | ] 40 | --- request eval 41 | [ 42 | 'GET /one/file.txt', 43 | 'GET /two/file.txt', 44 | 'GET /status/control?cmd=status&group=server&zone=one.example.org', 45 | 'GET /status/control?cmd=status&group=server&zone=two.example.org' 46 | ] 47 | --- response_body_like eval 48 | [ 49 | 'OK', 50 | 'OK', 51 | 'one.example.org', 52 | 'two.example.org' 53 | ] 54 | 55 | 56 | 57 | === TEST 2: vhost_traffic_status_filter off 58 | --- http_config 59 | vhost_traffic_status_zone; 60 | vhost_traffic_status_filter off; 61 | upstream backend { 62 | server 127.0.0.1; 63 | } 64 | server { 65 | server_name _; 66 | vhost_traffic_status_filter_by_host on; 67 | } 68 | --- config 69 | location /status { 70 | vhost_traffic_status_display; 71 | vhost_traffic_status_display_format json; 72 | access_log off; 73 | } 74 | location /one { 75 | proxy_set_header Host one.example.org; 76 | proxy_pass http://backend; 77 | } 78 | location /two { 79 | proxy_set_header Host two.example.org; 80 | proxy_pass http://backend; 81 | } 82 | --- user_files eval 83 | [ 84 | ['one/file.txt' => 'one.example.org:OK'], 85 | ['two/file.txt' => 'two.example.org:OK'] 86 | ] 87 | --- request eval 88 | [ 89 | 'GET /one/file.txt', 90 | 'GET /two/file.txt', 91 | 'GET /status/control?cmd=status&group=server&zone=one.example.org', 92 | 'GET /status/control?cmd=status&group=server&zone=two.example.org' 93 | ] 94 | --- response_body_like eval 95 | [ 96 | 'OK', 97 | 'OK', 98 | '{}', 99 | '{}' 100 | ] 101 | -------------------------------------------------------------------------------- /t/004.filter_by_set_key.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 12; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: filter_by_set_key 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | location ~ ^/storage/(.+)/.*$ { 21 | set $volume $1; 22 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 23 | } 24 | --- user_files eval 25 | [ 26 | ['storage/vol0/file.txt' => 'vol0:OK'], 27 | ['storage/vol1/file.txt' => 'vol1:OK'], 28 | ['storage/vol2/file.txt' => 'vol2:OK'] 29 | ] 30 | --- request eval 31 | [ 32 | 'GET /storage/vol0/file.txt', 33 | 'GET /storage/vol1/file.txt', 34 | 'GET /storage/vol2/file.txt', 35 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol0', 36 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol1', 37 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol2', 38 | ] 39 | --- response_body_like eval 40 | [ 41 | 'OK', 42 | 'OK', 43 | 'OK', 44 | 'vol0', 45 | 'vol1', 46 | 'vol2' 47 | ] 48 | -------------------------------------------------------------------------------- /t/005.filter_check_duplicate.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 12; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: filter_check_duplicate on 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | location ~ ^/storage/(.+)/.*$ { 21 | set $volume $1; 22 | vhost_traffic_status_filter_check_duplicate on; 23 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 24 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 25 | } 26 | --- user_files eval 27 | [ 28 | ['storage/vol0/file.txt' => 'vol0:OK'], 29 | ['storage/vol1/file.txt' => 'vol1:OK'], 30 | ['storage/vol2/file.txt' => 'vol2:OK'] 31 | ] 32 | --- request eval 33 | [ 34 | 'GET /storage/vol0/file.txt', 35 | 'GET /storage/vol1/file.txt', 36 | 'GET /storage/vol2/file.txt', 37 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol0', 38 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol1', 39 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol2', 40 | ] 41 | --- response_body_like eval 42 | [ 43 | 'OK', 44 | 'OK', 45 | 'OK', 46 | 'vol0.*"requestCounter":1', 47 | 'vol1.*"requestCounter":1', 48 | 'vol2.*"requestCounter":1' 49 | ] 50 | 51 | 52 | 53 | === TEST 2: filter_check_duplicate off 54 | --- http_config 55 | vhost_traffic_status_zone; 56 | --- config 57 | location /status { 58 | vhost_traffic_status_display; 59 | vhost_traffic_status_display_format json; 60 | access_log off; 61 | } 62 | location ~ ^/storage/(.+)/.*$ { 63 | set $volume $1; 64 | vhost_traffic_status_filter_check_duplicate off; 65 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 66 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 67 | } 68 | --- user_files eval 69 | [ 70 | ['storage/vol0/file.txt' => 'vol0:OK'], 71 | ['storage/vol1/file.txt' => 'vol1:OK'], 72 | ['storage/vol2/file.txt' => 'vol2:OK'] 73 | ] 74 | --- request eval 75 | [ 76 | 'GET /storage/vol0/file.txt', 77 | 'GET /storage/vol1/file.txt', 78 | 'GET /storage/vol2/file.txt', 79 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol0', 80 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol1', 81 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol2', 82 | ] 83 | --- response_body_like eval 84 | [ 85 | 'OK', 86 | 'OK', 87 | 'OK', 88 | 'vol0.*"requestCounter":2', 89 | 'vol1.*"requestCounter":2', 90 | 'vol2.*"requestCounter":2' 91 | ] 92 | -------------------------------------------------------------------------------- /t/006.control_status_fully.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 4; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/control?cmd=status&group=* 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | location ~ ^/storage/(.+)/.*$ { 21 | set $volume $1; 22 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 23 | } 24 | --- user_files eval 25 | [ 26 | ['storage/vol0/file.txt' => 'vol0:OK'] 27 | ] 28 | --- request eval 29 | [ 30 | 'GET /storage/vol0/file.txt', 31 | 'GET /status/control?cmd=status&group=*', 32 | ] 33 | --- response_body_like eval 34 | [ 35 | 'OK', 36 | 'nginxVersion' 37 | ] 38 | 39 | -------------------------------------------------------------------------------- /t/007.control_status_group.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 4 + 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/control?cmd=status&group=server&zone=::main 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | --- user_files eval 21 | [ 22 | ['storage/control/file.txt' => 'server:OK'] 23 | ] 24 | --- request eval 25 | [ 26 | 'GET /storage/control/file.txt', 27 | 'GET /status/control?cmd=status&group=server&zone=::main', 28 | ] 29 | --- response_body_like eval 30 | [ 31 | 'OK', 32 | 'hostName' 33 | ] 34 | 35 | 36 | 37 | === TEST 2: /status/control?cmd=status&group=server&zone=* 38 | --- http_config 39 | vhost_traffic_status_zone; 40 | --- config 41 | location /status { 42 | vhost_traffic_status_display; 43 | vhost_traffic_status_display_format json; 44 | access_log off; 45 | } 46 | --- user_files eval 47 | [ 48 | ['storage/control/file.txt' => 'server:OK'] 49 | ] 50 | --- request eval 51 | [ 52 | 'GET /storage/control/file.txt', 53 | 'GET /status/control?cmd=status&group=server&zone=*', 54 | ] 55 | --- response_body_like eval 56 | [ 57 | 'OK', 58 | '{"serverZones.*localhost' 59 | ] 60 | 61 | 62 | 63 | === TEST 3: /status/control?cmd=status&group=filter&zone=* 64 | --- http_config 65 | vhost_traffic_status_zone; 66 | --- config 67 | location /status { 68 | vhost_traffic_status_display; 69 | vhost_traffic_status_display_format json; 70 | access_log off; 71 | } 72 | location ~ ^/storage/(.+)/.*$ { 73 | set $volume $1; 74 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 75 | } 76 | --- user_files eval 77 | [ 78 | ['storage/vol0/file.txt' => 'filter:OK'] 79 | ] 80 | --- request eval 81 | [ 82 | 'GET /storage/vol0/file.txt', 83 | 'GET /status/control?cmd=status&group=filter&zone=*', 84 | ] 85 | --- response_body_like eval 86 | [ 87 | 'OK', 88 | '{"filterZones.*storage::' 89 | ] 90 | 91 | 92 | 93 | === TEST 4: /status/control?cmd=status&group=upstream@group&zone=* 94 | --- http_config 95 | vhost_traffic_status_zone; 96 | upstream backend { 97 | server localhost; 98 | } 99 | server { 100 | server_name backend; 101 | } 102 | --- config 103 | location /status { 104 | vhost_traffic_status_display; 105 | vhost_traffic_status_display_format json; 106 | access_log off; 107 | } 108 | location /backend { 109 | proxy_set_header Host backend; 110 | proxy_pass http://backend; 111 | } 112 | --- user_files eval 113 | [ 114 | ['backend/file.txt' => 'upstream@group:OK'] 115 | ] 116 | --- request eval 117 | [ 118 | 'GET /backend/file.txt', 119 | 'GET /status/control?cmd=status&group=upstream@group&zone=*', 120 | ] 121 | --- response_body_like eval 122 | [ 123 | 'OK', 124 | '{"upstreamZones.*backend' 125 | ] 126 | 127 | 128 | 129 | === TEST 5: /status/control?cmd=status&group=upstream@alone&zone=* 130 | --- http_config 131 | vhost_traffic_status_zone; 132 | --- config 133 | location /status { 134 | vhost_traffic_status_display; 135 | vhost_traffic_status_display_format json; 136 | access_log off; 137 | } 138 | location /backend { 139 | proxy_set_header Host backend; 140 | proxy_pass http://localhost:1981; 141 | } 142 | --- tcp_listen: 1981 143 | --- tcp_reply eval 144 | "HTTP/1.1 200 OK\r\n\r\nupstream\@alone:OK" 145 | --- request eval 146 | [ 147 | 'GET /backend/file.txt', 148 | 'GET /status/control?cmd=status&group=upstream@alone&zone=*', 149 | ] 150 | --- response_body_like eval 151 | [ 152 | 'OK', 153 | '{"::nogroups"' 154 | ] 155 | 156 | 157 | 158 | === TEST 6: /status/control?cmd=status&group=cache&zone=* 159 | --- http_config 160 | vhost_traffic_status_zone; 161 | proxy_cache_path /tmp/cache_one levels=1:2 keys_zone=cache_one:2m inactive=1m max_size=4m; 162 | proxy_cache_path /tmp/cache_two levels=1:2 keys_zone=cache_two:2m inactive=1m max_size=4m; 163 | upstream backend { 164 | server localhost; 165 | } 166 | server { 167 | server_name backend; 168 | } 169 | --- config 170 | location /status { 171 | vhost_traffic_status_display; 172 | vhost_traffic_status_display_format json; 173 | access_log off; 174 | } 175 | location /one { 176 | proxy_cache cache_one; 177 | proxy_cache_valid 200 10s; 178 | proxy_set_header Host backend; 179 | proxy_pass http://backend; 180 | } 181 | location /two { 182 | proxy_cache cache_two; 183 | proxy_cache_valid 200 10s; 184 | proxy_set_header Host backend; 185 | proxy_pass http://backend; 186 | } 187 | --- user_files eval 188 | [ 189 | ['one/file.txt' => 'cache_one:OK'], 190 | ['two/file.txt' => 'cache_two:OK'] 191 | ] 192 | --- request eval 193 | [ 194 | 'GET /one/file.txt', 195 | 'GET /two/file.txt', 196 | 'GET /status/control?cmd=status&group=cache&zone=*' 197 | ] 198 | --- response_body_like eval 199 | [ 200 | 'OK', 201 | 'OK', 202 | '{"cacheZones.*cache_(one|two)' 203 | ] 204 | 205 | === TEST 7: /status/control?cmd=status&group=upstream%40group&zone=%2A 206 | --- http_config 207 | vhost_traffic_status_zone; 208 | upstream backend { 209 | server localhost; 210 | } 211 | server { 212 | server_name backend; 213 | } 214 | --- config 215 | location /status { 216 | vhost_traffic_status_display; 217 | vhost_traffic_status_display_format json; 218 | access_log off; 219 | } 220 | location /backend { 221 | proxy_set_header Host backend; 222 | proxy_pass http://backend; 223 | } 224 | --- user_files eval 225 | [ 226 | ['backend/file.txt' => 'upstream@group:OK'] 227 | ] 228 | --- request eval 229 | [ 230 | 'GET /backend/file.txt', 231 | 'GET /status/control?cmd=status&group=upstream%40group&zone=%2A', 232 | ] 233 | --- response_body_like eval 234 | [ 235 | 'OK', 236 | '{"upstreamZones.*backend' 237 | ] 238 | 239 | === TEST 8: /status/control?cmd=status&group=upstream%40group&zone=%2a 240 | --- http_config 241 | vhost_traffic_status_zone; 242 | upstream backend { 243 | server localhost; 244 | } 245 | server { 246 | server_name backend; 247 | } 248 | --- config 249 | location /status { 250 | vhost_traffic_status_display; 251 | vhost_traffic_status_display_format json; 252 | access_log off; 253 | } 254 | location /backend { 255 | proxy_set_header Host backend; 256 | proxy_pass http://backend; 257 | } 258 | --- user_files eval 259 | [ 260 | ['backend/file.txt' => 'upstream@group:OK'] 261 | ] 262 | --- request eval 263 | [ 264 | 'GET /backend/file.txt', 265 | 'GET /status/control?cmd=status&group=upstream%40group&zone=%2a', 266 | ] 267 | --- response_body_like eval 268 | [ 269 | 'OK', 270 | '{"upstreamZones.*backend' 271 | ] 272 | 273 | === TEST 9: /status/control?cmd=status&group=server&zone=%3A%3Amain 274 | --- http_config 275 | vhost_traffic_status_zone; 276 | --- config 277 | location /status { 278 | vhost_traffic_status_display; 279 | vhost_traffic_status_display_format json; 280 | access_log off; 281 | } 282 | --- user_files eval 283 | [ 284 | ['storage/control/file.txt' => 'server:OK'] 285 | ] 286 | --- request eval 287 | [ 288 | 'GET /storage/control/file.txt', 289 | 'GET /status/control?cmd=status&group=server&zone=%3A%3Amain', 290 | ] 291 | --- response_body_like eval 292 | [ 293 | 'OK', 294 | 'hostName' 295 | ] 296 | 297 | -------------------------------------------------------------------------------- /t/008.control_status_zone.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 4 + 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/control?cmd=status&group=server&zone=localhost 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | --- user_files eval 21 | [ 22 | ['storage/control/file.txt' => 'server:OK'] 23 | ] 24 | --- request eval 25 | [ 26 | 'GET /storage/control/file.txt', 27 | 'GET /status/control?cmd=status&group=server&zone=localhost', 28 | ] 29 | --- response_body_like eval 30 | [ 31 | 'OK', 32 | '{"localhost"' 33 | ] 34 | 35 | 36 | 37 | === TEST 2: /status/control?cmd=status&group=filter&zone=storage::localhost@vol0 38 | --- http_config 39 | vhost_traffic_status_zone; 40 | --- config 41 | location /status { 42 | vhost_traffic_status_display; 43 | vhost_traffic_status_display_format json; 44 | access_log off; 45 | } 46 | location ~ ^/storage/(.+)/.*$ { 47 | set $volume $1; 48 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 49 | } 50 | --- user_files eval 51 | [ 52 | ['storage/vol0/file.txt' => 'filter:OK'] 53 | ] 54 | --- request eval 55 | [ 56 | 'GET /storage/vol0/file.txt', 57 | 'GET /status/control?cmd=status&group=filter&zone=storage::localhost@vol0', 58 | ] 59 | --- response_body_like eval 60 | [ 61 | 'OK', 62 | '{"vol0"' 63 | ] 64 | 65 | 66 | 67 | === TEST 3: /status/control?cmd=status&group=upstream@group&zone=backend@127.0.0.1:80 68 | --- http_config 69 | vhost_traffic_status_zone; 70 | upstream backend { 71 | server 127.0.0.1; 72 | } 73 | server { 74 | server_name backend; 75 | } 76 | --- config 77 | location /status { 78 | vhost_traffic_status_display; 79 | vhost_traffic_status_display_format json; 80 | access_log off; 81 | } 82 | location /backend { 83 | proxy_set_header Host backend; 84 | proxy_pass http://backend; 85 | } 86 | --- user_files eval 87 | [ 88 | ['backend/file.txt' => 'upstream@group:OK'] 89 | ] 90 | --- request eval 91 | [ 92 | 'GET /backend/file.txt', 93 | 'GET /status/control?cmd=status&group=upstream@group&zone=backend@127.0.0.1:80', 94 | ] 95 | --- response_body_like eval 96 | [ 97 | 'OK', 98 | '{"server":"127.0.0.1:80"' 99 | ] 100 | 101 | 102 | 103 | === TEST 4: /status/control?cmd=status&group=upstream@alone&zone=127.0.0.1:1981 104 | --- http_config 105 | vhost_traffic_status_zone; 106 | --- config 107 | location /status { 108 | vhost_traffic_status_display; 109 | vhost_traffic_status_display_format json; 110 | access_log off; 111 | } 112 | location /backend { 113 | proxy_set_header Host backend; 114 | proxy_pass http://127.0.0.1:1981; 115 | } 116 | --- tcp_listen: 1981 117 | --- tcp_reply eval 118 | "HTTP/1.1 200 OK\r\n\r\nupstream\@alone:OK" 119 | --- request eval 120 | [ 121 | 'GET /backend/file.txt', 122 | 'GET /status/control?cmd=status&group=upstream@alone&zone=127.0.0.1:1981', 123 | ] 124 | --- response_body_like eval 125 | [ 126 | 'OK', 127 | '{"server":"127.0.0.1:1981"' 128 | ] 129 | 130 | 131 | 132 | === TEST 5: /status/control?cmd=status&group=cache&zone=cache_one 133 | --- http_config 134 | vhost_traffic_status_zone; 135 | proxy_cache_path /tmp/cache_one levels=1:2 keys_zone=cache_one:2m inactive=1m max_size=4m; 136 | proxy_cache_path /tmp/cache_two levels=1:2 keys_zone=cache_two:2m inactive=1m max_size=4m; 137 | upstream backend { 138 | server 127.0.0.1; 139 | } 140 | server { 141 | server_name backend; 142 | } 143 | --- config 144 | location /status { 145 | vhost_traffic_status_display; 146 | vhost_traffic_status_display_format json; 147 | access_log off; 148 | } 149 | location /one { 150 | proxy_cache cache_one; 151 | proxy_cache_valid 200 10s; 152 | proxy_set_header Host backend; 153 | proxy_pass http://backend; 154 | } 155 | location /two { 156 | proxy_cache cache_two; 157 | proxy_cache_valid 200 10s; 158 | proxy_set_header Host backend; 159 | proxy_pass http://backend; 160 | } 161 | --- user_files eval 162 | [ 163 | ['one/file.txt' => 'cache_one:OK'], 164 | ['two/file.txt' => 'cache_two:OK'] 165 | ] 166 | --- request eval 167 | [ 168 | 'GET /one/file.txt', 169 | 'GET /two/file.txt', 170 | 'GET /status/control?cmd=status&group=cache&zone=cache_one' 171 | ] 172 | --- response_body_like eval 173 | [ 174 | 'OK', 175 | 'OK', 176 | '{"cache_one"' 177 | ] 178 | 179 | === TEST 6: /status/control?cmd=status&group=filter&zone=storage%3A%3Alocalhost%40vol0 180 | --- http_config 181 | vhost_traffic_status_zone; 182 | --- config 183 | location /status { 184 | vhost_traffic_status_display; 185 | vhost_traffic_status_display_format json; 186 | access_log off; 187 | } 188 | location ~ ^/storage/(.+)/.*$ { 189 | set $volume $1; 190 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 191 | } 192 | --- user_files eval 193 | [ 194 | ['storage/vol0/file.txt' => 'filter:OK'] 195 | ] 196 | --- request eval 197 | [ 198 | 'GET /storage/vol0/file.txt', 199 | 'GET /status/control?cmd=status&group=filter&zone=storage%3A%3Alocalhost%40vol0', 200 | ] 201 | --- response_body_like eval 202 | [ 203 | 'OK', 204 | '{"vol0"' 205 | ] 206 | 207 | === TEST 7: /status/control?cmd=status&group=upstream%40alone&zone=127.0.0.1%3A1981 208 | --- http_config 209 | vhost_traffic_status_zone; 210 | --- config 211 | location /status { 212 | vhost_traffic_status_display; 213 | vhost_traffic_status_display_format json; 214 | access_log off; 215 | } 216 | location /backend { 217 | proxy_set_header Host backend; 218 | proxy_pass http://127.0.0.1:1981; 219 | } 220 | --- tcp_listen: 1981 221 | --- tcp_reply eval 222 | "HTTP/1.1 200 OK\r\n\r\nupstream\@alone:OK" 223 | --- request eval 224 | [ 225 | 'GET /backend/file.txt', 226 | 'GET /status/control?cmd=status&group=upstream%40alone&zone=127.0.0.1%3A1981', 227 | ] 228 | --- response_body_like eval 229 | [ 230 | 'OK', 231 | '{"server":"127.0.0.1:1981"' 232 | ] 233 | 234 | === TEST 8: /status/control?cmd=status&group=filter&zone=storage%3a%3alocalhost%40vol0 235 | --- http_config 236 | vhost_traffic_status_zone; 237 | --- config 238 | location /status { 239 | vhost_traffic_status_display; 240 | vhost_traffic_status_display_format json; 241 | access_log off; 242 | } 243 | location ~ ^/storage/(.+)/.*$ { 244 | set $volume $1; 245 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 246 | } 247 | --- user_files eval 248 | [ 249 | ['storage/vol0/file.txt' => 'filter:OK'] 250 | ] 251 | --- request eval 252 | [ 253 | 'GET /storage/vol0/file.txt', 254 | 'GET /status/control?cmd=status&group=filter&zone=storage%3A%3Alocalhost%40vol0', 255 | ] 256 | --- response_body_like eval 257 | [ 258 | 'OK', 259 | '{"vol0"' 260 | ] 261 | 262 | -------------------------------------------------------------------------------- /t/009.control_reset_fully.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 4; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/control?cmd=reset&group=* 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | location ~ ^/storage/(.+)/.*$ { 21 | set $volume $1; 22 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 23 | } 24 | --- user_files eval 25 | [ 26 | ['storage/vol0/file.txt' => 'vol0:OK'] 27 | ] 28 | --- request eval 29 | [ 30 | 'GET /storage/vol0/file.txt', 31 | 'GET /status/control?cmd=reset&group=*', 32 | ] 33 | --- response_body_like eval 34 | [ 35 | 'OK', 36 | '"processingCounts":[1-9]' 37 | ] 38 | 39 | -------------------------------------------------------------------------------- /t/010.control_reset_group.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 4 + 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/control?cmd=reset&group=server&zone=* 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | --- user_files eval 21 | [ 22 | ['storage/control/file.txt' => 'server:OK'] 23 | ] 24 | --- request eval 25 | [ 26 | 'GET /storage/control/file.txt', 27 | "GET /status/control?cmd=reset&group=server&zone=*", 28 | ] 29 | --- response_body_like eval 30 | [ 31 | 'OK', 32 | '"processingCounts":[1-9]' 33 | ] 34 | 35 | 36 | 37 | === TEST 2: /status/control?cmd=reset&group=filter&zone=* 38 | --- http_config 39 | vhost_traffic_status_zone; 40 | --- config 41 | location /status { 42 | vhost_traffic_status_display; 43 | vhost_traffic_status_display_format json; 44 | access_log off; 45 | } 46 | location ~ ^/storage/(.+)/.*$ { 47 | set $volume $1; 48 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 49 | } 50 | --- user_files eval 51 | [ 52 | ['storage/vol0/file.txt' => 'filter:OK'] 53 | ] 54 | --- request eval 55 | [ 56 | 'GET /storage/vol0/file.txt', 57 | 'GET /status/control?cmd=reset&group=filter&zone=*', 58 | ] 59 | --- response_body_like eval 60 | [ 61 | 'OK', 62 | '"processingCounts":[1-9]' 63 | ] 64 | 65 | 66 | 67 | === TEST 3: /status/control?cmd=reset&group=upstream@group&zone=* 68 | --- http_config 69 | vhost_traffic_status_zone; 70 | upstream backend { 71 | server 127.0.0.1; 72 | } 73 | server { 74 | server_name backend; 75 | } 76 | --- config 77 | location /status { 78 | vhost_traffic_status_display; 79 | vhost_traffic_status_display_format json; 80 | access_log off; 81 | } 82 | location /backend { 83 | proxy_set_header Host backend; 84 | proxy_pass http://backend; 85 | } 86 | --- user_files eval 87 | [ 88 | ['backend/file.txt' => 'upstream@group:OK'] 89 | ] 90 | --- request eval 91 | [ 92 | 'GET /backend/file.txt', 93 | 'GET /status/control?cmd=reset&group=upstream@group&zone=*', 94 | ] 95 | --- response_body_like eval 96 | [ 97 | 'OK', 98 | '"processingCounts":[1-9]' 99 | ] 100 | 101 | 102 | 103 | === TEST 4: /status/control?cmd=reset&group=upstream@alone&zone=* 104 | --- http_config 105 | vhost_traffic_status_zone; 106 | --- config 107 | location /status { 108 | vhost_traffic_status_display; 109 | vhost_traffic_status_display_format json; 110 | access_log off; 111 | } 112 | location /backend { 113 | proxy_set_header Host backend; 114 | proxy_pass http://localhost:1981; 115 | } 116 | --- tcp_listen: 1981 117 | --- tcp_reply eval 118 | "HTTP/1.1 200 OK\r\n\r\nupstream\@alone:OK" 119 | --- request eval 120 | [ 121 | 'GET /backend/file.txt', 122 | 'GET /status/control?cmd=reset&group=upstream@alone&zone=*', 123 | ] 124 | --- response_body_like eval 125 | [ 126 | 'OK', 127 | '"processingCounts":[1-9]' 128 | ] 129 | 130 | 131 | 132 | === TEST 5: /status/control?cmd=reset&group=cache&zone=* 133 | --- http_config 134 | vhost_traffic_status_zone; 135 | proxy_cache_path /tmp/cache_one levels=1:2 keys_zone=cache_one:2m inactive=1m max_size=4m; 136 | proxy_cache_path /tmp/cache_two levels=1:2 keys_zone=cache_two:2m inactive=1m max_size=4m; 137 | upstream backend { 138 | server 127.0.0.1; 139 | } 140 | server { 141 | server_name backend; 142 | } 143 | --- config 144 | location /status { 145 | vhost_traffic_status_display; 146 | vhost_traffic_status_display_format json; 147 | access_log off; 148 | } 149 | location /one { 150 | proxy_cache cache_one; 151 | proxy_cache_valid 200 10s; 152 | proxy_set_header Host backend; 153 | proxy_pass http://backend; 154 | } 155 | location /two { 156 | proxy_cache cache_two; 157 | proxy_cache_valid 200 10s; 158 | proxy_set_header Host backend; 159 | proxy_pass http://backend; 160 | } 161 | --- user_files eval 162 | [ 163 | ['one/file.txt' => 'cache_one:OK'], 164 | ['two/file.txt' => 'cache_two:OK'] 165 | ] 166 | --- request eval 167 | [ 168 | 'GET /one/file.txt', 169 | 'GET /two/file.txt', 170 | 'GET /status/control?cmd=reset&group=cache&zone=*' 171 | ] 172 | --- response_body_like eval 173 | [ 174 | 'OK', 175 | 'OK', 176 | '"processingCounts":[1-9]' 177 | ] 178 | -------------------------------------------------------------------------------- /t/011.control_reset_zone.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 4 + 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/control?cmd=reset&group=server&zone=localhost 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | --- user_files eval 21 | [ 22 | ['storage/control/file.txt' => 'server:OK'] 23 | ] 24 | --- request eval 25 | [ 26 | 'GET /storage/control/file.txt', 27 | 'GET /status/control?cmd=reset&group=server&zone=localhost', 28 | ] 29 | --- response_body_like eval 30 | [ 31 | 'OK', 32 | '"processingCounts":[1-9]' 33 | ] 34 | 35 | 36 | 37 | === TEST 2: /status/control?cmd=reset&group=filter&zone=storage::localhost@vol0 38 | --- http_config 39 | vhost_traffic_status_zone; 40 | --- config 41 | location /status { 42 | vhost_traffic_status_display; 43 | vhost_traffic_status_display_format json; 44 | access_log off; 45 | } 46 | location ~ ^/storage/(.+)/.*$ { 47 | set $volume $1; 48 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 49 | } 50 | --- user_files eval 51 | [ 52 | ['storage/vol0/file.txt' => 'filter:OK'] 53 | ] 54 | --- request eval 55 | [ 56 | 'GET /storage/vol0/file.txt', 57 | 'GET /status/control?cmd=reset&group=filter&zone=storage::localhost@vol0', 58 | ] 59 | --- response_body_like eval 60 | [ 61 | 'OK', 62 | '"processingCounts":[1-9]' 63 | ] 64 | 65 | 66 | 67 | === TEST 3: /status/control?cmd=reset&group=upstream@group&zone=backend@127.0.0.1:80 68 | --- http_config 69 | vhost_traffic_status_zone; 70 | upstream backend { 71 | server 127.0.0.1; 72 | } 73 | server { 74 | server_name backend; 75 | } 76 | --- config 77 | location /status { 78 | vhost_traffic_status_display; 79 | vhost_traffic_status_display_format json; 80 | access_log off; 81 | } 82 | location /backend { 83 | proxy_set_header Host backend; 84 | proxy_pass http://backend; 85 | } 86 | --- user_files eval 87 | [ 88 | ['backend/file.txt' => 'upstream@group:OK'] 89 | ] 90 | --- request eval 91 | [ 92 | 'GET /backend/file.txt', 93 | 'GET /status/control?cmd=reset&group=upstream@group&zone=backend@127.0.0.1:80', 94 | ] 95 | --- response_body_like eval 96 | [ 97 | 'OK', 98 | '"processingCounts":[1-9]' 99 | ] 100 | 101 | 102 | 103 | === TEST 4: /status/control?cmd=reset&group=upstream@alone&zone=127.0.0.1:1981 104 | --- http_config 105 | vhost_traffic_status_zone; 106 | --- config 107 | location /status { 108 | vhost_traffic_status_display; 109 | vhost_traffic_status_display_format json; 110 | access_log off; 111 | } 112 | location /backend { 113 | proxy_set_header Host backend; 114 | proxy_pass http://127.0.0.1:1981; 115 | } 116 | --- tcp_listen: 1981 117 | --- tcp_reply eval 118 | "HTTP/1.1 200 OK\r\n\r\nupstream\@alone:OK" 119 | --- request eval 120 | [ 121 | 'GET /backend/file.txt', 122 | 'GET /status/control?cmd=reset&group=upstream@alone&zone=127.0.0.1:1981', 123 | ] 124 | --- response_body_like eval 125 | [ 126 | 'OK', 127 | '"processingCounts":[1-9]' 128 | ] 129 | 130 | 131 | 132 | === TEST 5: /status/control?cmd=reset&group=cache&zone=cache_one 133 | --- http_config 134 | vhost_traffic_status_zone; 135 | proxy_cache_path /tmp/cache_one levels=1:2 keys_zone=cache_one:2m inactive=1m max_size=4m; 136 | proxy_cache_path /tmp/cache_two levels=1:2 keys_zone=cache_two:2m inactive=1m max_size=4m; 137 | upstream backend { 138 | server 127.0.0.1; 139 | } 140 | server { 141 | server_name backend; 142 | } 143 | --- config 144 | location /status { 145 | vhost_traffic_status_display; 146 | vhost_traffic_status_display_format json; 147 | access_log off; 148 | } 149 | location /one { 150 | proxy_cache cache_one; 151 | proxy_cache_valid 200 10s; 152 | proxy_set_header Host backend; 153 | proxy_pass http://backend; 154 | } 155 | location /two { 156 | proxy_cache cache_two; 157 | proxy_cache_valid 200 10s; 158 | proxy_set_header Host backend; 159 | proxy_pass http://backend; 160 | } 161 | --- user_files eval 162 | [ 163 | ['one/file.txt' => 'cache_one:OK'], 164 | ['two/file.txt' => 'cache_two:OK'] 165 | ] 166 | --- request eval 167 | [ 168 | 'GET /one/file.txt', 169 | 'GET /two/file.txt', 170 | 'GET /status/control?cmd=reset&group=cache&zone=cache_one' 171 | ] 172 | --- response_body_like eval 173 | [ 174 | 'OK', 175 | 'OK', 176 | '"processingCounts":[1-9]' 177 | ] 178 | -------------------------------------------------------------------------------- /t/012.control_delete_fully.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 4; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/control?cmd=delete&group=* 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | location ~ ^/storage/(.+)/.*$ { 21 | set $volume $1; 22 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 23 | } 24 | --- user_files eval 25 | [ 26 | ['storage/vol0/file.txt' => 'vol0:OK'] 27 | ] 28 | --- request eval 29 | [ 30 | 'GET /storage/vol0/file.txt', 31 | 'GET /status/control?cmd=delete&group=*', 32 | ] 33 | --- response_body_like eval 34 | [ 35 | 'OK', 36 | '"processingCounts":[1-9]' 37 | ] 38 | 39 | -------------------------------------------------------------------------------- /t/013.control_delete_group.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 4 + 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/control?cmd=delete&group=server&zone=* 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | --- user_files eval 21 | [ 22 | ['storage/control/file.txt' => 'server:OK'] 23 | ] 24 | --- request eval 25 | [ 26 | 'GET /storage/control/file.txt', 27 | "GET /status/control?cmd=delete&group=server&zone=*", 28 | ] 29 | --- response_body_like eval 30 | [ 31 | 'OK', 32 | '"processingCounts":[1-9]' 33 | ] 34 | 35 | 36 | 37 | === TEST 2: /status/control?cmd=delete&group=filter&zone=* 38 | --- http_config 39 | vhost_traffic_status_zone; 40 | --- config 41 | location /status { 42 | vhost_traffic_status_display; 43 | vhost_traffic_status_display_format json; 44 | access_log off; 45 | } 46 | location ~ ^/storage/(.+)/.*$ { 47 | set $volume $1; 48 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 49 | } 50 | --- user_files eval 51 | [ 52 | ['storage/vol0/file.txt' => 'filter:OK'] 53 | ] 54 | --- request eval 55 | [ 56 | 'GET /storage/vol0/file.txt', 57 | 'GET /status/control?cmd=delete&group=filter&zone=*', 58 | ] 59 | --- response_body_like eval 60 | [ 61 | 'OK', 62 | '"processingCounts":[1-9]' 63 | ] 64 | 65 | 66 | 67 | === TEST 3: /status/control?cmd=delete&group=upstream@group&zone=* 68 | --- http_config 69 | vhost_traffic_status_zone; 70 | upstream backend { 71 | server 127.0.0.1; 72 | } 73 | server { 74 | server_name backend; 75 | } 76 | --- config 77 | location /status { 78 | vhost_traffic_status_display; 79 | vhost_traffic_status_display_format json; 80 | access_log off; 81 | } 82 | location /backend { 83 | proxy_set_header Host backend; 84 | proxy_pass http://backend; 85 | } 86 | --- user_files eval 87 | [ 88 | ['backend/file.txt' => 'upstream@group:OK'] 89 | ] 90 | --- request eval 91 | [ 92 | 'GET /backend/file.txt', 93 | 'GET /status/control?cmd=delete&group=upstream@group&zone=*', 94 | ] 95 | --- response_body_like eval 96 | [ 97 | 'OK', 98 | '"processingCounts":[1-9]' 99 | ] 100 | 101 | 102 | 103 | === TEST 4: /status/control?cmd=delete&group=upstream@alone&zone=* 104 | --- http_config 105 | vhost_traffic_status_zone; 106 | --- config 107 | location /status { 108 | vhost_traffic_status_display; 109 | vhost_traffic_status_display_format json; 110 | access_log off; 111 | } 112 | location /backend { 113 | proxy_set_header Host backend; 114 | proxy_pass http://localhost:1981; 115 | } 116 | --- tcp_listen: 1981 117 | --- tcp_reply eval 118 | "HTTP/1.1 200 OK\r\n\r\nupstream\@alone:OK" 119 | --- request eval 120 | [ 121 | 'GET /backend/file.txt', 122 | 'GET /status/control?cmd=delete&group=upstream@alone&zone=*', 123 | ] 124 | --- response_body_like eval 125 | [ 126 | 'OK', 127 | '"processingCounts":[1-9]' 128 | ] 129 | 130 | 131 | 132 | === TEST 5: /status/control?cmd=delete&group=cache&zone=* 133 | --- http_config 134 | vhost_traffic_status_zone; 135 | proxy_cache_path /tmp/cache_one levels=1:2 keys_zone=cache_one:2m inactive=1m max_size=4m; 136 | proxy_cache_path /tmp/cache_two levels=1:2 keys_zone=cache_two:2m inactive=1m max_size=4m; 137 | upstream backend { 138 | server 127.0.0.1; 139 | } 140 | server { 141 | server_name backend; 142 | } 143 | --- config 144 | location /status { 145 | vhost_traffic_status_display; 146 | vhost_traffic_status_display_format json; 147 | access_log off; 148 | } 149 | location /one { 150 | proxy_cache cache_one; 151 | proxy_cache_valid 200 10s; 152 | proxy_set_header Host backend; 153 | proxy_pass http://backend; 154 | } 155 | location /two { 156 | proxy_cache cache_two; 157 | proxy_cache_valid 200 10s; 158 | proxy_set_header Host backend; 159 | proxy_pass http://backend; 160 | } 161 | --- user_files eval 162 | [ 163 | ['one/file.txt' => 'cache_one:OK'], 164 | ['two/file.txt' => 'cache_two:OK'] 165 | ] 166 | --- request eval 167 | [ 168 | 'GET /one/file.txt', 169 | 'GET /two/file.txt', 170 | 'GET /status/control?cmd=delete&group=cache&zone=*' 171 | ] 172 | --- response_body_like eval 173 | [ 174 | 'OK', 175 | 'OK', 176 | '"processingCounts":[1-9]' 177 | ] 178 | -------------------------------------------------------------------------------- /t/014.control_delete_zone.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 4 + 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: /status/control?cmd=delete&group=server&zone=localhost 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_format json; 18 | access_log off; 19 | } 20 | --- user_files eval 21 | [ 22 | ['storage/control/file.txt' => 'server:OK'] 23 | ] 24 | --- request eval 25 | [ 26 | 'GET /storage/control/file.txt', 27 | 'GET /status/control?cmd=delete&group=server&zone=localhost', 28 | ] 29 | --- response_body_like eval 30 | [ 31 | 'OK', 32 | '"processingCounts":[1-9]' 33 | ] 34 | 35 | 36 | 37 | === TEST 2: /status/control?cmd=delete&group=filter&zone=storage::localhost@vol0 38 | --- http_config 39 | vhost_traffic_status_zone; 40 | --- config 41 | location /status { 42 | vhost_traffic_status_display; 43 | vhost_traffic_status_display_format json; 44 | access_log off; 45 | } 46 | location ~ ^/storage/(.+)/.*$ { 47 | set $volume $1; 48 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 49 | } 50 | --- user_files eval 51 | [ 52 | ['storage/vol0/file.txt' => 'filter:OK'] 53 | ] 54 | --- request eval 55 | [ 56 | 'GET /storage/vol0/file.txt', 57 | 'GET /status/control?cmd=delete&group=filter&zone=storage::localhost@vol0', 58 | ] 59 | --- response_body_like eval 60 | [ 61 | 'OK', 62 | '"processingCounts":[1-9]' 63 | ] 64 | 65 | 66 | 67 | === TEST 3: /status/control?cmd=delete&group=upstream@group&zone=backend@127.0.0.1:80 68 | --- http_config 69 | vhost_traffic_status_zone; 70 | upstream backend { 71 | server 127.0.0.1; 72 | } 73 | server { 74 | server_name backend; 75 | } 76 | --- config 77 | location /status { 78 | vhost_traffic_status_display; 79 | vhost_traffic_status_display_format json; 80 | access_log off; 81 | } 82 | location /backend { 83 | proxy_set_header Host backend; 84 | proxy_pass http://backend; 85 | } 86 | --- user_files eval 87 | [ 88 | ['backend/file.txt' => 'upstream@group:OK'] 89 | ] 90 | --- request eval 91 | [ 92 | 'GET /backend/file.txt', 93 | 'GET /status/control?cmd=delete&group=upstream@group&zone=backend@127.0.0.1:80', 94 | ] 95 | --- response_body_like eval 96 | [ 97 | 'OK', 98 | '"processingCounts":[1-9]' 99 | ] 100 | 101 | 102 | 103 | === TEST 4: /status/control?cmd=delete&group=upstream@alone&zone=127.0.0.1:1981 104 | --- http_config 105 | vhost_traffic_status_zone; 106 | --- config 107 | location /status { 108 | vhost_traffic_status_display; 109 | vhost_traffic_status_display_format json; 110 | access_log off; 111 | } 112 | location /backend { 113 | proxy_set_header Host backend; 114 | proxy_pass http://127.0.0.1:1981; 115 | } 116 | --- tcp_listen: 1981 117 | --- tcp_reply eval 118 | "HTTP/1.1 200 OK\r\n\r\nupstream\@alone:OK" 119 | --- request eval 120 | [ 121 | 'GET /backend/file.txt', 122 | 'GET /status/control?cmd=delete&group=upstream@alone&zone=127.0.0.1:1981', 123 | ] 124 | --- response_body_like eval 125 | [ 126 | 'OK', 127 | '"processingCounts":[1-9]' 128 | ] 129 | 130 | 131 | 132 | === TEST 5: /status/control?cmd=delete&group=cache&zone=cache_one 133 | --- http_config 134 | vhost_traffic_status_zone; 135 | proxy_cache_path /tmp/cache_one levels=1:2 keys_zone=cache_one:2m inactive=1m max_size=4m; 136 | proxy_cache_path /tmp/cache_two levels=1:2 keys_zone=cache_two:2m inactive=1m max_size=4m; 137 | upstream backend { 138 | server 127.0.0.1; 139 | } 140 | server { 141 | server_name backend; 142 | } 143 | --- config 144 | location /status { 145 | vhost_traffic_status_display; 146 | vhost_traffic_status_display_format json; 147 | access_log off; 148 | } 149 | location /one { 150 | proxy_cache cache_one; 151 | proxy_cache_valid 200 10s; 152 | proxy_set_header Host backend; 153 | proxy_pass http://backend; 154 | } 155 | location /two { 156 | proxy_cache cache_two; 157 | proxy_cache_valid 200 10s; 158 | proxy_set_header Host backend; 159 | proxy_pass http://backend; 160 | } 161 | --- user_files eval 162 | [ 163 | ['one/file.txt' => 'cache_one:OK'], 164 | ['two/file.txt' => 'cache_two:OK'] 165 | ] 166 | --- request eval 167 | [ 168 | 'GET /one/file.txt', 169 | 'GET /two/file.txt', 170 | 'GET /status/control?cmd=delete&group=cache&zone=cache_one' 171 | ] 172 | --- response_body_like eval 173 | [ 174 | 'OK', 175 | 'OK', 176 | '"processingCounts":[1-9]' 177 | ] 178 | -------------------------------------------------------------------------------- /t/015.vts_variables_by_lua.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 6; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST1: access embeded variables starting with a $vts_* by lua 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /variables { 16 | access_by_lua_block { 17 | local i 18 | local variables = { 19 | ngx.var.vts_request_counter, 20 | ngx.var.vts_in_bytes, 21 | ngx.var.vts_out_bytes, 22 | ngx.var.vts_1xx_counter, 23 | ngx.var.vts_2xx_counter, 24 | ngx.var.vts_3xx_counter, 25 | ngx.var.vts_4xx_counter, 26 | ngx.var.vts_5xx_counter, 27 | ngx.var.vts_request_time_counter, 28 | ngx.var.vts_request_time, 29 | ngx.var.vts_cache_miss_counter, 30 | ngx.var.vts_cache_bypass_counter, 31 | ngx.var.vts_cache_expired_counter, 32 | ngx.var.vts_cache_stale_counter, 33 | ngx.var.vts_cache_updating_counter, 34 | ngx.var.vts_cache_revalidated_counter, 35 | ngx.var.vts_cache_hit_counter, 36 | ngx.var.vts_cache_scarce_counter 37 | } 38 | ngx.print("embeded_variables: 18, find_variables: ", table.getn(variables), ", variables: "); 39 | for i=1, table.getn(variables) do 40 | if variables[i] then 41 | ngx.print(i, ":[", variables[i], "] ") 42 | else 43 | ngx.print(i, ":[nil] ") 44 | end 45 | end 46 | } 47 | } 48 | location ~ ^/storage/(.+)/.*$ { 49 | set $volume $1; 50 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 51 | } 52 | --- user_files eval 53 | [ 54 | ['storage/access/file.txt' => 'access:OK'] 55 | ] 56 | --- request eval 57 | [ 58 | 'GET /storage/access/file.txt', 59 | 'GET /variables', 60 | 'GET /variables' 61 | ] 62 | --- response_body_like eval 63 | [ 64 | 'OK', 65 | 'find_variables: 18', 66 | 'find_variables: 18' 67 | ] 68 | -------------------------------------------------------------------------------- /t/016.limit_traffic_by_lua.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 8; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST1: limit traffic using $vts_* by lua 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | set $limit_in 200; 16 | set $limit_out 256; 17 | access_by_lua_block { 18 | local limits = { 19 | ["request"] = tonumber(ngx.var.limit_request), 20 | ["in"] = tonumber(ngx.var.limit_in), 21 | ["out"] = tonumber(ngx.var.limit_out), 22 | ["1xx"] = tonumber(ngx.var.limit_1xx), 23 | ["2xx"] = tonumber(ngx.var.limit_2xx), 24 | ["3xx"] = tonumber(ngx.var.limit_3xx), 25 | ["4xx"] = tonumber(ngx.var.limit_4xx), 26 | ["5xx"] = tonumber(ngx.var.limit_5xx), 27 | ["miss"] = tonumber(ngx.var.limit_miss), 28 | ["bypass"] = tonumber(ngx.var.limit_bypass), 29 | ["expired"] = tonumber(ngx.var.limit_expired), 30 | ["stale"] = tonumber(ngx.var.limit_stale), 31 | ["updating"] = tonumber(ngx.var.limit_updating), 32 | ["revalidated"] = tonumber(ngx.var.limit_revalidated), 33 | ["hit"] = tonumber(ngx.var.limit_hit), 34 | ["scarce"] = tonumber(ngx.var.limit_scarce) 35 | } 36 | 37 | local stats = { 38 | ["request"] = limits["request"] and tonumber(ngx.var.vts_request_counter), 39 | ["in"] = limits["in"] and tonumber(ngx.var.vts_in_bytes), 40 | ["out"] = limits["out"] and tonumber(ngx.var.vts_out_bytes), 41 | ["1xx"] = limits["1xx"] and tonumber(ngx.var.vts_1xx_counter), 42 | ["2xx"] = limits["2xx"] and tonumber(ngx.var.vts_2xx_counter), 43 | ["3xx"] = limits["3xx"] and tonumber(ngx.var.vts_3xx_counter), 44 | ["4xx"] = limits["4xx"] and tonumber(ngx.var.vts_4xx_counter), 45 | ["5xx"] = limits["5xx"] and tonumber(ngx.var.vts_5xx_counter), 46 | ["miss"] = limits["miss"] and tonumber(ngx.var.vts_cache_miss_counter), 47 | ["bypass"] = limits["bypass"] and tonumber(ngx.var.vts_cache_bypass_counter), 48 | ["expired"] = limits["expired"] and tonumber(ngx.var.vts_cache_expired_counter), 49 | ["stale"] = limits["stale"] and tonumber(ngx.var.vts_cache_stale_counter), 50 | ["updating"] = limits["updating"] and tonumber(ngx.var.vts_cache_updating_counter), 51 | ["revalidated"] = limits["revalidated"] and tonumber(ngx.var.vts_cache_revalidated_counter), 52 | ["hit"] = limits["hit"] and tonumber(ngx.var.vts_cache_hit_counter), 53 | ["scarce"] = limits["scarce"] and tonumber(ngx.var.vts_cache_scarce_counter) 54 | } 55 | 56 | for k,v in pairs(limits) do 57 | if stats[k] and stats[k] > v then 58 | ngx.say("exceeded ", k, " traffic limit[", v, "] < current[", stats[k], "]"); 59 | end 60 | end 61 | } 62 | location ~ ^/storage/(.+)/.*$ { 63 | set $volume $1; 64 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 65 | } 66 | --- user_files eval 67 | [ 68 | ['storage/limit/file.txt' => 'limit:OK'] 69 | ] 70 | --- request eval 71 | [ 72 | 'GET /storage/limit/file.txt', 73 | 'GET /storage/limit/file.txt', 74 | 'GET /storage/limit/file.txt', 75 | 'GET /storage/limit/file.txt' 76 | ] 77 | --- response_body_like eval 78 | [ 79 | 'OK', 80 | 'OK', 81 | 'exceeded', 82 | 'exceeded' 83 | ] 84 | -------------------------------------------------------------------------------- /t/017.limit_traffic.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 12; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: vhost_traffic_status_limit_traffic request:n 402 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location ~ / { 16 | set $member request; 17 | vhost_traffic_status_limit_traffic $member:4 402; 18 | } 19 | error_page 402 /storage/limit/402.txt; 20 | location = /storage/limit/402.txt { 21 | internal; 22 | } 23 | --- user_files eval 24 | [ 25 | ['storage/limit/file.txt' => 'server:OK'], 26 | ['storage/limit/402.txt' => 'limited:OK'] 27 | ] 28 | --- request eval 29 | [ 30 | 'GET /storage/limit/file.txt', 31 | 'GET /storage/limit/file.txt', 32 | 'GET /storage/limit/file.txt', 33 | 'GET /storage/limit/file.txt', 34 | 'GET /storage/limit/file.txt', 35 | 'GET /storage/limit/file.txt' 36 | ] 37 | --- error_code eval 38 | [ 39 | 200, 40 | 200, 41 | 200, 42 | 200, 43 | 200, 44 | 402 45 | ] 46 | --- response_body_like eval 47 | [ 48 | 'OK', 49 | 'OK', 50 | 'OK', 51 | 'OK', 52 | 'OK', 53 | 'OK' 54 | ] 55 | 56 | 57 | 58 | === TEST 2: vhost_traffic_status_limit_traffic in:n 59 | --- http_config 60 | vhost_traffic_status_zone; 61 | --- config 62 | location ~ / { 63 | vhost_traffic_status_limit_traffic in:320; 64 | } 65 | error_page 503 /storage/limit/503.txt; 66 | location = /storage/limit/503.txt { 67 | internal; 68 | } 69 | --- user_files eval 70 | [ 71 | ['storage/limit/file.txt' => 'server:OK'], 72 | ['storage/limit/503.txt' => 'limited:OK'] 73 | ] 74 | --- request eval 75 | [ 76 | 'GET /storage/limit/file.txt', 77 | 'GET /storage/limit/file.txt', 78 | 'GET /storage/limit/file.txt', 79 | 'GET /storage/limit/file.txt', 80 | 'GET /storage/limit/file.txt', 81 | 'GET /storage/limit/file.txt' 82 | ] 83 | --- error_code eval 84 | [ 85 | 200, 86 | 200, 87 | 200, 88 | 200, 89 | 200, 90 | 503 91 | ] 92 | --- response_body_like eval 93 | [ 94 | 'OK', 95 | 'OK', 96 | 'OK', 97 | 'OK', 98 | 'OK', 99 | 'OK' 100 | ] 101 | 102 | 103 | 104 | === TEST 3: vhost_traffic_status_limit_traffic out:n 105 | --- http_config 106 | vhost_traffic_status_zone; 107 | --- config 108 | location ~ / { 109 | vhost_traffic_status_limit_traffic out:1024; 110 | } 111 | error_page 503 /storage/limit/503.txt; 112 | location = /storage/limit/503.txt { 113 | internal; 114 | } 115 | --- user_files eval 116 | [ 117 | ['storage/limit/file.txt' => 'server:OK'], 118 | ['storage/limit/503.txt' => 'limited:OK'] 119 | ] 120 | --- request eval 121 | [ 122 | 'GET /storage/limit/file.txt', 123 | 'GET /storage/limit/file.txt', 124 | 'GET /storage/limit/file.txt', 125 | 'GET /storage/limit/file.txt', 126 | 'GET /storage/limit/file.txt', 127 | 'GET /storage/limit/file.txt' 128 | ] 129 | --- error_code eval 130 | [ 131 | 200, 132 | 200, 133 | 200, 134 | 200, 135 | 200, 136 | 503 137 | ] 138 | --- response_body_like eval 139 | [ 140 | 'OK', 141 | 'OK', 142 | 'OK', 143 | 'OK', 144 | 'OK', 145 | 'OK' 146 | ] 147 | 148 | 149 | 150 | === TEST 4: vhost_traffic_status_limit off 151 | --- http_config 152 | vhost_traffic_status_zone; 153 | vhost_traffic_status_limit off; 154 | --- config 155 | location ~ / { 156 | set $member request; 157 | vhost_traffic_status_limit_traffic $member:4; 158 | } 159 | error_page 503 /storage/limit/503.txt; 160 | location = /storage/limit/503.txt { 161 | internal; 162 | } 163 | --- user_files eval 164 | [ 165 | ['storage/limit/file.txt' => 'server:OK'], 166 | ['storage/limit/503.txt' => 'limited:OK'] 167 | ] 168 | --- request eval 169 | [ 170 | 'GET /storage/limit/file.txt', 171 | 'GET /storage/limit/file.txt', 172 | 'GET /storage/limit/file.txt', 173 | 'GET /storage/limit/file.txt', 174 | 'GET /storage/limit/file.txt', 175 | 'GET /storage/limit/file.txt' 176 | ] 177 | --- error_code eval 178 | [ 179 | 200, 180 | 200, 181 | 200, 182 | 200, 183 | 200, 184 | 200 185 | ] 186 | --- response_body_like eval 187 | [ 188 | 'OK', 189 | 'OK', 190 | 'OK', 191 | 'OK', 192 | 'OK', 193 | 'OK' 194 | ] 195 | -------------------------------------------------------------------------------- /t/018.limit_traffic_by_set_key.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 11 + 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: vhost_traffic_status_limit_traffic_by_set_key FG@group@name request:n 402 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location ~ ^/storage/(.+)/.*$ { 16 | set $volume $1; 17 | set $member request; 18 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 19 | vhost_traffic_status_limit_traffic_by_set_key FG@storage::$server_name@$volume $member:4 402; 20 | } 21 | error_page 402 /storage/limit/402.txt; 22 | location = /storage/limit/402.txt { 23 | internal; 24 | } 25 | --- user_files eval 26 | [ 27 | ['storage/vol0/file.txt' => 'vol0:OK'], 28 | ['storage/limit/402.txt' => 'limited:OK'] 29 | ] 30 | --- request eval 31 | [ 32 | 'GET /storage/vol0/file.txt', 33 | 'GET /storage/vol0/file.txt', 34 | 'GET /storage/vol0/file.txt', 35 | 'GET /storage/vol0/file.txt', 36 | 'GET /storage/vol0/file.txt', 37 | 'GET /storage/vol0/file.txt', 38 | ] 39 | --- error_code eval 40 | [ 41 | 200, 42 | 200, 43 | 200, 44 | 200, 45 | 200, 46 | 402 47 | ] 48 | --- response_body_like eval 49 | [ 50 | 'OK', 51 | 'OK', 52 | 'OK', 53 | 'OK', 54 | 'OK', 55 | 'OK' 56 | ] 57 | 58 | 59 | 60 | === TEST 2: vhost_traffic_status_limit_traffic_by_set_key FG@group@name in:n 61 | --- http_config 62 | vhost_traffic_status_zone; 63 | --- config 64 | location ~ ^/storage/(.+)/.*$ { 65 | set $volume $1; 66 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 67 | vhost_traffic_status_limit_traffic_by_set_key FG@storage::$server_name@$volume in:300; 68 | } 69 | error_page 503 /storage/limit/503.txt; 70 | location = /storage/limit/503.txt { 71 | internal; 72 | } 73 | --- user_files eval 74 | [ 75 | ['storage/vol0/file.txt' => 'vol0:OK'], 76 | ['storage/limit/503.txt' => 'limited:OK'] 77 | ] 78 | --- request eval 79 | [ 80 | 'GET /storage/vol0/file.txt', 81 | 'GET /storage/vol0/file.txt', 82 | 'GET /storage/vol0/file.txt', 83 | 'GET /storage/vol0/file.txt', 84 | 'GET /storage/vol0/file.txt', 85 | 'GET /storage/vol0/file.txt', 86 | ] 87 | --- error_code eval 88 | [ 89 | 200, 90 | 200, 91 | 200, 92 | 200, 93 | 200, 94 | 503 95 | ] 96 | --- response_body_like eval 97 | [ 98 | 'OK', 99 | 'OK', 100 | 'OK', 101 | 'OK', 102 | 'OK', 103 | 'OK' 104 | ] 105 | 106 | 107 | 108 | === TEST 3: vhost_traffic_status_limit_traffic_by_set_key FG@group@name out:n 109 | --- http_config 110 | vhost_traffic_status_zone; 111 | --- config 112 | location ~ ^/storage/(.+)/.*$ { 113 | set $volume $1; 114 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 115 | vhost_traffic_status_limit_traffic_by_set_key FG@storage::$server_name@$volume out:1024; 116 | } 117 | error_page 503 /storage/limit/503.txt; 118 | location = /storage/limit/503.txt { 119 | internal; 120 | } 121 | --- user_files eval 122 | [ 123 | ['storage/vol0/file.txt' => 'vol0:OK'], 124 | ['storage/limit/503.txt' => 'limited:OK'] 125 | ] 126 | --- request eval 127 | [ 128 | 'GET /storage/vol0/file.txt', 129 | 'GET /storage/vol0/file.txt', 130 | 'GET /storage/vol0/file.txt', 131 | 'GET /storage/vol0/file.txt', 132 | 'GET /storage/vol0/file.txt', 133 | 'GET /storage/vol0/file.txt', 134 | ] 135 | --- error_code eval 136 | [ 137 | 200, 138 | 200, 139 | 200, 140 | 200, 141 | 200, 142 | 503 143 | ] 144 | --- response_body_like eval 145 | [ 146 | 'OK', 147 | 'OK', 148 | 'OK', 149 | 'OK', 150 | 'OK', 151 | 'OK' 152 | ] 153 | 154 | 155 | 156 | === TEST 4: vhost_traffic_status_limit_traffic_by_set_key UG@group@name request:n 157 | --- http_config 158 | vhost_traffic_status_zone; 159 | upstream backend { 160 | server 127.0.0.1; 161 | } 162 | server { 163 | server_name backend; 164 | } 165 | --- config 166 | location /backend { 167 | vhost_traffic_status_limit_traffic_by_set_key UG@backend@127.0.0.1:80 request:3; 168 | proxy_set_header Host backend; 169 | proxy_pass http://backend; 170 | } 171 | error_page 503 /backend/limit/503.txt; 172 | location = /backend/limit/503.txt { 173 | internal; 174 | } 175 | --- user_files eval 176 | [ 177 | ['backend/file.txt' => 'backend:OK'], 178 | ['backend/limit/503.txt' => 'limited:OK'] 179 | ] 180 | --- request eval 181 | [ 182 | 'GET /backend/file.txt', 183 | 'GET /backend/file.txt', 184 | 'GET /backend/file.txt', 185 | 'GET /backend/file.txt', 186 | 'GET /backend/file.txt', 187 | ] 188 | --- error_code eval 189 | [ 190 | 200, 191 | 200, 192 | 200, 193 | 200, 194 | 503, 195 | ] 196 | --- response_body_like eval 197 | [ 198 | 'OK', 199 | 'OK', 200 | 'OK', 201 | 'OK', 202 | 'OK', 203 | ] 204 | -------------------------------------------------------------------------------- /t/019.limit_traffic_check_duplicate.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 12; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: limit_check_duplicate on 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | error_page 503 /storage/limit/503.txt; 16 | location = /storage/limit/503.txt { 17 | internal; 18 | } 19 | location ~ ^/storage/(.+)/.*$ { 20 | set $volume $1; 21 | vhost_traffic_status_limit_check_duplicate on; 22 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 23 | vhost_traffic_status_limit_traffic_by_set_key FG@storage::$server_name@$volume request:8; 24 | vhost_traffic_status_limit_traffic_by_set_key FG@storage::$server_name@$volume request:4; 25 | } 26 | --- user_files eval 27 | [ 28 | ['storage/vol0/file.txt' => 'vol0:OK'], 29 | ['storage/limit/503.txt' => 'limited:OK'] 30 | ] 31 | --- request eval 32 | [ 33 | 'GET /storage/vol0/file.txt', 34 | 'GET /storage/vol0/file.txt', 35 | 'GET /storage/vol0/file.txt', 36 | 'GET /storage/vol0/file.txt', 37 | 'GET /storage/vol0/file.txt', 38 | 'GET /storage/vol0/file.txt' 39 | ] 40 | --- error_code eval 41 | [ 42 | 200, 43 | 200, 44 | 200, 45 | 200, 46 | 200, 47 | 200 48 | ] 49 | --- response_body_like eval 50 | [ 51 | 'OK', 52 | 'OK', 53 | 'OK', 54 | 'OK', 55 | 'OK', 56 | 'OK' 57 | ] 58 | 59 | 60 | 61 | === TEST 2: limit_check_duplicate off 62 | --- http_config 63 | vhost_traffic_status_zone; 64 | --- config 65 | error_page 503 /storage/limit/503.txt; 66 | location = /storage/limit/503.txt { 67 | internal; 68 | } 69 | location ~ ^/storage/(.+)/.*$ { 70 | set $volume $1; 71 | vhost_traffic_status_limit_check_duplicate off; 72 | vhost_traffic_status_filter_by_set_key $volume storage::$server_name; 73 | vhost_traffic_status_limit_traffic_by_set_key FG@storage::$server_name@$volume request:8; 74 | vhost_traffic_status_limit_traffic_by_set_key FG@storage::$server_name@$volume request:4; 75 | } 76 | --- user_files eval 77 | [ 78 | ['storage/vol0/file.txt' => 'vol0:OK'], 79 | ['storage/limit/503.txt' => 'limited:OK'] 80 | ] 81 | --- request eval 82 | [ 83 | 'GET /storage/vol0/file.txt', 84 | 'GET /storage/vol0/file.txt', 85 | 'GET /storage/vol0/file.txt', 86 | 'GET /storage/vol0/file.txt', 87 | 'GET /storage/vol0/file.txt', 88 | 'GET /storage/vol0/file.txt' 89 | ] 90 | --- error_code eval 91 | [ 92 | 200, 93 | 200, 94 | 200, 95 | 200, 96 | 200, 97 | 503 98 | ] 99 | --- response_body_like eval 100 | [ 101 | 'OK', 102 | 'OK', 103 | 'OK', 104 | 'OK', 105 | 'OK', 106 | 'OK' 107 | ] 108 | -------------------------------------------------------------------------------- /t/020.display_sum_key.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | plan tests => repeat_each() * blocks() * 2; 6 | no_shuffle(); 7 | run_tests(); 8 | 9 | __DATA__ 10 | 11 | === TEST 1: display_sum_key total 12 | --- http_config 13 | vhost_traffic_status_zone; 14 | --- config 15 | location /status { 16 | vhost_traffic_status_display; 17 | vhost_traffic_status_display_sum_key total; 18 | vhost_traffic_status_display_format json; 19 | access_log off; 20 | } 21 | --- request eval 22 | [ 23 | 'GET /status/format/json', 24 | ] 25 | --- response_body_like eval 26 | [ 27 | 'total', 28 | ] 29 | -------------------------------------------------------------------------------- /t/021.set_by_filter.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | use Fcntl; 5 | 6 | add_response_body_check( 7 | sub { 8 | my ($block, $body, $req_idx, $repeated_req_idx, $dry_run) = @_; 9 | 10 | my $path = 't/servroot/logs/access.log'; 11 | my @lines = FH->getlines() if (sysopen(FH, $path, O_RDONLY)); 12 | close(FH); 13 | my $ll = $lines[-1]; 14 | 15 | ($ll =~ /(requestCounter|inBytes|outBytes|2xx):[-0-9]/) or 16 | bail_out "variables by set_by_filter error($ll)"; 17 | 18 | ($req_idx > 1 && $ll !~ /(requestCounter|2xx):[1-9]/) and 19 | bail_out "variables by set_by_filter error($ll)"; 20 | 21 | if ($block->name =~ /TEST 5/) { 22 | ($req_idx > 1 && $ll !~ /(cacheMaxSize|cacheUsedSize|cacheHit):[0-9]/) and 23 | bail_out "variables by set_by_filter error($ll)"; 24 | } 25 | } 26 | ); 27 | 28 | add_cleanup_handler( 29 | sub { 30 | my $CacheDir = "t/servroot/cache_*"; 31 | system("rm -rf $CacheDir > /dev/null") == 0 or 32 | bail_out "Can't remove $CacheDir"; 33 | } 34 | ); 35 | 36 | 37 | plan tests => repeat_each() * blocks() * 6; 38 | no_shuffle(); 39 | run_tests(); 40 | 41 | __DATA__ 42 | 43 | === TEST 1: access variables by vhost_traffic_status_set_by_filter $* server/*/* 44 | --- http_config 45 | vhost_traffic_status_zone; 46 | log_format basic '[$time_local] requestCounter:$requestCounter ' 47 | 'inBytes:$inBytes outBytes:$outBytes ' 48 | '2xx:$2xx'; 49 | access_log logs/access.log basic; 50 | --- config 51 | location /v { 52 | set $group server; 53 | set $zone localhost; 54 | 55 | vhost_traffic_status_set_by_filter $requestCounter $group/$zone/requestCounter; 56 | vhost_traffic_status_set_by_filter $inBytes $group/$zone/inBytes; 57 | vhost_traffic_status_set_by_filter $outBytes $group/$zone/outBytes; 58 | vhost_traffic_status_set_by_filter $2xx $group/$zone/2xx; 59 | } 60 | --- user_files eval 61 | [ 62 | ['v/file.txt' => '{"return":"OK"}'] 63 | ] 64 | --- request eval 65 | [ 66 | 'GET /v/file.txt', 67 | 'GET /v/file.txt', 68 | 'GET /v/file.txt' 69 | ] 70 | --- response_body_like eval 71 | [ 72 | 'OK', 73 | 'OK', 74 | 'OK' 75 | ] 76 | 77 | 78 | 79 | === TEST 2: access variables by vhost_traffic_status_set_by_filter $* upstream@alone/*/* 80 | --- http_config 81 | vhost_traffic_status_zone; 82 | log_format basic '[$time_local] requestCounter:$requestCounter ' 83 | 'inBytes:$inBytes outBytes:$outBytes ' 84 | '2xx:$2xx'; 85 | access_log logs/access.log basic; 86 | upstream backend { 87 | server 127.0.0.1:1984; 88 | } 89 | --- config 90 | location /status { 91 | vhost_traffic_status_display; 92 | vhost_traffic_status_display_format json; 93 | access_log off; 94 | } 95 | location /v { 96 | set $group upstream@alone; 97 | set $zone 127.0.0.1:1984; 98 | 99 | vhost_traffic_status_set_by_filter $requestCounter $group/$zone/requestCounter; 100 | vhost_traffic_status_set_by_filter $inBytes $group/$zone/inBytes; 101 | vhost_traffic_status_set_by_filter $outBytes $group/$zone/outBytes; 102 | vhost_traffic_status_set_by_filter $2xx $group/$zone/2xx; 103 | 104 | proxy_pass http://127.0.0.1:1984/return; 105 | } 106 | --- user_files eval 107 | [ 108 | ['return/file.txt' => '{"return":"OK"}'] 109 | ] 110 | --- request eval 111 | [ 112 | 'GET /v/file.txt', 113 | 'GET /v/file.txt', 114 | 'GET /v/file.txt' 115 | ] 116 | --- response_body_like eval 117 | [ 118 | 'OK', 119 | 'OK', 120 | 'OK' 121 | ] 122 | 123 | 124 | 125 | === TEST 3: access variables by vhost_traffic_status_set_by_filter $* upstream@group/*/* 126 | --- http_config 127 | vhost_traffic_status_zone; 128 | log_format basic '[$time_local] requestCounter:$requestCounter ' 129 | 'inBytes:$inBytes outBytes:$outBytes ' 130 | '2xx:$2xx'; 131 | access_log logs/access.log basic; 132 | upstream backend { 133 | server 127.0.0.1:1984; 134 | } 135 | --- config 136 | location /v { 137 | set $group upstream@group; 138 | set $zone backend@127.0.0.1:1984; 139 | 140 | vhost_traffic_status_set_by_filter $requestCounter $group/$zone/requestCounter; 141 | vhost_traffic_status_set_by_filter $inBytes $group/$zone/inBytes; 142 | vhost_traffic_status_set_by_filter $outBytes $group/$zone/outBytes; 143 | vhost_traffic_status_set_by_filter $2xx $group/$zone/2xx; 144 | 145 | proxy_pass http://backend/return; 146 | } 147 | --- user_files eval 148 | [ 149 | ['return/file.txt' => '{"return":"OK"}'] 150 | ] 151 | --- request eval 152 | [ 153 | 'GET /v/file.txt', 154 | 'GET /v/file.txt', 155 | 'GET /v/file.txt' 156 | ] 157 | --- response_body_like eval 158 | [ 159 | 'OK', 160 | 'OK', 161 | 'OK' 162 | ] 163 | 164 | 165 | 166 | === TEST 4: access variables by vhost_traffic_status_set_by_filter $* filter/*/* 167 | --- http_config 168 | vhost_traffic_status_zone; 169 | log_format basic '[$time_local] requestCounter:$requestCounter ' 170 | 'inBytes:$inBytes outBytes:$outBytes ' 171 | '2xx:$2xx'; 172 | access_log logs/access.log basic; 173 | --- config 174 | location /v { 175 | vhost_traffic_status_filter_by_set_key v group; 176 | 177 | set $group filter; 178 | set $zone group@v; 179 | 180 | vhost_traffic_status_set_by_filter $requestCounter $group/$zone/requestCounter; 181 | vhost_traffic_status_set_by_filter $inBytes $group/$zone/inBytes; 182 | vhost_traffic_status_set_by_filter $outBytes $group/$zone/outBytes; 183 | vhost_traffic_status_set_by_filter $2xx $group/$zone/2xx; 184 | } 185 | --- user_files eval 186 | [ 187 | ['v/file.txt' => '{"return":"OK"}'] 188 | ] 189 | --- request eval 190 | [ 191 | 'GET /v/file.txt', 192 | 'GET /v/file.txt', 193 | 'GET /v/file.txt' 194 | ] 195 | --- response_body_like eval 196 | [ 197 | 'OK', 198 | 'OK', 199 | 'OK' 200 | ] 201 | 202 | 203 | 204 | === TEST 5: access variables by vhost_traffic_status_set_by_filter $* cache/*/* 205 | --- http_config 206 | vhost_traffic_status_zone; 207 | proxy_cache_path cache_one levels=1:2 keys_zone=cache_one:2m inactive=1m max_size=4m; 208 | log_format basic '[$time_local] requestCounter:$requestCounter ' 209 | 'inBytes:$inBytes outBytes:$outBytes ' 210 | '2xx:$2xx cacheMaxSize:$cacheMaxSize ' 211 | 'cacheUsedSize:$cacheUsedSize cacheHit:$cacheHit'; 212 | access_log logs/access.log basic; 213 | upstream backend { 214 | server 127.0.0.1:1984; 215 | } 216 | --- config 217 | location /v { 218 | proxy_cache cache_one; 219 | proxy_cache_valid 200 10s; 220 | 221 | set $group cache; 222 | set $zone cache_one; 223 | 224 | vhost_traffic_status_set_by_filter $requestCounter $group/$zone/requestCounter; 225 | vhost_traffic_status_set_by_filter $inBytes $group/$zone/inBytes; 226 | vhost_traffic_status_set_by_filter $outBytes $group/$zone/outBytes; 227 | vhost_traffic_status_set_by_filter $2xx $group/$zone/2xx; 228 | 229 | vhost_traffic_status_set_by_filter $cacheMaxSize $group/$zone/cacheMaxSize; 230 | vhost_traffic_status_set_by_filter $cacheUsedSize $group/$zone/cacheUsedSize; 231 | vhost_traffic_status_set_by_filter $cacheHit $group/$zone/cacheHit; 232 | 233 | proxy_pass http://backend/return; 234 | } 235 | --- user_files eval 236 | [ 237 | ['return/file.txt' => '{"return":"OK"}'] 238 | ] 239 | --- request eval 240 | [ 241 | 'GET /v/file.txt', 242 | 'GET /v/file.txt', 243 | 'GET /v/file.txt' 244 | ] 245 | --- response_body_like eval 246 | [ 247 | 'OK', 248 | 'OK', 249 | 'OK' 250 | ] 251 | -------------------------------------------------------------------------------- /t/022.display_prometheus.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | add_response_body_check( 6 | sub { 7 | my ($block, $body, $req_idx, $repeated_req_idx, $dry_run) = @_; 8 | system("echo '$body' | promtool check metrics") == 0 or 9 | bail_out "prometheus Syntax error($body)"; 10 | } 11 | ); 12 | 13 | plan tests => repeat_each(2) * blocks() * 3; 14 | no_shuffle(); 15 | run_tests(); 16 | 17 | __DATA__ 18 | 19 | === TEST 1: /status/format/prometheus 20 | --- http_config 21 | vhost_traffic_status_zone; 22 | --- config 23 | location /status { 24 | vhost_traffic_status_display; 25 | vhost_traffic_status_display_format prometheus; 26 | } 27 | --- request 28 | GET /status/format/prometheus 29 | --- response_headers_like 30 | Content-Type: text/plain 31 | --- response_body_like eval 32 | [ 33 | 'nginx_vts_info', 34 | ] 35 | 36 | 37 | 38 | === TEST 2: /status 39 | --- http_config 40 | vhost_traffic_status_zone; 41 | --- config 42 | location /status { 43 | vhost_traffic_status_display; 44 | vhost_traffic_status_display_format prometheus; 45 | } 46 | --- request 47 | GET /status 48 | --- response_headers_like 49 | Content-Type: text/plain 50 | --- response_body_like eval 51 | [ 52 | 'nginx_vts_info', 53 | ] 54 | -------------------------------------------------------------------------------- /t/023.histogram_buckets.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | use Fcntl; 5 | 6 | plan tests => repeat_each() * blocks() * 4; 7 | no_shuffle(); 8 | run_tests(); 9 | 10 | __DATA__ 11 | 12 | === TEST 1: access status with vhost_traffic_status_histogram_bucket to get the request and responseBuckets after accessing upstream backend 13 | --- http_config 14 | vhost_traffic_status_zone; 15 | vhost_traffic_status_histogram_buckets .1 .5 1 2; 16 | upstream backend { 17 | server 127.0.0.1; 18 | } 19 | server { 20 | server_name _; 21 | vhost_traffic_status_filter_by_host on; 22 | } 23 | --- config 24 | location /status { 25 | vhost_traffic_status_display; 26 | vhost_traffic_status_display_format json; 27 | access_log off; 28 | } 29 | location /one { 30 | proxy_set_header Host one.example.org; 31 | proxy_pass http://backend; 32 | } 33 | --- user_files eval 34 | [ 35 | ['one/file.txt' => 'one.example.org:OK'], 36 | ] 37 | --- request eval 38 | [ 39 | 'GET /one/file.txt', 40 | 'GET /status/', 41 | ] 42 | --- response_body_like eval 43 | [ 44 | 'OK', 45 | '\"requestBuckets\"\:\{\"msecs\"\:\[100,500,1000,2000\],\"counters\"\:\[1,1,1,1\].*\"responseBuckets\"\:\{\"msecs\"\:\[100,500,1000,2000\],\"counters\"\:\[1,1,1,1\]', 46 | ] 47 | -------------------------------------------------------------------------------- /t/024.upstream_check.t: -------------------------------------------------------------------------------- 1 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 2 | 3 | use Test::Nginx::Socket; 4 | 5 | master_on; 6 | plan skip_all => 'nginx_upstream_check test skipped' unless $ENV{TEST_UPSTREAM_CHECK}; 7 | plan tests => 4; 8 | no_shuffle(); 9 | run_tests(); 10 | 11 | __DATA__ 12 | 13 | === TEST 1: upstream peer->down is true 14 | --- http_config 15 | vhost_traffic_status_zone; 16 | upstream backend { 17 | zone backend 64k; 18 | server localhost:8080; 19 | check interval=1000 rise=1 fall=1 timeout=1000; 20 | } 21 | --- config 22 | location /backend { 23 | proxy_pass http://backend; 24 | } 25 | location /status { 26 | check_status; 27 | vhost_traffic_status_display; 28 | vhost_traffic_status_display_format json; 29 | access_log off; 30 | } 31 | --- request 32 | GET /status 33 | --- response_body_like eval 34 | '"down":true' 35 | 36 | === TEST 2: upstream peer->down is false 37 | --- http_config 38 | vhost_traffic_status_zone; 39 | upstream backend { 40 | zone backend 64k; 41 | server localhost:8080; 42 | check interval=1000 rise=1 fall=1 timeout=1000; 43 | } 44 | server { 45 | listen 8080; 46 | server_name localhost; 47 | location / { 48 | root html; 49 | } 50 | } 51 | --- config 52 | location /index.html { 53 | proxy_pass http://backend; 54 | } 55 | location /status { 56 | check_status; 57 | vhost_traffic_status_display; 58 | vhost_traffic_status_display_format json; 59 | access_log off; 60 | } 61 | --- request 62 | GET /status 63 | --- response_body_like eval 64 | '"down":false' 65 | -------------------------------------------------------------------------------- /util/fileToHex.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | # 3 | # @file: fileToHex.pl 4 | # @brief: 5 | # @author: YoungJoo.Kim 6 | # @version: 7 | # @date: 8 | 9 | package FileToHex; 10 | 11 | use strict; 12 | use Carp; 13 | 14 | sub new{ 15 | my($class, %cnf) = @_; 16 | 17 | my $path = delete $cnf{path}; 18 | my $handle = delete $cnf{handle}; 19 | 20 | my $self = 21 | bless { 22 | path => $path, 23 | handle => $handle 24 | }, $class; 25 | 26 | return bless $self; 27 | } 28 | 29 | sub __exit { 30 | my $self = shift if ref ($_[0]); 31 | my $res = shift; 32 | Carp::carp __PACKAGE__ . ": $res->{string}"; 33 | exit($res->{return} || 1); 34 | } 35 | 36 | sub fileOpen { 37 | my $self = shift if ref ($_[0]); 38 | my $path = shift || $self->{path}; 39 | $path = $self->{path} unless defined $path; 40 | (defined $path && -e $path) || $self->__exit({string => "error: [$path] is not defined or exists!"}); 41 | open(my $handle, "<", $path) || $self->__exit({string => "error: open(): $!"}); 42 | $self->{handle} = $handle; 43 | return $self->{handle}; 44 | } 45 | 46 | sub fileClose { 47 | my $self = shift if ref ($_[0]); 48 | my $handle = shift || $self->{handle}; 49 | $handle && close($handle); 50 | } 51 | 52 | sub fileReadByte { 53 | my $self = shift if ref ($_[0]); 54 | my $buf = \shift; 55 | my $byte = shift || 1; 56 | my $handle = shift || $self->{handle}; 57 | return read($handle, $$buf, $byte); 58 | } 59 | 60 | sub DESTROY { 61 | my $self = shift if ref ($_[0]); 62 | $self->fileClose(); 63 | } 64 | 65 | 1; 66 | 67 | package main; 68 | 69 | if ($#ARGV < 0) { 70 | print "Usage: $0 {path} {max} {type}\n"; 71 | exit(2); 72 | } 73 | 74 | my $path = $ARGV[0]; 75 | my $max = $ARGV[1] || 16; 76 | my $type = $ARGV[2] || "buffer"; 77 | my $plus = ""; 78 | my $buf = ""; 79 | my $i = 0; 80 | my $fth = FileToHex->new(path => $path); 81 | 82 | $fth->fileOpen(); 83 | if ($type eq "define") { 84 | # type: define 85 | while($fth->fileReadByte(my $c)) { 86 | $i++; 87 | $buf .= '\x' . unpack("H2", $c); 88 | if (!($i % $max)) { 89 | $plus .= "\"$buf\" \\\n"; 90 | $buf = ""; 91 | } 92 | } 93 | 94 | if (!($i % $max)) { 95 | print substr($plus, 0, -3) . "\n"; 96 | 97 | } else { 98 | print $plus . "\"$buf\"\n"; 99 | } 100 | 101 | } else { 102 | # type: buffer 103 | while($fth->fileReadByte(my $c)) { 104 | $i++; 105 | $buf .= '0x' . unpack("H2", $c) . ', '; 106 | if (!($i % $max)) { 107 | $plus .= "$buf\n"; 108 | $buf = ""; 109 | } 110 | } 111 | 112 | if (!($i % $max)) { 113 | print $plus . "0x00\n"; 114 | 115 | } else { 116 | print $plus . $buf . "0x00\n"; 117 | } 118 | } 119 | $fth->fileClose(); 120 | 121 | # vi:set ft=perl ts=4 sw=4 et fdm=marker: 122 | -------------------------------------------------------------------------------- /util/tplToBuffer.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | # 3 | # @file: tplToDefine.sh 4 | # @brief: 5 | # @author: YoungJoo.Kim 6 | # @version: 7 | # @date: 8 | 9 | # Set up a default search path. 10 | PATH="/sbin:/usr/sbin:/bin:/usr/bin" 11 | export PATH 12 | 13 | template=$1 14 | if [ -z "$template" ]; then 15 | echo "Usage: $0 {template.html}" 16 | exit 2 17 | fi 18 | 19 | tmp=$template.$(date '+%s') 20 | 21 | \cp -af $template $tmp 22 | 23 | if [ -f "$tmp" ]; then 24 | perl -p -i -e 's/%/%%/g' $tmp 25 | perl -p -i -e 's/{{uri}}/%V/g' $tmp 26 | fi 27 | 28 | echo "static char NGX_HTTP_VHOST_TRAFFIC_STATUS_HTML_DATA[] = {" 29 | 30 | \perl fileToHex.pl $tmp 16 buffer 31 | 32 | echo "};" 33 | 34 | \rm -f $tmp 35 | 36 | # vi:set ft=sh ts=4 sw=4 et fdm=marker: 37 | -------------------------------------------------------------------------------- /util/tplToDefine.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | # 3 | # @file: tplToDefine.sh 4 | # @brief: 5 | # @author: YoungJoo.Kim 6 | # @version: 7 | # @date: 8 | 9 | # Set up a default search path. 10 | PATH="/sbin:/usr/sbin:/bin:/usr/bin" 11 | export PATH 12 | 13 | template=$1 14 | if [ -z "$template" ]; then 15 | echo "Usage: $0 {template.html}" 16 | exit 2 17 | fi 18 | 19 | tmp=$template.$(date '+%s') 20 | 21 | \cp -af $template $tmp 22 | 23 | if [ -f "$tmp" ]; then 24 | perl -p -i -e 's/%/%%/g' $tmp 25 | perl -p -i -e 's/{{uri}}/%V/g' $tmp 26 | fi 27 | 28 | echo "#define NGX_HTTP_VHOST_TRAFFIC_STATUS_HTML_DATA \\" 29 | 30 | \perl fileToHex.pl $tmp 16 define 31 | 32 | \rm -f $tmp 33 | 34 | # vi:set ft=sh ts=4 sw=4 et fdm=marker: 35 | --------------------------------------------------------------------------------