UNPKG

codemirror-mode-nginx-renewed

Version:
555 lines (500 loc) 19.2 kB
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>CodeMirror nginx-renewed mode</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/codemirror.min.css" integrity="sha512-xIf9AdJauwKIVtrVRZ0i4nHP61Ogx9fSRAkCLecmE2dL/U8ioWpDvFCAy4dcfecN72HHB9+7FfQj3aiO68aaaw==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="stylesheet" href="https://xavierog.github.io/codemirror-mode-pcre/src/pcre.css"> <style> .CodeMirror { border-top: 1px solid black; border-bottom: 1px solid black; height: 100%; } </style> </head> <body> <h1>CodeMirror nginx-renewed mode</h1> <p> This is a <a href="https://codemirror.net/">CodeMirror</a> mode that brings syntax highlighting for nginx configuration files. </p> <p> <strong>Features:</strong> <ul> <li>recognize more than 870 nginx directives, including those from OpenResty and nginx-plus;</li> <li>unrecognized directives remain accepted (they are shown in italic)</li> <li>highlight directives that are recognized but should not occur in the current scope;</li> <li>highlight regular expressions -- requires <em>codemirror-mode-pcre</em><br> supported in: locations, maps, if conditions, rewrite statements and some proxy_* directives</li> <li>highlight variables in strings -- requires <em>codemirror-mode-variables</em>;</li> <li>highlight IP addresses -- requires <em>codemirror-mode-ipaddr</em>;<br> supported in: geo, set_real_ip_from</li> <li> highlight Lua code -- requires <a href="https://codemirror.net/mode/lua/index.html">CodeMirror's Lua mode</a>; without it, strings and comments are still highlighted;<br> supported in: blocks (<code>*_by_lua_block</code>)<br> not supported in: strings (<code>*_by_lua</code>)<br> </li> </ul> </p> <p> <strong>MIME types defined:</strong> <ul> <li>text/x-nginx-conf</li> </ul> </p> <p> <strong>Options:</strong> <ul> <li>initial_scope: string; default value: "main"</li> <li>check_directive_scope: boolean; default value: true</li> </ul> </p> <p> <strong>MIME types used to invoke nested modes:</strong> <ul> <li>text/x-regex</li> <li>text/x-variables</li> <li>text/x-ip-address</li> <li>text/x-lua</li> </ul> </p> <h2 id="h2direct">Direct use</h2> <p> By default, codemirror-mode-nginx-renewed: <ol> <li>assumes the textarea reflects nginx's main scope (i.e. the same scope as nginx.conf);</li> <li>checks whether each directive is allowed in the current scope.</li> </ol> The textareas below showcase codemirror-mode-nginx-renewed's capabilities. </p> <p> Useful commands: <ul> <li>Ctrl+d: toggle comments</li> <li>Ctrl+]: indent</li> <li>Ctrl+[: unindent</li> <li>Tab: auto-indent the current line or selection</li> <li>Shift+Tab: if something is selected, indent it by one indent unit; otherwise, insert a tab character</li> </ul> </p> <textarea data-mime="text/x-nginx-conf"> user www www; worker_processes 5; error_log logs/error.log; pid logs/nginx.pid; worker_rlimit_nofile 8192; events { worker_connections 4096; ## Default: 1024 } http { types { text/html html htm shtml; text/xml xml rss; image/gif gif; } map "${http_user_agent}" $mobile { default "${http_user_agent}"; hostnames; volatile; "Opera Mini" 1; "~(?:Opera Mini)" 1; "~*(?:Opera Mini)" 1; include "/path/to/map/file.conf"; } geo $country { default ZZ; include conf/geo.conf; delete 127.0.0.0/16; proxy 192.168.100.0/24; proxy 2001:0db8::/32; 127.0.0.0/24 US; 127.0.0.1/32 RU; 10.1.0.0/16 RU; 192.168.1.0/24 UK; 1.2.3.4 TEST; 2001:0db8:: TEST; 2001:0db8::/32 TEST; } geo $dummy_range { ranges; 1.2.3.4-1.2.3.6 "IPv4 range"; # nginx's IP ranges are IPv4-only; IPv6 ranges yield "[emerg] invalid network" 2001:0db8::-2001:0db8:ffff:ffff:ffff:ffff:ffff:ffff "IPv6 range"; } set_real_ip_from 10.20.30.40; set_real_ip_from 10.20.30.0/24; set_real_ip_from 2001:0db8::1234; set_real_ip_from 2001:0db8::/64; set_real_ip_from trustme.company.com; real_ip_header X-Forwarded-For; real_ip_recursive on; upstream bunch_of_servers { server server00001:80 resolve; server server00002:80 resolve; server server00003:80 resolve; server server00004:80 resolve; server server00005:80 resolve; } server { server_name "nginx.syntax-highlighting.demo.example"; server_name "~^nginx[^.]+\.syntax-highlighting\.demo\.example$"; # Examples from http://nginx.org/en/docs/http/server_names.html: server_name example.org www.example.org; server_name *.example.org; server_name mail.*; server_name ~^(?<user>.+)\.example\.net$; server_name ~^www\d+\.example\.net$; server_name "~^(?<name>\w+\d{1,3})\.example\.net$"; server_name ~^(www\.)?(?<domain>.+)$; server_name ~^(www\.)?(.+)$; server_name example.org www.example.org ""; # Locations, using double-quotes, single-quotes and unquoted strings: location = "/exact-match" {} location = '/exact-match' {} location = /exact-match {} location "/prefix-match-wi-re" {} location '/prefix-match-wi-re' {} location /prefix-match-wi-re {} location ^~ "/prefix-match-wo-re" {} location ^~ '/prefix-match-wo-re' {} location ^~ /prefix-match-wo-re {} location ~ "(cs|regex|match)" {} location ~ '(cs|regex|match)' {} location ~ (cs|regex|match) {} location ~* "(ci|regex|match)" {} location ~* '(ci|regex|match)' {} location ~* (ci|regex|match) {} # Conditions, using double-quotes, single-quotes and unquoted strings: if ($variable = "${text}/x-variables") {} if ($variable = '${text}/x-variables') {} if ($variable = ${text}/x-variables) {} if ($variable != "${text}/x-variables") {} if ($variable != '${text}/x-variables') {} if ($variable != ${text}/x-variables) {} if ($variable ~ "^text/(?x:-[regex])") {} if ($variable ~ '^text/(?x:-[regex])') {} if ($variable ~ ^text/(?x:-[regex])) {} if ($variable ~* "^text/(?x:-[regex])") {} if ($variable ~* '^text/(?x:-[regex])') {} if ($variable ~* ^text/(?x:-[regex])) {} if ($variable !~ "^text/(?x:-[regex])") {} if ($variable !~ '^text/(?x:-[regex])') {} if ($variable !~ ^text/(?x:-[regex])) {} if ($variable !~* "^text/(?x:-[regex])") {} if ($variable !~* '^text/(?x:-[regex])') {} if ($variable !~* ^text/(?x:-[regex])) {} if ( -f "${text}/x-variables") {} if ( -f '${text}/x-variables') {} if ( -f ${text}/x-variables) {} if (!-f "${text}/x-variables") {} if (!-f '${text}/x-variables') {} if (!-f ${text}/x-variables) {} if ( -d "${text}/x-variables") {} if ( -d '${text}/x-variables') {} if ( -d ${text}/x-variables) {} if (!-d "${text}/x-variables") {} if (!-d '${text}/x-variables') {} if (!-d ${text}/x-variables) {} if ( -e "${text}/x-variables") {} if ( -e '${text}/x-variables') {} if ( -e ${text}/x-variables) {} if (!-e "${text}/x-variables") {} if (!-e '${text}/x-variables') {} if (!-e ${text}/x-variables) {} if ( -x "${text}/x-variables") {} if ( -x '${text}/x-variables') {} if ( -x ${text}/x-variables) {} if (!-x "${text}/x-variables") {} if (!-x '${text}/x-variables') {} if (!-x ${text}/x-variables) {} # without forgetting the simplest case: if ($variable) {} # Unquoted strings in conditions: if ( # condition comment $variable !~* ^text/(?x:-[regex]) # condition comment ) # condition comment # post-condition, pre-scope comment { # scope comment # scope comment } # post-scope comment # Rewrites: rewrite "^/foo(?<tail>.*)" "/bar${tail}"; rewrite '^/foo(?<tail>.*)' '/bar${tail}' break; rewrite ^/foo(?<tail>.*) /bar${tail} last; # Other mod_rewrite directives: rewrite_log on; uninitialized_variable_warn off; break; location / { limit_except GET PROPFIND LOCK { deny all; } proxy_pass http://bunch_of_servers; proxy_redirect off; proxy_redirect default; proxy_redirect http://$proxy_host:8000/ /; proxy_redirect ~^(http://[^:]+):\d+(/.+)$ $1$2; proxy_redirect ~*/user/([^/]+)/(.+)$ http://$1.example.com/$2; location /foo { proxy_cookie_domain off; proxy_cookie_domain localhost example.org; proxy_cookie_domain www.$host $host; proxy_cookie_domain "~\.(?<domain_and_tld>[a-z]+\.[a-z]+)$" "${domain_and_tld}"; location /foo/bar { proxy_cookie_path off; proxy_cookie_path /two/ /; proxy_cookie_path $uri "/some${uri}"; proxy_cookie_path "~*^/user/(?<username>[^/]+)" "/u/${username}"; location /foo/bar/baz { proxy_cookie_flags off; proxy_cookie_flags one httponly; proxy_cookie_flags ${my_cookie} nohttponly; proxy_cookie_flags ${app_name}_session secure "samesite=${my_map}" httponly; proxy_cookie_flags ~^(?i:alpha_cookie|cookie_beta)$ nosecure samesite=strict; } } } } # Enforce HTTPS for domain.tld subdomains: set $scheme_and_domain "${map_real_scheme}://${host}"; if ($scheme_and_domain ~ "^http://.*\.domain\.tld\.?$") { return 308 "https://${host}${request_uri}"; } location /redirect { return "https://somewhere.else/"; } location /close { return 444; } location /prefix { root /path/to/document/root/; } location ~ "(?x) # Enable PCRE extended mode ^ /user /(?<action>login|logout|profile) # Also include 'profile' because... /(?<tail>.*) " { if ($tail ~ "^(some|[^/]+/really|compl(?:ex|icated)|stuff|t?here)$") { return 307 "${scheme}://${host}/somewhere/else/${tail}${is_args}${args}"; } # ... } } }</textarea> <p> This textarea is a copy of <a href="https://github.com/openresty/lua-nginx-module#synopsis">lua-nginx-module's synopsis</a>: </p> <textarea data-mime="text/x-nginx-conf" data-scope="main/http"> # set search paths for pure Lua external libraries (';;' is the default path): lua_package_path '/foo/bar/?.lua;/blah/?.lua;;'; # set search paths for Lua external libraries written in C (can also use ';;'): lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;'; server { location /lua_content { # MIME type determined by default_type: default_type 'text/plain'; content_by_lua_block { ngx.say('Hello,world!') } } location /nginx_var { # MIME type determined by default_type: default_type 'text/plain'; # try access /nginx_var?a=hello,world content_by_lua_block { ngx.say(ngx.var.arg_a) } } location = /request_body { client_max_body_size 50k; client_body_buffer_size 50k; content_by_lua_block { ngx.req.read_body() -- explicitly read the req body local data = ngx.req.get_body_data() if data then ngx.say("body data:") ngx.print(data) return end -- body may get buffered in a temp file: local file = ngx.req.get_body_file() if file then ngx.say("body is in file ", file) else ngx.say("no body found") end } } # transparent non-blocking I/O in Lua via subrequests # (well, a better way is to use cosockets) location = /lua { # MIME type determined by default_type: default_type 'text/plain'; content_by_lua_block { local res = ngx.location.capture("/some_other_location") if res then ngx.say("status: ", res.status) ngx.say("body:") ngx.print(res.body) end } } location = /foo { rewrite_by_lua_block { res = ngx.location.capture("/memc", { args = { cmd = "incr", key = ngx.var.uri } } ) } proxy_pass http://blah.blah.com; } location = /mixed { rewrite_by_lua_file /path/to/rewrite.lua; access_by_lua_file /path/to/access.lua; content_by_lua_file /path/to/content.lua; } # use nginx var in code path # CAUTION: contents in nginx var must be carefully filtered, # otherwise there'll be great security risk! location ~ ^/app/([-_a-zA-Z0-9/]+) { set $path $1; content_by_lua_file /path/to/lua/app/root/$path.lua; } location / { client_max_body_size 100k; client_body_buffer_size 100k; access_by_lua_block { -- check the client IP address is in our black list if ngx.var.remote_addr == "132.5.72.3" then ngx.exit(ngx.HTTP_FORBIDDEN) end -- check if the URI contains bad words if ngx.var.uri and string.match(ngx.var.request_body, "evil") -- Alteration to test double square bracket strings: page = [==[ <HTML> <HEAD> <TITLE>An HTML Page</TITLE> </HEAD> <BODY> <A HREF="http://www.lua.org">Lua</A> [[a text between double brackets]] </BODY> </HTML>]==] then return ngx.redirect("/terms_of_use.html") end -- tests passed } # proxy_pass/fastcgi_pass/etc settings } }</textarea> <h2 id="h2customgeneric">Custom use without a specific scope</h2> <p> Sometimes, it is hard, if not impossible, to predict the scope of an nginx snippet. Typical cases: </p> <ul> <li>include files, which may contain directives suited for various scopes;</li> <li>the output of "nginx -T", which may combine many files and thus many scopes.</li> </ul> <p> To deal with these situations, state "check_directive_scope: false" when configuring codemirror-mode-nginx-renewed. </p> <textarea data-mime="text/x-nginx-conf" data-scope="unknown"> # configuration file /etc/nginx/nginx.conf: worker_rlimit_nofile 8192; # main # configuration file /etc/nginx/event.conf: worker_connections 4096; # events # configuration file /etc/nginx/include/enforce_org_tld.conf: uninitialized_variable_warn off; # http, server or location? if ($host ~* "^(?<domain_head>.+)\.net$") { # if in server or if in location? return 308 "${scheme}://${domain_head}.org${request_uri}"; }</textarea> <h2 id="h2customspecific">Custom use with specific scopes</h2> <p> By playing with the "initial_scope" setting, it is possible to change the nginx scope of the textarea. initial_scope defaults to "main" and can take various values reflecting the desired scope. Examples: <ul> <li>main/events</li> <li>main/http/server/location/limit_except</li> <li>main/http/server/location/if</li> <li>main/http/server/location/types</li> <li>main/http/server/if</li> <li>main/mail/server</li> <li>main/stream/upstream</li> </ul> Other examples and demonstrations follow. </p> <h3 id="h3mainhttpmap">main/http/map</h3> <textarea data-mime="text/x-nginx-conf" data-scope="main/http/map"> # Everything in this textarea is considered inside an HTTP map: "~*^[a-m]" "alphabet-1st-half"; "~*^[n-z]" "alphabet-2nd-half"; "~*^[0-9]" "start-with-digit"; default "other";</textarea> <h3 id="h3mainhttpgeo">main/http/geo</h3> <textarea data-mime="text/x-nginx-conf" data-scope="main/http/geo"> # Everything in this textarea is considered inside an HTTP geo: 0.0.0.0/1 ipv4-1st-half; 128.0.0.0/1 ipv4-2nd-half; 0000::/1 ipv6-1st-half; 8000::/1 ipv6-2nd-half; default no-clue;</textarea> <h3 id="h3mainhttptypes">main/http/types</h3> <textarea data-mime="text/x-nginx-conf" data-scope="main/http/types"> # Everything in this textarea is considered inside a types {} directive: application/octet-stream bin exe dll; application/octet-stream deb; application/octet-stream dmg;</textarea> <h3 id="h3mainhttpserver">main/http/server</h3> <textarea data-mime="text/x-nginx-conf" data-scope="main/http/server"> # Everything in this textarea is considered inside an HTTP server: listen 80; listen 443 ssl; ssl_certificate "/path/to/myapp.mycompany.com.cert.pem"; ssl_certificate_key "/path/to/myapp.mycompany.com.key.pem"; server_name myapp.mycompany.com; if ($real_scheme = "http") { return 308 "https://${server_name}${request_uri}"; } location / { return 200 "Under construction"; } # proxy_pass cannot be used directly in server, which is why it is shown in bold red: proxy_pass https://target_upstream;</textarea> <h3 id="h3mainhttpserverlocation">main/http/server/location</h3> <textarea data-mime="text/x-nginx-conf" data-scope="main/http/server/location"> # Everything in this textarea is considered inside an HTTP location: rewrite "^/prefix(?<tail>.*)" "${tail}" break; proxy_pass https://target_upstream; proxy_set_header "X-Forwarded-Prefix" "/prefix"; # listen does not make sense in location, which is why it is shown in bold red: listen 42;</textarea> <h3 id="mainhttpupstream">main/http/upstream</h3> <textarea data-mime="text/x-nginx-conf" data-scope="main/http/upstream"> # Everything in this textarea is considered inside an HTTP upstream: zone target_upstream_zone 64k; server mybackend1.mycompany.com:443 resolve; server mybackend2.mycompany.com:443 resolve; server mybackend3.mycompany.com:443 resolve backup; sticky cookie myappsession path=/; # proxy_ssl_name cannot be set directly in upstream, which is why it is shown in bold red: proxy_ssl_name mybackendX.mycompany.com;</textarea> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/codemirror.min.js" integrity="sha512-6Q5cHfb86ZJ3qWx47Pw7P5CN1/pXcBMmz3G0bXLIQ67wOtRF7brCaK5QQLPz2CWLBqjWRNH+/bV5MwwWxFGxww==" crossorigin="anonymous" referrerpolicy="no-referrer"> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/addon/comment/comment.min.js" integrity="sha512-U0udq4Tvwzb+OiL6f7aak/n07mlIutTl+pEPtKoK0oOTF4Q9SkLOQYvKpIaCIM8CKTufd7szgeN9VDcgXDVvWg==" crossorigin="anonymous" referrerpolicy="no-referrer"> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.2/mode/lua/lua.min.js" integrity="sha512-MXR/wu8WxkFikybMYGuaR9O0SgRrcSReZUNuherC0XZ7SJN/db3W+qQCh+4rAiBBeNk/yd/NdnQd/s2nO4q4fA==" crossorigin="anonymous" referrerpolicy="no-referrer"> </script> <script src="https://xavierog.github.io/codemirror-mode-pcre/src/pcre.js"></script> <script src="https://xavierog.github.io/codemirror-mode-ipaddr/src/ipaddr.js"></script> <script src="https://xavierog.github.io/codemirror-mode-variables/src/variables.js"></script> <script src="src/nginx-renewed.js"></script> <script> function demo(textarea) { var mime = textarea.attributes['data-mime']; if (!mime) return; var scope = textarea.attributes['data-scope']; scope = scope ? scope.value : false; var mode = { name: mime.value }; if (scope === 'unknown') mode.check_directive_scope = false; else if (scope) mode.initial_scope = scope; var conf = { lineNumbers: true, indentWithTabs: true, indentUnit: 4, showCursorWhenSelecting: true, extraKeys: { 'Ctrl-D': 'toggleComment', }, mode: mode, }; CodeMirror.fromTextArea(textarea, conf); } var i, textareas = document.getElementsByTagName('textarea'); for (i = 0; i < textareas.length; ++ i) demo(textareas[i]); </script> </body> </html>