The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitattributes
├── .gitignore
├── .travis.yml
├── Dockerfile
├── LICENSE.txt
├── install.py
├── nginx.conf
├── readme.md
├── readme_zh.md
├── test
    ├── base_case.py
    ├── requirements.txt
    ├── test.py
    └── testcase
    │   ├── __init__.py
    │   ├── a_1_start_without_config
    │       ├── __init__.py
    │       └── case.py
    │   ├── a_2_basic_behaviour
    │       ├── __init__.py
    │       ├── case.py
    │       └── config.json
    │   ├── a_3_matcher_host
    │       ├── __init__.py
    │       ├── case.py
    │       └── config.json
    │   ├── a_4_matcher_useragent
    │       ├── __init__.py
    │       ├── case.py
    │       └── config.json
    │   └── a_5_matcher_refer
    │       ├── __init__.py
    │       ├── case.py
    │       └── config.json
└── verynginx
    ├── configs
        └── .gitignore
    ├── dashboard
        ├── css
        │   ├── bs_callout.css
        │   ├── pace.css
        │   ├── tables.css
        │   └── webInterface.css
        ├── index.html
        ├── index_zh.html
        └── js
        │   ├── config.js
        │   ├── dashboard.js
        │   ├── data_stat.js
        │   ├── matcher_editor.js
        │   ├── monitor.js
        │   ├── pace.min.js
        │   ├── tips.js
        │   ├── upstream_editor.js
        │   ├── util.js
        │   ├── verify.js
        │   └── vnform.js
    ├── lua_script
        ├── VeryNginxConfig.lua
        ├── module
        │   ├── backend_proxy.lua
        │   ├── backend_static.lua
        │   ├── browser_verify.lua
        │   ├── cookie.lua
        │   ├── dkjson.lua
        │   ├── encrypt_seed.lua
        │   ├── filter.lua
        │   ├── frequency_limit.lua
        │   ├── json.lua
        │   ├── redirect.lua
        │   ├── request_tester.lua
        │   ├── router.lua
        │   ├── scheme_lock.lua
        │   ├── status.lua
        │   ├── summary.lua
        │   ├── uri_rewrite.lua
        │   └── util.lua
        ├── on_access.lua
        ├── on_banlance.lua
        ├── on_init.lua
        ├── on_log.lua
        ├── on_rewrite.lua
        └── resty
        │   └── dns
        │       └── resolver.lua
    ├── nginx_conf
        ├── in_external.conf
        ├── in_http_block.conf
        └── in_server_block.conf
    └── support
        └── verify_javascript.html


/.gitattributes:
--------------------------------------------------------------------------------
1 | *.html  linguist-vendored=true
2 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.swo
3 | *.swn
4 | *.swm
5 | __pycache__
6 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
 1 | sudo: required
 2 | dist: trusty
 3 | 
 4 | os: linux
 5 | 
 6 | language: c
 7 | 
 8 | compiler:
 9 |   - gcc
10 | 
11 | addons:
12 |   apt:
13 |     packages:
14 |     - python3
15 |     - python-pip
16 |     - python3-pip
17 | 
18 | cache:
19 |   apt: true
20 |   directories:
21 |   - $HOME/.cache/pip
22 |   - /opt/verynginx
23 | 
24 | before_install:
25 |   - if [ ! -d /opt/verynginx/openresty ]; then python3 install.py install openresty ; fi
26 |   - sudo rm -rf /opt/verynginx/verynginx  
27 |   - sudo adduser --system --no-create-home --group nginx
28 | 
29 | install:
30 |   - sudo python3 -m pip install -r test/requirements.txt 
31 |   - sudo python3 install.py install verynginx
32 | 
33 | before_script:
34 |   - if [ ! -f /opt/verynginx/openresty/nginx/logs/error.log ]; then touch /opt/verynginx/openresty/nginx/logs/error.log;sudo chown nginx:nginx /opt/verynginx/openresty/nginx/logs/error.log ; fi
35 |   - sudo /opt/verynginx/openresty/nginx/sbin/nginx 
36 |   - sudo /opt/verynginx/openresty/nginx/sbin/nginx -s stop
37 |   - echo -e "127.0.0.1   a.vntest.com\n127.0.0.1   b.vntest.com\n127.0.0.1   c.vntest.com" | sudo tee -a /etc/hosts
38 | 
39 | script:
40 |   - sudo python3 test/test.py
41 | 


--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
 1 | FROM centos:7
 2 | 
 3 | RUN yum clean all && yum update -y  && yum install -y     gcc pcre-devel openssl-devel wget perl make build-essential procps     libreadline-dev libncurses5-dev libpcre3-dev libssl-dev
 4 | 
 5 | RUN mkdir /code
 6 | COPY ./ /code/
 7 | WORKDIR /code
 8 | RUN groupadd -r nginx && useradd -r -g nginx nginx
 9 | RUN python install.py install
10 | 
11 | EXPOSE 80
12 | 
13 | CMD ["/opt/verynginx/openresty/nginx/sbin/nginx", "-g", "daemon off; error_log /dev/stderr info;"]
14 | 


--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
  1 |                    GNU LESSER GENERAL PUBLIC LICENSE
  2 |                        Version 3, 29 June 2007
  3 | 
  4 |  Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
  5 |  Everyone is permitted to copy and distribute verbatim copies
  6 |  of this license document, but changing it is not allowed.
  7 | 
  8 | 
  9 |   This version of the GNU Lesser General Public License incorporates
 10 | the terms and conditions of version 3 of the GNU General Public
 11 | License, supplemented by the additional permissions listed below.
 12 | 
 13 |   0. Additional Definitions.
 14 | 
 15 |   As used herein, "this License" refers to version 3 of the GNU Lesser
 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
 17 | General Public License.
 18 | 
 19 |   "The Library" refers to a covered work governed by this License,
 20 | other than an Application or a Combined Work as defined below.
 21 | 
 22 |   An "Application" is any work that makes use of an interface provided
 23 | by the Library, but which is not otherwise based on the Library.
 24 | Defining a subclass of a class defined by the Library is deemed a mode
 25 | of using an interface provided by the Library.
 26 | 
 27 |   A "Combined Work" is a work produced by combining or linking an
 28 | Application with the Library.  The particular version of the Library
 29 | with which the Combined Work was made is also called the "Linked
 30 | Version".
 31 | 
 32 |   The "Minimal Corresponding Source" for a Combined Work means the
 33 | Corresponding Source for the Combined Work, excluding any source code
 34 | for portions of the Combined Work that, considered in isolation, are
 35 | based on the Application, and not on the Linked Version.
 36 | 
 37 |   The "Corresponding Application Code" for a Combined Work means the
 38 | object code and/or source code for the Application, including any data
 39 | and utility programs needed for reproducing the Combined Work from the
 40 | Application, but excluding the System Libraries of the Combined Work.
 41 | 
 42 |   1. Exception to Section 3 of the GNU GPL.
 43 | 
 44 |   You may convey a covered work under sections 3 and 4 of this License
 45 | without being bound by section 3 of the GNU GPL.
 46 | 
 47 |   2. Conveying Modified Versions.
 48 | 
 49 |   If you modify a copy of the Library, and, in your modifications, a
 50 | facility refers to a function or data to be supplied by an Application
 51 | that uses the facility (other than as an argument passed when the
 52 | facility is invoked), then you may convey a copy of the modified
 53 | version:
 54 | 
 55 |    a) under this License, provided that you make a good faith effort to
 56 |    ensure that, in the event an Application does not supply the
 57 |    function or data, the facility still operates, and performs
 58 |    whatever part of its purpose remains meaningful, or
 59 | 
 60 |    b) under the GNU GPL, with none of the additional permissions of
 61 |    this License applicable to that copy.
 62 | 
 63 |   3. Object Code Incorporating Material from Library Header Files.
 64 | 
 65 |   The object code form of an Application may incorporate material from
 66 | a header file that is part of the Library.  You may convey such object
 67 | code under terms of your choice, provided that, if the incorporated
 68 | material is not limited to numerical parameters, data structure
 69 | layouts and accessors, or small macros, inline functions and templates
 70 | (ten or fewer lines in length), you do both of the following:
 71 | 
 72 |    a) Give prominent notice with each copy of the object code that the
 73 |    Library is used in it and that the Library and its use are
 74 |    covered by this License.
 75 | 
 76 |    b) Accompany the object code with a copy of the GNU GPL and this license
 77 |    document.
 78 | 
 79 |   4. Combined Works.
 80 | 
 81 |   You may convey a Combined Work under terms of your choice that,
 82 | taken together, effectively do not restrict modification of the
 83 | portions of the Library contained in the Combined Work and reverse
 84 | engineering for debugging such modifications, if you also do each of
 85 | the following:
 86 | 
 87 |    a) Give prominent notice with each copy of the Combined Work that
 88 |    the Library is used in it and that the Library and its use are
 89 |    covered by this License.
 90 | 
 91 |    b) Accompany the Combined Work with a copy of the GNU GPL and this license
 92 |    document.
 93 | 
 94 |    c) For a Combined Work that displays copyright notices during
 95 |    execution, include the copyright notice for the Library among
 96 |    these notices, as well as a reference directing the user to the
 97 |    copies of the GNU GPL and this license document.
 98 | 
 99 |    d) Do one of the following:
100 | 
101 |        0) Convey the Minimal Corresponding Source under the terms of this
102 |        License, and the Corresponding Application Code in a form
103 |        suitable for, and under terms that permit, the user to
104 |        recombine or relink the Application with a modified version of
105 |        the Linked Version to produce a modified Combined Work, in the
106 |        manner specified by section 6 of the GNU GPL for conveying
107 |        Corresponding Source.
108 | 
109 |        1) Use a suitable shared library mechanism for linking with the
110 |        Library.  A suitable mechanism is one that (a) uses at run time
111 |        a copy of the Library already present on the user's computer
112 |        system, and (b) will operate properly with a modified version
113 |        of the Library that is interface-compatible with the Linked
114 |        Version.
115 | 
116 |    e) Provide Installation Information, but only if you would otherwise
117 |    be required to provide such information under section 6 of the
118 |    GNU GPL, and only to the extent that such information is
119 |    necessary to install and execute a modified version of the
120 |    Combined Work produced by recombining or relinking the
121 |    Application with a modified version of the Linked Version. (If
122 |    you use option 4d0, the Installation Information must accompany
123 |    the Minimal Corresponding Source and Corresponding Application
124 |    Code. If you use option 4d1, you must provide the Installation
125 |    Information in the manner specified by section 6 of the GNU GPL
126 |    for conveying Corresponding Source.)
127 | 
128 |   5. Combined Libraries.
129 | 
130 |   You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 | 
136 |    a) Accompany the combined library with a copy of the same work based
137 |    on the Library, uncombined with any other library facilities,
138 |    conveyed under the terms of this License.
139 | 
140 |    b) Give prominent notice with the combined library that part of it
141 |    is a work based on the Library, and explaining where to find the
142 |    accompanying uncombined form of the same work.
143 | 
144 |   6. Revised Versions of the GNU Lesser General Public License.
145 | 
146 |   The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 | 
151 |   Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 | 
161 |   If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 | 


--------------------------------------------------------------------------------
/install.py:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env python
  2 | # -*- coding: utf-8 -*-
  3 | # @Date    : 2016-04-04 23:48
  4 | # @Author  : Alexa (AlexaZhou@163.com)
  5 | # @Link    : https://github.com/alexazhou/VeryNginx
  6 | # @Disc    : install VeryNginx
  7 | # @Disc    : support python 2.x and 3.x
  8 | 
  9 | import os
 10 | import sys
 11 | import getopt
 12 | import filecmp
 13 | 
 14 | #最新版openresty
 15 | 
 16 | openresty_pkg_url = 'https://openresty.org/download/openresty-1.15.8.1.tar.gz'
 17 | openresty_pkg = 'openresty-1.15.8.1.tar.gz'
 18 | 
 19 | work_path = os.getcwd()
 20 | 
 21 | def install_openresty( ):
 22 |     #check if the old version of VeryNginx installed( use upcase directory )
 23 |     if os.path.exists('/opt/VeryNginx/VeryNginx') == True:
 24 |         print("Seems that a old version of VeryNginx was installed in /opt/verynginx/...\nBefore install, please delete it and backup the configs if you need.")
 25 |         sys.exit(1)
 26 |     
 27 |     #makesure the dir is clean
 28 |     print('### makesure the work directory is clean')
 29 |     exec_sys_cmd('rm -rf ' + openresty_pkg.replace('.tar.gz',''))
 30 |     
 31 |     #download openresty
 32 |     down_flag = True
 33 |     if os.path.exists( './' + openresty_pkg ):
 34 |         ans = ''
 35 |         while ans not in ['y','n']:
 36 |             ans = common_input(' Found %s in current directory, use it?(y/n)'%openresty_pkg)
 37 |         if ans == 'y':
 38 |             down_flag = False
 39 | 
 40 |     if down_flag == True:
 41 |         print('### start download openresty package...')
 42 |         exec_sys_cmd('rm -rf ' + openresty_pkg)
 43 |         exec_sys_cmd( 'wget ' + openresty_pkg_url )
 44 |     else:
 45 |         print('### use local openresty package...')
 46 |     
 47 |     print('### release the package ...')
 48 |     exec_sys_cmd( 'tar -xzf ' + openresty_pkg )
 49 | 
 50 |     #configure && compile && install openresty
 51 |     print('### configure openresty ...')
 52 |     os.chdir( openresty_pkg.replace('.tar.gz','') )
 53 |     exec_sys_cmd( './configure --prefix=/opt/verynginx/openresty --user=nginx --group=nginx --with-http_v2_module --with-http_sub_module --with-http_stub_status_module --with-luajit' )
 54 |     
 55 |     print('### compile openresty ...')
 56 |     exec_sys_cmd( 'make' )
 57 |     
 58 |     print('### install openresty ...')
 59 |     exec_sys_cmd( 'make install' )
 60 | 
 61 |     # print('### make nginx into PATH ...')
 62 |     # May Not Work in CI
 63 |     # exec_sys_cmd( 'export PATH=/opt/verynginx/openresty/nginx/sbin:$PATH' )
 64 | 
 65 |     # print('### add user and group nginx:nginx')
 66 |     # May Not Work in CI
 67 |     # exec_sys_cmd( 'sudo groupadd -f nginx && useradd -g nginx nginx' )
 68 | 
 69 | def install_verynginx():
 70 |     
 71 |     #install VeryNginx file
 72 |     print('### copy VeryNginx files ...')
 73 |     os.chdir( work_path )
 74 |     if os.path.exists('/opt/verynginx/') == False:
 75 |         exec_sys_cmd( 'mkdir -p /opt/verynginx' )
 76 |     
 77 |     exec_sys_cmd( 'cp -r -f ./verynginx /opt/verynginx' )
 78 | 
 79 |     #copy nginx config file to openresty
 80 |     if os.path.exists('/opt/verynginx/openresty') == True:
 81 |         if filecmp.cmp( '/opt/verynginx/openresty/nginx/conf/nginx.conf', '/opt/verynginx/openresty/nginx/conf/nginx.conf.default', False ) == True:
 82 |             print('cp nginx config file to openresty')
 83 |             exec_sys_cmd( 'cp -f ./nginx.conf  /opt/verynginx/openresty/nginx/conf/' )
 84 |     else:
 85 |         print( 'openresty not fount, so not copy nginx.conf' )
 86 | 
 87 |     # set mask for the path which used for save configs
 88 |     # file too open;
 89 |     # exec_sys_cmd( 'chmod -R 777 /opt/verynginx/verynginx/configs' )
 90 |     exec_sys_cmd( 'chmod -R 755 /opt/verynginx/verynginx/configs' )
 91 | 
 92 | 
 93 | def update_verynginx():
 94 |     install_verynginx()    
 95 | 
 96 | 
 97 | def exec_sys_cmd(cmd, accept_failed = False):
 98 |     print( cmd )
 99 |     ret = os.system( cmd )
100 |     if  ret == 0:
101 |         return ret
102 |     else:
103 |         if accept_failed == False:
104 |             print('*** The installing stopped because something was wrong')
105 |             exit(1)
106 |         else:
107 |             return False
108 | 
109 | def common_input( s ):
110 |     if sys.version_info[0] == 3:
111 |         return input( s )
112 |     else:
113 |         return raw_input( s )
114 | 
115 | def safe_pop(l):
116 |     if len(l) == 0:
117 |         return None
118 |     else:
119 |         return l.pop(0)
120 | 
121 | def show_help_and_exit():
122 |     help_doc = 'usage: install.py <cmd> <args> ... \n\n\
123 | install cmds and args:\n\
124 |     install\n\
125 |         all        :  install verynginx and openresty(default)\n\
126 |         openresty  :  install openresty\n\
127 |         verynginx  :  install verynginx\n\
128 |     update\n\
129 |         verynginx  :  update the installed verynginx\n\
130 |     '
131 |     print(help_doc)
132 |     exit()
133 | 
134 | 
135 | if __name__ == '__main__':
136 | 
137 |     opts, args = getopt.getopt(sys.argv[1:], '', []) 
138 |   
139 |     cmd = safe_pop(args)
140 |     if cmd == 'install':
141 |         cmd = safe_pop(args)
142 |         if cmd == 'all' or cmd == None:
143 |             install_openresty()
144 |             install_verynginx()
145 |         elif cmd == 'openresty':
146 |             install_openresty()
147 |         elif cmd == 'verynginx':
148 |             install_verynginx()
149 |         else:
150 |             show_help_and_exit()
151 |     elif cmd == 'update':
152 |         cmd = safe_pop(args)
153 |         if cmd == 'verynginx':
154 |             update_verynginx()
155 |         else:
156 |             show_help_and_exit()
157 |     else:
158 |         show_help_and_exit()
159 | 
160 |     print('*** All work finished successfully, enjoy it~')
161 | 
162 | 
163 | else:
164 |     print ('install.py had been imported as a module')
165 |     print ('please add group and user nginx:nginx')
166 |     print ('to use nginx, add it in PATH: \nexport PATH=/opt/verynginx/openresty/nginx/sbin:$PATH')
167 | 


--------------------------------------------------------------------------------
/nginx.conf:
--------------------------------------------------------------------------------
 1 | 
 2 | user  nginx;
 3 | worker_processes  auto;
 4 | 
 5 | #error_log  logs/error.log;
 6 | #error_log  logs/error.log  notice;
 7 | #error_log  logs/error.log  info;
 8 | 
 9 | #pid        logs/nginx.pid;
10 | 
11 | 
12 | events {
13 |     worker_connections  1024;
14 | }
15 | 
16 | include /opt/verynginx/verynginx/nginx_conf/in_external.conf;
17 | 
18 | http {
19 |     include       mime.types;
20 |     default_type  application/octet-stream;
21 | 
22 |     #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
23 |     #                  '$status $body_bytes_sent "$http_referer" '
24 |     #                  '"$http_user_agent" "$http_x_forwarded_for"';
25 | 
26 |     #access_log  logs/access.log  main;
27 |     sendfile        on;
28 |     #tcp_nopush     on;
29 | 
30 |     #keepalive_timeout  0;
31 |     keepalive_timeout  65;
32 | 	client_body_buffer_size 128k;
33 | 
34 |     #gzip  on;
35 | 	
36 | 	#this line shoud be include in every http block
37 |     include /opt/verynginx/verynginx/nginx_conf/in_http_block.conf;
38 | 
39 |     server {
40 |         listen       80;
41 |         
42 |         #this line shoud be include in every server block
43 |         include /opt/verynginx/verynginx/nginx_conf/in_server_block.conf;
44 | 
45 |         location = / {
46 |             root   html;
47 |             index  index.html index.htm;
48 |         }
49 |     }
50 | 
51 | }
52 | 


--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
  1 | # VeryNginx
  2 | VeryNginx is a very powerful and friendly nginx .
  3 | 
  4 | [中文文档](https://github.com/alexazhou/VeryNginx/blob/master/readme_zh.md)
  5 | 
  6 | ### Notice
  7 | After v0.2 , The entry uri of control panel was moved to `/verynginx/index.html`
  8 | 
  9 | 
 10 | ## Description
 11 | 
 12 | VeryNginx is based on `lua_nginx_module(openrestry)`. It implements advanced firewall(waf), access statistics and some other features. It strengthens the Nginx's functions, and provides a friendly Web interface.
 13 | 
 14 | [VeryNginx online demo](http://alexazhou.xyz/vn/index.html) 
 15 | 
 16 | User / Password: **verynginx / verynginx**
 17 | 
 18 | The full version of config guide can be found on: [VeryNginx Wiki](https://github.com/alexazhou/VeryNginx/wiki) .
 19 | 
 20 | ### Nginx run status analyzing
 21 | 
 22 | * Request per second
 23 | * Response time
 24 | * Net Traffic
 25 | * Tcp connectinn num
 26 | 
 27 | ![Nginx status](http://ww2.sinaimg.cn/mw690/3fcd0ed3jw1f17en7oc1yj20z00ol0wl.jpg)
 28 | 
 29 | 
 30 | ### Custom Action
 31 | 
 32 | VeryNginx supports custom actions, which can do a lot of things.
 33 | 
 34 | Custom action consists of two parts, `Matcher` and `Action` . `Matcher` is used to test whether a request meets the rule, `Action` is the logic you want to run.
 35 | 
 36 | >The advantage of this design is that the `Matcher` includes all select rule, and can be reused, making use of rules to describe a very complex logic possible.
 37 | 
 38 | #### Matcher
 39 | 
 40 | A `Matcher` is used to select a part of all requests, a `Matcher` may contain one or more condition. The following conditions are currently supported:
 41 | 
 42 | * Client IP
 43 | * Host
 44 | * UserAgent
 45 | * URI
 46 | * Referer
 47 | * Request Args
 48 | 
 49 | When a request does not conflict with any of the conditions of the Matcher, the request will be selected by the `Matcher`
 50 | 
 51 | #### Action
 52 | 
 53 | Every `Action` refers to a `Matcher` , and will run on the requests selected by the `Matcher` 
 54 | 
 55 | Now we have these `Action`s
 56 | 
 57 | * **Scheme** Lock lock the scheme to http/https
 58 | * **Redirect** Redirect request
 59 | * **URI Rewrite** Do internal rewrite on the request
 60 | * **Browser Verify** Use set-cookies and javascript to verify the client is a browser,and block traffic of the robot. This action may block the spider of search engine, so please enable it when under attack only.
 61 | * **Frequency Limit** Limit max request time in a specified time period
 62 | * **Filter** Block some request, can do the WAF
 63 | 
 64 | Matcher can select requests by multiple conditions, so with Filter Action, we got a powerful waf. The waf can filter requests with complex rules and return special status code when it block a request.
 65 | 
 66 | VeryNginx presets some simple filter rules, which can prevent simple SQL injection, Git and SVN file disclosure, directory traversal attacks and common scanning tool.
 67 | 
 68 | ![VeryNginx Matcher](http://ww2.sinaimg.cn/mw690/3fcd0ed3jw1f17en8ovthj20zs0pdn1x.jpg)
 69 | 
 70 | ![VeryNginx filter](http://ww3.sinaimg.cn/mw690/3fcd0ed3jw1f17en9lrarj20zw0piq77.jpg)
 71 | 
 72 | #### Backend
 73 | 
 74 | Every `Backend` refers to a `Matcher`,and will handle the requests selected by the `Matcher`
 75 | 
 76 | Now we have these `Backend`
 77 | 
 78 | * **Proxy Pass** Proxy the request to other server
 79 | * **Static File** Use local file to handle the request file 
 80 | 
 81 | ### Request statistics
 82 | 
 83 | VeryNginx can record the request of URI, include these data of every URI:
 84 | 
 85 | * All Request Count
 86 | * Request count of every status code
 87 | * Total Bytes 
 88 | * Avg Bytes
 89 | * Total response time
 90 | * Avg reqponse time
 91 | 
 92 | 
 93 | ![request statistics](http://ww1.sinaimg.cn/mw690/3fcd0ed3jw1f17ena2ipyj20zw0piqag.jpg)
 94 | 
 95 | 
 96 | ## Installation
 97 | 
 98 | ### Install Nginx / OpenResty
 99 | 
100 | VeryNginx is based on OpenResty, so you need to install it first. But don't warry, VeryNginx gives a script to do it automatically.
101 | 
102 | 
103 | ```
104 | python install.py install
105 | ```
106 | 
107 | Just run this command, openresty and verynginx will be installed automatically.
108 |  
109 | #### Want using custom nginx?
110 | 
111 | VeryNginx can install openresty automatically so that you **needn't install nginx(openresty) manually**.
112 | 
113 | But if you want use a nginx compiled by you self, that also ok. You can see that for some help 
114 | 
115 | [Use-own-nginx](https://github.com/alexazhou/VeryNginx/wiki/Use-own-nginx)
116 | 
117 | ### Usage
118 | 
119 | #### Edit nginx configuration file
120 | 
121 | The configuration file of VeryNginx is `/opt/verynginx/openresty/nginx/conf/nginx.conf`, that's a demo. It just can let verynginx run so that you can see the dashboard of verynginx. If you want do something really useful, you need edit that file and add your own nginx configuration into it.
122 | 
123 | >
124 | This configuration file add three `include` command to embeded verynginx into original nginx( openresty ) 
125 | 
126 | >
127 | * include /opt/verynginx/verynginx/nginx_conf/in_external.conf;
128 | * include /opt/verynginx/verynginx/nginx_conf/in_http_block.conf;
129 | * include /opt/verynginx/verynginx/nginx_conf/in_server_block.conf;
130 | 
131 | >These `include` command were placed outside a block, block http internal configuration, server configuration block inside, Remenber keep these three line when modifying. If you add a new Server configuration block or http configuration block, also need add suitable `include` line into it.
132 | 
133 | ### Start / Stop / Restart Service
134 | 
135 | ```
136 | #Start Service
137 | /opt/verynginx/openresty/nginx/sbin/nginx
138 | 
139 | #Stop Service
140 | /opt/verynginx/openresty/nginx/sbin/nginx -s stop
141 | 
142 | #Restart Service
143 | /opt/verynginx/openresty/nginx/sbin/nginx -s reload
144 | 
145 | ```
146 | 
147 | ### Configure VeryNginx on dashboard
148 | 
149 | After the service begin running, you can see server status and do config on dashboard.
150 | 
151 | The address  of dashboard is `http://{{your_machine_address}}/verynginx/index.html`.
152 | 
153 | Default user and password is `verynginx` / `verynginx`. You should be able to work through all the options now.
154 | 
155 | The full version of config guide can be found in [VeryNginx Wiki](https://github.com/alexazhou/VeryNginx/) .
156 | 
157 | ### Trouble Shooting
158 | 
159 | If you have any problems during **installation** / **configuration** / **use** , you can refer the Trouble Shooting document.
160 | 
161 | [Trouble Shooting](https://github.com/alexazhou/VeryNginx/wiki/Trouble-Shooting) 
162 | 
163 | #### Tips
164 | 
165 | * New configs will be effective immediately upon saving. It's not necessary to restart or reload nginx.
166 | 
167 | * When you save config, VeryNginx will write all configs to `/opt/verynginx/verynginx/configs/config.json`.
168 | 
169 | * If the chat in status page is stuck, you can click the gear icon in the upper right corner to turn off animation
170 | 
171 | * If you lock yourself out of VeryNginx by doing something stupid, you can always delete `config.json` to revert VeryNginx to its default.
172 | 
173 | ### Update VeryNginx / OpenResty
174 | 
175 | Over time, VeryNginx own will evolve, and can also support newer version of OpenResty. New version of VeryNginx might support some new features or fix some old bugs. If you want to update locally installed VeryNginx, you just need pull the latest code from github to local, and run the following commands:
176 | 
177 | ```
178 | #Update VeryNginx
179 | python install.py update verynginx
180 | 
181 | #Update OpenResty
182 | python install.py update openresty
183 | 
184 | ```
185 | 
186 | install.py will keep the old config.json and nginx.conf during update. So that you **will not lost configuration** after update.
187 | 
188 | ### Build VeryNginx docker Image
189 | 
190 | After cloning code to your local filesystem, you can run the following commands:
191 | 
192 | ```
193 | docker build -t verynginx .
194 | docker run verynginx
195 | ```
196 | 
197 | Then you can navigate to your browser `http://{{your_docker_machine_address}}/verynginx/index.html`
198 | 
199 | Optionally you can run `docker run -p xxxx:80 verynginx` to map your container port 80 to your host's xxxx port
200 | 
201 | 
202 | ## Donate
203 | 
204 | If you like VeryNginx, you can donate to support my development VeryNginx. With your support, I will be able to make VeryNginx better 😎.
205 | 
206 | ### PayPal 
207 | 
208 | [Support VeryNginx via PayPal](https://www.paypal.me/alexazhou)
209 | 
210 | ### We Chat
211 | 
212 | Scan the QRcode to support VeryNginx.
213 | 
214 | <img title="WeChat QRcode" src="http://ww4.sinaimg.cn/mw690/3fcd0ed3jw1f6kecm1e3nj20f00emq59.jpg" width="200">
215 | 
216 | ## Thanks
217 | 
218 | [VeryNginx thanks for the help](https://github.com/alexazhou/VeryNginx/wiki/Thanks)
219 | 
220 | 
221 | ### Enjoy~
222 | 
223 | [^openresty]: [OpenResty](https://github.com/openresty/openresty) Openresty is a enhanced nginx,bundle standard nginx core and lots of 3rd-party nginx module. 
224 | 
225 | 


--------------------------------------------------------------------------------
/readme_zh.md:
--------------------------------------------------------------------------------
  1 | # VeryNginx
  2 | VeryNginx 是一个功能强大而对人类友好的 Nginx 扩展程序.
  3 | 
  4 | ### 提示
  5 |  `v0.2` 版本之后,控制台入口被移动到了 `/verynginx/index.html`
  6 | 
  7 | ## 介绍
  8 | 
  9 | VeryNginx 基于 `lua_nginx_module(openrestry)` 开发,实现了高级的防火墙、访问统计和其他的一些功能。 集成在 Nginx 中运行,扩展了 Nginx 本身的功能,并提供了友好的 Web 交互界面。
 10 | 
 11 | [VeryNginx在线实例](http://alexazhou.xyz/vn/index.html) 
 12 | 
 13 | 用户名 / 密码: **verynginx / verynginx**
 14 | 
 15 | 详细配置说明见:[VeryNginx Github WiKi](https://github.com/alexazhou/VeryNginx/wiki/目录)
 16 | 
 17 | ### Nginx 运行状态分析
 18 | 
 19 | * 每秒请求数
 20 | * 响应时间
 21 | * 网络流量
 22 | * 网络连接数
 23 | 
 24 | ![Nginx 运行状态](http://ww2.sinaimg.cn/mw690/3fcd0ed3jw1f17en7oc1yj20z00ol0wl.jpg)
 25 | 
 26 | 
 27 | ### 自定义行为
 28 | 
 29 | VeryNginx 包含强大的自定义功能,可以做很多事情
 30 | 
 31 | 自定义行为包含两部分, Matcher 和 Action 。 Matcher 用来对请求进行匹配, Action 为要执行的动作
 32 | 
 33 | 这样的优势在于把所有的前置判断整合在Matcher里一起来实现了,使复杂(组合)规则的实现变成了可能
 34 | 
 35 | #### Matcher
 36 | 
 37 | 一个 Matcher 用来判断一个 Http 请求是否符合指定的条件, 一个 Matcher 可以包含一个或者多个约束条件,目前支持以下几种约束:
 38 | 
 39 | * Client IP
 40 | * Host
 41 | * UserAgent
 42 | * URI
 43 | * Referer
 44 | * Request Args
 45 | 
 46 | 当一个请求没有违反 Matcher 中包含的全部条件时,即命中了这个 Matcher 
 47 | 
 48 | #### Action
 49 | 
 50 | 每个 Action 会引用一个 Matcher ,当 Matcher 命中时, Action 会被执行
 51 | 
 52 | 目前已经实现了以下 Action
 53 | 
 54 | * **Scheme Lock** 将访问协议锁定为 Https 或者 Http
 55 | * **Redirect** 对请求进行重定向
 56 | * **URI Rewrite** 对请求的 URI 进行内部重写
 57 | * **Browser Verify** 通过set-cookies 和 js 验证客户端是否为浏览器,并拦截非浏览器流量。本功能可能会阻拦搜索引擎爬虫,建议仅在被攻击时开启,或者针对搜索引擎编写特别的规则。
 58 | * **Frequency Limit** 访问频率限制
 59 | * **Filter(waf)** 过滤器
 60 | 
 61 | 因为 Matcher 可以对请求进行细致的匹配,所以结合 Filter Action,就可以实现一个高级的WAF,可以利用Matcher中所有的条件来对请求进行过滤,并返回指定状态码
 62 | 
 63 | VeryNginx 预置了常用的过滤规则,可以在一定程度上阻止常见的 SQL 注入、Git 及 SVN 文件泄露、目录遍历攻击,并拦截常见的扫描工具。
 64 | 
 65 | ![VeryNginx Matcher](http://ww2.sinaimg.cn/mw690/3fcd0ed3jw1f17en8ovthj20zs0pdn1x.jpg)
 66 | 
 67 | ![VeryNginx filter](http://ww3.sinaimg.cn/mw690/3fcd0ed3jw1f17en9lrarj20zw0piq77.jpg)
 68 | 
 69 | #### Backend
 70 | 
 71 | 每个 Backend 会引用一个 Matcher ,当 Matcher 命中时, 请求会通过 Backend 进行处理
 72 | 
 73 | 目前已经实现了以下 Backend
 74 | 
 75 | * **Proxy Pass** 将请求反向代理到其它服务器
 76 | * **Static File** 使用本地文件处理请求
 77 | 
 78 | ### 访问统计
 79 | 
 80 | VeryNginx 可以统计网站每个URI的访问情况,包括每个URI的:
 81 | 
 82 | * 总请求次数
 83 | * 各状态码次数
 84 | * 返回总字节数
 85 | * 每请求平均字节数
 86 | * 总响应时间
 87 | * 平均响应时间
 88 | 
 89 | 并且可以按各种规则排序进行分析。
 90 | 
 91 | ![Nginx 运行状态](http://ww1.sinaimg.cn/mw690/3fcd0ed3jw1f17ena2ipyj20zw0piqag.jpg)
 92 | 
 93 | ## 安装和使用说明
 94 | 
 95 | VeryNginx 基于 OpenResty[^openresty],所以安装 VeryNginx 需要先安装好 OpenResty。不过并不用担心安装过程中可能的麻烦,VeryNginx 自身提供了脚本来进行安装工作。
 96 | 
 97 | ### 安装 VeryNginx
 98 | 
 99 | 克隆 VeryNginx 仓库到本地, 然后进入仓库目录,执行以下命令 
100 | 
101 | ```
102 | python install.py install
103 | ```
104 | 
105 | 即可一键安装 VeryNginx 和 以及依赖的 OpenResty
106 | 
107 | #### 想使用自己的 Nginx?
108 | 
109 | VeryNginx 可以自动为你安装依赖的 OpenResty,通常情况下你**没有必要**再自己安装 OpenResty。
110 | 
111 | 但如果你想要**使用自己编译的 Nginx( OpenResty )**,也是可以的。具体方法请阅读Wiki中的这篇说明:[Use own nginx](https://github.com/alexazhou/VeryNginx/wiki/Use-own-nginx)
112 | ### 使用
113 | 
114 | #### 编辑 Nginx 配置文件
115 | 
116 | VeryNginx 的配置文件位置为 **/opt/verynginx/openresty/nginx/conf/nginx.conf**,这是一个简单的示例文件,可以让你访问到 VeryNginx的控制面板。如果你想真正的用 VeryNginx 来做点什么,那你需要编辑这个文件,并将自己的 Nginx 配置加入到其中。
117 | 
118 | >这个配置文件在普通的 Nginx 配置文件基础上添加了三条 Include 指令来实现功能,分别为 
119 | >
120 | * include /opt/verynginx/verynginx/nginx_conf/in_external.conf;
121 | * include /opt/verynginx/verynginx/nginx_conf/in_http_block.conf;
122 | * include /opt/verynginx/verynginx/nginx_conf/in_server_block.conf;
123 | >
124 | 以上三条指令分别放在 http 配置块外部,http 配置块内部,server 配置块内部,在修改时请保留这三条。如果添加了新的 Server 配置块或 http 配置块,也需要在新的块内部加入对应的 include 行。
125 | 
126 | ### 启动/停止/重启 服务
127 | 
128 | 完成安装工作以后,可以通过以下命令来运行 VeryNginx
129 | 
130 | ```
131 | #启动服务
132 | /opt/verynginx/openresty/nginx/sbin/nginx
133 | 
134 | #停止服务
135 | /opt/verynginx/openresty/nginx/sbin/nginx -s stop
136 | 
137 | #重启服务
138 | /opt/verynginx/openresty/nginx/sbin/nginx -s reload
139 | ```
140 | 
141 | 
142 | ### 通过web面板对 VeryNginx 进行配置
143 | 
144 | VeryNginx 启动后,可以通过浏览器访问管理面板来查看状态以及进行配置。
145 | 
146 | 管理面板地址为 `http://{{your_machine_address}}/verynginx/index.html`。
147 | 
148 | 默认用户名和密码是 `verynginx` / `verynginx`。
149 | 
150 | 登录之后就可以查看状态,并对配置进行修改了。修改配置后,点击保存才会生效.
151 | 
152 | ### 故障排除
153 | 
154 | 如果你在 **安装** / **配置** / **使用** 的过程中遇到任何问题, 你可以参考故障排除文档来解决.
155 | 
156 | [故障排除](https://github.com/alexazhou/VeryNginx/wiki/Trouble-Shooting) 
157 | 
158 | 
159 | #### 详细的配置说明
160 | 
161 | VeryNginx 按照易于使用的思想进行设计,如果你有一定的基础,或是对 Nginx 较了解,那么你应该可以直接在界面上使用。
162 | 
163 | 当然 VeryNginx 也提供了详细的文档供你查阅。
164 | [VeryNginx Wiki](https://github.com/alexazhou/VeryNginx/wiki/目录)
165 | 
166 | #### 提示
167 | 
168 | * 通过 VeryNginx 控制面板保存新配置之后,会立刻生效,并不需要 restart/reload Nginx。
169 | 
170 | * VeryNginx 把配置保存在 `/opt/verynginx/verynginx/configs/config.json` 里面。
171 | 
172 | * 状态页面图表默认带有动画效果,如果有卡顿,可以点右上角齿轮图标关掉动画效果
173 | 
174 | * 如果因为配错了什么选项,导致无法登录,可以手动删除 `config.json` 来清空配置,或者手动编辑这个文件来修复。
175 | 
176 | ### 更新 VeryNginx / OpenResty
177 | 
178 | 随着时间的发展,VeryNginx 本身的代码会演进,也可以会支持更新版本的 OpenResty ,更新的代码可能会支持一些新的功能,或是修复了一些旧的bug。如果要更新本地已经安装的 VeryNginx ,你只需要先 pull github 上最新的代码到本地,然后通过以下命令来进行更新:
179 | 
180 | 
181 | ```
182 | #更新 Verynginx
183 | python install.py update verynginx
184 | 
185 | #更新 OpenResty
186 | python install.py update openresty
187 | 
188 | ```
189 | 
190 | install.py脚本在升级过程中,将保留原有的 config.json 和 nginx.conf, 所以**更新的过程并不会丢失配置**
191 | 
192 | ## 构建VeryNginx docker 镜像
193 | 
194 | 在将代码clone到本地之后,你可以运行下面的命令:
195 | 
196 | ```
197 | cd VeryNginx
198 | docker build -t verynginx .
199 | docker run -d --name=verynginx -p 8080:80 verynginx
200 | ```
201 | 然后用浏览器打开 `http://{{your_docker_machine_address}}/verynginx/index.html`
202 | 
203 | 当然你也可以运行 `docker run -d --name=verynginx -p xxxx:80 verynginx` 来映射一下你的container的端口到你的宿主机,默认是80,你可以把xxxx改成你希望的在宿主机上的端口号
204 | 
205 | ## 捐赠
206 | 
207 | 如果你喜欢 VeryNginx,那么你可以通过捐赠来支持我开发 VeryNginx。有了你的支持,我将可以让 VeryNginx 变的更好😎
208 | 
209 | ### PayPal 
210 | 
211 | [通过 PayPal 来支持 VeryNginx](https://www.paypal.me/alexazhou)
212 | 
213 | ### 微信
214 | 
215 | 扫描下方的二维码来支持 VeryNginx
216 | 
217 | <img title="WeChat QRcode" src="http://ww4.sinaimg.cn/mw690/3fcd0ed3jw1f6kecm1e3nj20f00emq59.jpg" width="200">
218 | 
219 | 
220 | ## 致谢
221 | 
222 | [感谢大家对VeryNginx的帮助](https://github.com/alexazhou/VeryNginx/wiki/Thanks)
223 | 
224 | 
225 | 
226 | 


--------------------------------------------------------------------------------
/test/base_case.py:
--------------------------------------------------------------------------------
 1 | import os
 2 | import time
 3 | import unittest
 4 | 
 5 | 
 6 | 
 7 | class Base_Case(unittest.TestCase):
 8 |     def __init__(self, *args, **kwargs):
 9 |         super(Base_Case, self).__init__(*args, **kwargs)
10 |         self.desc = "Base Case"
11 |         self.ngx_bin = '/opt/verynginx/openresty/nginx/sbin/nginx'
12 |         self.ngx_errlog = '/opt/verynginx/openresty/nginx/logs/error.log'
13 | 
14 |         self.ngx_conf_dir = None
15 |         self.ngx_conf = None
16 |         
17 |         self.vn_conf_dir = self.ngx_conf_dir
18 |         self.vn_conf = 'config.json'
19 |         
20 |         self.f_ngx_errlog = None
21 | 
22 |     def exec_sys_cmd(self, cmd):
23 |         ret = os.system(cmd)
24 |         assert ret == 0
25 |     
26 |     def cfg_str(self):
27 |         if self.ngx_conf == None:
28 |             return ''
29 |         else:
30 |             return ' -c %s'%self.ngx_conf
31 | 
32 |     def get_ngx_stderr(self):
33 |         self.exec_sys_cmd(self.ngx_bin + self.cfg_str() + ' -s reopen')#refresh nginx log
34 |         ret = self.f_ngx_errlog.read() 
35 |         assert len(self.f_ngx_errlog.read(1)) == 0 #make sure no more log
36 |         return ret
37 |     
38 |     def check_ngx_stderr(self, log_str=None, ignore_flag=[]):
39 |         if log_str == None:
40 |             log_str = self.get_ngx_stderr()
41 | 
42 |         mark = [ '[lua]','stack traceback','coroutine','in function','aborted','runtime error' ]
43 |         for item in ignore_flag:
44 |             mark.remove(item)
45 | 
46 |         for item in mark:
47 |             if item in log_str:
48 |                 print( 'Got unexpected flag "%s" in nginx error.log:%s'%(item,log_str))
49 |                 assert False
50 | 
51 |     def setUp(self):
52 |         print('Run: %s'%self.desc)
53 |         #open nginx error.log
54 |         self.f_ngx_errlog = open(self.ngx_errlog,'r')
55 |         self.f_ngx_errlog.seek(0,os.SEEK_END)
56 |         
57 |         #prepare config.json for verynginx
58 |         self.exec_sys_cmd('rm -rf /opt/verynginx/verynginx/configs/*')
59 |         if self.vn_conf != None:
60 |             self.exec_sys_cmd('cp %s/%s /opt/verynginx/verynginx/configs/config.json'%(self.vn_conf_dir, self.vn_conf))
61 | 
62 |         #start nginx
63 |         self.exec_sys_cmd(self.ngx_bin + self.cfg_str())
64 |     
65 |     def tearDown(self):
66 |         #close nginx log file
67 |         self.f_ngx_errlog.close()
68 |         #stop nginx
69 |         self.exec_sys_cmd(self.ngx_bin + self.cfg_str() + ' -s stop')
70 | 


--------------------------------------------------------------------------------
/test/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
2 | 


--------------------------------------------------------------------------------
/test/test.py:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env python
 2 | # -*- coding: utf-8 -*-
 3 | # @Date    : 2016-08-21 23:06
 4 | # @Author  : Alexa (AlexaZhou@163.com)
 5 | # @Link    : https://github.com/alexazhou/VeryNginx
 6 | # @Disc    : test VeryNginx
 7 | # @Disc    : support python 3.x
 8 | 
 9 | import os
10 | import time
11 | import unittest
12 | 
13 | 
14 | def load_test_case():
15 |     ret = {}
16 | 
17 |     dir_filter = lambda x:os.path.exists( './testcase/' + x + '/case.py' )
18 |     case_list = list(filter( dir_filter, os.listdir('./testcase/') ))
19 |    
20 |     for case in case_list:
21 |         tmp = __import__( 'testcase.%s.case'%case )
22 |         ret[case] = eval( 'tmp.%s.case'%case )
23 |     
24 |     return ret
25 | 
26 | 
27 | if __name__ == "__main__":
28 | 
29 |     script_path = os.path.dirname( os.path.abspath(__file__) )
30 |     os.chdir( script_path )
31 | 
32 |     all_case = load_test_case()
33 |     major_suite = unittest.TestSuite()
34 |     for k in all_case.keys():
35 |         suite = unittest.defaultTestLoader.loadTestsFromTestCase(all_case[k].Case)
36 |         major_suite.addTests( suite )
37 |     
38 |     runner = unittest.TextTestRunner()
39 |     ret = runner.run(major_suite)
40 |     assert ret.wasSuccessful()
41 | 


--------------------------------------------------------------------------------
/test/testcase/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexazhou/VeryNginx/b66ef2ee6a108279653316323bd8ac026dd7ccd8/test/testcase/__init__.py


--------------------------------------------------------------------------------
/test/testcase/a_1_start_without_config/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexazhou/VeryNginx/b66ef2ee6a108279653316323bd8ac026dd7ccd8/test/testcase/a_1_start_without_config/__init__.py


--------------------------------------------------------------------------------
/test/testcase/a_1_start_without_config/case.py:
--------------------------------------------------------------------------------
 1 | import base_case
 2 | import requests
 3 | import os
 4 | 
 5 | class Case(base_case.Base_Case):
 6 |     def __init__(self, *args, **kwargs):
 7 |         super(Case, self).__init__(*args, **kwargs)
 8 |         self.desc = "test verynginx start without config.json"
 9 |         self.vn_conf = None
10 | 
11 |     def test_vn_start_without_conf(self):
12 |         r = requests.get('http://127.0.0.1')
13 |         assert r.status_code == 200
14 |         assert r.headers.get('content-type') == 'text/html'
15 |         f = open('/opt/verynginx/openresty/nginx/html/index.html', 'rb')
16 |         index_content = f.read(1*1024*1024)
17 |         f.close()
18 |         assert index_content==r.content 
19 |         self.check_ngx_stderr()
20 | 
21 | 


--------------------------------------------------------------------------------
/test/testcase/a_2_basic_behaviour/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexazhou/VeryNginx/b66ef2ee6a108279653316323bd8ac026dd7ccd8/test/testcase/a_2_basic_behaviour/__init__.py


--------------------------------------------------------------------------------
/test/testcase/a_2_basic_behaviour/case.py:
--------------------------------------------------------------------------------
 1 | import base_case
 2 | import requests
 3 | import os
 4 | 
 5 | class Case(base_case.Base_Case):
 6 |     def __init__(self, *args, **kwargs):
 7 |         super(Case, self).__init__(*args, **kwargs)
 8 |         self.desc = "test basic behaviour with config.json"
 9 |         self.vn_conf_dir = os.path.dirname(os.path.abspath(__file__))
10 | 
11 |     def test_vn_start_with_conf(self):
12 |         #test index.html
13 |         r = requests.get('http://127.0.0.1')
14 |         assert r.status_code == 200
15 |         assert r.headers.get('content-type') == 'text/html'
16 |         f = open('/opt/verynginx/openresty/nginx/html/index.html', 'rb')
17 |         index_content = f.read(1*1024*1024)
18 |         f.close()
19 |         assert index_content==r.content 
20 |         self.check_ngx_stderr()
21 |     
22 |     def test_404(self): 
23 |         #test notexist.html
24 |         r = requests.get('http://127.0.0.1/notexist.html')
25 |         assert r.status_code == 404
26 |         assert r.headers.get('content-type') == 'text/html'
27 |         assert b'404' in r.content
28 |         self.check_ngx_stderr()
29 | 
30 |     def test_vn_index(self):
31 |         r = requests.get('http://127.0.0.1/verynginx/index.html')
32 |         assert r.status_code == 200
33 |         f = open('/opt/verynginx/verynginx/dashboard/index.html', 'rb')
34 |         index_content = f.read(1*1024*1024)
35 |         f.close()
36 |         assert index_content==r.content
37 |         self.check_ngx_stderr()
38 | 
39 |     def test_vn_index_with_short_url(self):
40 |         r = requests.get('http://127.0.0.1/vn/index.html')
41 |         assert r.status_code == 200
42 |         f = open('/opt/verynginx/verynginx/dashboard/index.html', 'rb')
43 |         index_content = f.read(1*1024*1024)
44 |         f.close()
45 |         assert index_content==r.content 
46 |         self.check_ngx_stderr()
47 | 
48 | 


--------------------------------------------------------------------------------
/test/testcase/a_2_basic_behaviour/config.json:
--------------------------------------------------------------------------------
  1 | {
  2 |   "redirect_rule":[{
  3 |       "to_uri":"/verynginx/index.html",
  4 |       "matcher":"demo_other_verynginx_uri",
  5 |       "enable":true
  6 |     }],
  7 |   "filter_rule":[{
  8 |       "enable":false,
  9 |       "action":"accept",
 10 |       "matcher":"localhost"
 11 |     },{
 12 |       "enable":true,
 13 |       "action":"block",
 14 |       "code":"403",
 15 |       "matcher":"attack_sql_0"
 16 |     },{
 17 |       "enable":true,
 18 |       "action":"block",
 19 |       "code":"403",
 20 |       "matcher":"attack_backup_0"
 21 |     },{
 22 |       "enable":true,
 23 |       "action":"block",
 24 |       "code":"403",
 25 |       "matcher":"attack_scan_0"
 26 |     },{
 27 |       "enable":true,
 28 |       "action":"block",
 29 |       "code":"403",
 30 |       "matcher":"attack_code_0"
 31 |     }],
 32 |   "config_version":"0.36",
 33 |   "static_file_rule":[],
 34 |   "proxy_pass_rule":[],
 35 |   "summary_collect_rule":[],
 36 |   "summary_group_persistent_enable":true,
 37 |   "admin":[{
 38 |       "user":"verynginx",
 39 |       "password":"verynginx",
 40 |       "enable":true
 41 |     }],
 42 |   "dashboard_host":"",
 43 |   "response":{
 44 |     "demo_response_html":{
 45 |       "content_type":"text/html",
 46 |       "body":"This is a html demo response"
 47 |     },
 48 |     "demo_response_json":{
 49 |       "content_type":"application/json",
 50 |       "body":"{\"msg\":\"soms text\",\"status\":\"success\"}"
 51 |     }
 52 |   },
 53 |   "scheme_lock_enable":false,
 54 |   "uri_rewrite_rule":[{
 55 |       "to_uri":"/verynginx/$1",
 56 |       "matcher":"demo_verynginx_short_uri",
 57 |       "replace_re":"^/vn/(.*)",
 58 |       "enable":true
 59 |     }],
 60 |   "cookie_prefix":"verynginx",
 61 |   "filter_enable":true,
 62 |   "browser_verify_enable":true,
 63 |   "scheme_lock_rule":[{
 64 |       "enable":false,
 65 |       "matcher":"verynginx",
 66 |       "scheme":"https"
 67 |     }],
 68 |   "redirect_enable":true,
 69 |   "backend_upstream":{
 70 |   },
 71 |   "frequency_limit_enable":true,
 72 |   "matcher":{
 73 |     "demo_verynginx_short_uri":{
 74 |       "URI":{
 75 |         "operator":"≈",
 76 |         "value":"^/vn"
 77 |       }
 78 |     },
 79 |     "all_request":{
 80 |     },
 81 |     "attack_sql_0":{
 82 |       "Args":{
 83 |         "name_operator":"*",
 84 |         "operator":"≈",
 85 |         "value":"select.*from"
 86 |       }
 87 |     },
 88 |     "verynginx":{
 89 |       "URI":{
 90 |         "operator":"≈",
 91 |         "value":"^/verynginx/"
 92 |       }
 93 |     },
 94 |     "attack_scan_0":{
 95 |       "UserAgent":{
 96 |         "operator":"≈",
 97 |         "value":"(nmap|w3af|netsparker|nikto|fimap|wget)"
 98 |       }
 99 |     },
100 |     "attack_code_0":{
101 |       "URI":{
102 |         "operator":"≈",
103 |         "value":"\\.(git|svn|\\.)"
104 |       }
105 |     },
106 |     "localhost":{
107 |       "IP":{
108 |         "operator":"=",
109 |         "value":"127.0.0.1"
110 |       }
111 |     },
112 |     "demo_other_verynginx_uri":{
113 |       "URI":{
114 |         "operator":"=",
115 |         "value":"/redirect_to_verynginx"
116 |       }
117 |     },
118 |     "attack_backup_0":{
119 |       "URI":{
120 |         "operator":"≈",
121 |         "value":"\\.(htaccess|bash_history|ssh|sql)
quot;
122 |       }
123 |     }
124 |   },
125 |   "static_file_enable":true,
126 |   "summary_temporary_period":60,
127 |   "summary_group_temporary_enable":true,
128 |   "summary_with_host":false,
129 |   "frequency_limit_rule":[],
130 |   "uri_rewrite_enable":true,
131 |   "base_uri":"/verynginx",
132 |   "summary_request_enable":true,
133 |   "proxy_pass_enable":true,
134 |   "readonly":false,
135 |   "browser_verify_rule":[]
136 | }


--------------------------------------------------------------------------------
/test/testcase/a_3_matcher_host/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexazhou/VeryNginx/b66ef2ee6a108279653316323bd8ac026dd7ccd8/test/testcase/a_3_matcher_host/__init__.py


--------------------------------------------------------------------------------
/test/testcase/a_3_matcher_host/case.py:
--------------------------------------------------------------------------------
 1 | import base_case
 2 | import requests
 3 | import os
 4 | 
 5 | class Case(base_case.Base_Case):
 6 |     def __init__(self, *args, **kwargs):
 7 |         super(Case, self).__init__(*args, **kwargs)
 8 |         self.desc = "test matcher with multiform condition of host"
 9 |         self.vn_conf_dir = os.path.dirname(os.path.abspath(__file__))
10 | 
11 |     def test_host_equal(self):
12 |         r = requests.get('http://a.vntest.com/testhostequal')
13 |         assert r.status_code == 400
14 |         assert r.headers.get('content-type') == 'text/html'
15 |         assert 'hited' in r.text
16 |         self.check_ngx_stderr()
17 |     
18 |         r = requests.get('http://b.vntest.com/testhostequal')
19 |         assert r.status_code == 404
20 |         assert r.headers.get('content-type') == 'text/html'
21 |         assert 'hited' not in r.text
22 |         self.check_ngx_stderr()
23 | 
24 |         r = requests.get('http://c.vntest.com/testhostequal')
25 |         assert r.status_code == 404
26 |         assert r.headers.get('content-type') == 'text/html'
27 |         assert 'hited' not in r.text
28 |         self.check_ngx_stderr()
29 |     
30 |     def test_host_not_equal(self): 
31 |         r = requests.get('http://b.vntest.com/testhostnotequal')
32 |         assert r.status_code == 400
33 |         assert r.headers.get('content-type') == 'text/html'
34 |         assert 'hited' in r.text
35 |         self.check_ngx_stderr()
36 | 
37 |         r = requests.get('http://c.vntest.com/testhostnotequal')
38 |         assert r.status_code == 400
39 |         assert r.headers.get('content-type') == 'text/html'
40 |         assert 'hited' in r.text
41 |         self.check_ngx_stderr()
42 |         
43 |         r = requests.get('http://a.vntest.com/testhostnotequal')
44 |         assert r.status_code == 404
45 |         assert r.headers.get('content-type') == 'text/html'
46 |         assert 'hited' not in r.text
47 |         self.check_ngx_stderr()
48 | 
49 |     def test_host_match(self): 
50 |         r = requests.get('http://a.vntest.com/testhostmatch')
51 |         assert r.status_code == 400
52 |         assert r.headers.get('content-type') == 'text/html'
53 |         assert 'hited' in r.text
54 |         self.check_ngx_stderr()
55 | 
56 |         r = requests.get('http://b.vntest.com/testhostmatch')
57 |         assert r.status_code == 400
58 |         assert r.headers.get('content-type') == 'text/html'
59 |         assert 'hited' in r.text
60 |         self.check_ngx_stderr()
61 |     
62 |         r = requests.get('http://127.0.0.1/testhostmatch')
63 |         assert r.status_code == 404
64 |         assert r.headers.get('content-type') == 'text/html'
65 |         assert 'hited' not in r.text
66 |         self.check_ngx_stderr()
67 | 
68 |     def test_host_not_match(self): 
69 |         r = requests.get('http://127.0.0.1/testhostnotmatch')
70 |         assert r.status_code == 400
71 |         assert r.headers.get('content-type') == 'text/html'
72 |         assert 'hited' in r.text
73 |         self.check_ngx_stderr()
74 |         
75 |         r = requests.get('http://a.vntest.com/testhostnotmatch')
76 |         assert r.status_code == 404
77 |         assert r.headers.get('content-type') == 'text/html'
78 |         assert 'hited' not in r.text
79 |         self.check_ngx_stderr()
80 | 
81 |         r = requests.get('http://b.vntest.com/testhostnotmatch')
82 |         assert r.status_code == 404
83 |         assert r.headers.get('content-type') == 'text/html'
84 |         assert 'hited' not in r.text
85 |         self.check_ngx_stderr()
86 | 
87 | 


--------------------------------------------------------------------------------
/test/testcase/a_3_matcher_host/config.json:
--------------------------------------------------------------------------------
  1 | {
  2 |   "redirect_rule":[],
  3 |   "filter_rule":[{
  4 |       "enable":true,
  5 |       "code":"400",
  6 |       "response":"hit",
  7 |       "matcher":"host_equal",
  8 |       "custom_response":true,
  9 |       "action":"block"
 10 |     },{
 11 |       "enable":true,
 12 |       "code":"400",
 13 |       "response":"hit",
 14 |       "matcher":"host_match",
 15 |       "custom_response":true,
 16 |       "action":"block"
 17 |     },{
 18 |       "enable":true,
 19 |       "code":"400",
 20 |       "response":"hit",
 21 |       "matcher":"host_not_equal",
 22 |       "custom_response":true,
 23 |       "action":"block"
 24 |     },{
 25 |       "enable":true,
 26 |       "code":"400",
 27 |       "response":"hit",
 28 |       "matcher":"host_not_match",
 29 |       "custom_response":true,
 30 |       "action":"block"
 31 |     }],
 32 |   "config_version":"0.36",
 33 |   "static_file_rule":[],
 34 |   "proxy_pass_rule":[],
 35 |   "summary_collect_rule":[],
 36 |   "summary_group_persistent_enable":true,
 37 |   "admin":[{
 38 |       "user":"verynginx",
 39 |       "password":"verynginx",
 40 |       "enable":true
 41 |     }],
 42 |   "dashboard_host":"",
 43 |   "response":{
 44 |     "hit":{
 45 |       "content_type":"text/html",
 46 |       "body":"hited"
 47 |     },
 48 |     "didn'thit":{
 49 |       "content_type":"text/html",
 50 |       "body":"didn't hited"
 51 |     }
 52 |   },
 53 |   "scheme_lock_enable":true,
 54 |   "uri_rewrite_rule":[],
 55 |   "cookie_prefix":"verynginx",
 56 |   "filter_enable":true,
 57 |   "browser_verify_enable":true,
 58 |   "scheme_lock_rule":[],
 59 |   "redirect_enable":true,
 60 |   "backend_upstream":{
 61 |   },
 62 |   "frequency_limit_enable":true,
 63 |   "matcher":{
 64 |     "host_match":{
 65 |       "Host":{
 66 |         "value":".*vntest.*",
 67 |         "operator":"≈"
 68 |       },
 69 |       "URI":{
 70 |         "value":"/testhostmatch",
 71 |         "operator":"="
 72 |       }
 73 |     },
 74 |     "host_not_equal":{
 75 |       "URI":{
 76 |         "value":"/testhostnotequal",
 77 |         "operator":"="
 78 |       },
 79 |       "Host":{
 80 |         "value":"a.vntest.com",
 81 |         "operator":"!="
 82 |       }
 83 |     },
 84 |     "host_not_match":{
 85 |       "Host":{
 86 |         "value":".*vntest.*",
 87 |         "operator":"!≈"
 88 |       },
 89 |       "URI":{
 90 |         "value":"/testhostnotmatch",
 91 |         "operator":"≈"
 92 |       }
 93 |     },
 94 |     "all_request":{
 95 |     },
 96 |     "host_equal":{
 97 |       "Host":{
 98 |         "value":"a.vntest.com",
 99 |         "operator":"="
100 |       },
101 |       "URI":{
102 |         "value":"/testhostequal",
103 |         "operator":"="
104 |       }
105 |     }
106 |   },
107 |   "static_file_enable":true,
108 |   "summary_temporary_period":60,
109 |   "summary_group_temporary_enable":true,
110 |   "summary_with_host":false,
111 |   "frequency_limit_rule":[],
112 |   "uri_rewrite_enable":true,
113 |   "base_uri":"/verynginx",
114 |   "summary_request_enable":true,
115 |   "proxy_pass_enable":true,
116 |   "readonly":false,
117 |   "browser_verify_rule":[]
118 | }


--------------------------------------------------------------------------------
/test/testcase/a_4_matcher_useragent/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexazhou/VeryNginx/b66ef2ee6a108279653316323bd8ac026dd7ccd8/test/testcase/a_4_matcher_useragent/__init__.py


--------------------------------------------------------------------------------
/test/testcase/a_4_matcher_useragent/case.py:
--------------------------------------------------------------------------------
  1 | import base_case
  2 | import requests
  3 | import os
  4 | 
  5 | class Case(base_case.Base_Case):
  6 |     def __init__(self, *args, **kwargs):
  7 |         super(Case, self).__init__(*args, **kwargs)
  8 |         self.desc = "test matcher with multiform condition of UserAgent"
  9 |         self.vn_conf_dir = os.path.dirname(os.path.abspath(__file__))
 10 | 
 11 |     def test_useragent_equal(self): 
 12 |         r = requests.get('http://127.0.0.1/test_useragent_equal',headers={'User-Agent':'vntestflag'})
 13 |         assert r.status_code == 400
 14 |         assert r.headers.get('content-type') == 'text/html'
 15 |         assert 'hited' in r.text
 16 |         self.check_ngx_stderr()
 17 | 
 18 |         r = requests.get('http://127.0.0.1/test_useragent_equal',headers={'User-Agent':'vntestflag1'})
 19 |         assert r.status_code == 404
 20 |         assert r.headers.get('content-type') == 'text/html'
 21 |         assert 'hited' not in r.text
 22 |         self.check_ngx_stderr()
 23 | 
 24 |         r = requests.get('http://127.0.0.1/test_useragent_equal',headers={'User-Agent':'otherflag'})
 25 |         assert r.status_code == 404
 26 |         assert r.headers.get('content-type') == 'text/html'
 27 |         assert 'hited' not in r.text
 28 |         self.check_ngx_stderr()
 29 |         
 30 |         r = requests.get('http://127.0.0.1/test_useragent_equal',headers={'User-Agent':''})
 31 |         assert r.status_code == 404
 32 |         assert r.headers.get('content-type') == 'text/html'
 33 |         assert 'hited' not in r.text
 34 |         self.check_ngx_stderr()
 35 | 
 36 |         r = requests.get('http://127.0.0.1/test_useragent_equal',headers={'User-Agent':None})
 37 |         assert r.status_code == 404
 38 |         assert r.headers.get('content-type') == 'text/html'
 39 |         assert 'hited' not in r.text
 40 |         self.check_ngx_stderr()
 41 | 
 42 |     def test_useragent_not_equal(self): 
 43 |         r = requests.get('http://127.0.0.1/test_useragent_not_equal',headers={'User-Agent':'otherflag'})
 44 |         assert r.status_code == 400
 45 |         assert r.headers.get('content-type') == 'text/html'
 46 |         assert 'hited' in r.text
 47 |         self.check_ngx_stderr()
 48 | 
 49 |         r = requests.get('http://127.0.0.1/test_useragent_not_equal',headers={'User-Agent':'vntestflagz'})
 50 |         assert r.status_code == 400
 51 |         assert r.headers.get('content-type') == 'text/html'
 52 |         assert 'hited' in r.text
 53 |         self.check_ngx_stderr()
 54 | 
 55 |         r = requests.get('http://127.0.0.1/test_useragent_not_equal',headers={'User-Agent':''})
 56 |         assert r.status_code == 400
 57 |         assert r.headers.get('content-type') == 'text/html'
 58 |         assert 'hited' in r.text
 59 |         self.check_ngx_stderr()
 60 | 
 61 |         r = requests.get('http://127.0.0.1/test_useragent_not_equal',headers={'User-Agent':None})
 62 |         assert r.status_code == 400
 63 |         assert r.headers.get('content-type') == 'text/html'
 64 |         assert 'hited' in r.text
 65 |         self.check_ngx_stderr()
 66 | 
 67 |         r = requests.get('http://127.0.0.1/test_useragent_not_equal',headers={'User-Agent':'vntestflag'})
 68 |         assert r.status_code == 404
 69 |         assert r.headers.get('content-type') == 'text/html'
 70 |         assert 'hited' not in r.text
 71 |         self.check_ngx_stderr()
 72 | 
 73 |     def test_useragent_match(self): 
 74 |         r = requests.get('http://127.0.0.1/test_useragent_match',headers={'User-Agent':'aaatestbbb'})
 75 |         assert r.status_code == 400
 76 |         assert r.headers.get('content-type') == 'text/html'
 77 |         assert 'hited' in r.text
 78 |         self.check_ngx_stderr()
 79 | 
 80 |         r = requests.get('http://127.0.0.1/test_useragent_match',headers={'User-Agent':'aaaotherbbb'})
 81 |         assert r.status_code == 404
 82 |         assert r.headers.get('content-type') == 'text/html'
 83 |         assert 'hited' not in r.text
 84 |         self.check_ngx_stderr()
 85 | 
 86 |         r = requests.get('http://127.0.0.1/test_useragent_match',headers={'User-Agent':'aaazestbbb'})
 87 |         assert r.status_code == 404
 88 |         assert r.headers.get('content-type') == 'text/html'
 89 |         assert 'hited' not in r.text
 90 |         self.check_ngx_stderr()
 91 | 
 92 |         r = requests.get('http://127.0.0.1/test_useragent_match',headers={'User-Agent':''})
 93 |         assert r.status_code == 404
 94 |         assert r.headers.get('content-type') == 'text/html'
 95 |         assert 'hited' not in r.text
 96 |         self.check_ngx_stderr()
 97 | 
 98 |         r = requests.get('http://127.0.0.1/test_useragent_match',headers={'User-Agent':None})
 99 |         assert r.status_code == 404
100 |         assert r.headers.get('content-type') == 'text/html'
101 |         assert 'hited' not in r.text
102 |         self.check_ngx_stderr()
103 | 
104 |     def test_useragent_not_match(self): 
105 |         r = requests.get('http://127.0.0.1/test_useragent_not_match',headers={'User-Agent':'aaaotherbbb'})
106 |         assert r.status_code == 400
107 |         assert r.headers.get('content-type') == 'text/html'
108 |         assert 'hited' in r.text
109 |         self.check_ngx_stderr()
110 | 
111 |         r = requests.get('http://127.0.0.1/test_useragent_not_match',headers={'User-Agent':'aaazestbbb'})
112 |         assert r.status_code == 400
113 |         assert r.headers.get('content-type') == 'text/html'
114 |         assert 'hited' in r.text
115 |         self.check_ngx_stderr()
116 | 
117 |         r = requests.get('http://127.0.0.1/test_useragent_not_match',headers={'User-Agent':''})
118 |         assert r.status_code == 400
119 |         assert r.headers.get('content-type') == 'text/html'
120 |         assert 'hited' in r.text
121 |         self.check_ngx_stderr()
122 | 
123 |         r = requests.get('http://127.0.0.1/test_useragent_not_match',headers={'User-Agent':None})
124 |         assert r.status_code == 400
125 |         assert r.headers.get('content-type') == 'text/html'
126 |         assert 'hited' in r.text
127 |         self.check_ngx_stderr()
128 | 
129 |         r = requests.get('http://127.0.0.1/test_useragent_not_match',headers={'User-Agent':'aaatestbbb'})
130 |         assert r.status_code == 404
131 |         assert r.headers.get('content-type') == 'text/html'
132 |         assert 'hited' not in r.text
133 |         self.check_ngx_stderr()
134 | 
135 |     def test_useragent_existed(self): 
136 |         r = requests.get('http://127.0.0.1/test_useragent_existed',headers={'User-Agent':'aaatestbbb'})
137 |         assert r.status_code == 400
138 |         assert r.headers.get('content-type') == 'text/html'
139 |         assert 'hited' in r.text
140 |         self.check_ngx_stderr()
141 | 
142 |         r = requests.get('http://127.0.0.1/test_useragent_existed',headers={'User-Agent':'aaaotherbbb'})
143 |         assert r.status_code == 400
144 |         assert r.headers.get('content-type') == 'text/html'
145 |         assert 'hited' in r.text
146 |         self.check_ngx_stderr()
147 | 
148 |         r = requests.get('http://127.0.0.1/test_useragent_existed',headers={'User-Agent':''})
149 |         assert r.status_code == 400
150 |         assert r.headers.get('content-type') == 'text/html'
151 |         assert 'hited' in r.text
152 |         self.check_ngx_stderr()
153 | 
154 |         r = requests.get('http://127.0.0.1/test_useragent_existed',headers={'User-Agent':None})
155 |         assert r.status_code == 404
156 |         assert r.headers.get('content-type') == 'text/html'
157 |         assert 'hited' not in r.text
158 |         self.check_ngx_stderr()
159 | 
160 |     def test_useragent_not_existed(self): 
161 |         r = requests.get('http://127.0.0.1/test_useragent_not_existed',headers={'User-Agent':None})
162 |         assert r.status_code == 400
163 |         assert r.headers.get('content-type') == 'text/html'
164 |         assert 'hited' in r.text
165 |         self.check_ngx_stderr()
166 | 
167 |         r = requests.get('http://127.0.0.1/test_useragent_not_existed',headers={'User-Agent':'aaatestbbb'})
168 |         assert r.status_code == 404
169 |         assert r.headers.get('content-type') == 'text/html'
170 |         assert 'hited' not in r.text
171 |         self.check_ngx_stderr()
172 | 
173 |         r = requests.get('http://127.0.0.1/test_useragent_not_existed',headers={'User-Agent':'aaaotherbbb'})
174 |         assert r.status_code == 404
175 |         assert r.headers.get('content-type') == 'text/html'
176 |         assert 'hited' not in r.text
177 |         self.check_ngx_stderr()
178 |         
179 |         r = requests.get('http://127.0.0.1/test_useragent_not_existed',headers={'User-Agent':''})
180 |         assert r.status_code == 404
181 |         assert r.headers.get('content-type') == 'text/html'
182 |         assert 'hited' not in r.text
183 |         self.check_ngx_stderr()
184 | 
185 | 
186 | 
187 | 
188 | 
189 | 
190 |     
191 | 


--------------------------------------------------------------------------------
/test/testcase/a_4_matcher_useragent/config.json:
--------------------------------------------------------------------------------
  1 | {
  2 |   "redirect_rule":[],
  3 |   "filter_rule":[{
  4 |       "enable":true,
  5 |       "code":"400",
  6 |       "response":"hit",
  7 |       "matcher":"useragent_equal",
  8 |       "custom_response":true,
  9 |       "action":"block"
 10 |     },{
 11 |       "enable":true,
 12 |       "code":"400",
 13 |       "response":"hit",
 14 |       "matcher":"useragent_not_equal",
 15 |       "custom_response":true,
 16 |       "action":"block"
 17 |     },{
 18 |       "enable":true,
 19 |       "code":"400",
 20 |       "response":"hit",
 21 |       "matcher":"useragent_match",
 22 |       "custom_response":true,
 23 |       "action":"block"
 24 |     },{
 25 |       "enable":true,
 26 |       "code":"400",
 27 |       "response":"hit",
 28 |       "matcher":"useragent_not_match",
 29 |       "custom_response":true,
 30 |       "action":"block"
 31 |     },{
 32 |       "enable":true,
 33 |       "code":"400",
 34 |       "response":"hit",
 35 |       "matcher":"useragent_existed",
 36 |       "custom_response":true,
 37 |       "action":"block"
 38 |     },{
 39 |       "enable":true,
 40 |       "code":"400",
 41 |       "response":"hit",
 42 |       "action":"block",
 43 |       "custom_response":true,
 44 |       "matcher":"useragent_not_existed"
 45 |     }],
 46 |   "config_version":"0.36",
 47 |   "static_file_rule":[],
 48 |   "proxy_pass_rule":[],
 49 |   "summary_collect_rule":[],
 50 |   "summary_group_persistent_enable":true,
 51 |   "admin":[{
 52 |       "user":"verynginx",
 53 |       "password":"verynginx",
 54 |       "enable":true
 55 |     }],
 56 |   "dashboard_host":"",
 57 |   "response":{
 58 |     "hit":{
 59 |       "content_type":"text/html",
 60 |       "body":"hited"
 61 |     },
 62 |     "didn'thit":{
 63 |       "content_type":"text/html",
 64 |       "body":"didn't hited"
 65 |     }
 66 |   },
 67 |   "scheme_lock_enable":true,
 68 |   "uri_rewrite_rule":[],
 69 |   "cookie_prefix":"verynginx",
 70 |   "filter_enable":true,
 71 |   "browser_verify_enable":true,
 72 |   "scheme_lock_rule":[],
 73 |   "redirect_enable":true,
 74 |   "backend_upstream":{
 75 |   },
 76 |   "frequency_limit_enable":true,
 77 |   "matcher":{
 78 |     "useragent_equal":{
 79 |       "URI":{
 80 |         "value":"/test_useragent_equal",
 81 |         "operator":"="
 82 |       },
 83 |       "UserAgent":{
 84 |         "value":"vntestflag",
 85 |         "operator":"="
 86 |       }
 87 |     },
 88 |     "useragent_not_match":{
 89 |       "URI":{
 90 |         "value":"/test_useragent_not_match",
 91 |         "operator":"="
 92 |       },
 93 |       "UserAgent":{
 94 |         "value":".*test.*",
 95 |         "operator":"!≈"
 96 |       }
 97 |     },
 98 |     "useragent_existed":{
 99 |       "URI":{
100 |         "value":"/test_useragent_existed",
101 |         "operator":"="
102 |       },
103 |       "UserAgent":{
104 |         "operator":"Exist"
105 |       }
106 |     },
107 |     "useragent_not_existed":{
108 |       "URI":{
109 |         "value":"/test_useragent_not_existed",
110 |         "operator":"="
111 |       },
112 |       "UserAgent":{
113 |         "operator":"!Exist"
114 |       }
115 |     },
116 |     "all_request":{
117 |     },
118 |     "useragent_not_equal":{
119 |       "URI":{
120 |         "value":"/test_useragent_not_equal",
121 |         "operator":"="
122 |       },
123 |       "UserAgent":{
124 |         "value":"vntestflag",
125 |         "operator":"!="
126 |       }
127 |     },
128 |     "useragent_match":{
129 |       "URI":{
130 |         "value":"/test_useragent_match",
131 |         "operator":"="
132 |       },
133 |       "UserAgent":{
134 |         "value":".*test.*",
135 |         "operator":"≈"
136 |       }
137 |     }
138 |   },
139 |   "static_file_enable":true,
140 |   "summary_temporary_period":60,
141 |   "summary_group_temporary_enable":true,
142 |   "summary_with_host":false,
143 |   "frequency_limit_rule":[],
144 |   "uri_rewrite_enable":true,
145 |   "base_uri":"/verynginx",
146 |   "summary_request_enable":true,
147 |   "proxy_pass_enable":true,
148 |   "readonly":false,
149 |   "browser_verify_rule":[]
150 | }


--------------------------------------------------------------------------------
/test/testcase/a_5_matcher_refer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexazhou/VeryNginx/b66ef2ee6a108279653316323bd8ac026dd7ccd8/test/testcase/a_5_matcher_refer/__init__.py


--------------------------------------------------------------------------------
/test/testcase/a_5_matcher_refer/case.py:
--------------------------------------------------------------------------------
  1 | import base_case
  2 | import requests
  3 | import os
  4 | 
  5 | class Case(base_case.Base_Case):
  6 |     def __init__(self, *args, **kwargs):
  7 |         super(Case, self).__init__(*args, **kwargs)
  8 |         self.desc = "test matcher with multiform condition of referer"
  9 |         self.vn_conf_dir = os.path.dirname(os.path.abspath(__file__))
 10 | 
 11 |     def test_referer_equal(self): 
 12 |         r = requests.get('http://127.0.0.1/test_referer_equal',headers={'Referer':'vntestflag'})
 13 |         assert r.status_code == 400
 14 |         assert r.headers.get('content-type') == 'text/html'
 15 |         assert 'hited' in r.text
 16 |         self.check_ngx_stderr()
 17 | 
 18 |         r = requests.get('http://127.0.0.1/test_referer_equal',headers={'Referer':'vntestflag1'})
 19 |         assert r.status_code == 404
 20 |         assert r.headers.get('content-type') == 'text/html'
 21 |         assert 'hited' not in r.text
 22 |         self.check_ngx_stderr()
 23 | 
 24 |         r = requests.get('http://127.0.0.1/test_referer_equal',headers={'Referer':'otherflag'})
 25 |         assert r.status_code == 404
 26 |         assert r.headers.get('content-type') == 'text/html'
 27 |         assert 'hited' not in r.text
 28 |         self.check_ngx_stderr()
 29 |         
 30 |         r = requests.get('http://127.0.0.1/test_referer_equal',headers={'Referer':''})
 31 |         assert r.status_code == 404
 32 |         assert r.headers.get('content-type') == 'text/html'
 33 |         assert 'hited' not in r.text
 34 |         self.check_ngx_stderr()
 35 | 
 36 |         r = requests.get('http://127.0.0.1/test_referer_equal',headers={'Referer':None})
 37 |         assert r.status_code == 404
 38 |         assert r.headers.get('content-type') == 'text/html'
 39 |         assert 'hited' not in r.text
 40 |         self.check_ngx_stderr()
 41 | 
 42 |     def test_referer_not_equal(self): 
 43 |         r = requests.get('http://127.0.0.1/test_referer_not_equal',headers={'Referer':'otherflag'})
 44 |         assert r.status_code == 400
 45 |         assert r.headers.get('content-type') == 'text/html'
 46 |         assert 'hited' in r.text
 47 |         self.check_ngx_stderr()
 48 | 
 49 |         r = requests.get('http://127.0.0.1/test_referer_not_equal',headers={'Referer':'vntestflagz'})
 50 |         assert r.status_code == 400
 51 |         assert r.headers.get('content-type') == 'text/html'
 52 |         assert 'hited' in r.text
 53 |         self.check_ngx_stderr()
 54 | 
 55 |         r = requests.get('http://127.0.0.1/test_referer_not_equal',headers={'Referer':''})
 56 |         assert r.status_code == 400
 57 |         assert r.headers.get('content-type') == 'text/html'
 58 |         assert 'hited' in r.text
 59 |         self.check_ngx_stderr()
 60 | 
 61 |         r = requests.get('http://127.0.0.1/test_referer_not_equal',headers={'Referer':None})
 62 |         assert r.status_code == 400
 63 |         assert r.headers.get('content-type') == 'text/html'
 64 |         assert 'hited' in r.text
 65 |         self.check_ngx_stderr()
 66 | 
 67 |         r = requests.get('http://127.0.0.1/test_referer_not_equal',headers={'Referer':'vntestflag'})
 68 |         assert r.status_code == 404
 69 |         assert r.headers.get('content-type') == 'text/html'
 70 |         assert 'hited' not in r.text
 71 |         self.check_ngx_stderr()
 72 | 
 73 |     def test_referer_match(self): 
 74 |         r = requests.get('http://127.0.0.1/test_referer_match',headers={'Referer':'aaatestbbb'})
 75 |         assert r.status_code == 400
 76 |         assert r.headers.get('content-type') == 'text/html'
 77 |         assert 'hited' in r.text
 78 |         self.check_ngx_stderr()
 79 | 
 80 |         r = requests.get('http://127.0.0.1/test_referer_match',headers={'Referer':'aaaotherbbb'})
 81 |         assert r.status_code == 404
 82 |         assert r.headers.get('content-type') == 'text/html'
 83 |         assert 'hited' not in r.text
 84 |         self.check_ngx_stderr()
 85 | 
 86 |         r = requests.get('http://127.0.0.1/test_referer_match',headers={'Referer':'aaazestbbb'})
 87 |         assert r.status_code == 404
 88 |         assert r.headers.get('content-type') == 'text/html'
 89 |         assert 'hited' not in r.text
 90 |         self.check_ngx_stderr()
 91 | 
 92 |         r = requests.get('http://127.0.0.1/test_referer_match',headers={'Referer':''})
 93 |         assert r.status_code == 404
 94 |         assert r.headers.get('content-type') == 'text/html'
 95 |         assert 'hited' not in r.text
 96 |         self.check_ngx_stderr()
 97 | 
 98 |         r = requests.get('http://127.0.0.1/test_referer_match',headers={'Referer':None})
 99 |         assert r.status_code == 404
100 |         assert r.headers.get('content-type') == 'text/html'
101 |         assert 'hited' not in r.text
102 |         self.check_ngx_stderr()
103 | 
104 |     def test_referer_not_match(self): 
105 |         r = requests.get('http://127.0.0.1/test_referer_not_match',headers={'Referer':'aaaotherbbb'})
106 |         assert r.status_code == 400
107 |         assert r.headers.get('content-type') == 'text/html'
108 |         assert 'hited' in r.text
109 |         self.check_ngx_stderr()
110 | 
111 |         r = requests.get('http://127.0.0.1/test_referer_not_match',headers={'Referer':'aaazestbbb'})
112 |         assert r.status_code == 400
113 |         assert r.headers.get('content-type') == 'text/html'
114 |         assert 'hited' in r.text
115 |         self.check_ngx_stderr()
116 | 
117 |         r = requests.get('http://127.0.0.1/test_referer_not_match',headers={'Referer':''})
118 |         assert r.status_code == 400
119 |         assert r.headers.get('content-type') == 'text/html'
120 |         assert 'hited' in r.text
121 |         self.check_ngx_stderr()
122 | 
123 |         r = requests.get('http://127.0.0.1/test_referer_not_match',headers={'Referer':None})
124 |         assert r.status_code == 400
125 |         assert r.headers.get('content-type') == 'text/html'
126 |         assert 'hited' in r.text
127 |         self.check_ngx_stderr()
128 | 
129 |         r = requests.get('http://127.0.0.1/test_referer_not_match',headers={'Referer':'aaatestbbb'})
130 |         assert r.status_code == 404
131 |         assert r.headers.get('content-type') == 'text/html'
132 |         assert 'hited' not in r.text
133 |         self.check_ngx_stderr()
134 | 
135 |     def test_referer_existed(self): 
136 |         r = requests.get('http://127.0.0.1/test_referer_existed',headers={'Referer':'aaatestbbb'})
137 |         assert r.status_code == 400
138 |         assert r.headers.get('content-type') == 'text/html'
139 |         assert 'hited' in r.text
140 |         self.check_ngx_stderr()
141 | 
142 |         r = requests.get('http://127.0.0.1/test_referer_existed',headers={'Referer':'aaaotherbbb'})
143 |         assert r.status_code == 400
144 |         assert r.headers.get('content-type') == 'text/html'
145 |         assert 'hited' in r.text
146 |         self.check_ngx_stderr()
147 | 
148 |         r = requests.get('http://127.0.0.1/test_referer_existed',headers={'Referer':''})
149 |         assert r.status_code == 400
150 |         assert r.headers.get('content-type') == 'text/html'
151 |         assert 'hited' in r.text
152 |         self.check_ngx_stderr()
153 | 
154 |         r = requests.get('http://127.0.0.1/test_referer_existed',headers={'Referer':None})
155 |         assert r.status_code == 404
156 |         assert r.headers.get('content-type') == 'text/html'
157 |         assert 'hited' not in r.text
158 |         self.check_ngx_stderr()
159 | 
160 |     def test_referer_not_existed(self): 
161 |         r = requests.get('http://127.0.0.1/test_referer_not_existed',headers={'Referer':None})
162 |         assert r.status_code == 400
163 |         assert r.headers.get('content-type') == 'text/html'
164 |         assert 'hited' in r.text
165 |         self.check_ngx_stderr()
166 | 
167 |         r = requests.get('http://127.0.0.1/test_referer_not_existed',headers={'Referer':'aaatestbbb'})
168 |         assert r.status_code == 404
169 |         assert r.headers.get('content-type') == 'text/html'
170 |         assert 'hited' not in r.text
171 |         self.check_ngx_stderr()
172 | 
173 |         r = requests.get('http://127.0.0.1/test_referer_not_existed',headers={'Referer':'aaaotherbbb'})
174 |         assert r.status_code == 404
175 |         assert r.headers.get('content-type') == 'text/html'
176 |         assert 'hited' not in r.text
177 |         self.check_ngx_stderr()
178 |         
179 |         r = requests.get('http://127.0.0.1/test_referer_not_existed',headers={'Referer':''})
180 |         assert r.status_code == 404
181 |         assert r.headers.get('content-type') == 'text/html'
182 |         assert 'hited' not in r.text
183 |         self.check_ngx_stderr()
184 | 
185 | 
186 | 
187 | 
188 | 
189 | 
190 |     
191 | 


--------------------------------------------------------------------------------
/test/testcase/a_5_matcher_refer/config.json:
--------------------------------------------------------------------------------
  1 | {
  2 |   "redirect_rule":[],
  3 |   "filter_rule":[{
  4 |       "enable":true,
  5 |       "code":"400",
  6 |       "response":"hit",
  7 |       "matcher":"referer_equal",
  8 |       "custom_response":true,
  9 |       "action":"block"
 10 |     },{
 11 |       "enable":true,
 12 |       "code":"400",
 13 |       "response":"hit",
 14 |       "matcher":"referer_not_equal",
 15 |       "custom_response":true,
 16 |       "action":"block"
 17 |     },{
 18 |       "enable":true,
 19 |       "code":"400",
 20 |       "response":"hit",
 21 |       "matcher":"referer_match",
 22 |       "custom_response":true,
 23 |       "action":"block"
 24 |     },{
 25 |       "enable":true,
 26 |       "code":"400",
 27 |       "response":"hit",
 28 |       "matcher":"referer_not_match",
 29 |       "custom_response":true,
 30 |       "action":"block"
 31 |     },{
 32 |       "enable":true,
 33 |       "code":"400",
 34 |       "response":"hit",
 35 |       "matcher":"referer_existed",
 36 |       "custom_response":true,
 37 |       "action":"block"
 38 |     },{
 39 |       "enable":true,
 40 |       "code":"400",
 41 |       "response":"hit",
 42 |       "matcher":"referer_not_existed",
 43 |       "custom_response":true,
 44 |       "action":"block"
 45 |     }],
 46 |   "config_version":"0.36",
 47 |   "static_file_rule":[],
 48 |   "proxy_pass_rule":[],
 49 |   "summary_collect_rule":[],
 50 |   "summary_group_persistent_enable":true,
 51 |   "admin":[{
 52 |       "user":"verynginx",
 53 |       "password":"verynginx",
 54 |       "enable":true
 55 |     }],
 56 |   "dashboard_host":"",
 57 |   "response":{
 58 |     "hit":{
 59 |       "content_type":"text/html",
 60 |       "body":"hited"
 61 |     },
 62 |     "didn'thit":{
 63 |       "content_type":"text/html",
 64 |       "body":"didn't hited"
 65 |     }
 66 |   },
 67 |   "scheme_lock_enable":true,
 68 |   "uri_rewrite_rule":[],
 69 |   "cookie_prefix":"verynginx",
 70 |   "filter_enable":true,
 71 |   "browser_verify_enable":true,
 72 |   "scheme_lock_rule":[],
 73 |   "redirect_enable":true,
 74 |   "backend_upstream":{
 75 |   },
 76 |   "frequency_limit_enable":true,
 77 |   "matcher":{
 78 |     "referer_equal":{
 79 |       "URI":{
 80 |         "value":"/test_referer_equal",
 81 |         "operator":"="
 82 |       },
 83 |       "Referer":{
 84 |         "value":"vntestflag",
 85 |         "operator":"="
 86 |       }
 87 |     },
 88 |     "referer_not_existed":{
 89 |       "URI":{
 90 |         "value":"/test_referer_not_existed",
 91 |         "operator":"="
 92 |       },
 93 |       "Referer":{
 94 |         "operator":"!Exist"
 95 |       }
 96 |     },
 97 |     "referer_existed":{
 98 |       "URI":{
 99 |         "value":"/test_referer_existed",
100 |         "operator":"="
101 |       },
102 |       "Referer":{
103 |         "operator":"Exist"
104 |       }
105 |     },
106 |     "all_request":{
107 |     },
108 |     "referer_match":{
109 |       "URI":{
110 |         "value":"/test_referer_match",
111 |         "operator":"="
112 |       },
113 |       "Referer":{
114 |         "value":".*test.*",
115 |         "operator":"≈"
116 |       }
117 |     },
118 |     "referer_not_equal":{
119 |       "URI":{
120 |         "value":"/test_referer_not_equal",
121 |         "operator":"="
122 |       },
123 |       "Referer":{
124 |         "value":"vntestflag",
125 |         "operator":"!="
126 |       }
127 |     },
128 |     "referer_not_match":{
129 |       "URI":{
130 |         "value":"/test_referer_not_match",
131 |         "operator":"="
132 |       },
133 |       "Referer":{
134 |         "value":".*test.*",
135 |         "operator":"!≈"
136 |       }
137 |     }
138 |   },
139 |   "static_file_enable":true,
140 |   "summary_temporary_period":60,
141 |   "summary_group_temporary_enable":true,
142 |   "summary_with_host":false,
143 |   "frequency_limit_rule":[],
144 |   "uri_rewrite_enable":true,
145 |   "base_uri":"/verynginx",
146 |   "summary_request_enable":true,
147 |   "proxy_pass_enable":true,
148 |   "readonly":false,
149 |   "browser_verify_rule":[]
150 | }


--------------------------------------------------------------------------------
/verynginx/configs/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory 
2 | * 
3 | # Except this file !.gitignore 
4 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/css/bs_callout.css:
--------------------------------------------------------------------------------
 1 | .bs-callout {
 2 |     padding: 20px;
 3 |     margin: 20px 0;
 4 |     border: 1px solid #eee;
 5 |     border-left-width: 5px;
 6 |     border-radius: 3px;
 7 | }
 8 | .bs-callout h4 {
 9 |     margin-top: 0;
10 |     margin-bottom: 5px;
11 | }
12 | .bs-callout p:last-child {
13 |     margin-bottom: 0;
14 | }
15 | .bs-callout code {
16 |     border-radius: 3px;
17 | }
18 | .bs-callout+.bs-callout {
19 |     margin-top: -5px;
20 | }
21 | .bs-callout-default {
22 |     border-left-color: #777;
23 | }
24 | .bs-callout-default h4 {
25 |     color: #777;
26 | }
27 | .bs-callout-primary {
28 |     border-left-color: #428bca;
29 | }
30 | .bs-callout-primary h4 {
31 |     color: #428bca;
32 | }
33 | .bs-callout-success {
34 |     border-left-color: #5cb85c;
35 | }
36 | .bs-callout-success h4 {
37 |     color: #5cb85c;
38 | }
39 | .bs-callout-danger {
40 |     border-left-color: #d9534f;
41 | }
42 | .bs-callout-danger h4 {
43 |     color: #d9534f;
44 | }
45 | .bs-callout-warning {
46 |     border-left-color: #f0ad4e;
47 | }
48 | .bs-callout-warning h4 {
49 |     color: #f0ad4e;
50 | }
51 | .bs-callout-info {
52 |     border-left-color: #5bc0de;
53 | }
54 | .bs-callout-info h4 {
55 |     color: #5bc0de;
56 | }
57 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/css/pace.css:
--------------------------------------------------------------------------------
 1 | .pace {
 2 |   -webkit-pointer-events: none;
 3 |   pointer-events: none;
 4 | 
 5 |   -webkit-user-select: none;
 6 |   -moz-user-select: none;
 7 |   user-select: none;
 8 | }
 9 | 
10 | .pace-inactive {
11 |   display: none;
12 | }
13 | 
14 | .pace .pace-progress {
15 |   background: #29d;
16 |   position: fixed;
17 |   z-index: 2000;
18 |   top: 0;
19 |   right: 100%;
20 |   width: 100%;
21 |   height: 2px;
22 | }
23 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/css/tables.css:
--------------------------------------------------------------------------------
 1 | .table-bordered>tbody>tr>td,
 2 | .table-bordered>tbody>tr>th,
 3 | .table-bordered>tfoot>tr>td,
 4 | .table-bordered>tfoot>tr>th,
 5 | .table-bordered>thead>tr>td {
 6 |     border-bottom: none;
 7 |     border-right: none;
 8 | }
 9 | 
10 | #page_summary {
11 |     padding-left: 10px;
12 |     padding-right: 10px;
13 | }
14 | .dataTables_wrapper .dataTables_info {
15 |     color: #AAA;
16 | }
17 | 
18 | #summary_title {
19 |     font-size:1.5em;
20 |     font-weight:bold;
21 | }
22 | 
23 | #summary_type_note {
24 |     color:#AAA;
25 | }
26 | 
27 | #summary_action_bar {
28 |     margin-top:10px;
29 |     margin-bottom:10px;
30 | }
31 | 
32 | #summary_filter_label {
33 |     display:inline-block;
34 |     font-size:1.3em;
35 |     margin-right:10px;
36 | }
37 | 
38 | #summary_filter_input {
39 |     display:inline-block;
40 |     width:200px;
41 | }
42 | 
43 | #summary_control_label {
44 |     display:inline-block;
45 |     font-size:1.3em;
46 |     margin-right:10px;
47 | }
48 | 
49 | #summary_unmatched_table_filter {
50 |     display:none;
51 | }
52 | 
53 | #summary_matched_table_filter {
54 |     display:none;
55 | }
56 | 
57 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/css/webInterface.css:
--------------------------------------------------------------------------------
  1 | .btn_up,.btn_down{
  2 |     color:#337CB3;
  3 | }
  4 | .btn_edit{
  5 |     color:#337CB3;
  6 | }
  7 | .btn_del{
  8 |     color:#337CB3;
  9 | }
 10 | 
 11 | .leftnav {
 12 |     color:#777;
 13 | }
 14 | 
 15 | .leftnav:hover {
 16 |     color:#000;
 17 | }
 18 | 
 19 | .leftnav_1 {
 20 |     font-size:1.1em;
 21 |     padding-top:5px;
 22 |     padding-bottom:5px;
 23 | }
 24 | 
 25 | .leftnav_1.active {
 26 |     font-weight:bold;
 27 |     color:#333;
 28 | }
 29 | 
 30 | .leftnav_2 {
 31 |     padding-left:20px;
 32 |     color:#888;
 33 | }
 34 | 
 35 | .leftnav_2.active {
 36 |     font-weight:bold;
 37 |     border-left:solid;
 38 |     border-width:2px;
 39 |     border-color:#4078c0;
 40 |     color:#4078c0;
 41 | }
 42 | 
 43 | .config_title {
 44 |     font-size:2em;
 45 |     font-weight:bold;
 46 |     margin-bottom:10px;
 47 | }
 48 | 
 49 | .config_title_2 {
 50 |     font-size:1.2em;
 51 |     font-weight:bold;
 52 | 	margin-bottom:10px;
 53 | }
 54 | 
 55 | .config_sub_title {
 56 |     font-size:1.2em;
 57 |     font-weight:bold;
 58 | 	margin-top:15px;
 59 | 	margin-bottom:10px;
 60 | }
 61 | 
 62 | .config_table thead{
 63 |     font-weight:bold;
 64 | }
 65 | 
 66 | .config_table > tbody > tr > td:first-child{
 67 |     font-weight:bold;
 68 |     color:#666;
 69 | }
 70 | 
 71 | .config_matcher_block{
 72 |     border-width: 1px;
 73 | 	border-style:solid;
 74 |     border-radius: 5px;
 75 | 	padding-right:5px;	
 76 | 	margin-top:5px;
 77 | 	margin-bottom:5px;
 78 | 	margin-right:5px;
 79 | 	margin-left:5px;
 80 | 	border-color:#CCC;
 81 | 	background-color:#F5F5F5;
 82 |     position:relative;
 83 | 	display:inline-block;
 84 | }
 85 | 
 86 | .config_matcher_block_type{
 87 | 	padding-left:5px;
 88 | 	padding-right:3px;
 89 |     font-weight:bold;
 90 | 	display:inline-block;
 91 | }
 92 | 
 93 | .config_matcher_block_operator{
 94 |     font-weight:bold;
 95 | 	color:#999;
 96 | 	font-family:Times;
 97 | }
 98 | 
 99 | .config_matcher_block_value{
100 |     color: #555;
101 |     word-break: break-word;
102 | }
103 | 
104 | .config_matcher_block_btn_delete{
105 |     float:right;
106 | 	margin-left:5px;
107 | 	margin-top: 2px;
108 |     margin-right: 2px;
109 |     color: #666;
110 | }
111 | 
112 | .config_node_block_weight{
113 |     color: #888;
114 | }
115 | 
116 | .matched {
117 |     font-weight:bold;
118 |     color:#337AB7;
119 | }
120 | 
121 | .config_test_container {
122 |     padding-left:0;
123 |     padding-right:0;
124 |     margin-bottom:15px;
125 | }
126 | 
127 | .config_test_output {
128 |     font-size:1.4em;
129 | }
130 | 
131 | .config_test_sub_output {
132 |     font-size:1.2em;
133 |     color:#BBB;
134 | }
135 | 
136 | .config_form_line{
137 |     padding-top:5px;
138 | }
139 | 
140 | .config_card{
141 |     background-color:#F0F0F0;
142 |     border:solid;
143 | 	border-width:1px ;
144 | 	border-radius:3px;
145 | 	border-color:#CCC;
146 |     display:inline-block;
147 | 	margin-left:5px;
148 | 	margin-right:5px;
149 | 	padding-left:3px;
150 | 	padding-right:3px;
151 | }
152 | 
153 | .editing {
154 |     background-color:#EEE;
155 | }
156 | 
157 | .no_break {
158 |     white-space: nowrap;
159 | }
160 | 
161 | .monitor_container{
162 |     position:relative;
163 | }
164 | 
165 | .status_info_title{
166 |     font-size:1.2em;
167 | }
168 | 
169 | .status_info_value{
170 |     font-size:1.2em;
171 |     color:#AAA;
172 |     margin-left:20px;
173 |     font-weight:bold;
174 | }
175 | 
176 | .status_group_title {
177 |     font-size:1.5em;
178 |     font-weight: bold;
179 |     margin-bottom:12px;
180 |     display:inline-block;
181 | }
182 | 
183 | .status_group_title_desc {
184 |     font-weight:bold;
185 |     display:inline-block;
186 |     color:#BBB;
187 | }
188 | 
189 | .status_sub_group_title {
190 |     font-size:1.2em;
191 |     font-weight: bold;
192 |     margin-top:8px;
193 |     margin-bottom:8px;
194 |     display:inline-block;
195 | }
196 | 
197 | .status_sub_group_title_desc {
198 |     font-weight:bold;
199 |     color:#BBB;
200 |     display:inline-block;
201 | }
202 | 
203 | .status_color_mark {
204 |     width:14px;
205 |     height:14px;
206 |     display:inline-block;
207 |     border-radius:3px;
208 |     text-align:center;
209 | }
210 | 
211 | .status_color_desc {
212 |     color:#999;
213 | }
214 | 
215 | .vn_modal_mid {
216 |     width:400px ;
217 | }
218 | 
219 | .vn-config-box {
220 |     max-width:700px;
221 | 	margin-top:15px;
222 | }
223 | 
224 | .vn-config-label {
225 |     text-align:right;
226 | 	margin-top:5px;
227 | }
228 | 
229 | .tips_container {
230 |     margin-right:5px;
231 |     padding:5px;
232 |     border:solid;
233 |     border-color:lightgray;
234 |     border-width:1px;
235 |     border-radius:5px;
236 |     margin-bottom:20px;
237 | }
238 | 
239 | .tips_tips {
240 |     color:#BBB;
241 |     font-size:0.9em;
242 | }
243 | 
244 | .tips_title {
245 |     font-size:0.9em;
246 |     font-weight:bold;
247 | }
248 | 
249 | .tips_content {
250 |     font-size:0.8em;
251 |     font-color:gray;
252 | }
253 | 
254 | .tips_content ul {
255 |     padding-left:20px;
256 | }
257 | 
258 | #config_bottom_bar {
259 |     position:fixed;
260 | 	bottom:0px;
261 | 	background:rgba(0,0,0,0.6);
262 | 	color:#fff;
263 | 	width:100%;
264 | 	height:45px;
265 | }
266 | 
267 | #config_bottom_bar_message {
268 |     display:inline-block;
269 | 	font-size:1.2em;
270 | 	margin-left:10px;
271 | 	margin-top:10px;
272 | 	font-weight:lighter;
273 | }
274 | 
275 | #config_bottom_bar_btns {
276 |     display:inline-block;
277 | 	margin-right:20px;
278 | 	margin-top:7px;
279 | }
280 | 
281 | .bottom_bar_btn {
282 |     border-color:#fff;
283 | 	background-color:rgba(0,0,0,0);
284 | 	color:#fff;
285 | }
286 | 
287 | .bottom_bar_btn_save {
288 |     font-weight:bold;
289 | }
290 | 
291 | .notice{
292 |     font-size:1.2em;
293 |     border-style:solid;
294 |     border-left-width:3px;
295 |     border-right-width:1px;
296 |     border-top-width:1px;
297 |     border-bottom-width:1px;
298 |     padding:5px;
299 |     border-color:#ddd;
300 |     color:#999;
301 |     border-radius:3px;
302 |     margin-top:15px;
303 | }
304 | 
305 | .editing_rule_name{
306 | 	font-weight:bold;
307 | 	color:#333;
308 | }
309 | 
310 | #interface_login {
311 |     width:500px;
312 |     margin-left:auto;
313 |     margin-right:auto;
314 |     margin-top:50px;
315 | }
316 | 
317 | #about_title {
318 |     font-size:4em;
319 |     color:#CCC;
320 | }
321 | 
322 | #about_sub_title {
323 |     font-size:1.5em;
324 |     color:#333;
325 | }
326 | 
327 | #about_sub_btn_container {
328 |     text-align:center;
329 |     margin-top:30px
330 | }
331 | 
332 | #about_sub_btn {
333 |     float: none;
334 |     margin: 0 auto;
335 |     font-size: 1.8em;
336 |     color: #999;
337 |     font-weight: lighter;
338 |     border: solid;
339 |     border-width: 1px;
340 |     padding-top: 5px;
341 |     padding-bottom: 5px;
342 |     padding-left: 40px;
343 |     padding-right: 40px;
344 |     border-radius: 5px;
345 |     transition: all 0.5s;
346 | }
347 | 
348 | #about_sub_btn:hover {
349 |     color:#337AB7;
350 |     border-color:#337AB7;
351 |     text-decoration:none;
352 | }
353 | 
354 | .summary_url_table th { 
355 |     font-size: 14px;
356 |     font-weight: normal;
357 |     color: #777;
358 | }
359 | 
360 | .summary_url_table td {
361 |     word-break: break-word;
362 | }
363 | 
364 | .summary_sub_title {
365 |     font-weight: bold;
366 |     margin-bottom: 5px;
367 |     color: #555;
368 | }
369 | 
370 | .vn-has-error {
371 |     border-color: #a94442;
372 | }
373 | 
374 | .vn-has-error:focus {
375 |     border-color: #a94442;
376 | 	-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
377 |     box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483
378 | }
379 | 
380 | .vn_summary_detail_btn {
381 | 	border-color:#bbb;	
382 | 	color:#bbb;	
383 | 	background-color:#fff;	
384 | }
385 | 
386 | .vn_summary_detail_btn:hover {
387 | 	border-color:#4078c0;	
388 | 	color:#4078c0;	
389 | 	background-color:#fff;	
390 | }
391 | 
392 | .vn_summary_detail_popover_table {
393 | }
394 | 
395 | .vn_summary_detail_popover_table td {
396 |     padding-right:8px;
397 | }
398 | 
399 | .vn_summary_detail_popover_count {
400 |     color:#666;
401 | 	font-weight:bold;
402 | }
403 | 
404 | .vn_summary_detail_popover_rate {
405 |     color:#888;
406 | }
407 | 
408 | #config_modal_matcher_input_group .input_group_container{
409 | 	margin-top:10px;
410 | 	margin-bottom:10px;
411 | 	padding:5px;
412 | 	border:solid;
413 | 	border-width:1px;
414 | 	border-color:#4078c0;
415 | 	border-radius:10px;
416 | }
417 | 
418 | #config_modal_matcher_input_group .input_group_title{
419 | 	font-weight: bold;
420 | 	padding-left: 30px;
421 | 	padding-bottom: 5px;
422 | 	margin-top: -15px;
423 | 	color:#4078c0;
424 | }
425 | 
426 | #config_modal_matcher_input_group .input_item{
427 | 	margin-bottom:10px;
428 | }
429 | 
430 | #config_modal_matcher_input_group .input_item_title{
431 | 	font-weight:bold;
432 | 	text-align:right;
433 | }
434 | 
435 | #config_modal_matcher_input_group .input_item_inputer{
436 | }
437 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/config.js:
--------------------------------------------------------------------------------
  1 | var config = new Object();
  2 | 
  3 | Vue.config.debug = true;
  4 | config.config_vm = null; 
  5 | config.verynginx_config = {};
  6 | 
  7 | config.original_config_json = null;
  8 | 
  9 | //the reusable matcher condition template
 10 | config.vue_component_condition = Vue.extend({
 11 |     props : ['matcher_conditions','del_action'],
 12 |     template: '<template v-for="(condition_name,condition_value) in matcher_conditions | orderBy \'v\'">\
 13 |                    <div class="config_matcher_block">\
 14 |                        <span class="glyphicon glyphicon-remove config_matcher_block_btn_delete" v-if="(del_action != null)" onclick="{{del_action}}"></span>\
 15 |                        <span class="config_matcher_block_type">{{condition_name}}</span>\
 16 |                        <span class="config_matcher_block_name">\
 17 |                            <template v-if="(condition_value.name_operator != null)">[ name\
 18 |                                {{ condition_value.name_operator}}\
 19 |                                <template v-if="(condition_value.name_value != null)">\
 20 |                                    {{condition_value.name_value }}\
 21 |                                </template>\
 22 |                            ]</template>\
 23 |                        </span>\
 24 |                        <span class="config_matcher_block_operator">{{condition_value.operator | show_operator}}</span>\
 25 |                        <span class="config_matcher_block_value" >{{condition_value.value}}</span>\
 26 |                    </div>\
 27 |                </template>'
 28 | });
 29 | 
 30 | config.vue_upstream_node = Vue.extend({
 31 |     props : ['node','del_action'],
 32 |     template: '<template v-for="(node_name,node_value) in node ">\
 33 |               <div class="config_matcher_block">\
 34 |                   <span class="glyphicon glyphicon-remove config_matcher_block_btn_delete" v-if="(del_action != null)" onclick="{{del_action}}"></span>\
 35 |                   <span class="config_matcher_block_type">{{node_name}}</span>\
 36 |                   <span class="config_matcher_block_name">\
 37 |                       {{node_value.scheme}}://{{node_value.host}}<template v-if="(node_value.port.length != 0)">:{{node_value.port}};</template>\
 38 |                   </span>\
 39 |                   <span class="config_node_block_weight"> weight:{{node_value.weight}}</span>\
 40 |               </div>\
 41 |           </template>'
 42 | });
 43 | 
 44 | Vue.component('condition', config.vue_component_condition );
 45 | Vue.component('upstream', config.vue_upstream_node );
 46 | 
 47 | Vue.filter('show_operator', function (operator) {
 48 |     return operator;
 49 | });
 50 | 
 51 | config.config_changed = function(){
 52 |     
 53 |     if( config.config_vm == null )
 54 |         return false;
 55 |     
 56 |     var original_config = JSON.parse( config.original_config_json );
 57 |     var new_config = JSON.parse( config.config_vm.all_config_json );
 58 | 
 59 |     if( _.isEqual( original_config, new_config ) == true )
 60 |         return false;
 61 |     else
 62 |         return true;
 63 | }
 64 | 
 65 | config.refresh_bottom_bar = function(){
 66 | 
 67 |     if( config.config_changed() ){
 68 |         $('#config_bottom_div').show();
 69 |     }else{
 70 |         $('#config_bottom_div').hide();
 71 |     } 
 72 | };
 73 | 
 74 | config.reset_input_form = function(){
 75 |     util.reset_input_area('.config_form')
 76 | }
 77 | 
 78 | config.get_config = function(){
 79 |     $.get("./config",function(data,status){
 80 |         config.original_config_json = JSON.stringify( data , null, 2);
 81 |         config.verynginx_config = data; 
 82 | 
 83 |         if( config.config_vm != null ){
 84 |             config.config_vm.$set( 'config_now', data);
 85 |             dashboard.notify("Reread config success");
 86 |             return;
 87 |         }
 88 | 
 89 |         config.config_vm = new Vue({
 90 |             el: '#verynginx_config',
 91 |             data: {
 92 |                 'config_now':config.verynginx_config,
 93 |                 'editor':{}
 94 |             },
 95 |             computed : {
 96 |                 all_config_json: function(){
 97 |                     return JSON.stringify( this.config_now , null, 2);
 98 |                 }
 99 |             },
100 |             ready: function(){
101 |                 this.$nextTick( config.reset_input_form );
102 |             }
103 |         });
104 | 
105 |         config.config_vm.$watch('all_config_json',config.refresh_bottom_bar);
106 |     }); 
107 | }
108 | 
109 | 
110 | config.save_config = function(){
111 |     console.log("save_config");
112 |     var config_json = JSON.stringify( config.config_vm.$data['config_now'] , null, 2);
113 | 
114 |     //step 1, use encodeURIComponent to escape special char 
115 |     var config_json_escaped = window.encodeURIComponent( config_json );
116 |     //step 2, use base64 to encode data to avoid be blocked by verynginx args filter
117 |     var config_json_escaped_base64 = window.btoa( config_json_escaped );
118 | 
119 |     $.post("./config",{ config:config_json_escaped_base64 },function(data){
120 |         console.log(data);
121 |         if( data['ret'] == 'success' ){
122 |             config.original_config_json = config.config_vm.all_config_json;
123 |             config.refresh_bottom_bar();
124 |             dashboard.notify("Save config success.");
125 |         }else{
126 |             dashboard.show_notice( 'warning', "Save config failed [" + data['err'] + "].");
127 |         }
128 |     });
129 | }
130 | 
131 | //modify: give group, index ,value
132 | //delete: let value = null
133 | //add: let index == null 
134 | config.config_mod = function(rule_group_name,index,value){
135 | 
136 |     if( index == null && value == null ){
137 |         //is a error call
138 |         return;
139 |     }
140 |     
141 |     console.log('-->',rule_group_name,index,value);
142 |     if( value == null ){
143 |         if( typeof index == 'string' ){
144 |             Vue.delete( config.verynginx_config[rule_group_name], index );
145 |         }else{
146 |             config.verynginx_config[rule_group_name].splice( index, 1 );
147 |         }
148 |     }else{
149 |         if( index == undefined || index == null ){
150 |             config.verynginx_config[rule_group_name].push(value);
151 |         }else if( typeof index == 'string' ){
152 |             Vue.set( config.verynginx_config[rule_group_name], index ,value );
153 |         }else{
154 |             config.verynginx_config[rule_group_name].$set( index, value );
155 |         }
156 |     }
157 | }
158 | 
159 | 
160 | config.config_move_up = function(rule_group_name,index){
161 |     
162 |     if(index == 0){
163 |         dashboard.notify("The item already at the first");
164 |         return;
165 |     }
166 | 
167 |     var tmp = config.verynginx_config[rule_group_name][index-1];
168 |     config.verynginx_config[rule_group_name].$set(index-1, config.verynginx_config[rule_group_name][index]);
169 |     config.verynginx_config[rule_group_name].$set(index, tmp);
170 | }
171 | 
172 | config.config_move_down = function(rule_group_name,index){
173 |     if(index >= config.verynginx_config[rule_group_name].length - 1){
174 |         dashboard.notify("The item already at the bottom");
175 |         return;
176 |     }
177 |     
178 |     var tmp = config.verynginx_config[rule_group_name][index+1];
179 |     config.verynginx_config[rule_group_name].$set(index+1, config.verynginx_config[rule_group_name][index]);
180 |     config.verynginx_config[rule_group_name].$set(index, tmp);
181 | }
182 | 
183 | 
184 | config.edit_flag_set = function( group, flag ){
185 |     var config_group = config.verynginx_config[ group ];
186 |     config_group = JSON.parse( JSON.stringify(config_group) );
187 | 
188 |     if( flag != null ){
189 |         Object.defineProperty( config_group , "_editing", { value : flag, enumerable:false, writable:true });
190 |     }
191 |     //reset data to refresh the view
192 |     config.config_vm.$set( 'config_now.' + group, config_group );
193 | }
194 | 
195 | //set a rule to edit status and fill data of the rule into editor form
196 | //default: include_key == undefined
197 | config.config_edit_begin = function( rule_group_name, index, form_id, index_key_name ){
198 |     var config_group = config.verynginx_config[ rule_group_name ];
199 |     var data = util.clone( config_group[index] );
200 |     if( index_key_name != undefined ){
201 |         data[index_key_name] = index;
202 |     }
203 |     config.edit_flag_set( rule_group_name, index );
204 |     vnform.set_data( form_id, data );
205 | }
206 | 
207 | config.config_edit_save = function( rule_group_name, form_id , index_key_name ){
208 |     var editing = config.verynginx_config[rule_group_name]._editing;
209 |     var err_msg = vnform.verify_form( form_id ); 
210 |     if( err_msg != null ){
211 |         dashboard.show_notice('warning', err_msg );
212 |         return;
213 |     }
214 |     
215 |     var value = vnform.get_data( form_id );
216 |     if( editing != undefined ){
217 |         config.verynginx_config[rule_group_name]._editing = null;
218 |     }
219 | 
220 |     if( index_key_name != undefined){
221 |         editing = value[index_key_name];
222 |         delete value[index_key_name];
223 |     }
224 |     
225 |     config.config_mod( rule_group_name, editing, value );
226 |     config.edit_flag_set( rule_group_name, null );
227 |     vnform.reset( form_id ); 
228 | }
229 | 
230 | config.config_edit_cacel = function( rule_group_name, form_id ){
231 |     config.edit_flag_set( rule_group_name, null );
232 | 
233 |     if( form_id != undefined ){
234 |         vnform.reset( form_id ); 
235 |     }
236 | }
237 | 
238 | 
239 | //for matcher only
240 | config.config_matcher_delete_condition = function( matcher_name, condition_name ){
241 |     Vue.delete( config.verynginx_config['matcher'][matcher_name], condition_name  );
242 | }
243 | 
244 | //add the content of matcher editor to global config
245 | config.config_matcher_add = function(){
246 |     var matcher_name = matcher_editor.matcher_name();
247 | 
248 |     if( matcher_name == '' ){
249 |         dashboard.notify('Name of the matcher mush not be empty');
250 |         return;
251 |     }
252 |     
253 |     if( matcher_name.substring(0,1) == '_' ){
254 |         dashboard.notify('Name of the matcher must not started with "_"');
255 |         return;
256 |     }
257 | 
258 |     if( config.verynginx_config['matcher'][matcher_name] != null ){
259 |         dashboard.notify('Matcher [' + matcher_name + '] already existed');
260 |         return;
261 |     }
262 | 
263 |     Vue.set( config.verynginx_config['matcher'], matcher_name ,matcher_editor.tmp_conditions );
264 |     matcher_editor.clear();
265 | }
266 | 
267 | 
268 | 
269 | 
270 | config.test_match_factory = function( type ){
271 | 
272 |     var match_core = function(){
273 |     
274 |         var target_str = $(this).val();
275 |         var test_container = $(this).closest('.config_test_container'); 
276 |         var rule_table_id = test_container.attr('test_rule_table'); 
277 |         var rule_table = $('#' + rule_table_id); 
278 |         var test_args = eval(test_container.attr('test_args')); 
279 |         var test_output = test_container.find('.config_test_output');
280 |         var test_sub_output = test_container.find('.config_test_sub_output'); 
281 | 
282 |         var rows = rule_table.find('tbody > tr');
283 |         var matched_count = 0;
284 |     
285 |         test_output.text('');
286 |         test_sub_output.text('');
287 |         for( i=0; i<rows.length; i++ ){
288 |             $( rows[i] ).removeClass('matched');
289 |             
290 |             if( type == 're' ){
291 |                 var re_str = $($(rows[i]).children()[test_args[0]]).text();
292 |                 var re_obj = new RegExp(re_str, 'igm' );
293 |         
294 |                 if( target_str.match(re_obj) != null ){
295 |                     $( rows[i] ).addClass('matched');
296 |                     matched_count += 1;
297 |                 }
298 |             }
299 |             
300 |             if( type == 're_replace' ){
301 |                 var re_str = $($(rows[i]).children()[test_args[0]]).text();
302 |                 var replace_str = $($(rows[i]).children()[test_args[1]]).text();
303 |                 var re_obj = new RegExp(re_str, 'igm' );
304 |         
305 |                 if( target_str.match(re_obj) != null ){
306 |                     $( rows[i] ).addClass('matched');
307 |                     matched_count += 1;
308 | 
309 |                     if( test_sub_output.text() == ''){
310 |                         test_sub_output.text( 'will be redirect to: ' + target_str.replace( re_obj, replace_str ) );
311 |                     }
312 |                 }
313 |             }
314 | 
315 |             if( type == 'equal' ){
316 |                 var re_str = $($(rows[i]).children()[test_args[0]]).text();
317 |         
318 |                 if( target_str == re_str ){
319 |                     $( rows[i] ).addClass('matched');
320 |                     matched_count += 1;
321 |                 }
322 |             }
323 | 
324 |         }
325 |     
326 |         if( target_str == '' && matched_count == 0 ){
327 |             test_output.text('');
328 |         }else{
329 |             test_output.text( matched_count + ' rule matched ');
330 |         }
331 |     };
332 | 
333 |     return match_core;
334 | }
335 | 
336 | 
337 | 
338 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/dashboard.js:
--------------------------------------------------------------------------------
  1 | var dashboard = new Object();
  2 | 
  3 | dashboard.version = '0.3';
  4 | dashboard.disable_log = false;
  5 | dashboard.last_failed_jqxhr;
  6 | 
  7 | paceOptions = {
  8 |     catchupTime: 100,
  9 |     minTime: 200,
 10 |     restartOnRequestAfter: -1,
 11 |     ajax :{
 12 |         trackMethods: ['GET','POST'],
 13 |         ignoreURLs: ['./status']
 14 |     }
 15 | };
 16 | 
 17 | dashboard.init = function(){
 18 |     
 19 |     if( dashboard.disable_log == true ){
 20 |         window.console={log:function(){}};	
 21 |     }
 22 | 
 23 |     dashboard.switch_to_interface('login');
 24 |     $(".init_click").click();
 25 | 
 26 |     $(".btn").mouseup(function(){
 27 |         $(this).blur();
 28 |     });
 29 | 
 30 |     // Reposition when a modal is shown
 31 |     $('.modal').on('show.bs.modal', dashboard.modal_reposition);
 32 |     // Reposition when the window is resized
 33 |     $(window).on('resize', function() {
 34 |         $('.modal:visible').each(dashboard.modal_reposition );
 35 |     });
 36 | 
 37 |     if( localStorage.dashboard_status_enable_animation == undefined ){
 38 |         localStorage.dashboard_status_enable_animation = "true";
 39 |     }
 40 | 
 41 |     if( localStorage.dashboard_status_refresh_interval == undefined ){
 42 |         localStorage.dashboard_status_refresh_interval = '3';
 43 |     }
 44 | 
 45 |     //add event listener for input event on rule test form
 46 |     $(".config_test_container").each(function(){
 47 |         var test_action = eval( $(this).attr('test_action') ) ;
 48 |         var form_input = $(this).find(".config_test_input");
 49 |         
 50 |         form_input.on( 'input',test_action );
 51 |     });
 52 | 
 53 |     $( document ).ajaxError( dashboard.handle_ajax_error );
 54 | 
 55 |     //init the small vue vm at first
 56 |     matcher_editor.init();
 57 |     upstream_editor.init();
 58 |     window.onbeforeunload = dashboard.check_saved;
 59 |     
 60 |     dashboard.try_recover_login();
 61 | }
 62 | 
 63 | //if already login( with cookie saved ), let judge it and auto jump to dashboard 
 64 | dashboard.try_recover_login = function(){
 65 |      $.ajax({
 66 |         url: "./status",
 67 |         type: "get",
 68 |         success: dashboard.start,
 69 |         beforeSend: util.mark_ajax_slince
 70 |     });
 71 | }
 72 | 
 73 | dashboard.start = function(){
 74 |     dashboard.switch_to_interface('dashboard');
 75 |     config.get_config();
 76 |     dashboard.notify("Login Success");
 77 |     window.setTimeout( monitor.build_chart, 0 );
 78 |     window.setTimeout( monitor.start, 0 );
 79 | }
 80 | 
 81 | dashboard.login = function(){
 82 |     function login_success(data,status){
 83 |         var uri = document.location.pathname;
 84 |         var path = uri.substring(0, uri.lastIndexOf('/') );
 85 |             
 86 |         for( name in data['cookies']  ){
 87 |             $.cookie( name, data['cookies'][name],{ path: path} );
 88 |         }
 89 |         dashboard.start();
 90 |     }
 91 |     
 92 |     $.post("./login", data=vnform.get_data('login_form'),success=login_success);
 93 | }
 94 | 
 95 | dashboard.logout = function(){
 96 |     monitor.stop();
 97 |     $.cookie( 'verynginx_user', null,{ path: '/verynginx'} );
 98 |     $.cookie( 'verynginx_session', null, { path: '/verynginx'} );  
 99 |     location.reload(); 
100 | }
101 | 
102 | dashboard.check_saved = function(){
103 | 
104 |     if( config.config_changed() )
105 |         return "Configs hasn't been saved. If you leave, then the new configuration will be lost.";
106 | 
107 |     return null;
108 | }
109 | 
110 | dashboard.switch_to_interface = function( name ){
111 |     $(".interface").hide();
112 |     $("#interface_"+name).show();
113 | }
114 | 
115 | dashboard.switch_to_page = function( page ){
116 |     $(".page").hide();
117 |     $("#page_"+page).show();
118 | 
119 |     $(".topnav").removeClass("active");
120 |     $("#topnav_"+page).addClass("active");
121 | 
122 |     monitor.update_config();
123 |     
124 |     //if switch to summary page, make sure has a summary table
125 |     if( page == "summary" ){
126 |         data_stat.make_sure_have_table();
127 |     }else if( page == "status" ){
128 |         //generate a resize event to work around chart disapple bug of chart.js
129 |         window.setTimeout( function(){ 
130 |             window.dispatchEvent(new Event('resize')); 
131 |         }, 200);
132 |     }
133 | }
134 | 
135 | dashboard.switch_config_nav_group = function( item ){
136 | 
137 |     var group_name = $(item).attr("group");
138 |     $(".leftnav_group").hide();
139 |     $(".leftnav_1").removeClass('active');
140 |     $(item).addClass('active');
141 | 
142 |     var config_group_container = $(".leftnav_group[group=" + group_name + "]" );
143 |     config_group_container.show();
144 |     
145 |     //switch to first children config page
146 |     $(".leftnav_group[group=" + group_name + "]" ).children()[0].click();
147 | }
148 | 
149 | dashboard.switch_to_config = function( item ){
150 |     var config_name = $(item).attr("config_name");
151 |     $(".config_container").hide();
152 |     $("#config_" + config_name ).show();
153 |     
154 |     $(".leftnav_2").removeClass('active');
155 |     $(item).addClass('active');
156 | 
157 |     //show tips of the config 
158 |     tips.show_tips(config_name);
159 | }
160 | 
161 | dashboard.switch_tab_bar = function( group, tag ){
162 |     $(".nav [group=" + group + "]").removeClass('active');
163 |     $(".nav [group=" + group + "][tag=" + tag + "]").addClass('active');
164 | 
165 |     $(".nav_box[group=" + group + "]").hide();
166 |     $(".nav_box[group=" + group + "][tag=" + tag + "]").show();
167 | }
168 | 
169 | dashboard.nav_tab_click = function( item ){
170 |     var group = $(item).attr('group');
171 |     var tag = $(item).attr('tag');
172 |     
173 |     dashboard.switch_tab_bar( group, tag );
174 | }
175 | 
176 | 
177 | dashboard.show_notice = function( type, message ){
178 |     $.smkAlert({
179 |         text: message,
180 |         type: type,
181 |         position:"top-right",
182 |         time:5,
183 |     });
184 | }
185 | 
186 | dashboard.notify = function(message){
187 |     $.smkAlert({
188 |         text: message,
189 |         type: 'info',
190 |         position:"top-right",
191 |         time:5,
192 |     });
193 | }
194 | 
195 | dashboard.open_modal_dashboard_config = function(){
196 |     //load status dashboard config
197 |     $('#status_config_modal').modal('show');
198 | 
199 |     var enable_animation = localStorage.dashboard_status_enable_animation;
200 |     var refresh_interval = localStorage.dashboard_status_refresh_interval;
201 | 
202 |     if( enable_animation != undefined ){
203 |         if( enable_animation == "false" ){
204 |             enable_animation = false;
205 |         }else{
206 |             enable_animation = true;
207 |         }
208 |         $('#status_config_modal [name=enable_animation]')[0].checked = enable_animation;
209 |     }
210 |     
211 |     if( refresh_interval != undefined ){
212 |         $('#status_config_modal [name=refresh_interval]').val( refresh_interval );
213 |     }
214 | 
215 |     dashboard.status_dashboard_update_interval_label();
216 | }
217 | 
218 | dashboard.save_status_dashboard_config = function(){
219 |     
220 |     var enable_animation = $('#status_config_modal [name=enable_animation]')[0].checked;
221 |     var refresh_interval = $('#status_config_modal [name=refresh_interval]').val();
222 | 
223 |     localStorage.dashboard_status_enable_animation = enable_animation;
224 |     localStorage.dashboard_status_refresh_interval = refresh_interval;
225 | 
226 |     $('#status_config_modal').modal('hide');
227 |     monitor.update_config();
228 | }
229 | 
230 | dashboard.status_dashboard_update_interval_label = function(){
231 | 
232 |     var refresh_interval = $('#status_config_modal [name=refresh_interval]').val();
233 |     $('#status_config_modal [name=refresh_interval_label]').text(refresh_interval + "s");
234 | }
235 | 
236 | 
237 | dashboard.handle_ajax_error = function( e,jqxhr ) {
238 |     console.log('ajax_err_handle:',e,jqxhr);
239 |     var err = '';
240 |     dashboard.last_failed_jqxhr = jqxhr;
241 |     
242 |     if( jqxhr.slince == true ){
243 |         return;
244 |     }
245 | 
246 |     if( jqxhr.status != 0 ){
247 |         if( jqxhr.status == 400 && jqxhr.responseJSON != null) {
248 |             err = jqxhr.responseJSON['message'];
249 |         }else{
250 |             err = 'Ajax request failed[status code = ' + jqxhr.status + ']';
251 |         }
252 |     }else{
253 |         err = 'Ajax request failed[Network error]';
254 |     }
255 |     
256 |     dashboard.show_notice( 'warning', err);
257 | }
258 | 
259 | /**
260 |  * Vertically center Bootstrap 3 modals so they aren't always stuck at the top
261 |  */
262 | 
263 | dashboard.modal_reposition = function() {
264 |     var modal = $(this),
265 |     dialog = modal.find('.modal-dialog');
266 |     modal.css('display', 'block');
267 |     
268 |     // Dividing by two centers the modal exactly, but dividing by three 
269 |     // or four works better for larger screens.
270 |     dialog.css("margin-top", Math.max(0, ($(window).height() - dialog.height()) / 2));
271 | }
272 | 
273 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/data_stat.js:
--------------------------------------------------------------------------------
  1 | var data_stat = new Object();
  2 | 
  3 | data_stat.latest_data = null;
  4 | 
  5 | data_stat.url_table = null;
  6 | data_stat.collect_table = null;
  7 | 
  8 | data_stat.search = function( s ){
  9 |     if( data_stat.url_table != null ){
 10 |         data_stat.url_table.search( s ).draw();
 11 |     }
 12 |     
 13 |     if( data_stat.collect_table != null ){
 14 |         data_stat.collect_table.search( s ).draw();
 15 |     }
 16 | }
 17 | 
 18 | data_stat.current_group = function(){
 19 |     var group = null;
 20 |     if ($("#def_btn").text() == 'All') {
 21 |         group = 'persistent';
 22 |     } else {
 23 |         group = 'temporary';
 24 |     };
 25 |     
 26 |     return group;
 27 | }
 28 | 
 29 | data_stat.tab_switch = function ( item ) {
 30 |     // 标签切换文字变化
 31 |     if( $(item).attr('id') == 'summary_data_all' ){
 32 |         $("#def_btn").html("All<span class=\"caret\"></span>");
 33 |         $("#summary_type_note").css("display", "none");
 34 |     }else{
 35 |         $("#def_btn").html("Temporary<span class=\"caret\"></span>");
 36 |         $("#summary_type_note").css("display", "inline-block");
 37 |     } 
 38 |     
 39 |     data_stat.get_data();
 40 | }
 41 | 
 42 | data_stat.clear_data = function( ){
 43 |     var group = data_stat.current_group();
 44 |     $.post('./status/clear',data={group:group},function(){
 45 |         dashboard.show_notice( 'info', 'Clear data group [' + group + '] success' );
 46 |         data_stat.get_data();
 47 |     })
 48 | }
 49 | 
 50 | data_stat.make_sure_have_table = function(){
 51 |     if( data_stat.url_table == null || data_stat.collect_table == null ){
 52 |         data_stat.get_data();
 53 |     }
 54 | }
 55 | 
 56 | data_stat.fill_data_info_table = function( data_type, data_dict ){
 57 | 
 58 |     var table_id = null;
 59 |     if(data_type == 'collect'){
 60 |         table_id = 'matched_details';
 61 |     }else if( data_type == 'uri'){
 62 |         table_id = 'unmatched_details';
 63 |     }else{
 64 |         throw new Error("unknown data_type");
 65 |         return;
 66 |     }
 67 | 
 68 |     $('#' + table_id).html(""); // 动态生成表格前将表格清空
 69 | 
 70 |     var url_index = 1;
 71 |     for (var key in data_dict ) {
 72 |         var data_group = data_dict[key];
 73 | 
 74 |         // 计算访问成功率
 75 |         var success_count = 0;
 76 |         var total_count = 0;
 77 |         for( var status_code in data_group['status'] ){
 78 |             var this_status_count = data_group['status'][status_code];
 79 |             if( parseInt( status_code ) < 400 ){
 80 |                 success_count += this_status_count;
 81 |             }
 82 |             total_count += this_status_count;
 83 |         }
 84 |         
 85 |         var success_rate = (success_count / total_count) * 100;
 86 |         var count = parseInt(data_dict[key].count);
 87 |         var size = parseFloat(data_dict[key].size);
 88 |         var avg_size = size / count;
 89 |         var time = parseFloat(data_dict[key].time);
 90 |         var avg_time = time / count;
 91 | 
 92 |         // 动态增加每一列关于各URL/URI的详细访问信息
 93 |         var dyn_tab =  "<tr><td >" + url_index + "</td>" +
 94 |                        "<td>" + util.html_encode(key) + "</td>" +
 95 |                        "<td>" + count + "</td>" +
 96 |                        "<td>" + size + "</td>" +
 97 |                        "<td>" + avg_size.toFixed(2) + "</td>" +
 98 |                        "<td><nobr>" + success_rate.toFixed(2) + '% <button detail_type="' + data_type + '" detail_key="' + util.html_encode(key) + '" class="btn vn_summary_detail_btn btn-xs">Details</button> </nobr></td>' +
 99 |                        "<td>" + time.toFixed(3) + "</td>" +
100 |                        "<td>" + avg_time.toFixed(3) + "</td></tr>";
101 | 
102 |         $('#' + table_id).append(dyn_tab);
103 | 
104 |         url_index++; // 增加访问序列
105 |     }
106 | }
107 | 
108 | 
109 | data_stat.get_data = function () {
110 | 
111 |     var url_short = "./summary?type=short";
112 |     var url_long  = "./summary?type=long";
113 |     var data_url;
114 |     var group = data_stat.current_group();
115 | 
116 |     if( group == 'persistent' ){
117 |         data_url = url_long;
118 |     }else if( group == 'temporary' ){
119 |         data_url = url_short;
120 |     }else{
121 |         return;
122 |     }
123 |     
124 |     $.ajax({
125 |         type: "GET",
126 |         url: data_url,
127 |         // url: "/verynginx/summary?type=long",
128 |         data_Type: "json",
129 | 
130 |         success: function (json_data) {
131 |             data_stat.latest_data = json_data;
132 |             var data_uri = json_data['uri'];
133 |             var data_collect = json_data['collect'];
134 | 
135 |             data_stat.json_data = json_data;
136 | 
137 |             if( data_stat.url_table != null ){
138 |                 data_stat.url_table.clear().destroy();
139 |             }
140 |             
141 |             if( data_stat.collect_table != null ){
142 |                 data_stat.collect_table.clear().destroy();
143 |             }
144 |             
145 |             data_stat.fill_data_info_table( 'uri', data_uri );
146 |             data_stat.fill_data_info_table( 'collect', data_collect );
147 | 
148 |             // 添加表格排序
149 |             data_stat.url_table = $('#summary_unmatched_table').DataTable( {
150 |                                 autoWidth: false, // 设置表格自动适配宽度
151 |                                 scrollY:    "500px",
152 |                                 scrollCollapse: true,
153 |                                 paging: false, // 去掉页头页脚信息
154 |                                 "stripeClasses": [], // 去掉斑马色
155 |                                 renderer: true,
156 |                                 searching: true, // 增加过滤功能
157 |                                 "order": [[ 0, "asc" ]] // 载入时默认使用index升序排列
158 |                             } );
159 | 
160 |             data_stat.collect_table = $('#summary_matched_table').DataTable( {
161 |                                 autoWidth: false, // 设置表格自动适配宽度
162 |                                 scrollY:    "500px",
163 |                                 scrollCollapse: true,
164 |                                 paging: false, // 去掉页头页脚信息
165 |                                 "stripeClasses": [], // 去掉斑马色
166 |                                 renderer: true,
167 |                                 searching: true, // 增加过滤功能
168 |                                 "order": [[ 0, "asc" ]] // 载入时默认使用index升序排列
169 |                             } );
170 | 
171 |             $('#summary_unmatched_table tbody').unbind('mouseover');
172 |             $('#summary_unmatched_table tbody').unbind('mouseout');
173 |             $('#summary_matched_table tbody').unbind('mouseover');
174 |             $('#summary_matched_table tbody').unbind('mouseout');
175 | 
176 |             $('#summary_unmatched_table tbody').on('mouseover', data_stat.detail_btn_mouse_out   );
177 |             $('#summary_unmatched_table tbody').on('mouseout', data_stat.detail_btn_mouse_over  );
178 |             $('#summary_matched_table tbody').on('mouseover', data_stat.detail_btn_mouse_out   );
179 |             $('#summary_matched_table tbody').on('mouseout', data_stat.detail_btn_mouse_over  );
180 |         }
181 |     });   
182 | }
183 | 
184 | 
185 | data_stat.popover_item = null;
186 | 
187 | data_stat.clean_popover = function(){
188 |     if( data_stat.popover_item != null ){
189 |         data_stat.popover_item.popover('destroy');
190 |         data_stat.popover_item.popover_item = null;
191 |     }
192 | }
193 | 
194 | data_stat.detail_btn_mouse_over = function( e ){
195 |     var target = $(e.relatedTarget);
196 |     if( target.hasClass('vn_summary_detail_btn') == false )
197 |         return;
198 |    
199 |     data_stat.clean_popover();
200 | 
201 |     var detail_key = target.attr('detail_key');
202 |     var detail_type = target.attr('detail_type');
203 | 
204 |     var response_status = null;
205 |     var response_count = null;  
206 |     if( detail_type == 'uri' ){
207 |         response_status = data_stat.latest_data['uri'][detail_key]['status']; 
208 |         response_count = data_stat.latest_data['uri'][detail_key]['count'];  
209 |     }else{
210 |         response_status = data_stat.latest_data['collect'][detail_key]['status'];
211 |         response_count = data_stat.latest_data['collect'][detail_key]['count'];  
212 |     }
213 | 
214 |     var content = "<table class='vn_summary_detail_popover_table'>";
215 |     var status_list = Object.keys(response_status);
216 |     for( var i=0; i<status_list.length; i++ ){
217 |         var status_code = status_list[i];
218 |         var rate = (100 * response_status[status_code] / response_count).toFixed(2);
219 |         content += "<tr><td><span class='label label-info'>"+status_code+"</span></td>" + 
220 |                        "<td class='vn_summary_detail_popover_count'>" + response_status[status_code] + "</td>" + 
221 |                        "<td class='vn_summary_detail_popover_rate'>" + rate + "%</td></tr>";
222 |     }
223 | 
224 |     content += "</table>";
225 |     target.popover({
226 |         container:'#page_summary',
227 |         animation : false,
228 |         html : true,
229 |         placement : 'right', //placement of the popover. also can use top, bottom, left or right
230 |         title : 'Response Count', //this is the top title bar of the popover. add some basic css
231 |         content : content, //this is the content of the html box. add the image here or anything you want really.
232 |     })
233 |     target.popover('show');
234 |     data_stat.popover_item = target;
235 | }
236 | 
237 | data_stat.detail_btn_mouse_out = function( e ){
238 |     if( $(e.relatedTarget).hasClass('vn_summary_detail_btn') == true ){
239 |         data_stat.clean_popover();
240 |     }
241 | }
242 | 
243 | 
244 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/monitor.js:
--------------------------------------------------------------------------------
  1 | var monitor = new Object();
  2 | 
  3 | monitor.chart_size = 17;
  4 | 
  5 | monitor.refresh_timer = null;
  6 | 
  7 | monitor.chart_request = null;
  8 | monitor.chart_connection = null;
  9 | 
 10 | monitor.latest_status = null;
 11 | 
 12 | monitor.spin_list = [];
 13 | 
 14 | monitor.time_str = function(){
 15 |     var time_str = (new Date()).toTimeString();
 16 |     return time_str.split(' ')[0];
 17 | }
 18 | 
 19 | 
 20 | monitor.show_loading_img = function(){
 21 |     var default_opts = {
 22 |         length: 28
 23 |         ,radius: 32 // The radius of the inner circle
 24 |         ,width: 9 // The line thickness
 25 |         ,color: '#5190be' // #rgb or #rrggbb or array of colors
 26 |     }
 27 |     var sm_opts = {
 28 |         length: 28
 29 |         ,radius: 21 // The radius of the inner circle
 30 |         ,width: 6 // The line thickness
 31 |         ,color: '#5190be' // #rgb or #rrggbb or array of colors
 32 |     }
 33 |     
 34 |     var target = $('.monitor_container'); 
 35 |     for( var i=0; i<target.length; i++ ){
 36 |         var item = target[i];
 37 |         var size = $(item).attr('spin_size');
 38 |         var opts = default_opts;
 39 | 
 40 |         if( size == "sm" ){
 41 |             opts = sm_opts;  
 42 |         }
 43 |         var spinner = new Spinner( opts ).spin();
 44 |         item.appendChild(spinner.el);
 45 |         monitor.spin_list.push(spinner);
 46 |     } 
 47 | }
 48 | 
 49 | monitor.hide_loading_img = function(){
 50 |     for( var i=0; i<monitor.spin_list.length; i++ ){
 51 |         var spin = monitor.spin_list[i];
 52 |         spin.stop();
 53 |     } 
 54 | }
 55 | 
 56 | monitor.build_chart = function(){
 57 |     //request chart
 58 |     monitor.show_loading_img();
 59 |     var ctx_request = $("#chart_request").get(0).getContext("2d");
 60 |     var options_request={responsive:true};
 61 |     var data_request = {
 62 |         labels: [],
 63 |         datasets: [
 64 |             {
 65 |                 label: "all request",
 66 |                 fillColor: "rgba(220,220,220,0.2)",
 67 |                 strokeColor: "rgba(220,220,220,1)",
 68 |                 pointColor: "rgba(220,220,220,1)",
 69 |                 pointStrokeColor: "#fff",
 70 |                 pointHighlightFill: "#fff",
 71 |                 pointHighlightStroke: "rgba(220,220,220,1)",
 72 |                 data: []
 73 |             },
 74 |             {
 75 |                 label: "200 request",
 76 |                 fillColor: "rgba(151,187,205,0.2)",
 77 |                 strokeColor: "rgba(151,187,205,1)",
 78 |                 pointColor: "rgba(151,187,205,1)",
 79 |                 pointStrokeColor: "#fff",
 80 |                 pointHighlightFill: "#fff",
 81 |                 pointHighlightStroke: "rgba(151,187,205,1)",
 82 |                 data: []
 83 |             }
 84 |         ]
 85 |     };
 86 |     monitor.chart_request = new Chart(ctx_request).Line(data_request,options_request);
 87 |     
 88 |     //connection chart
 89 |     var ctx_connection = $("#chart_connection").get(0).getContext("2d");
 90 |     var options_connection={ responsive:true };
 91 |     var data_connection = {
 92 |         labels: [],
 93 |         datasets: [
 94 |             {
 95 |                 label: "connection",
 96 |                 fillColor: "rgba(220,220,220,0.2)",
 97 |                 strokeColor: "rgba(220,220,220,1)",
 98 |                 pointColor: "rgba(220,220,220,1)",
 99 |                 pointStrokeColor: "#fff",
100 |                 pointHighlightFill: "#fff",
101 |                 pointHighlightStroke: "rgba(220,220,220,1)",
102 |                 data: []
103 |             },
104 |             {
105 |                 label: "writing",
106 |                 fillColor: "rgba(151,187,205,0.2)",
107 |                 strokeColor: "rgba(151,187,205,1)",
108 |                 pointColor: "rgba(151,187,205,1)",
109 |                 pointStrokeColor: "#fff",
110 |                 pointHighlightFill: "#fff",
111 |                 pointHighlightStroke: "rgba(151,187,205,1)",
112 |                 data: []
113 |             },
114 |             {
115 |                 label: "reading",
116 |                 fillColor: "rgba(151,205,187,0.2)",
117 |                 strokeColor: "rgba(151,205,187,1)",
118 |                 pointColor: "rgba(151,205,187,1)",
119 |                 pointStrokeColor: "#fff",
120 |                 pointHighlightFill: "#fff",
121 |                 pointHighlightStroke: "rgba(151,205,187,1)",
122 |                 data: []
123 |             }
124 | 
125 | 
126 |         ]
127 |     };
128 |     monitor.chart_connection = new Chart(ctx_connection).Line(data_connection,options_connection);
129 | 
130 |     //response time chart
131 |     var ctx_response_time = $("#chart_response_time").get(0).getContext("2d");
132 |     var options_response_time={ responsive:true };
133 |     var data_response_time = {
134 |         labels: [],
135 |         datasets: [
136 |             {
137 |                 label: "response_time",
138 |                 fillColor: "rgba(220,220,220,0.2)",
139 |                 strokeColor: "rgba(220,220,220,1)",
140 |                 pointColor: "rgba(220,220,220,1)",
141 |                 pointStrokeColor: "#fff",
142 |                 pointHighlightFill: "#fff",
143 |                 pointHighlightStroke: "rgba(220,220,220,1)",
144 |                 data: []
145 |             },
146 |         ]
147 |     };
148 |     monitor.chart_response_time = new Chart(ctx_response_time).Line(data_response_time,options_response_time);
149 |     
150 |     //traffic
151 |     var ctx_traffic = $("#chart_traffic").get(0).getContext("2d");
152 |     var options_traffic={ responsive:true };
153 |     var data_traffic = {
154 |         labels: [],
155 |         datasets: [
156 |             {
157 |                 label: "traffic_read",
158 |                 fillColor: "rgba(220,220,220,0.2)",
159 |                 strokeColor: "rgba(220,220,220,1)",
160 |                 pointColor: "rgba(220,220,220,1)",
161 |                 pointStrokeColor: "#fff",
162 |                 pointHighlightFill: "#fff",
163 |                 pointHighlightStroke: "rgba(220,220,220,1)",
164 |                 data: []
165 |             },
166 |             {
167 |                 label: "traffic_write",
168 |                 fillColor: "rgba(151,187,205,0.2)",
169 |                 strokeColor: "rgba(151,187,205,1)",
170 |                 pointColor: "rgba(151,187,205,1)",
171 |                 pointStrokeColor: "#fff",
172 |                 pointHighlightFill: "#fff",
173 |                 pointHighlightStroke: "rgba(151,187,205,1)",
174 |                 data: []
175 |             }
176 | 
177 |         ]
178 |     };
179 | 
180 |     monitor.chart_traffic = new Chart(ctx_traffic).Line(data_traffic,options_traffic);
181 | 
182 |     //add visibilityChange event listener for different web browser
183 |     var hidden, state, visibilityChange; 
184 |     if (typeof document.hidden !== "undefined") {
185 |         hidden = "hidden";
186 |         visibilityChange = "visibilitychange";
187 |         state = "visibilityState";
188 |     } else if (typeof document.mozHidden !== "undefined") {
189 |         hidden = "mozHidden";
190 |         visibilityChange = "mozvisibilitychange";
191 |         state = "mozVisibilityState";
192 |     } else if (typeof document.msHidden !== "undefined") {
193 |         hidden = "msHidden";
194 |         visibilityChange = "msvisibilitychange";
195 |         state = "msVisibilityState";
196 |     } else if (typeof document.webkitHidden !== "undefined") {
197 |         hidden = "webkitHidden";
198 |         visibilityChange = "webkitvisibilitychange";
199 |         state = "webkitVisibilityState";
200 |     }
201 | 
202 |     var on_change = function(){
203 |         if( document[state] != hidden ){
204 |             console.log('on visiable');
205 |             if( localStorage.dashboard_status_enable_animation == "true" ){
206 |                 monitor.animation_enable();
207 |             }
208 |         }else{
209 |             console.log('on hidden');
210 |             monitor.animation_disable();
211 |         }
212 |     };
213 | 
214 |     // add event listener for visibilityChange
215 |     document.addEventListener( visibilityChange, on_change );
216 | 
217 | }
218 | 
219 | monitor.start = function(){
220 |    
221 |     var refresh_interval = 3;
222 |     if( localStorage.dashboard_status_refresh_interval != undefined ){
223 |         refresh_interval = parseInt( localStorage.dashboard_status_refresh_interval );
224 |     }
225 | 
226 |     if( monitor.refresh_timer != null ){
227 |         console.log("Error:Monitor is already running");
228 |         return;
229 |     }
230 | 
231 |     if( monitor.chart_request == null || monitor.chart_connection == null || monitor.chart_response_time == null || monitor.chart_traffic == null ){
232 |         console.log("Error:Monitor chart not init");
233 |         return;
234 |     }
235 | 
236 |     var enable_animation = localStorage.dashboard_status_enable_animation;
237 |     if( enable_animation == 'true' && $('#page_status').is(":visible") ){
238 |         monitor.animation_enable(); 
239 |     }else{
240 |         monitor.animation_disable();
241 |     }
242 |     
243 |     monitor.refresh_timer = window.setInterval( monitor.refresh , refresh_interval * 1000);
244 |     monitor.refresh();
245 | }
246 | 
247 | monitor.stop = function(){
248 |     if( monitor.refresh_timer != null ){
249 |         window.clearInterval( monitor.refresh_timer );
250 |         monitor.refresh_timer = null;
251 |     }
252 | }
253 | 
254 | monitor.animation_disable = function(){
255 |     monitor.chart_request.options['animation'] = false;
256 |     monitor.chart_connection.options['animation'] = false;
257 |     monitor.chart_response_time.options['animation'] = false;
258 |     monitor.chart_traffic.options['animation'] = false;
259 | }
260 | 
261 | monitor.animation_enable = function(){
262 |     monitor.chart_request.options['animation'] = true;
263 |     monitor.chart_connection.options['animation'] = true;
264 |     monitor.chart_response_time.options['animation'] = true;
265 |     monitor.chart_traffic.options['animation'] = true;
266 | }
267 | 
268 | monitor.refresh = function(){
269 |     //console.log("monitor refresh");
270 | 
271 |     $.get("./status",function(data,status){
272 |         if( status != 'success' ){
273 |             return;
274 |         }
275 | 
276 |         //console.log('status:',status);
277 |         //console.log('data:',data);
278 |         if( monitor.latest_status != null ){
279 |             var time_change = data['time'] - monitor.latest_status['time'];
280 |             //console.log('time_change',time_change);
281 |             if(time_change == 0 ){
282 |                 return;
283 |             }
284 | 
285 |             if( monitor.spin_list.length != 0 ){
286 |                 monitor.hide_loading_img();
287 |             } 
288 | 
289 |             var requests_all_change = data['request_all_count'] - monitor.latest_status['request_all_count']; 
290 |             var requests_success_change = data['request_success_count'] - monitor.latest_status['request_success_count'];
291 |             var connections_active = data['connections_active'];
292 |             var connections_reading = data['connections_reading'];
293 |             var connections_writing = data['connections_writing'];
294 |             var avg_request_all = requests_all_change / time_change;
295 |             var avg_request_success = requests_success_change / time_change;
296 |             var time_str = monitor.time_str();
297 |             var sub_label = '';
298 |             var response_time_change = data['response_time_total'] - monitor.latest_status['response_time_total'];
299 |             var avg_response_time = 0;
300 |             if( requests_all_change != 0 ){
301 |                 avg_response_time = 1000 * response_time_change / requests_all_change ;
302 |             }
303 |             
304 |             var traffic_read_change = data['traffic_read'] - monitor.latest_status['traffic_read'];
305 |             var traffic_write_change = data['traffic_write'] - monitor.latest_status['traffic_write'];
306 |             
307 |             var avg_traffic_read = traffic_read_change / (time_change*1024);
308 |             var avg_traffic_write = traffic_write_change / (time_change*1024);
309 |             
310 |             monitor.chart_request.addData([avg_request_all,avg_request_success], time_str);
311 |             monitor.chart_connection.addData( [ connections_active,connections_writing,connections_reading ], sub_label )
312 |             monitor.chart_response_time.addData( [ avg_response_time ], sub_label )
313 |             monitor.chart_traffic.addData( [ avg_traffic_read, avg_traffic_write ], sub_label )
314 | 
315 |             while( monitor.chart_request.datasets[0].points.length >= monitor.chart_size ){
316 |                 monitor.chart_request.removeData();
317 |             }
318 |             
319 |             while( monitor.chart_connection.datasets[0].points.length >= monitor.chart_size ){
320 |                 monitor.chart_connection.removeData();
321 |             }
322 |             
323 |             while( monitor.chart_response_time.datasets[0].points.length >= monitor.chart_size ){
324 |                 monitor.chart_response_time.removeData();
325 |             }
326 |             
327 |             while( monitor.chart_traffic.datasets[0].points.length >= monitor.chart_size ){
328 |                 monitor.chart_traffic.removeData();
329 |             }
330 |         }
331 |         monitor.latest_status = data;
332 |     });
333 | }
334 | 
335 | monitor.update_config =function(){
336 | 
337 |     console.log('monitor.save_config');
338 |     monitor.stop();
339 |     monitor.start();
340 | }
341 | 
342 | 
343 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/pace.min.js:
--------------------------------------------------------------------------------
1 | /*! pace 1.0.2 */
2 | (function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X=[].slice,Y={}.hasOwnProperty,Z=function(a,b){function c(){this.constructor=a}for(var d in b)Y.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},$=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};for(u={catchupTime:100,initialRate:.03,minTime:250,ghostTime:100,maxProgressPerFrame:20,easeFactor:1.25,startOnPageLoad:!0,restartOnPushState:!0,restartOnRequestAfter:500,target:"body",elements:{checkInterval:100,selectors:["body"]},eventLag:{minSamples:10,sampleCount:3,lagThreshold:3},ajax:{trackMethods:["GET"],trackWebSockets:!0,ignoreURLs:[]}},C=function(){var a;return null!=(a="undefined"!=typeof performance&&null!==performance&&"function"==typeof performance.now?performance.now():void 0)?a:+new Date},E=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame,t=window.cancelAnimationFrame||window.mozCancelAnimationFrame,null==E&&(E=function(a){return setTimeout(a,50)},t=function(a){return clearTimeout(a)}),G=function(a){var b,c;return b=C(),(c=function(){var d;return d=C()-b,d>=33?(b=C(),a(d,function(){return E(c)})):setTimeout(c,33-d)})()},F=function(){var a,b,c;return c=arguments[0],b=arguments[1],a=3<=arguments.length?X.call(arguments,2):[],"function"==typeof c[b]?c[b].apply(c,a):c[b]},v=function(){var a,b,c,d,e,f,g;for(b=arguments[0],d=2<=arguments.length?X.call(arguments,1):[],f=0,g=d.length;g>f;f++)if(c=d[f])for(a in c)Y.call(c,a)&&(e=c[a],null!=b[a]&&"object"==typeof b[a]&&null!=e&&"object"==typeof e?v(b[a],e):b[a]=e);return b},q=function(a){var b,c,d,e,f;for(c=b=0,e=0,f=a.length;f>e;e++)d=a[e],c+=Math.abs(d),b++;return c/b},x=function(a,b){var c,d,e;if(null==a&&(a="options"),null==b&&(b=!0),e=document.querySelector("[data-pace-"+a+"]")){if(c=e.getAttribute("data-pace-"+a),!b)return c;try{return JSON.parse(c)}catch(f){return d=f,"undefined"!=typeof console&&null!==console?console.error("Error parsing inline pace options",d):void 0}}},g=function(){function a(){}return a.prototype.on=function(a,b,c,d){var e;return null==d&&(d=!1),null==this.bindings&&(this.bindings={}),null==(e=this.bindings)[a]&&(e[a]=[]),this.bindings[a].push({handler:b,ctx:c,once:d})},a.prototype.once=function(a,b,c){return this.on(a,b,c,!0)},a.prototype.off=function(a,b){var c,d,e;if(null!=(null!=(d=this.bindings)?d[a]:void 0)){if(null==b)return delete this.bindings[a];for(c=0,e=[];c<this.bindings[a].length;)e.push(this.bindings[a][c].handler===b?this.bindings[a].splice(c,1):c++);return e}},a.prototype.trigger=function(){var a,b,c,d,e,f,g,h,i;if(c=arguments[0],a=2<=arguments.length?X.call(arguments,1):[],null!=(g=this.bindings)?g[c]:void 0){for(e=0,i=[];e<this.bindings[c].length;)h=this.bindings[c][e],d=h.handler,b=h.ctx,f=h.once,d.apply(null!=b?b:this,a),i.push(f?this.bindings[c].splice(e,1):e++);return i}},a}(),j=window.Pace||{},window.Pace=j,v(j,g.prototype),D=j.options=v({},u,window.paceOptions,x()),U=["ajax","document","eventLag","elements"],Q=0,S=U.length;S>Q;Q++)K=U[Q],D[K]===!0&&(D[K]=u[K]);i=function(a){function b(){return V=b.__super__.constructor.apply(this,arguments)}return Z(b,a),b}(Error),b=function(){function a(){this.progress=0}return a.prototype.getElement=function(){var a;if(null==this.el){if(a=document.querySelector(D.target),!a)throw new i;this.el=document.createElement("div"),this.el.className="pace pace-active",document.body.className=document.body.className.replace(/pace-done/g,""),document.body.className+=" pace-running",this.el.innerHTML='<div class="pace-progress">\n  <div class="pace-progress-inner"></div>\n</div>\n<div class="pace-activity"></div>',null!=a.firstChild?a.insertBefore(this.el,a.firstChild):a.appendChild(this.el)}return this.el},a.prototype.finish=function(){var a;return a=this.getElement(),a.className=a.className.replace("pace-active",""),a.className+=" pace-inactive",document.body.className=document.body.className.replace("pace-running",""),document.body.className+=" pace-done"},a.prototype.update=function(a){return this.progress=a,this.render()},a.prototype.destroy=function(){try{this.getElement().parentNode.removeChild(this.getElement())}catch(a){i=a}return this.el=void 0},a.prototype.render=function(){var a,b,c,d,e,f,g;if(null==document.querySelector(D.target))return!1;for(a=this.getElement(),d="translate3d("+this.progress+"%, 0, 0)",g=["webkitTransform","msTransform","transform"],e=0,f=g.length;f>e;e++)b=g[e],a.children[0].style[b]=d;return(!this.lastRenderedProgress||this.lastRenderedProgress|0!==this.progress|0)&&(a.children[0].setAttribute("data-progress-text",""+(0|this.progress)+"%"),this.progress>=100?c="99":(c=this.progress<10?"0":"",c+=0|this.progress),a.children[0].setAttribute("data-progress",""+c)),this.lastRenderedProgress=this.progress},a.prototype.done=function(){return this.progress>=100},a}(),h=function(){function a(){this.bindings={}}return a.prototype.trigger=function(a,b){var c,d,e,f,g;if(null!=this.bindings[a]){for(f=this.bindings[a],g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(c.call(this,b));return g}},a.prototype.on=function(a,b){var c;return null==(c=this.bindings)[a]&&(c[a]=[]),this.bindings[a].push(b)},a}(),P=window.XMLHttpRequest,O=window.XDomainRequest,N=window.WebSocket,w=function(a,b){var c,d,e;e=[];for(d in b.prototype)try{e.push(null==a[d]&&"function"!=typeof b[d]?"function"==typeof Object.defineProperty?Object.defineProperty(a,d,{get:function(){return b.prototype[d]},configurable:!0,enumerable:!0}):a[d]=b.prototype[d]:void 0)}catch(f){c=f}return e},A=[],j.ignore=function(){var a,b,c;return b=arguments[0],a=2<=arguments.length?X.call(arguments,1):[],A.unshift("ignore"),c=b.apply(null,a),A.shift(),c},j.track=function(){var a,b,c;return b=arguments[0],a=2<=arguments.length?X.call(arguments,1):[],A.unshift("track"),c=b.apply(null,a),A.shift(),c},J=function(a){var b;if(null==a&&(a="GET"),"track"===A[0])return"force";if(!A.length&&D.ajax){if("socket"===a&&D.ajax.trackWebSockets)return!0;if(b=a.toUpperCase(),$.call(D.ajax.trackMethods,b)>=0)return!0}return!1},k=function(a){function b(){var a,c=this;b.__super__.constructor.apply(this,arguments),a=function(a){var b;return b=a.open,a.open=function(d,e){return J(d)&&c.trigger("request",{type:d,url:e,request:a}),b.apply(a,arguments)}},window.XMLHttpRequest=function(b){var c;return c=new P(b),a(c),c};try{w(window.XMLHttpRequest,P)}catch(d){}if(null!=O){window.XDomainRequest=function(){var b;return b=new O,a(b),b};try{w(window.XDomainRequest,O)}catch(d){}}if(null!=N&&D.ajax.trackWebSockets){window.WebSocket=function(a,b){var d;return d=null!=b?new N(a,b):new N(a),J("socket")&&c.trigger("request",{type:"socket",url:a,protocols:b,request:d}),d};try{w(window.WebSocket,N)}catch(d){}}}return Z(b,a),b}(h),R=null,y=function(){return null==R&&(R=new k),R},I=function(a){var b,c,d,e;for(e=D.ajax.ignoreURLs,c=0,d=e.length;d>c;c++)if(b=e[c],"string"==typeof b){if(-1!==a.indexOf(b))return!0}else if(b.test(a))return!0;return!1},y().on("request",function(b){var c,d,e,f,g;return f=b.type,e=b.request,g=b.url,I(g)?void 0:j.running||D.restartOnRequestAfter===!1&&"force"!==J(f)?void 0:(d=arguments,c=D.restartOnRequestAfter||0,"boolean"==typeof c&&(c=0),setTimeout(function(){var b,c,g,h,i,k;if(b="socket"===f?e.readyState<2:0<(h=e.readyState)&&4>h){for(j.restart(),i=j.sources,k=[],c=0,g=i.length;g>c;c++){if(K=i[c],K instanceof a){K.watch.apply(K,d);break}k.push(void 0)}return k}},c))}),a=function(){function a(){var a=this;this.elements=[],y().on("request",function(){return a.watch.apply(a,arguments)})}return a.prototype.watch=function(a){var b,c,d,e;return d=a.type,b=a.request,e=a.url,I(e)?void 0:(c="socket"===d?new n(b):new o(b),this.elements.push(c))},a}(),o=function(){function a(a){var b,c,d,e,f,g,h=this;if(this.progress=0,null!=window.ProgressEvent)for(c=null,a.addEventListener("progress",function(a){return h.progress=a.lengthComputable?100*a.loaded/a.total:h.progress+(100-h.progress)/2},!1),g=["load","abort","timeout","error"],d=0,e=g.length;e>d;d++)b=g[d],a.addEventListener(b,function(){return h.progress=100},!1);else f=a.onreadystatechange,a.onreadystatechange=function(){var b;return 0===(b=a.readyState)||4===b?h.progress=100:3===a.readyState&&(h.progress=50),"function"==typeof f?f.apply(null,arguments):void 0}}return a}(),n=function(){function a(a){var b,c,d,e,f=this;for(this.progress=0,e=["error","open"],c=0,d=e.length;d>c;c++)b=e[c],a.addEventListener(b,function(){return f.progress=100},!1)}return a}(),d=function(){function a(a){var b,c,d,f;for(null==a&&(a={}),this.elements=[],null==a.selectors&&(a.selectors=[]),f=a.selectors,c=0,d=f.length;d>c;c++)b=f[c],this.elements.push(new e(b))}return a}(),e=function(){function a(a){this.selector=a,this.progress=0,this.check()}return a.prototype.check=function(){var a=this;return document.querySelector(this.selector)?this.done():setTimeout(function(){return a.check()},D.elements.checkInterval)},a.prototype.done=function(){return this.progress=100},a}(),c=function(){function a(){var a,b,c=this;this.progress=null!=(b=this.states[document.readyState])?b:100,a=document.onreadystatechange,document.onreadystatechange=function(){return null!=c.states[document.readyState]&&(c.progress=c.states[document.readyState]),"function"==typeof a?a.apply(null,arguments):void 0}}return a.prototype.states={loading:0,interactive:50,complete:100},a}(),f=function(){function a(){var a,b,c,d,e,f=this;this.progress=0,a=0,e=[],d=0,c=C(),b=setInterval(function(){var g;return g=C()-c-50,c=C(),e.push(g),e.length>D.eventLag.sampleCount&&e.shift(),a=q(e),++d>=D.eventLag.minSamples&&a<D.eventLag.lagThreshold?(f.progress=100,clearInterval(b)):f.progress=100*(3/(a+3))},50)}return a}(),m=function(){function a(a){this.source=a,this.last=this.sinceLastUpdate=0,this.rate=D.initialRate,this.catchup=0,this.progress=this.lastProgress=0,null!=this.source&&(this.progress=F(this.source,"progress"))}return a.prototype.tick=function(a,b){var c;return null==b&&(b=F(this.source,"progress")),b>=100&&(this.done=!0),b===this.last?this.sinceLastUpdate+=a:(this.sinceLastUpdate&&(this.rate=(b-this.last)/this.sinceLastUpdate),this.catchup=(b-this.progress)/D.catchupTime,this.sinceLastUpdate=0,this.last=b),b>this.progress&&(this.progress+=this.catchup*a),c=1-Math.pow(this.progress/100,D.easeFactor),this.progress+=c*this.rate*a,this.progress=Math.min(this.lastProgress+D.maxProgressPerFrame,this.progress),this.progress=Math.max(0,this.progress),this.progress=Math.min(100,this.progress),this.lastProgress=this.progress,this.progress},a}(),L=null,H=null,r=null,M=null,p=null,s=null,j.running=!1,z=function(){return D.restartOnPushState?j.restart():void 0},null!=window.history.pushState&&(T=window.history.pushState,window.history.pushState=function(){return z(),T.apply(window.history,arguments)}),null!=window.history.replaceState&&(W=window.history.replaceState,window.history.replaceState=function(){return z(),W.apply(window.history,arguments)}),l={ajax:a,elements:d,document:c,eventLag:f},(B=function(){var a,c,d,e,f,g,h,i;for(j.sources=L=[],g=["ajax","elements","document","eventLag"],c=0,e=g.length;e>c;c++)a=g[c],D[a]!==!1&&L.push(new l[a](D[a]));for(i=null!=(h=D.extraSources)?h:[],d=0,f=i.length;f>d;d++)K=i[d],L.push(new K(D));return j.bar=r=new b,H=[],M=new m})(),j.stop=function(){return j.trigger("stop"),j.running=!1,r.destroy(),s=!0,null!=p&&("function"==typeof t&&t(p),p=null),B()},j.restart=function(){return j.trigger("restart"),j.stop(),j.start()},j.go=function(){var a;return j.running=!0,r.render(),a=C(),s=!1,p=G(function(b,c){var d,e,f,g,h,i,k,l,n,o,p,q,t,u,v,w;for(l=100-r.progress,e=p=0,f=!0,i=q=0,u=L.length;u>q;i=++q)for(K=L[i],o=null!=H[i]?H[i]:H[i]=[],h=null!=(w=K.elements)?w:[K],k=t=0,v=h.length;v>t;k=++t)g=h[k],n=null!=o[k]?o[k]:o[k]=new m(g),f&=n.done,n.done||(e++,p+=n.tick(b));return d=p/e,r.update(M.tick(b,d)),r.done()||f||s?(r.update(100),j.trigger("done"),setTimeout(function(){return r.finish(),j.running=!1,j.trigger("hide")},Math.max(D.ghostTime,Math.max(D.minTime-(C()-a),0)))):c()})},j.start=function(a){v(D,a),j.running=!0;try{r.render()}catch(b){i=b}return document.querySelector(".pace")?(j.trigger("start"),j.go()):setTimeout(j.start,50)},"function"==typeof define&&define.amd?define(["pace"],function(){return j}):"object"==typeof exports?module.exports=j:D.startOnPageLoad&&j.start()}).call(this);
3 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/tips.js:
--------------------------------------------------------------------------------
 1 | var tips = new Object();
 2 | 
 3 | tips.tips_vm = null;
 4 | tips.show_tips = function(group){
 5 | 
 6 |     $('.tips_content').collapse('hide');
 7 | 
 8 |     if(tips.tips_vm != null){
 9 |         tips.tips_vm.$data = {tips:tips.data[group]};
10 |         return;
11 |     } 
12 | 
13 |     tips.tips_vm = new Vue({
14 |         el: '#verynginx_tips',
15 |         data: {tips:tips.data[group]},
16 |     });
17 | 
18 | }
19 | 
20 | tips.toggle = function(tips_container){
21 |     $(tips_container).children(':last').collapse('toggle');
22 | }
23 | 
24 | tips.data = {
25 |     'basic_matcher':[
26 |         {"tips":"Purpose","content":"A Matcher used to match request"},
27 |         {"tips":"Introduce","content":"When a request match all condition in a matcher, the request hit the matcher"},
28 |         {"tips":"Usage","content":["You can add one or more conditions to a matcher",
29 | 		                              "A empty matcher will match all request"]}
30 |     ],
31 |     'action_scheme_lock':[
32 |         {"tips":"Purpose","content":"Lock all request on http or https"},
33 |         {"tips":"Introduce","content":"This action will check if the scheme current using fit to the rule. If scheme wrong, it will give a 302 redirect to the right scheme" },
34 |         {"tips":"Usage","content":["https/http means only https/http,both means not limit",
35 |                                    "From top to bottom to match, and only use the first match rule"]
36 |         
37 |         },
38 |     ],
39 |     'action_redirect':[
40 |         {"tips":"Purpose","content":"Redirect to other address"},
41 |         {"tips":"Usage","content":["From top to bottom to match, and only use the first match rule"]}
42 |     ],
43 |     'filter_ipwhitelist':[
44 |         {"tips":"功能介绍","content":"IP白名单功能可以指定免过滤的IP"},
45 |         {"tips":"实现原理","content":"来自该列表中IP的访问请求将跳过过滤阶段"},
46 |         {"tips":"配置说明","content":"请填写完整的IP地址"}
47 |     ],
48 |     'filter_ip':[
49 |         {"tips":"功能介绍","content":"IP过滤功能可以拦截来自某些IP的所有访问"},
50 |         {"tips":"实现原理","content":"来自该列表中IP的访问请求将返回503"},
51 |         {"tips":"配置说明","content":"请填写完整的IP地址"}
52 |     ],
53 |     'filter_useragent':[
54 |         {"tips":"功能介绍","content":"UserAgent过滤功能可以拦截来自某些客户端访问"},
55 |         {"tips":"实现原理","content":"本功能在收到一个请求时,检查请求所携带的useragent是否和规则一致,如果一致则返回503禁止访问"},
56 |         {"tips":"配置说明","content":["参数UserAgent为一个正则表达式,用来匹配请求的UserAgent",
57 | 									  "规则匹配时不区分大小写,按照从上到下的顺序进行匹配,有一条规则匹配到即被拦截"]}
58 |     ],
59 |     'filter_uri':[
60 |         {"tips":"功能介绍","content":"URI过滤功能可以拦截对某些URI的访问请求"},
61 |         {"tips":"实现原理","content":"本功能在收到一个请求时,检查所请求的URI是否和规则一致,如果一致则返回503禁止访问"},
62 |         {"tips":"配置说明","content":["参数URI为一个正则表达式,用来匹配请求的URI",
63 | 		                              "URI和Nginx的变量URI一致,表示请求地址中域名之后的部分,不包含查询字符串",
64 | 									  "规则匹配时不区分大小写,按照从上到下的顺序进行匹配,有一条规则匹配到即被拦截"]}
65 |     ],
66 |     'filter_arg':[
67 |         {"tips":"功能介绍","content":"参数过滤功能可以拦截带有危险参数的访问请求"},
68 |         {"tips":"实现原理","content":"本功能在收到一个请求时,检查请求所携带的参数是否和规则一致,如果一致则返回503禁止访问"},
69 |         {"tips":"配置说明","content":["参数ARG为一个正则表达式,用来匹配请求所携带的参数值",
70 | 		                              "规则将检查GET和POST请求的每一个参数",
71 | 									  "规则匹配时不区分大小写,按照从上到下的顺序进行匹配,有一条规则匹配到即被拦截"]}
72 |     ],
73 |     'summarg_request':[
74 |         {"tips":"功能介绍","content":"访问统计功能可以统计各URI的访问情况"},
75 |     ],
76 |     
77 |     'system_allconfig':[
78 |         {"tips":"功能介绍","content":"可以在这里看到全部的配置情况"},
79 |         {"tips":"操作说明","content":["点击保存配置将保存全部配置到服务器,并即刻生效",
80 | 		                              "点击读取配置将从服务器获取当前使用的配置",
81 | 									  "配置保存在VeryNginx目录下的config.json文件。备份/删除 该文件可以 备份/恢复默认 设置"]}
82 |     ],
83 | 
84 | 
85 | }
86 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/upstream_editor.js:
--------------------------------------------------------------------------------
 1 | var upstream_editor = new Object();
 2 | 
 3 | upstream_editor.tmp_node_vm = null;
 4 | //upstream_editor.tmp_node = [];
 5 | 
 6 | upstream_editor.tmp_node = {};
 7 | 
 8 | upstream_editor.init = function(){
 9 |     
10 |     upstream_editor.tmp_node_vm = new Vue({
11 |         el: '#config_proxy_upstream_editor_node',
12 |         data: {
13 |             node:upstream_editor.tmp_node
14 |         },
15 |     });
16 | }
17 | 
18 | upstream_editor.tmp_node_delete = function( btn ){
19 |     
20 |     //console.log('tmp_conditions_delete:',btn);
21 |     var key = $(btn).parent().children('.config_matcher_block_type').text();
22 |     //console.log('key:',key);
23 | 
24 |     Vue.delete( upstream_editor.tmp_node, key );
25 | }
26 | 
27 | upstream_editor.get_data = function(){
28 |     var data = {};
29 |     data['name'] = $('#config_upstream_form [name=name]').val();
30 |     data['method'] = $('#config_upstream_form [name=method]').val();
31 |     data['node'] = upstream_editor.tmp_node;
32 | 
33 |     return data;
34 | }
35 | 
36 | upstream_editor.set_data = function( data ){
37 |     $('#config_upstream_form [name=name]').val( data['name'] );
38 |     $('#config_upstream_form [name=method]').val( data['method'] );
39 |     upstream_editor.tmp_node = data['node'];
40 |     upstream_editor.tmp_node_vm.$data = {node:upstream_editor.tmp_node};
41 | }
42 | 
43 | upstream_editor.modal_node_open = function(){
44 |     $('#config_modal_node').modal('show');
45 | }
46 | 
47 | upstream_editor.modal_node_save = function(){
48 |     var data = vnform.get_data('config_modal_node_form');
49 |     console.log( data );
50 |     var node_name = data['name'];
51 |     delete data['name'];
52 | 
53 |     //verify
54 |     var err_msg = vnform.verify_form( "config_modal_node_form" ); 
55 |     if( err_msg != null ){
56 |         dashboard.show_notice('warning', err_msg );
57 |         return;
58 |     }
59 | 
60 |     Vue.set(upstream_editor.tmp_node, node_name, data);
61 |     $('#config_modal_node').modal('hide');
62 |     upstream_editor.clean_modal();
63 | }
64 | 
65 | upstream_editor.reset = function(){
66 | 
67 |     console.log('upstream_editor.reset');
68 |     $('#config_upstream_form [name=name]').val('');
69 |     $('#config_upstream_form [name=method]').val('random');
70 |     
71 |     upstream_editor.tmp_node = {};
72 |     upstream_editor.tmp_node_vm.$data = {node:upstream_editor.tmp_node};
73 | }
74 | 
75 | upstream_editor.clean_modal = function(){
76 |     $('#config_modal_node input').val('');
77 | }
78 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/util.js:
--------------------------------------------------------------------------------
 1 | var util = new Object();
 2 | 
 3 | util.html_encode = function( value ){
 4 |   return $('<div/>').text(value).html();
 5 | }
 6 | 
 7 | util.html_decode = function( value ){
 8 |   return $('<div/>').html(value).text();
 9 | }
10 | 
11 | util.clone = function( data ){
12 |     return JSON.parse( JSON.stringify( data ) );
13 | }
14 | 
15 | util.sync_vue_model = function( selector ){
16 |     $( selector ).find( 'input,textarea,select' ).each( function(){
17 |         util.dispatchEvent( this,'change')
18 |     });
19 | }
20 | 
21 | util.reset_input_area = function( selector ){
22 |   //reset inpu
23 |   $( selector ).find('input[type="text"],textarea').each(function(){
24 |       $(this).val("");
25 |   });
26 |   
27 |   $( selector ).find('input[type="checkbox"]').each(function(){
28 |       this.checked = false;
29 |   });
30 | 
31 |   //reset select
32 |   $( selector ).find('select').each(function(){
33 |       $(this).prop('selectedIndex', 0);
34 |   });
35 | 
36 |   util.sync_vue_model( selector );
37 | };
38 | 
39 | util.dispatchEvent = function( element, event_name ){
40 |     if ("createEvent" in document) {
41 |         var evt = document.createEvent("HTMLEvents");
42 |         evt.initEvent( event_name , false, true);
43 |         element.dispatchEvent(evt);
44 |     }else{
45 |         element.fireEvent( "on" + event_name);
46 |     }
47 | }
48 | 
49 | util.mark_ajax_slince = function( request ){
50 |     request.slince = true;
51 | }
52 | 
53 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/verify.js:
--------------------------------------------------------------------------------
  1 | //提供对输入表单的校验
  2 | //符合返回 ''
  3 | //不符合返回错误原因描述字符串
  4 | 
  5 | var verify = new Object();
  6 | 
  7 | verify.unsigned_integer = function()
  8 | {
  9 |     var handle = function(v){
 10 |         
 11 |         if( parseInt(v) != v )
 12 |             return "must be integer";
 13 |         
 14 |         if(v.indexOf('-') ==0 )
 15 |             return "must be a positive integer";
 16 |         
 17 |         if( parseInt(v) <= 0 )
 18 |             return "must be a positive integer";
 19 |     }
 20 |     return handle;
 21 | }
 22 | 
 23 | //校验数值范围,需要是整数
 24 | verify.integer_in_range = function (min,max)
 25 | {
 26 |     var handle = function(v){
 27 |         
 28 |         if( parseInt(v) != v )
 29 |             return "The value must be integer";
 30 | 
 31 |         var err_msg = "The value must between " +min+ " and " + max;
 32 |         
 33 |         if( max != null && v > max  ){
 34 |             return err_msg;
 35 |         }
 36 |         
 37 |         if( min != null && v < min  ){
 38 |             return err_msg;
 39 |         }
 40 |         return null;    
 41 |     }
 42 | 
 43 |     return handle;
 44 | }
 45 | 
 46 | //校验数值范围,可以是小数
 47 | verify.floatRange = function (min,max)
 48 | {
 49 |     var handle = function(v){
 50 |         
 51 |         if( (v<min) && (v<max))
 52 |             return "必须在"+min+"和"+max+"之间";
 53 | 
 54 |         return "";
 55 |     }
 56 | 
 57 |     return handle;
 58 | }
 59 | 
 60 | 
 61 | //校验值是否在数值范围内,或者为0
 62 | verify.rangeOrZero = function (min,max)
 63 | {
 64 |     var handle = function(v) {
 65 |         
 66 |         var err_message = "必须为整数,在"+min+"和"+max+"之间,或者为0";
 67 | 
 68 |         if(v==0)
 69 |             return "";
 70 | 
 71 |         if(v.indexOf('.') >=0 )
 72 |             return err_message;
 73 | 
 74 |         if( ( min <= v ) && ( v <= max ) ){
 75 |             return "";
 76 |         }else{
 77 |             return err_message;
 78 |         }
 79 |     }
 80 | 
 81 |     return handle;
 82 | }
 83 | 
 84 | 
 85 | //verify length of the string, null means not limit
 86 | verify.str_len = function( min,max )
 87 | {
 88 |     var handle = function(v){
 89 |         if( min != null && v.length < min ){
 90 |             return "string mast more then " + min + " character";
 91 |         }
 92 |         
 93 |         if( max != null && v.length > max ){
 94 |             return "string mast less then " + max + " character";
 95 |         }
 96 |         return null;
 97 |     }
 98 | 
 99 |     return handle;
100 | }
101 | 
102 | verify.ip =function ()
103 | {
104 |     
105 |     var handle = function( ip ){
106 |         var re=/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
107 |         if(re.test(ip))     
108 |         {     
109 |            if( RegExp.$1<256 && RegExp.$2<256 && RegExp.$3<256 && RegExp.$4<256)   
110 |                return null;
111 |         }     
112 | 
113 |         return "The value isn't a valid IP";
114 |     }
115 | 
116 |     return handle;
117 | }
118 | 
119 | verify.port = function()
120 | {
121 |     return this.integer_in_range(0,65535);
122 | }
123 | 
124 | verify.port_or_blank = function(){
125 |     var handle = function( v ){
126 |         if( v == '')
127 |             return null;
128 | 
129 |         return verify.port()(v)
130 |     }
131 |     return handle;
132 | }
133 | 
134 | verify.url = function(){
135 |     var handle = function(v){
136 |         if( v.indexOf('https://') != 0 &&  v.indexOf('http://') != 0  ){
137 |             return 'URL should start with a "https://" or "http://" ';
138 |         }
139 |     }
140 |     return handle;
141 | }
142 | 
143 | verify.uri = function(){
144 |     var handle = function( v ){
145 |         if( v.length == 0  ){
146 |             return "The value require to contain at least one character";
147 |         }
148 | 
149 |         if( v.indexOf('/') != 0  ){
150 |             return "The value require to start with '/' ";
151 |         }
152 |         return null;
153 |     }
154 |     return handle;
155 | }
156 | 
157 | //base uri of VerifyNginx dashboard
158 | verify.base_uri = function(){
159 |     var handle = function(v){
160 |         var v_uri = verify.uri();
161 |         var v_uri_ret = v_uri(v);
162 |         
163 |         if( v_uri_ret != null)
164 |             return v_uri_ret;
165 |         
166 |         if( v.length > 1 && v.lastIndexOf('/')  == v.length - 1  ){
167 |             return "Base URI should not end with '/' ";
168 |         }
169 | 
170 |         return null;
171 |     }
172 |     return handle;
173 | }
174 | 
175 | //url or uri 
176 | verify.url_or_uri = function(){
177 |     var handle = function(v){
178 |         var v_uri = verify.uri();
179 |         var v_uri_ret = v_uri(v);
180 |         
181 |         var v_url = verify.url();
182 |         var v_url_ret = v_url(v);
183 | 
184 |         if( v_uri_ret != null && v_url_ret != null ){
185 |             return "The string is not a valid uri or url";
186 |         }
187 | 
188 |         return;
189 |     }
190 |     return handle;
191 | }
192 | 
193 | verify.path = verify.uri;
194 | 
195 | verify.ngx_time = function(){
196 |     var handle = function(v){
197 |         
198 |         if( v == '' ){
199 |             return "The value can't be empty string";
200 |         }
201 | 
202 |         if( v == 'epoch'){
203 |             return null;
204 |         }
205 | 
206 |         var map = 'smhd';//char can be used
207 |         var err_msg = 'The value must be "epoch" or end with the character in "' + map + '"';
208 |         
209 |         var last_char = v.substring( v.length - 1 );
210 |         if( map.indexOf( last_char ) < 0 ){
211 |             return err_msg;
212 |         }
213 |         
214 |         return null;
215 |     }
216 |     return handle;
217 | }
218 | 
219 | verify.host = function(){
220 |     var handle = function(v){
221 |         if( v == ''  ){
222 |             return "The value can't empty";
223 |         }
224 | 
225 |         if( v.indexOf(" ") >= 0 ){
226 |             return "The value can't include ' '";
227 |         }
228 | 
229 |         return null;
230 |     }
231 |     return handle;
232 | }
233 | 
234 | verify.host_or_empty = function(){
235 |     var handle = function(v){
236 |         if( v == '' ){
237 |             return null;
238 |         }
239 |         return verify.host()(v);
240 |     }
241 |     return handle;
242 | }
243 | 
244 | 
245 | 
246 | 


--------------------------------------------------------------------------------
/verynginx/dashboard/js/vnform.js:
--------------------------------------------------------------------------------
  1 | var vnform = new Object();
  2 | 
  3 | vnform.clean_all_err_mark = function(){
  4 |     $(".vn-has-error").removeClass('vn-has-error');
  5 | }
  6 | 
  7 | vnform.add_err_mark = function( item ){
  8 |     item.addClass("vn-has-error"); 
  9 | }
 10 | 
 11 | vnform.verify_input_with_notice = function( input_item ){
 12 |     vnform.clean_all_err_mark();
 13 |     var err_msg = vnform.verify_input( input_item );
 14 | 
 15 |     if( err_msg != null )
 16 |         dashboard.show_notice('warning',err_msg);
 17 | }
 18 | 
 19 | vnform.verify_input = function( input_item ){
 20 |     var item = $( input_item );
 21 |     var verifyer_str = item.attr('vn_data_verify')
 22 |     
 23 |     if( verifyer_str == undefined ){
 24 |         return null;
 25 |     }
 26 |     
 27 |     if( item.attr('type') == "checkbox" ){
 28 |         return null;
 29 |     }
 30 | 
 31 |     var verifyer = eval( verifyer_str );
 32 |     var value = item.val();
 33 | 
 34 |     var err_msg = verifyer( value );
 35 |     
 36 |     if( err_msg != null  ){
 37 |         vnform.add_err_mark( item );
 38 |         return err_msg;
 39 |     }
 40 | 
 41 |     return null;
 42 | }
 43 | 
 44 | vnform.verify_form = function( form_id ){
 45 |    console.log('verify form:',form_id);
 46 |    vnform.clean_all_err_mark();
 47 |    
 48 |    var inputs = $('#' + form_id).find("input,select,textarea");
 49 |    for( var i=0; i < inputs.length; i++ ){
 50 |         var err_msg = vnform.verify_input( inputs[i] );
 51 |         
 52 |         if( err_msg != null )
 53 |             return err_msg;
 54 |    }
 55 | 
 56 |    return null;
 57 | }
 58 | 
 59 | vnform.get_data = function( form_id ){
 60 |     
 61 |     var data = {}
 62 |     var form_obj = $('#'+form_id);
 63 |     var form_data_getter = form_obj.attr('vn_data_getter');
 64 |     if( form_data_getter != null ){
 65 |         data = eval( form_data_getter )( );
 66 |         return data;
 67 |     }
 68 | 
 69 |     var inputs = $('#' + form_id).find("input:visible,select:visible,textarea:visible");
 70 | 
 71 |     for( var i=0; i < inputs.length; i++ ){
 72 |         var item = $(inputs[i]);
 73 |         var name = item.attr('name');
 74 |         var tagName = item.prop('tagName').toLowerCase();
 75 |         if( tagName == "input" || tagName == 'textarea' ){
 76 | 
 77 |             if( item.attr('type') == "checkbox" ){
 78 |                 var config_group = item.attr('config_group');
 79 |                 if( config_group != null ){
 80 |                     if( data[config_group] == null ){
 81 |                         data[config_group] = [];
 82 |                     }
 83 | 
 84 |                     if( item.prop( "checked" ) ){
 85 |                         data[config_group].push( name );
 86 |                     }
 87 |                 }else{
 88 |                     if( item.prop( "checked" )){
 89 |                         data[name] = true;
 90 |                     }else{
 91 |                         data[name] = false;
 92 |                     }
 93 |                 }
 94 |             }else{
 95 |                 var getter = item.attr['vn_data_getter'];
 96 |                 var val = null;
 97 |                 if( getter != null){
 98 |                     val = eval( getter )();
 99 |                 }else{
100 |                     val = item.val();
101 |                 }
102 |                 data[name] = val;
103 |             }
104 | 
105 |         }else if( item.prop('tagName').toLowerCase() == "select" ){
106 |             data[ name ] = item.val();
107 |         }
108 |     }
109 | 
110 |     //console.log('output data:',data);
111 |     return data;
112 | }
113 | 
114 | vnform.set_data = function( form_id, data ){
115 | 
116 |     var form_obj = $('#'+form_id);
117 |     var form_data_setter = form_obj.attr('vn_data_setter');
118 |     if( form_data_setter != null ){
119 |         eval( form_data_setter )( data );
120 |         return;
121 |     }
122 |     
123 |     var inputs = $('#'+form_id).find("input,textarea,select");
124 |     for( var i=0; i < inputs.length; i++ ){
125 |         var item = $(inputs[i]);
126 |         var name = item.attr('name');
127 |         var tagName = item.prop('tagName').toLowerCase();
128 |         //console.log('process item',item)
129 |         //console.log('name',name)
130 |         if( tagName == "input" || tagName == 'textarea' ){
131 |             if( item.attr('type') == "checkbox" ){
132 |                 var config_group = item.attr('config_group');
133 |                 if( config_group != null && data[config_group] != null ){
134 |                     if(  data[config_group].indexOf( name ) >= 0 ){
135 |                         item.prop('checked',true);
136 |                     }else{
137 |                         item.prop('checked',false);
138 |                     }
139 |                 }else{
140 |                     if( data[name] == true ){
141 |                         item.prop('checked',true);
142 |                     }else{
143 |                         item.prop('checked',false);
144 |                     }
145 |                 }
146 |             }else{
147 |                 item.val( data[name] );
148 |             }
149 | 
150 |         }else if( item.prop('tagName').toLowerCase() == "select" ){
151 |              item.val( data[ item.attr('name') ] );
152 |         }
153 |     }
154 |     util.sync_vue_model( '#' + form_id );
155 | }
156 | 
157 | vnform.reset = function(form_id){
158 |     var form_obj = $('#'+form_id);
159 |     var form_data_resetter = form_obj.attr('vn_data_resetter');
160 |     if( form_data_resetter != null ){
161 |          eval( form_data_resetter )();
162 |          return;   
163 |     }
164 |     util.reset_input_area( '#' + form_id );    
165 | }
166 | 
167 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/backend_proxy.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2016-04-20 
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    : 
 5 | -- @Disc    : proxy_pass backend for verynginx
 6 | 
 7 | local _M = {}
 8 | 
 9 | local VeryNginxConfig = require "VeryNginxConfig"
10 | local request_tester = require "request_tester"
11 | local resolver = require "resty.dns.resolver"
12 | local math = require "math"
13 | local util = require "util"
14 | 
15 | math.randomseed(ngx.time()) 
16 | 
17 | 
18 | function _M.find_node( upstream )
19 |    
20 |     local node_list = upstream['node']
21 |     local balance_method = upstream['method']
22 |     local rate_sum = 0
23 |     
24 |     for name,node in pairs( node_list ) do
25 |         rate_sum = rate_sum + tonumber(node['weight']) 
26 |     end
27 |     --ngx.log( ngx.ERR, 'rate_sum',rate_sum)
28 |    
29 |     local p = nil
30 |     if balance_method == 'ip_hash' then
31 |         p =  math.fmod( ngx.crc32_short( ngx.var.remote_addr), rate_sum) + 1
32 |     else
33 |         p = math.random( rate_sum )
34 |     end
35 | 
36 |     --ngx.log( ngx.ERR, 'p',p)
37 | 
38 |     for name,node in pairs( node_list ) do
39 |         local tmp = tonumber(node['weight'])
40 |         if p <= tmp then
41 |             return node
42 |         else
43 |             p = p - tmp
44 |         end
45 |     end
46 | end
47 | 
48 | function _M.filter()
49 | 
50 |     if VeryNginxConfig.configs["proxy_pass_enable"] ~= true then
51 |         return
52 |     end
53 |     
54 |     local matcher_list = VeryNginxConfig.configs['matcher']
55 |     local upstream_list = VeryNginxConfig.configs['backend_upstream']
56 | 
57 |     for i, rule in ipairs( VeryNginxConfig.configs["proxy_pass_rule"] ) do
58 |         local enable = rule['enable']
59 |         local matcher = matcher_list[ rule['matcher'] ] 
60 |         if enable == true and request_tester.test( matcher ) == true then
61 |             --ngx.log(ngx.STDERR,'upstream:',rule['upstream'])
62 | 
63 |             local upstream = upstream_list[ rule['upstream'] ]
64 |             local node = _M.find_node( upstream )
65 |             
66 |             ngx.var.vn_proxy_scheme = node['scheme']
67 |             ngx.var.vn_proxy_host = node['host']
68 |             
69 |             if node['port'] == '' then
70 |                 if node['scheme'] == 'http' then
71 |                     ngx.var.vn_proxy_port = 80
72 |                 elseif node['scheme'] == 'https' then 
73 |                     ngx.var.vn_proxy_port = 443
74 |                 end
75 |             else
76 |                 ngx.var.vn_proxy_port = tonumber( node['port'] )
77 |             end
78 |             
79 |             --set vn_header_host
80 |             if rule['proxy_host'] ~= '' then
81 |                 ngx.var.vn_header_host = rule['proxy_host']
82 |             else
83 |                 ngx.var.vn_header_host = ngx.var.host
84 |             end
85 | 
86 |             util.ngx_ctx_dump() 
87 |             ngx.var.vn_exec_flag = '1' --use the var as a mark, so that lua can know that's a inside redirect
88 |             return ngx.exec('@vn_proxy')  --will jump out at the exec, so the return not run in fact
89 |         end
90 |     end
91 | end
92 | 
93 | return _M
94 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/backend_static.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2016-04-20 
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    : 
 5 | -- @Disc    : static backend for verynginx
 6 | 
 7 | local _M = {}
 8 | 
 9 | local VeryNginxConfig = require "VeryNginxConfig"
10 | local request_tester = require "request_tester"
11 | local util = require "util"
12 | 
13 | function _M.filter()
14 |     
15 |     if VeryNginxConfig.configs["static_file_enable"] ~= true then
16 |         return
17 |     end
18 |     
19 |     local matcher_list = VeryNginxConfig.configs['matcher']
20 |     for i, rule in ipairs( VeryNginxConfig.configs["static_file_rule"] ) do
21 |         local enable = rule['enable']
22 |         local matcher = matcher_list[ rule['matcher'] ] 
23 |         if enable == true and request_tester.test( matcher ) == true then
24 |             ngx.var.vn_static_root = rule['root']
25 |             ngx.var.vn_static_expires = rule['expires']
26 |             ngx.var.vn_exec_flag = '1'-- use the var as a mark, so that lua can know that's a inside redirect
27 |             util.ngx_ctx_dump() 
28 |             return ngx.exec('@vn_static') -- will jump out at the exec 
29 |         end
30 |     end
31 | end
32 | 
33 | return _M
34 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/browser_verify.lua:
--------------------------------------------------------------------------------
  1 | -- -*- coding: utf-8 -*-
  2 | -- @Date    : 2016-02-24 
  3 | -- @Author  : Alexa (AlexaZhou@163.com)
  4 | -- @Link    : 
  5 | -- @Disc    : verify that the remote client is a browser
  6 | 
  7 | local _M = {}
  8 | 
  9 | local VeryNginxConfig = require "VeryNginxConfig"
 10 | local request_tester = require "request_tester"
 11 | local encrypt_seed = require "encrypt_seed"
 12 | local util = require "util"
 13 | 
 14 | _M.verify_javascript_html = nil
 15 | 
 16 | function _M.sign( mark )
 17 |     local ua = ngx.var.http_user_agent
 18 |     local forwarded  = ngx.var.http_x_forwarded_for
 19 |     
 20 |     if ua == nil then
 21 |         ua = ''
 22 |     end
 23 |     
 24 |     if forwarded == nil then
 25 |         forwarded = ''
 26 |     end
 27 | 
 28 |     local sign = ngx.md5( 'VN' .. ngx.var.remote_addr .. forwarded .. ua .. mark .. encrypt_seed.get_seed() )
 29 |     return sign 
 30 | end
 31 | 
 32 | function _M.verify_cookie()
 33 |     local sign = _M.sign('cookie')
 34 |     if ngx.var.http_cookie ~= nil then
 35 |         if string.find( ngx.var.http_cookie , sign) ~= nil then
 36 |             return
 37 |         end
 38 |     end
 39 | 
 40 |     local cookie_prefix = VeryNginxConfig.configs['cookie_prefix']
 41 |     ngx.header["Set-Cookie"] =  cookie_prefix .. "_sign_cookie=" .. sign .. '; path=/' 
 42 |     
 43 |     if ngx.var.args ~= nil then
 44 |         ngx.redirect( ngx.var.scheme.."://"..ngx.var.http_host..ngx.var.uri.."?"..ngx.var.query_string , ngx.HTTP_MOVED_TEMPORARILY)
 45 |     else
 46 |         ngx.redirect( ngx.var.scheme.."://"..ngx.var.http_host..ngx.var.uri , ngx.HTTP_MOVED_TEMPORARILY)
 47 |     end
 48 | end
 49 | 
 50 | function _M.verify_javascript()
 51 |     local sign = _M.sign('javascript')
 52 |     if ngx.var.http_cookie ~= nil then
 53 |         if string.find( ngx.var.http_cookie , sign) ~= nil then
 54 |             return
 55 |         end
 56 |     end
 57 |     
 58 |     if _M.verify_javascript_html == nil then
 59 |         local path = VeryNginxConfig.home_path() .."/support/verify_javascript.html"
 60 |         f = io.open( path, 'r' )
 61 |         if f ~= nil then
 62 |             _M.verify_javascript_html = f:read("*all")
 63 |             f:close()
 64 |         end
 65 |     end
 66 |     
 67 |     local cookie_prefix = VeryNginxConfig.configs['cookie_prefix']
 68 | 
 69 |     local redirect_to = nil
 70 |     local html = _M.verify_javascript_html
 71 | 
 72 |     html = string.gsub( html,'INFOCOOKIE',sign )
 73 |     html = string.gsub( html,'COOKIEPREFIX',cookie_prefix )
 74 | 
 75 |     if ngx.var.args ~= nil then
 76 | 		redirect_to =  ngx.var.scheme.."://"..ngx.var.http_host..ngx.var.uri.."?"..ngx.var.args , ngx.HTTP_MOVED_TEMPORARILY
 77 | 	else
 78 | 		redirect_to =  ngx.var.scheme.."://"..ngx.var.http_host..ngx.var.uri , ngx.HTTP_MOVED_TEMPORARILY
 79 | 	end
 80 | 
 81 |     html = util.string_replace( html,'INFOURI',redirect_to, 1 )
 82 |     
 83 |     ngx.header.content_type = "text/html"
 84 |     ngx.header['cache-control'] = "no-cache, no-store, must-revalidate"
 85 |     ngx.header['pragma'] = "no-cache"
 86 |     ngx.header['expires'] = "0"
 87 |     ngx.header.charset = "utf-8"
 88 |     ngx.say( html )
 89 |     
 90 |     ngx.exit(200)
 91 | end
 92 | 
 93 | function _M.filter()
 94 |     if VeryNginxConfig.configs["browser_verify_enable"] ~= true then
 95 |         return
 96 |     end
 97 |     
 98 |     local matcher_list = VeryNginxConfig.configs['matcher']
 99 |     for i,rule in ipairs( VeryNginxConfig.configs["browser_verify_rule"] ) do
100 |         local enable = rule['enable']
101 |         local matcher = matcher_list[ rule['matcher'] ] 
102 |         if enable == true and request_tester.test( matcher ) == true then
103 |             local verify_cookie,verify_javascript = false,false
104 |             
105 |             for idx,verify_type in ipairs( rule['type']) do
106 |                 if verify_type == 'cookie' then
107 |                     verify_cookie = true
108 |                 elseif verify_type == 'javascript' then
109 |                     verify_javascript = true
110 |                 end
111 |             end
112 | 
113 |             if verify_cookie == true then
114 |                 _M.verify_cookie()
115 |             end
116 |             
117 |             if verify_javascript == true then
118 |                 _M.verify_javascript()
119 |             end
120 | 
121 |             return
122 |         end
123 |     end
124 | end
125 | 
126 | return _M
127 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/cookie.lua:
--------------------------------------------------------------------------------
  1 | -- Copyright (C) 2013 Jiale Zhi (calio), Cloudflare Inc.
  2 | -- See RFC6265 http://tools.ietf.org/search/rfc6265
  3 | -- require "luacov"
  4 | 
  5 | local type          = type
  6 | local byte          = string.byte
  7 | local sub           = string.sub
  8 | local format        = string.format
  9 | local log           = ngx.log
 10 | local ERR           = ngx.ERR
 11 | local ngx_header    = ngx.header
 12 | 
 13 | local EQUAL         = byte("=")
 14 | local SEMICOLON     = byte(";")
 15 | local SPACE         = byte(" ")
 16 | local HTAB          = byte("\t")
 17 | 
 18 | -- table.new(narr, nrec)
 19 | local ok, new_tab = pcall(require, "table.new")
 20 | if not ok then
 21 |     new_tab = function () return {} end
 22 | end
 23 | 
 24 | local ok, clear_tab = pcall(require, "table.clear")
 25 | if not ok then
 26 |     clear_tab = function(tab) for k, _ in pairs(tab) do tab[k] = nil end end
 27 | end
 28 | 
 29 | local _M = new_tab(0, 2)
 30 | 
 31 | _M._VERSION = '0.01'
 32 | 
 33 | 
 34 | local function get_cookie_table(text_cookie)
 35 |     if type(text_cookie) ~= "string" then
 36 |         log(ERR, format("expect text_cookie to be \"string\" but found %s",
 37 |                 type(text_cookie)))
 38 |         return {}
 39 |     end
 40 | 
 41 |     local EXPECT_KEY    = 1
 42 |     local EXPECT_VALUE  = 2
 43 |     local EXPECT_SP     = 3
 44 | 
 45 |     local n = 0
 46 |     local len = #text_cookie
 47 | 
 48 |     for i=1, len do
 49 |         if byte(text_cookie, i) == SEMICOLON then
 50 |             n = n + 1
 51 |         end
 52 |     end
 53 | 
 54 |     local cookie_table  = new_tab(0, n + 1)
 55 | 
 56 |     local state = EXPECT_SP
 57 |     local i = 1
 58 |     local j = 1
 59 |     local key, value
 60 | 
 61 |     while j <= len do
 62 |         if state == EXPECT_KEY then
 63 |             if byte(text_cookie, j) == EQUAL then
 64 |                 key = sub(text_cookie, i, j - 1)
 65 |                 state = EXPECT_VALUE
 66 |                 i = j + 1
 67 |             end
 68 |         elseif state == EXPECT_VALUE then
 69 |             if byte(text_cookie, j) == SEMICOLON
 70 |                     or byte(text_cookie, j) == SPACE
 71 |                     or byte(text_cookie, j) == HTAB
 72 |             then
 73 |                 value = sub(text_cookie, i, j - 1)
 74 |                 cookie_table[key] = value
 75 | 
 76 |                 key, value = nil, nil
 77 |                 state = EXPECT_SP
 78 |                 i = j + 1
 79 |             end
 80 |         elseif state == EXPECT_SP then
 81 |             if byte(text_cookie, j) ~= SPACE
 82 |                 and byte(text_cookie, j) ~= HTAB
 83 |             then
 84 |                 state = EXPECT_KEY
 85 |                 i = j
 86 |                 j = j - 1
 87 |             end
 88 |         end
 89 |         j = j + 1
 90 |     end
 91 | 
 92 |     if key ~= nil and value == nil then
 93 |         cookie_table[key] = sub(text_cookie, i)
 94 |     end
 95 | 
 96 |     return cookie_table
 97 | end
 98 | 
 99 | function _M.new(self)
100 |     local _cookie = ngx.var.http_cookie
101 |     --if not _cookie then
102 |         --return nil, "no cookie found in current request"
103 |     --end
104 |     return setmetatable({ _cookie = _cookie, set_cookie_table = new_tab(4, 0) },
105 |         { __index = self })
106 | end
107 | 
108 | function _M.get(self, key)
109 |     if not self._cookie then
110 |         return nil, "no cookie found in the current request"
111 |     end
112 |     if self.cookie_table == nil then
113 |         self.cookie_table = get_cookie_table(self._cookie)
114 |     end
115 | 
116 |     return self.cookie_table[key]
117 | end
118 | 
119 | function _M.get_all(self)
120 |     if not self._cookie then
121 |         return nil, "no cookie found in the current request"
122 |     end
123 | 
124 |     if self.cookie_table == nil then
125 |         self.cookie_table = get_cookie_table(self._cookie)
126 |     end
127 | 
128 |     return self.cookie_table
129 | end
130 | 
131 | local function bake(cookie)
132 |     if not cookie.key or not cookie.value then
133 |         return nil, 'missing cookie field "key" or "value"'
134 |     end
135 | 
136 |     if cookie["max-age"] then
137 |         cookie.max_age = cookie["max-age"]
138 |     end
139 |     local str = cookie.key .. "=" .. cookie.value
140 |         .. (cookie.expires and "; Expires=" .. cookie.expires or "")
141 |         .. (cookie.max_age and "; Max-Age=" .. cookie.max_age or "")
142 |         .. (cookie.domain and "; Domain=" .. cookie.domain or "")
143 |         .. (cookie.path and "; Path=" .. cookie.path or "")
144 |         .. (cookie.secure and "; Secure" or "")
145 |         .. (cookie.httponly and "; HttpOnly" or "")
146 |         .. (cookie.extension and "; " .. cookie.extension or "")
147 |     return str
148 | end
149 | 
150 | function _M.set(self, cookie)
151 |     local cookie_str, err = bake(cookie)
152 |     if not cookie_str then
153 |         return nil, err
154 |     end
155 | 
156 |     local set_cookie = ngx_header['Set-Cookie']
157 |     local set_cookie_type = type(set_cookie)
158 |     local t = self.set_cookie_table
159 |     clear_tab(t)
160 | 
161 |     if set_cookie_type == "string" then
162 |         -- only one cookie has been setted
163 |         if set_cookie ~= cookie_str then
164 |             t[1] = set_cookie
165 |             t[2] = cookie_str
166 |             ngx_header['Set-Cookie'] = t
167 |         end
168 |     elseif set_cookie_type == "table" then
169 |         -- more than one cookies has been setted
170 |         local size = #set_cookie
171 | 
172 |         -- we can not set cookie like ngx.header['Set-Cookie'][3] = val
173 |         -- so create a new table, copy all the values, and then set it back
174 |         for i=1, size do
175 |             t[i] = ngx_header['Set-Cookie'][i]
176 |             if t[i] == cookie_str then
177 |                 -- new cookie is duplicated
178 |                 return true
179 |             end
180 |         end
181 |         t[size + 1] = cookie_str
182 |         ngx_header['Set-Cookie'] = t
183 |     else
184 |         -- no cookie has been setted
185 |         ngx_header['Set-Cookie'] = cookie_str
186 |     end
187 |     return true
188 | end
189 | 
190 | return _M
191 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/encrypt_seed.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- -- @Date    : 2016-02-02 13:37
 3 | -- -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- -- @Link    : 
 5 | -- -- @Disc    : auto generate encrypt_seed
 6 | 
 7 | local VeryNginxConfig = require "VeryNginxConfig"
 8 | local dkjson = require "dkjson"
 9 | 
10 | 
11 | local _M = {}
12 | _M.seed = nil
13 | 
14 | function _M.get_seed()
15 | 
16 |     --return seed from memory
17 |     if _M.seed ~= nil then
18 |         return _M.seed
19 |     end
20 |     
21 |     --return saved seed
22 |     local seed_path = VeryNginxConfig.home_path() .. "/configs/encrypt_seed.json"
23 |     
24 |     local file = io.open( seed_path, "r")
25 |     if file ~= nil then
26 |         local data = file:read("*all");
27 |         file:close();
28 |         local tmp = dkjson.decode( data )
29 | 
30 |         _M.seed = tmp['encrypt_seed']
31 | 
32 |         return _M.seed
33 |     end
34 | 
35 | 
36 |     --if no saved seed, generate a new seed and saved
37 |     _M.seed = ngx.md5( ngx.now() )
38 |     local new_seed_json = dkjson.encode( { ["encrypt_seed"]= _M.seed }, {indent=true} )
39 |     local file,err = io.open( seed_path, "w")
40 |     
41 |     if file ~= nil then
42 |         file:write( new_seed_json )
43 |         file:close()
44 |         return _M.seed
45 |     else
46 |         ngx.log(ngx.STDERR, 'save encrypt_seed failed' )
47 |         return ''
48 |     end
49 |         
50 | end
51 | 
52 | function _M.generate()
53 | end
54 | 
55 | return _M
56 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/filter.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2016-01-02 00:46
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    : 
 5 | -- @Disc    : filter request'uri maybe attack
 6 | 
 7 | local _M = {}
 8 | 
 9 | local VeryNginxConfig = require "VeryNginxConfig"
10 | local request_tester = require "request_tester"
11 | 
12 | 
13 | function _M.filter()
14 |     
15 |     if VeryNginxConfig.configs["filter_enable"] ~= true then
16 |         return
17 |     end
18 |     
19 |     local matcher_list = VeryNginxConfig.configs['matcher']
20 |     local response_list = VeryNginxConfig.configs['response']
21 |     local response = nil
22 | 
23 |     for i,rule in ipairs( VeryNginxConfig.configs["filter_rule"] ) do
24 |         local enable = rule['enable']
25 |         local matcher = matcher_list[ rule['matcher'] ] 
26 |         if enable == true and request_tester.test( matcher ) == true then
27 |             local action = rule['action']
28 |             if action == 'accept' then
29 |                 return
30 |             else
31 |                 if rule['response'] ~= nil then
32 |                     ngx.status = tonumber( rule['code'] )
33 |                     response = response_list[rule['response']]
34 |                     if response ~= nil then
35 |                         ngx.header.content_type = response['content_type']
36 |                         ngx.say( response['body'] )
37 |                         ngx.exit( ngx.HTTP_OK )
38 |                     end
39 |                 else
40 |                     ngx.exit( tonumber( rule['code'] ) )
41 |                 end
42 |             end
43 |         end
44 |     end
45 | end
46 | 
47 | return _M
48 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/frequency_limit.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2016-04-20 23:13
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    : 
 5 | -- @Disc    : request frequency limit
 6 | 
 7 | local _M = {}
 8 | 
 9 | 
10 | local VeryNginxConfig = require "VeryNginxConfig"
11 | local request_tester = require "request_tester"
12 | local util = require "util"
13 | 
14 | local limit_dict = ngx.shared.frequency_limit
15 | 
16 | function _M.filter()
17 | 
18 |     if VeryNginxConfig.configs["frequency_limit_enable"] ~= true then
19 |         return
20 |     end
21 | 
22 |     local matcher_list = VeryNginxConfig.configs['matcher']
23 |     local response_list = VeryNginxConfig.configs['response']
24 |     local response = nil
25 | 
26 |     for i, rule in ipairs( VeryNginxConfig.configs["frequency_limit_rule"] ) do
27 |         local enable = rule['enable']
28 |         local matcher = matcher_list[ rule['matcher'] ] 
29 |         if enable == true and request_tester.test( matcher ) == true then
30 |             
31 |             local key = i 
32 |             if util.existed( rule['separate'], 'ip' ) then
33 |                 key = key..'-'..ngx.var.remote_addr
34 |             end
35 | 
36 |             if util.existed( rule['separate'], 'uri' ) then
37 |                 key = key..'-'..ngx.var.uri
38 |             end
39 | 
40 |             local time = rule['time']
41 |             local count = rule['count']
42 |             local code = rule['code']
43 | 
44 |             --ngx.log(ngx.STDERR,'-----');
45 |             --ngx.log(ngx.STDERR,key);
46 |             
47 |             local count_now = limit_dict:get( key )
48 |             --ngx.log(ngx.STDERR, tonumber(count_now) );
49 |             
50 |             if count_now == nil then
51 |                 limit_dict:set( key, 1, tonumber(time) )
52 |                 count_now = 0
53 |             end
54 |             
55 |             limit_dict:incr( key, 1 )
56 | 
57 |             if count_now > tonumber(count) then
58 |                 if rule['response'] ~= nil then
59 |                     ngx.status = tonumber( rule['code'] )
60 |                     response = response_list[rule['response']]
61 |                     if response ~= nil then
62 |                         ngx.header.content_type = response['content_type']
63 |                         ngx.say( response['body'] )
64 |                     end
65 |                     ngx.exit( ngx.HTTP_OK )
66 |                 else
67 |                     ngx.exit( tonumber( rule['code'] ) )
68 |                 end
69 |             end
70 |             
71 |             return
72 |         end
73 |     end
74 | end
75 | 
76 | return _M
77 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/json.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2016-03-08 
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    : 
 5 | -- @Disc    : import cjson module , but use dkjson to replace if cjson module is not ready 
 6 | 
 7 | local _M = {}
 8 | 
 9 | _M.json = nil
10 | 
11 | function _M.import_cjson()
12 |     _M.json = require 'cjson'
13 | end
14 | 
15 | if pcall( _M.import_cjson ) ~= true then
16 |     _M.json = require 'dkjson'
17 | end
18 | 
19 | return _M.json
20 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/redirect.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2016-01-02 20:39
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    : 
 5 | -- @Disc    : redirect path
 6 | 
 7 | local _M = {}
 8 | 
 9 | local VeryNginxConfig = require "VeryNginxConfig"
10 | local request_tester = require "request_tester"
11 | 
12 | 
13 | function _M.run()
14 |     
15 |     if VeryNginxConfig.configs["redirect_enable"] ~= true then
16 |         return
17 |     end
18 |     
19 |     local new_url = nil 
20 |     local re_gsub = ngx.re.gsub
21 |     local ngx_var = ngx.var 
22 |     local ngx_redirect = ngx.redirect
23 |     local ngx_var_uri = ngx_var.uri
24 |     local ngx_var_scheme = ngx_var.scheme
25 |     local ngx_var_host = ngx_var.host
26 |     local matcher_list = VeryNginxConfig.configs['matcher']
27 | 
28 | 
29 |     for i, rule in ipairs( VeryNginxConfig.configs["redirect_rule"] ) do
30 |         local enable = rule['enable']
31 |         local matcher = matcher_list[ rule['matcher'] ] 
32 |         if enable == true and request_tester.test( matcher ) == true then
33 |             replace_re = rule['replace_re']
34 |             if replace_re ~= nil and string.len( replace_re ) > 0  then
35 |                 new_url = re_gsub( ngx_var_uri, replace_re, rule['to_uri'] ) 
36 |             else
37 |                 new_url = rule['to_uri']
38 |             end
39 | 
40 |             if new_url ~= ngx_var_uri then
41 | 
42 |                 if string.find( new_url, 'http') ~= 1 then
43 |                     new_url = ngx_var_scheme.."://"..ngx_var_host..new_url
44 |                 end
45 | 
46 |                 if ngx_var.args ~= nil then
47 |                     ngx_redirect( new_url.."?"..ngx_var.args , ngx.HTTP_MOVED_TEMPORARILY)
48 |                 else
49 |                     ngx_redirect( new_url , ngx.HTTP_MOVED_TEMPORARILY)
50 |                 end
51 |             end
52 |             return
53 |         end
54 |     end
55 | 
56 | end
57 | 
58 | return _M
59 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/request_tester.lua:
--------------------------------------------------------------------------------
  1 | -- -*- coding: utf-8 -*-
  2 | -- @Date    : 2016-02-06 22:26
  3 | -- @Author  : Alexa (AlexaZhou@163.com)
  4 | -- @Link    : 
  5 | -- @Disc    : test a request hit a matcher or not
  6 | 
  7 | local cookie = require "cookie"
  8 | 
  9 | local _M = {}
 10 | 
 11 | local tester = {}
 12 | 
 13 | function _M.test( matcher )
 14 |     if matcher == nil then
 15 |         return false
 16 |     end
 17 |     
 18 | 	for name, v in pairs( matcher ) do
 19 |         if tester[name] ~= nil then
 20 |             if tester[name]( v ) ~= true then
 21 |                 return false
 22 | 			end
 23 | 		end
 24 | 	end
 25 | 
 26 | 	return true
 27 | end
 28 | 
 29 | --test_var is a basic test method, used by other test method 
 30 | function _M.test_var( match_operator, match_value, target_var )
 31 |     
 32 |    if match_operator == "=" then
 33 |         if target_var == match_value then
 34 |             return true
 35 |         end
 36 |     elseif match_operator == "*" then
 37 |         return true
 38 |     elseif match_operator == "!=" then
 39 |         if target_var ~= match_value then
 40 |             return true
 41 |         end
 42 |     elseif match_operator == '≈' then
 43 |         if type(target_var) == 'string' and ngx.re.find( target_var, match_value, 'isjo' ) ~= nil then
 44 |             return true
 45 |         end
 46 |     elseif match_operator == '!≈' then
 47 |         if type(target_var) ~= 'string' or ngx.re.find( target_var, match_value, 'isjo' ) == nil then
 48 |             return true
 49 |         end
 50 |     elseif match_operator == 'Exist' then
 51 |         if target_var ~= nil then
 52 |             return true
 53 |         end
 54 |     elseif match_operator == '!Exist' then
 55 |         if target_var == nil then
 56 |             return true
 57 |         end
 58 |     elseif match_operator == '!' then
 59 |         if target_var == nil then
 60 |             return true
 61 |         end
 62 |     end
 63 | 
 64 |     return false
 65 | end
 66 | 
 67 | 
 68 | --test a group of var in table with a condition 
 69 | function _M.test_many_var( var_table, condition )
 70 |     
 71 |     local find = ngx.re.find
 72 |     local test_var = _M.test_var
 73 | 
 74 |     local name_operator = condition['name_operator']
 75 |     local name_value = condition['name_value']
 76 |     local operator = condition['operator']
 77 |     local value = condition['value']
 78 | 	
 79 |      -- Insert !Exist Check here as it is only applied to operator
 80 |     if operator == '!Exist' then
 81 |         for k, v in pairs(var_table) do
 82 |             if test_var ( name_operator, name_value, k ) == true then
 83 |                 return false
 84 |             end
 85 |         end
 86 |         return true
 87 |     else
 88 |      -- Normal process
 89 |         for k, v in pairs(var_table) do
 90 |             if test_var( name_operator, name_value, k ) == true then
 91 |                 if test_var( operator, value, v ) == true then -- if any one value match the condition, means the matcher has been hited 
 92 |                     return true 
 93 |                 end
 94 |             end
 95 |         end
 96 |     end
 97 | 
 98 |     return false
 99 | end
100 | 
101 | function _M.test_uri( condition )
102 |     local uri = ngx.var.uri;
103 |     return _M.test_var( condition['operator'], condition['value'], uri )
104 | end
105 | 
106 | function _M.test_ip( condition )
107 |     local remote_addr = ngx.var.remote_addr
108 |     return _M.test_var( condition['operator'], condition['value'], remote_addr )
109 | end
110 | 
111 | function _M.test_ua( condition )
112 |     local http_user_agent = ngx.var.http_user_agent;
113 |     return _M.test_var( condition['operator'], condition['value'], http_user_agent )
114 | end
115 | 
116 | function _M.test_referer( condition )
117 |     local http_referer = ngx.var.http_referer;
118 |     return _M.test_var( condition['operator'], condition['value'], http_referer )
119 | end
120 | 
121 | --uncompleted
122 | function _M.test_method( condition )
123 |     local method_name = ngx.req.get_method()
124 |     return _M.test_var( condition['operator'], condition['value'], method_name )
125 | end
126 | 
127 | function _M.test_args( condition )
128 |     local find = ngx.re.find
129 |     local test_var = _M.test_var
130 |     
131 |     local name_operator = condition['name_operator']
132 |     local name_value = condition['name_value']
133 |     local operator = condition['operator']
134 |     local value = condition['value']
135 | 
136 |     --handle args behind uri
137 |     for k,v in pairs( ngx.req.get_uri_args()) do
138 |         if test_var( name_operator, name_value, k ) == true then
139 |             if type(v) == "table" then
140 |                 for arg_idx,arg_value in ipairs(v) do
141 |                     if test_var( operator, value, arg_value ) == true then 
142 |                         return true 
143 |                     end
144 |                 end
145 |             else
146 |                 if test_var( operator, value, v ) == true then
147 |                     return true 
148 |                 end
149 |             end
150 |         end
151 |     end
152 |     
153 |     ngx.req.read_body()
154 |     --ensure body has not be cached into temp file
155 |     if ngx.req.get_body_file() ~= nil then
156 |         return false
157 |     end
158 |     
159 |     local body_args,err = ngx.req.get_post_args()
160 |     if body_args == nil then
161 |         ngx.say("failed to get post args: ", err)
162 |         return false
163 |     end
164 |     
165 |     --check args in body
166 |     for k,v in pairs( body_args ) do
167 |         if test_var( name_operator, name_value, k ) == true then
168 |             if type(v) == "table" then
169 |                 for arg_idx,arg_value in ipairs(v) do
170 |                     if test_var( operator, value, arg_value ) == true then 
171 |                         return true 
172 |                     end
173 |                 end
174 |             else
175 |                 if test_var( operator, value, v ) == true then
176 |                     return true 
177 |                 end
178 |             end
179 |         end
180 |     end
181 | 
182 |     return false
183 | end
184 | 
185 | function _M.test_host( condition )
186 |     local hostname = ngx.var.host
187 |     return _M.test_var( condition['operator'], condition['value'], hostname )
188 | end
189 | 
190 | function _M.test_header( condition )
191 |     local header_table = ngx.req.get_headers()
192 |     return _M.test_many_var( header_table, condition )
193 | end
194 | 
195 | function _M.test_cookie( condition )
196 |     local cookie_obj, err = cookie:new()
197 |     local cookie_table = cookie_obj:get_all()
198 | 
199 |     if cookie_table == nil then
200 |         cookie_table = {}
201 |     end
202 |     return _M.test_many_var( cookie_table, condition )
203 | end
204 | 
205 | tester["URI"] = _M.test_uri
206 | tester["IP"] = _M.test_ip
207 | tester["UserAgent"] = _M.test_ua
208 | tester["Method"] = _M.test_method
209 | tester["Args"] = _M.test_args
210 | tester["Referer"] = _M.test_referer
211 | tester["Host"] = _M.test_host
212 | tester["Header"] = _M.test_header
213 | tester["Cookie"] = _M.test_cookie
214 | 
215 | 
216 | return _M
217 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/router.lua:
--------------------------------------------------------------------------------
  1 | -- -*- coding: utf-8 -*-
  2 | -- -- @Date    : 2016-01-02 00:35
  3 | -- -- @Author  : Alexa (AlexaZhou@163.com)
  4 | -- -- @Link    : 
  5 | -- -- @Disc    : url router of verynginx's control panel 
  6 | 
  7 | local summary = require "summary"
  8 | local status = require "status"
  9 | local cookie = require "cookie"
 10 | local VeryNginxConfig = require "VeryNginxConfig"
 11 | local encrypt_seed = require "encrypt_seed"
 12 | local json = require "json"
 13 | local util = require "util"
 14 | 
 15 | local _M = {}
 16 | 
 17 | _M.url_route = {}
 18 | _M.mime_type = {}
 19 | _M.mime_type['.js'] = "application/x-javascript"
 20 | _M.mime_type['.css'] = "text/css"
 21 | _M.mime_type['.html'] = "text/html"
 22 | 
 23 | 
 24 | function _M.filter()
 25 |     local method = ngx.req.get_method()
 26 |     local uri = ngx.var.uri
 27 |     local base_uri = VeryNginxConfig.configs['base_uri']
 28 |     local dashboard_host = VeryNginxConfig.configs['dashboard_host']
 29 | 
 30 |     if dashboard_host ~= '' then
 31 |         if ngx.var.host ~= dashboard_host then
 32 |             return
 33 |         end
 34 |     end
 35 | 
 36 |     if string.find( uri, base_uri ) == 1 then
 37 |         local path = string.sub( uri, string.len( base_uri ) + 1 )
 38 |        
 39 |         for i,item in ipairs( _M.route_table ) do
 40 |             if method == item['method'] and path == item['path'] then
 41 |                 ngx.header.content_type = "application/json"
 42 |                 ngx.header.charset = "utf-8"
 43 |                 
 44 |                 if item['auth'] == true and _M.check_session() == false then
 45 |                     local info = json.encode({["ret"]="failed",["message"]="need login"})
 46 |                     ngx.status = 400                    
 47 |                     ngx.say( info )
 48 |                 else
 49 |                     ngx.say( item['handle']() )
 50 |                 end
 51 |                 ngx.exit( ngx.HTTP_OK )
 52 |             end
 53 |         end
 54 |         
 55 |         ngx.req.set_uri( path )
 56 |         ngx.var.vn_static_root = VeryNginxConfig.home_path() .."/dashboard"
 57 |         ngx.var.vn_exec_flag = '1'-- use the var as a mark, so that lua can know that's a inside redirect
 58 |         util.ngx_ctx_dump() 
 59 |         return ngx.exec('@vn_static') -- will jump out at the exec 
 60 |     end
 61 | end
 62 | 
 63 | function _M.check_session()
 64 |     -- get all cookies
 65 |     local user, session
 66 |     
 67 |     local cookie_obj, err = cookie:new()
 68 |     local fields = cookie_obj:get_all()
 69 |     if not fields then
 70 |         return false
 71 |     end
 72 | 
 73 |     local cookie_prefix = VeryNginxConfig.configs['cookie_prefix']
 74 |     
 75 |     user = fields[ cookie_prefix .. '_user'] 
 76 |     session = fields[ cookie_prefix .. '_session']
 77 |     
 78 |     if user == nil or session == nil then
 79 |         return false
 80 |     end
 81 |     
 82 |     for i,v in ipairs( VeryNginxConfig.configs['admin'] ) do
 83 |         if v["user"] == user and v["enable"] == true then
 84 |             if session == ngx.md5( encrypt_seed.get_seed()..v["user"]) then
 85 |                 return true
 86 |             else
 87 |                 return false
 88 |             end
 89 |         end
 90 |     end
 91 |     
 92 |     return false
 93 | end
 94 | 
 95 | 
 96 | function _M.login()
 97 |     local args = util.get_request_args()
 98 |     
 99 |     for i,v in ipairs( VeryNginxConfig.configs['admin'] ) do
100 |         if v['user'] == args['user'] and v['password'] == args["password"] and v['enable'] == true then
101 |             local cookie_prefix = VeryNginxConfig.configs['cookie_prefix']
102 |             local session = ngx.md5(encrypt_seed.get_seed()..v['user']) 
103 |             
104 |             local data = {}
105 |             data['ret'] = 'success'
106 |             data['cookies'] = {
107 |                 [cookie_prefix .. '_session'] = session, 
108 |                 [cookie_prefix .. '_user'] = v['user'], 
109 |             }
110 |             return json.encode( data )
111 |         end
112 |     end 
113 |     
114 |     ngx.status = 400
115 |     return json.encode({["ret"]="failed",["message"]="Username and password not match"})
116 | end
117 | 
118 | _M.route_table = {
119 |     { ['method'] = "POST", ['auth']= false, ["path"] = "/login", ['handle'] = _M.login },
120 |     { ['method'] = "GET",  ['auth']= true,  ["path"] = "/summary", ['handle'] = summary.report },
121 |     { ['method'] = "GET",  ['auth']= true,  ["path"] = "/status", ['handle'] = status.report },
122 |     { ['method'] = "POST",  ['auth']= true,  ["path"] = "/status/clear", ['handle'] = summary.clear },
123 |     { ['method'] = "GET",  ['auth']= true,  ["path"] = "/config", ['handle'] = VeryNginxConfig.report },
124 |     { ['method'] = "POST", ['auth']= true,  ["path"] = "/config", ['handle'] = VeryNginxConfig.set },
125 |     { ['method'] = "GET",  ['auth']= true,  ["path"] = "/loadconfig", ['handle'] = VeryNginxConfig.load_from_file },
126 | }
127 | 
128 | 
129 | 
130 | return _M
131 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/scheme_lock.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2015-10-25 15:56:46
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    :
 5 | -- @Disc    : redirect request to right scheme
 6 | 
 7 | local _M = {}
 8 | 
 9 | 
10 | local VeryNginxConfig = require "VeryNginxConfig"
11 | local request_tester = require "request_tester"
12 | 
13 | 
14 | function scheme_judge(uri)
15 |     local ngx_re_find  = ngx.re.find
16 |     local matcher_list = VeryNginxConfig.configs['matcher']
17 | 
18 |     for i, rule in ipairs( VeryNginxConfig.configs["scheme_lock_rule"] ) do
19 |         local enable = rule['enable']
20 |         local matcher = matcher_list[ rule['matcher'] ]
21 |         if enable == true and request_tester.test( matcher ) == true then
22 |             return rule['scheme']
23 |         end
24 |     end
25 |     return 'none'
26 | end
27 | 
28 | function _M.run()
29 | 
30 |     if VeryNginxConfig.configs["scheme_lock_enable"] ~= true then
31 |         return
32 |     end
33 | 
34 |     local ngx_var = ngx.var
35 |     local scheme = scheme_judge( ngx_var.uri )
36 |     if scheme == "none" or scheme == ngx_var.scheme then
37 |         return
38 |     end
39 | 
40 |     -- Used on VeryNginx behind Proxy situation
41 |     if scheme == ngx.req.get_headers()["X-Forwarded-Proto"] then
42 |         -- ngx.log(ngx.STDERR, "Compare the protocol from more frontend level proxy, ", ngx.req.get_headers()["X-Forwarded-Protol"])
43 |         return
44 |     end
45 | 
46 |     if ngx_var.args ~= nil then
47 |         ngx.redirect( scheme.."://"..ngx_var.host..ngx_var.uri.."?"..ngx_var.args , ngx.HTTP_MOVED_TEMPORARILY)
48 |     else
49 |         ngx.redirect( scheme.."://"..ngx_var.host..ngx_var.uri , ngx.HTTP_MOVED_TEMPORARILY)
50 |     end
51 | end
52 | 
53 | return _M
54 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/status.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- -- @Date    : 2015-01-27 05:56
 3 | -- -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- -- @Link    : 
 5 | -- -- @Disc    : record nginx infomation 
 6 | 
 7 | local json = require "json"
 8 | 
 9 | local _M = {}
10 | 
11 | local KEY_STATUS_INIT = "I_"
12 | 
13 | local KEY_START_TIME = "G_"
14 | 
15 | local KEY_TOTAL_COUNT = "F_"
16 | local KEY_TOTAL_COUNT_SUCCESS = "H_"
17 | 
18 | local KEY_TRAFFIC_READ = "J_"
19 | local KEY_TRAFFIC_WRITE = "K_"
20 | 
21 | local KEY_TIME_TOTAL = "L_"
22 | 
23 | function _M.init()
24 | 
25 |     local ok, err = ngx.shared.status:add( KEY_STATUS_INIT,true )
26 |     if ok then
27 | 		ngx.shared.status:set( KEY_START_TIME, ngx.time() )
28 | 		ngx.shared.status:set( KEY_TOTAL_COUNT, 0 )
29 | 		ngx.shared.status:set( KEY_TOTAL_COUNT_SUCCESS, 0 )
30 | 		
31 |         ngx.shared.status:set( KEY_TRAFFIC_READ, 0 )
32 | 		ngx.shared.status:set( KEY_TRAFFIC_WRITE, 0 )
33 | 		
34 |         ngx.shared.status:set( KEY_TIME_TOTAL, 0 )
35 |     end
36 | 
37 | end
38 | 
39 | --add global count info
40 | function _M.log()
41 |     ngx.shared.status:incr( KEY_TOTAL_COUNT, 1 )
42 | 
43 |     if tonumber(ngx.var.status) < 400 then
44 |         ngx.shared.status:incr( KEY_TOTAL_COUNT_SUCCESS, 1 )
45 |     end
46 | 
47 |     ngx.shared.status:incr( KEY_TRAFFIC_READ, ngx.var.request_length)
48 |     ngx.shared.status:incr( KEY_TRAFFIC_WRITE, ngx.var.bytes_sent )
49 |     ngx.shared.status:incr( KEY_TIME_TOTAL, ngx.var.request_time )
50 | 
51 | end
52 | 
53 | function _M.report()
54 | 
55 |     local report = {}
56 |     report['request_all_count'] = ngx.shared.status:get( KEY_TOTAL_COUNT )
57 |     report['request_success_count'] = ngx.shared.status:get( KEY_TOTAL_COUNT_SUCCESS )
58 |     report['time'] = ngx.now()
59 |     report['boot_time'] = ngx.shared.status:get( KEY_START_TIME )
60 |     report['response_time_total'] = ngx.shared.status:get( KEY_TIME_TOTAL )
61 |     report['connections_active'] = ngx.var.connections_active
62 |     report['connections_reading'] = ngx.var.connections_reading
63 |     report['connections_writing'] = ngx.var.connections_writing
64 |     report['connections_waiting'] = ngx.var.connections_waiting
65 |     
66 |     report['traffic_read'] = ngx.shared.status:get( KEY_TRAFFIC_READ )
67 |     report['traffic_write'] = ngx.shared.status:get( KEY_TRAFFIC_WRITE )
68 |     
69 |     report['ret'] = 'success'
70 |     
71 |     return json.encode( report )
72 | 
73 | end
74 | 
75 | return _M
76 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/summary.lua:
--------------------------------------------------------------------------------
  1 | -- -*- coding: utf-8 -*-
  2 | -- -- @Date    : 2015-12-26 23:58
  3 | -- -- @Author  : Alexa (AlexaZhou@163.com)
  4 | -- -- @Link    : 
  5 | -- -- @Disc    : summary all the request
  6 | 
  7 | local json = require "json"
  8 | local VeryNginxConfig = require "VeryNginxConfig"
  9 | local util = require "util"
 10 | local request_tester = require "request_tester"
 11 | 
 12 | local _M = {}
 13 | 
 14 | local KEY_SUMMARY_REFRESHING_FLAG = "A_"
 15 | 
 16 | local KEY_URI_STATUS = "B_"
 17 | local KEY_URI_SIZE = "C_"
 18 | local KEY_URI_TIME = "D_"
 19 | local KEY_URI_COUNT = "E_"
 20 | 
 21 | local KEY_COLLECT_STATUS = "F_"
 22 | local KEY_COLLECT_SIZE = "G_"
 23 | local KEY_COLLECT_TIME = "H_"
 24 | local KEY_COLLECT_COUNT = "I_"
 25 | 
 26 | function _M.refresh()
 27 |     local period = tonumber( VeryNginxConfig.configs["summary_request_enable"] )
 28 |     
 29 |     if period == nil or period < 10 then
 30 |         period = 10
 31 |     end
 32 | 
 33 |     ngx.timer.at( period, _M.refresh )
 34 |     ngx.shared.summary_short:flush_all()
 35 |     --update flag timeout 
 36 |     ngx.shared.status:set( KEY_SUMMARY_REFRESHING_FLAG, true, 120 )
 37 | end
 38 | 
 39 | function _M.pre_run_matcher()
 40 |     if VeryNginxConfig.configs["summary_request_enable"] ~= true or VeryNginxConfig.configs["summary_collect_enable"] ~= true then
 41 |         return
 42 |     end
 43 |     
 44 |     local matcher_list = VeryNginxConfig.configs['matcher']
 45 |     for i,rule in ipairs( VeryNginxConfig.configs["summary_collect_rule"] ) do
 46 |         local enable = rule['enable']
 47 |         local matcher = matcher_list[ rule['matcher'] ] 
 48 |         if enable == true and request_tester.test( matcher ) == true then
 49 |             ngx.ctx.log_collect_name = rule['collect_name']
 50 |             break
 51 |         end
 52 |     end
 53 | end
 54 | 
 55 | function _M.log()
 56 | 
 57 |     local ok, err = ngx.shared.status:add( KEY_SUMMARY_REFRESHING_FLAG, true, 120 )
 58 |     --here use set a 120s timeout for the flag key, so when the nginx worker exit( for example nginx-s reload may cause that ), 
 59 |     --a other worker will continue to refresh the data period
 60 |     if ok then
 61 |         _M.refresh()
 62 |     end
 63 |     
 64 |     if VeryNginxConfig.configs["summary_request_enable"] ~= true then
 65 |         return
 66 |     end
 67 |     
 68 |     local with_host_info = VeryNginxConfig.configs["summary_with_host"]
 69 |     local uri = ngx.var.request_uri
 70 |     local status_code = ngx.var.status;
 71 |     local key_status = nil
 72 |     local key_size = nil
 73 |     local key_time = nil
 74 |     local key_count = nil
 75 |     local log_collect_name = ngx.ctx.log_collect_name
 76 |     local index = nil
 77 |     
 78 |     if log_collect_name ~= nil then
 79 |         key_status = KEY_COLLECT_STATUS..log_collect_name.."_"..status_code
 80 |         key_size = KEY_COLLECT_SIZE..log_collect_name
 81 |         key_time = KEY_COLLECT_TIME..log_collect_name
 82 |         key_count = KEY_COLLECT_COUNT..log_collect_name
 83 |     else
 84 |         if uri ~= nil then
 85 |             index = string.find( uri, '?' )
 86 |             if index ~= nil then
 87 |                 uri = string.sub( uri, 1 , index - 1 )
 88 |             end
 89 |         end
 90 |         if with_host_info then
 91 |             uri = ngx.var.host..uri
 92 |         end
 93 |         key_status = KEY_URI_STATUS..(uri or '').."_"..status_code
 94 |         key_size = KEY_URI_SIZE..uri
 95 |         key_time = KEY_URI_TIME..uri
 96 |         key_count = KEY_URI_COUNT..uri
 97 |     end
 98 |     
 99 |     if VeryNginxConfig.configs["summary_group_persistent_enable"] == true then
100 |         if ngx.shared.summary_long:get( key_count ) == nil then
101 |             ngx.shared.summary_long:set( key_count, 0 )
102 |         end
103 | 
104 |         if ngx.shared.summary_long:get( key_status ) == nil then
105 |             ngx.shared.summary_long:set( key_status, 0 )
106 |         end
107 |         
108 |         if ngx.shared.summary_long:get( key_size ) == nil then
109 |             ngx.shared.summary_long:set( key_size, 0 )
110 |         end
111 |         
112 |         if ngx.shared.summary_long:get( key_time ) == nil then
113 |             ngx.shared.summary_long:set( key_time, 0 )
114 |         end
115 | 
116 |         ngx.shared.summary_long:incr( key_count, 1 )
117 |         ngx.shared.summary_long:incr( key_status, 1 )
118 |         ngx.shared.summary_long:incr( key_size, ngx.var.body_bytes_sent )
119 |         ngx.shared.summary_long:incr( key_time, ngx.var.request_time )
120 |     end
121 |     
122 |     if VeryNginxConfig.configs["summary_group_temporary_enable"] == true then
123 |         if ngx.shared.summary_short:get( key_count ) == nil then
124 |             ngx.shared.summary_short:set( key_count, 0 )
125 |         end
126 | 
127 |         if ngx.shared.summary_short:get( key_status ) == nil then
128 |             ngx.shared.summary_short:set( key_status, 0 )
129 |         end
130 |         
131 |         if ngx.shared.summary_short:get( key_size ) == nil then
132 |             ngx.shared.summary_short:set( key_size, 0 )
133 |         end
134 |         
135 |         if ngx.shared.summary_short:get( key_time ) == nil then
136 |             ngx.shared.summary_short:set( key_time, 0 )
137 |         end
138 |         
139 |         ngx.shared.summary_short:incr( key_count, 1 )
140 |         ngx.shared.summary_short:incr( key_status, 1 )
141 |         ngx.shared.summary_short:incr( key_size, ngx.var.body_bytes_sent )
142 |         ngx.shared.summary_short:incr( key_time, ngx.var.request_time )
143 |     end
144 | end
145 | 
146 | function _M.report() 
147 |     
148 |     local dict = nil
149 |     local report = {}
150 |     local uri_report = {}
151 |     local collect_report = {}
152 |     local record_key = nil
153 |     local status = nil 
154 |     local size = nil 
155 |     local time = nil 
156 |     local count = nil
157 | 
158 |     report['uri'] = uri_report
159 |     report['collect'] = collect_report
160 | 
161 |     local args = ngx.req.get_uri_args()
162 |     if args['type'] == 'long' then
163 |         dict = ngx.shared.summary_long
164 |     elseif args['type'] == 'short' then
165 |         dict = ngx.shared.summary_short
166 |     else
167 |         return json.encode({["ret"]="failed",["err"]="type error"})
168 |     end
169 | 
170 |     local keys = dict:get_keys(0)
171 |     local str_sub = string.sub
172 |     local str_len = string.len
173 |     local str_format = string.format
174 | 
175 | 
176 |     for k, v in pairs( keys ) do
177 |         record_key = nil
178 |         record_table = nil
179 |         status = nil 
180 |         size = nil 
181 |         time = nil 
182 |         count = nil 
183 |         
184 |         if v.find(v, KEY_URI_STATUS) == 1 then
185 |             record_key = str_sub( v, str_len(KEY_URI_STATUS) + 1, -5 ) 
186 |             record_table = uri_report
187 |             status = str_sub( v,-3 )
188 |         elseif v.find(v, KEY_URI_SIZE) == 1 then
189 |             record_key = str_sub( v, str_len(KEY_URI_SIZE) + 1 ) 
190 |             record_table = uri_report
191 |             size = dict:get( v )
192 |         elseif v.find(v, KEY_URI_TIME) == 1 then
193 |             record_key = str_sub( v, str_len(KEY_URI_TIME) + 1 ) 
194 |             record_table = uri_report
195 |             time = dict:get( v )
196 |         elseif v.find(v, KEY_URI_COUNT) == 1 then
197 |             record_key = str_sub( v, str_len(KEY_URI_COUNT) + 1 ) 
198 |             record_table = uri_report
199 |             count = dict:get( v )
200 |         elseif v.find(v, KEY_COLLECT_STATUS) == 1 then
201 |             record_key = str_sub( v, str_len(KEY_COLLECT_STATUS) + 1, -5 ) 
202 |             record_table = collect_report
203 |             status = str_sub( v,-3 )
204 |         elseif v.find(v, KEY_COLLECT_SIZE) == 1 then
205 |             record_key = str_sub( v, str_len(KEY_COLLECT_SIZE) + 1 ) 
206 |             record_table = collect_report
207 |             size = dict:get( v )
208 |         elseif v.find(v, KEY_COLLECT_TIME) == 1 then
209 |             record_key = str_sub( v, str_len(KEY_COLLECT_TIME) + 1 ) 
210 |             record_table = collect_report
211 |             time = dict:get( v )
212 |         elseif v.find(v, KEY_COLLECT_COUNT) == 1 then
213 |             record_key = str_sub( v, str_len(KEY_COLLECT_COUNT) + 1 ) 
214 |             record_table = collect_report
215 |             count = dict:get( v )
216 |         end
217 | 
218 |         
219 |         if record_key ~= nil then
220 |             if record_table[record_key] == nil then
221 |                 record_table[record_key] = {}
222 |                 record_table[record_key]["status"] = {}
223 |             end
224 |             
225 |             if status ~= nil then
226 |                 record_table[record_key]["status"][status] = dict:get( v )
227 |             elseif time ~= nil then
228 |                 record_table[record_key]["time"] = time         
229 |             elseif
230 |                 size ~= nil then
231 |                 record_table[record_key]["size"] = size
232 |             elseif count ~= nil then
233 |                 record_table[record_key]["count"] = count
234 |             end
235 |         end
236 |     end
237 | 
238 |     --remove incomplete record
239 |     for name,record_table  in pairs( report ) do
240 |         for k, v in pairs( record_table ) do
241 |             if v['time'] == nil or v['count'] == nil or v['size'] == nil then
242 |                 record_table[k] = nil
243 |             end
244 |         end
245 |     end
246 | 
247 |     return json.encode( report )
248 | end
249 | 
250 | function _M.clear()
251 |     local args = util.get_request_args()
252 |     local group = args['group']
253 | 
254 |     if group == 'temporary' then
255 |         ngx.shared.summary_short:flush_all()
256 |     elseif group == 'persistent' then
257 |         ngx.shared.summary_long:flush_all()
258 |     elseif group == 'all' then
259 |         ngx.shared.summary_short:flush_all()
260 |         ngx.shared.summary_long:flush_all()
261 |     end
262 | 
263 |     return '{}'
264 | end
265 | 
266 | return _M
267 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/uri_rewrite.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2016-02-21 
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    : 
 5 | -- @Disc    : rewrite uri inside nginx
 6 | 
 7 | local _M = {}
 8 | 
 9 | local VeryNginxConfig = require "VeryNginxConfig"
10 | local request_tester = require "request_tester"
11 | 
12 | 
13 | function _M.run()
14 |     
15 |     if VeryNginxConfig.configs["uri_rewrite_enable"] ~= true then
16 |         return
17 |     end
18 |     
19 |     local new_uri = nil 
20 |     local re_gsub = ngx.re.gsub
21 |     local ngx_var = ngx.var 
22 |     local ngx_set_uri = ngx.req.set_uri
23 |     local ngx_var_uri = ngx_var.uri
24 |     local ngx_var_scheme = ngx_var.scheme
25 |     local ngx_var_host = ngx_var.host
26 |     local matcher_list = VeryNginxConfig.configs['matcher']
27 | 
28 | 
29 |     for i, rule in ipairs( VeryNginxConfig.configs["uri_rewrite_rule"] ) do
30 |         local enable = rule['enable']
31 |         local matcher = matcher_list[ rule['matcher'] ] 
32 |         if enable == true and request_tester.test( matcher ) == true then
33 |             replace_re = rule['replace_re']
34 |             if replace_re ~= nil and string.len( replace_re ) >0 then
35 |                 new_uri = re_gsub( ngx_var_uri, replace_re, rule['to_uri'] ) 
36 |             else
37 |                 new_uri = rule['to_uri']
38 |             end
39 | 
40 |             if new_uri ~= ngx_var_uri then
41 |                 ngx_set_uri( new_uri , false )
42 |             end
43 |             return
44 |         end
45 |     end
46 | 
47 | end
48 | 
49 | return _M
50 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/module/util.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2016-02-29 
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    : 
 5 | -- @Disc    : some tools
 6 | 
 7 | local json = require "json"
 8 | 
 9 | local _M = {}
10 | 
11 | 
12 | function _M.string_replace(s, pattern, replace, times)
13 |     local ret = nil
14 |     while times >= 0 do
15 |         times =  times - 1
16 |         local s_start,s_stop = string.find(s, pattern , 1, true ) -- 1,true means plain searches from index 1
17 |         if s_start ~= nil and s_stop ~= nil then 
18 |             s = string.sub( s, 1, s_start-1 ) .. replace .. string.sub( s, s_stop+1 )
19 |         end
20 |     end
21 |     return s
22 | end
23 | 
24 | function _M.existed( list, value )
25 |     for idx,item in ipairs( list ) do
26 |         if item == value then
27 |             return true
28 |         end
29 |     end
30 |     return false
31 | end
32 | 
33 | function _M.ngx_ctx_dump()
34 |     local dump_str = json.encode( ngx.ctx )
35 |     ngx.var.vn_ctx_dump = dump_str
36 | end
37 | 
38 | function _M.ngx_ctx_load()
39 |     
40 |     if ngx.var.vn_ctx_dump == nil then
41 |         return
42 |     end
43 | 
44 |     local dump_str = ngx.var.vn_ctx_dump
45 |     if dump_str ~= '' then
46 |         ngx.ctx = json.decode( dump_str ) 
47 |     end
48 | end
49 | 
50 | function _M.get_request_args()
51 |     local args = ngx.req.get_uri_args()
52 |     local post_args, err = nil
53 | 
54 |     ngx.req.read_body()
55 |     post_args, err = ngx.req.get_post_args()
56 |     if post_args == nil then
57 |         return args 
58 |     end
59 | 
60 |     for k,v in pairs(post_args) do
61 |         args[k] = v
62 |     end
63 | 
64 |     return args
65 | end
66 | 
67 | return _M
68 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/on_access.lua:
--------------------------------------------------------------------------------
 1 | local summary = require "summary"
 2 | local filter = require "filter"
 3 | local browser_verify = require "browser_verify"
 4 | local frequency_limit = require "frequency_limit"
 5 | local router = require "router"
 6 | local backend_static = require "backend_static"
 7 | local backend_proxy = require "backend_proxy"
 8 | 
 9 | if ngx.var.vn_exec_flag and ngx.var.vn_exec_flag ~= '' then
10 |     return
11 | end
12 | 
13 | summary.pre_run_matcher()
14 | 
15 | filter.filter()
16 | browser_verify.filter()
17 | frequency_limit.filter()
18 | router.filter()
19 | 
20 | 
21 | backend_static.filter()
22 | backend_proxy.filter()
23 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/on_banlance.lua:
--------------------------------------------------------------------------------
 1 | -- -*- coding: utf-8 -*-
 2 | -- @Date    : 2016-04-23 
 3 | -- @Author  : Alexa (AlexaZhou@163.com)
 4 | -- @Link    : 
 5 | -- @Disc    : load balancer by lua
 6 | 
 7 | local _M = {}
 8 | 
 9 | local balancer = require "ngx.balancer"
10 | 
11 | function _M.run()
12 |     --ngx.log( ngx.ERR, ngx.var.vn_proxy_target)
13 |     local ok, err = balancer.set_current_peer( ngx.var.vn_proxy_host , ngx.var.vn_proxy_port )
14 |     if not ok then
15 |         ngx.log(ngx.ERR, "failed to set the current peer: ", err)
16 |         return ngx.exit(500)
17 |     end
18 | 
19 |     return
20 | end
21 | 
22 | _M.run()
23 | 
24 | return _M
25 | 
26 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/on_init.lua:
--------------------------------------------------------------------------------
1 | local VeryNginxConfig = require "VeryNginxConfig" 
2 | 
3 | local status = require "status"
4 | status.init()
5 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/on_log.lua:
--------------------------------------------------------------------------------
1 | local status = require "status"
2 | local summary = require "summary"
3 | 
4 | status.log()
5 | summary.log()
6 | 


--------------------------------------------------------------------------------
/verynginx/lua_script/on_rewrite.lua:
--------------------------------------------------------------------------------
 1 | local util = require "util"
 2 | local VeryNginxConfig = require "VeryNginxConfig"
 3 | local scheme_lock = require "scheme_lock"
 4 | local redirect = require "redirect"
 5 | local uri_rewrite = require "uri_rewrite"
 6 | 
 7 | if ngx.var.vn_exec_flag and ngx.var.vn_exec_flag ~= '' then
 8 |     util.ngx_ctx_load()
 9 |     return
10 | end
11 | 
12 | --At first , make sure every request use latest running config
13 | VeryNginxConfig.update_config()
14 | 
15 | scheme_lock.run()
16 | redirect.run()
17 | uri_rewrite.run()
18 | 


--------------------------------------------------------------------------------
/verynginx/nginx_conf/in_external.conf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexazhou/VeryNginx/b66ef2ee6a108279653316323bd8ac026dd7ccd8/verynginx/nginx_conf/in_external.conf


--------------------------------------------------------------------------------
/verynginx/nginx_conf/in_http_block.conf:
--------------------------------------------------------------------------------
 1 | upstream vn_upstream{
 2 |     server 127.0.0.1;
 3 |     balancer_by_lua_file /opt/verynginx/verynginx/lua_script/on_banlance.lua;
 4 | 	keepalive 1024; #Connection pool
 5 | }
 6 | 
 7 | lua_package_path '/opt/verynginx/verynginx/lua_script/?.lua;;/opt/verynginx/verynginx/lua_script/module/?.lua;;';
 8 | lua_package_cpath '/opt/verynginx/verynginx/lua_script/?.so;;';   
 9 | lua_code_cache on;
10 | 
11 | lua_shared_dict status 1m;
12 | lua_shared_dict frequency_limit 10m;
13 | lua_shared_dict summary_long 10m;
14 | lua_shared_dict summary_short 10m;
15 | 
16 | init_by_lua_file /opt/verynginx/verynginx/lua_script/on_init.lua;
17 | rewrite_by_lua_file /opt/verynginx/verynginx/lua_script/on_rewrite.lua;
18 | access_by_lua_file /opt/verynginx/verynginx/lua_script/on_access.lua;
19 | log_by_lua_file /opt/verynginx/verynginx/lua_script/on_log.lua;
20 | 


--------------------------------------------------------------------------------
/verynginx/nginx_conf/in_server_block.conf:
--------------------------------------------------------------------------------
 1 | set $vn_exec_flag '';
 2 | set $vn_ctx_dump '';
 3 | 
 4 | #for proxy_pass backend
 5 | set $vn_proxy_scheme '';
 6 | set $vn_proxy_host '';
 7 | set $vn_proxy_port '';
 8 | set $vn_header_host '';
 9 | set $vn_static_expires 'epoch';
10 | 
11 | #for static file backend
12 | set $vn_static_root '';
13 | 
14 | location @vn_static {
15 | 	expires $vn_static_expires;
16 |     root $vn_static_root;
17 | }
18 | 
19 | location @vn_proxy {
20 | 	proxy_set_header Host $vn_header_host;
21 | 	#proxy_set_header X-Real-IP $remote_addr;
22 |     #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
23 |     proxy_set_header User-Agent $http_user_agent;
24 |     proxy_pass $vn_proxy_scheme://vn_upstream;
25 | 	proxy_ssl_verify off;
26 | }
27 | 
28 | 
29 | 


--------------------------------------------------------------------------------
/verynginx/support/verify_javascript.html:
--------------------------------------------------------------------------------
 1 | <!doctype html>
 2 | <html>
 3 |     <head>
 4 |         <meta charset="utf-8">
 5 |         <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
 6 |         <meta http-equiv="Pragma" content="no-cache">
 7 |         <meta http-equiv="Expires" content="0">
 8 |         <script type="text/javascript">
 9 |             var data = {
10 |                 'cookie' : "INFOCOOKIE",
11 |                 'uri' : "INFOURI",
12 |             };
13 |             
14 |             function getCookie(c_name){
15 |                 if (document.cookie.length>0){ 
16 |                     c_start=document.cookie.indexOf(c_name + "=")
17 |                     if (c_start!=-1){ 
18 |                         c_start=c_start + c_name.length+1 
19 |                         c_end=document.cookie.indexOf(";",c_start)
20 |                         if (c_end==-1) c_end=document.cookie.length
21 |                             return unescape(document.cookie.substring(c_start,c_end))
22 |                     } 
23 |                 }
24 |                 return "";
25 |             }
26 | 
27 |             function setCookie(c_name,value,expiredays){
28 |                 var exdate=new Date();
29 |                 exdate.setDate(exdate.getDate()+expiredays);
30 |                 document.cookie=c_name+ "=" +escape(value)+((expiredays==null) ? "" : "; path=/; expires="+exdate.toGMTString());
31 |             }
32 |             
33 |             function jump(){
34 |                 setCookie('COOKIEPREFIX_sign_javascript',data['cookie'],365);
35 |                 window.location = data['uri'];
36 |             }
37 |         </script>
38 |     </head>
39 |     <body onLoad="javascript:jump()">
40 |     </body>
41 | </html>
42 | 


--------------------------------------------------------------------------------