test_raw_parser.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. from nose.tools import assert_equals
  2. from gixy.parser.raw_parser import *
  3. def test_directive():
  4. config = '''
  5. access_log syslog:server=127.0.0.1,tag=nginx_sentry toolsformat;
  6. user http;
  7. internal;
  8. set $foo "bar";
  9. set $foo 'bar';
  10. proxy_pass http://unix:/run/sock.socket;
  11. rewrite ^/([a-zA-Z0-9]+)$ /$1/${arg_v}.pb break;
  12. server_name some.tld ~^(www\.)?podberi.(?:ru|com|ua)$
  13. ~^(www\.)?guru.yandex.ru$;
  14. '''
  15. expected = [
  16. ['access_log', 'syslog:server=127.0.0.1,tag=nginx_sentry', 'toolsformat'],
  17. ['user', 'http'],
  18. ['internal'],
  19. ['set', '$foo', 'bar'],
  20. ['set', '$foo', 'bar'],
  21. ['proxy_pass', 'http://unix:/run/sock.socket'],
  22. ['rewrite', '^/([a-zA-Z0-9]+)$', '/$1/${arg_v}.pb', 'break'],
  23. ['server_name', 'some.tld', '~^(www\.)?podberi.(?:ru|com|ua)$', '~^(www\.)?guru.yandex.ru$']
  24. ]
  25. assert_config(config, expected)
  26. def test_block():
  27. config = '''
  28. http {
  29. }
  30. '''
  31. expected = [['http', [], []]]
  32. assert_config(config, expected)
  33. def test_block_with_child():
  34. config = '''
  35. http {
  36. gzip on;
  37. }
  38. '''
  39. expected = [['http', [], [['gzip', 'on']]]]
  40. assert_config(config, expected)
  41. def test_location_simple():
  42. config = '''
  43. location / {
  44. }
  45. location = /foo {
  46. }
  47. location ~ ^/bar {
  48. }
  49. location ~* ^/baz$ {
  50. }
  51. location ^~ ^/bazz {
  52. }
  53. # Whitespace may be omitted:((
  54. location ~\.(js|css)$ {
  55. }
  56. '''
  57. expected = [['location', ['/'], []],
  58. ['location', ['=', '/foo'], []],
  59. ['location', ['~', '^/bar'], []],
  60. ['location', ['~*', '^/baz$'], []],
  61. ['location', ['^~', '^/bazz'], []],
  62. ['Whitespace may be omitted:(('],
  63. ['location', ['~', '\.(js|css)$'], []]]
  64. assert_config(config, expected)
  65. def test_quoted_strings():
  66. config = '''
  67. some_sq '\\'la\\.\\/\\"';
  68. some_dq '\\'la\\.\\/\\"';
  69. '''
  70. expected = [['some_sq', '\'la\\.\\/\"'],
  71. ['some_dq', '\'la\\.\\/\"']]
  72. assert_config(config, expected)
  73. def test_location_child():
  74. config = '''
  75. location = /foo {
  76. proxy_pass http://unix:/run/sock.socket;
  77. }
  78. '''
  79. expected = [['location', ['=', '/foo'], [
  80. ['proxy_pass', 'http://unix:/run/sock.socket']
  81. ]]]
  82. assert_config(config, expected)
  83. def test_nested_location():
  84. config = '''
  85. location ~* ^/foo {
  86. location = /foo/bar {
  87. internal;
  88. proxy_pass http://any.yandex.ru;
  89. }
  90. location = /foo/baz {
  91. proxy_pass upstream;
  92. }
  93. }
  94. '''
  95. expected = [['location', ['~*', '^/foo'], [
  96. ['location', ['=', '/foo/bar'], [
  97. ['internal'],
  98. ['proxy_pass', 'http://any.yandex.ru']
  99. ]],
  100. ['location', ['=', '/foo/baz'], [
  101. ['proxy_pass', 'upstream']
  102. ]],
  103. ]]]
  104. assert_config(config, expected)
  105. def test_hash_block():
  106. config = '''
  107. geo $geo {
  108. default 0;
  109. 127.0.0.1 2;
  110. 192.168.1.0/24 1;
  111. 10.1.0.0/16 1;
  112. ::1 2;
  113. 2001:0db8::/32 1;
  114. }
  115. '''
  116. expected = [['geo', ['$geo'], [
  117. ['default', '0'],
  118. ['127.0.0.1', '2'],
  119. ['192.168.1.0/24', '1'],
  120. ['10.1.0.0/16', '1'],
  121. ['::1', '2'],
  122. ['2001:0db8::/32', '1']
  123. ]]]
  124. assert_config(config, expected)
  125. def test_hash_block_in_location():
  126. config = '''
  127. location /iphone/ {
  128. types {
  129. text/html html htm shtml;
  130. application/json json;
  131. application/rss+xml rss;
  132. text/vnd.sun.j2me.app-descriptor jad;
  133. }
  134. }
  135. '''
  136. expected = [['location', ['/iphone/'], [
  137. ['types', [], [
  138. ['text/html', 'html', 'htm', 'shtml'],
  139. ['application/json', 'json'],
  140. ['application/rss+xml', 'rss'],
  141. ['text/vnd.sun.j2me.app-descriptor', 'jad']
  142. ]],
  143. ]]]
  144. assert_config(config, expected)
  145. def test_named_location():
  146. config = '''
  147. location @foo {
  148. proxy_pass http://any.yandex.ru;
  149. }
  150. '''
  151. expected = [['location', ['@foo'], [
  152. ['proxy_pass', 'http://any.yandex.ru']
  153. ]]]
  154. assert_config(config, expected)
  155. def test_if():
  156. config = '''
  157. # http://nginx.org/ru/docs/http/ngx_http_rewrite_module.html#if
  158. if ($http_user_agent ~ MSIE) {
  159. rewrite ^(.*)$ /msie/$1 break;
  160. }
  161. if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
  162. set $id $1;
  163. }
  164. if ($request_method = POST) {
  165. return 405;
  166. }
  167. if ($slow) {
  168. limit_rate 10k;
  169. }
  170. if ($invalid_referer) {
  171. return 403;
  172. }
  173. if (!-e "/var/data/$dataset") {
  174. return 503;
  175. }
  176. if ($https_or_slb = (by_\(sl\)b|https)) {
  177. }
  178. if ($host ~* (lori|rage2)\.yandex\.(ru|ua|com|com\.tr)) {
  179. set $x_frame_options ALLOW;
  180. }
  181. if ($request_filename ~* ^.*?/(\d+_)([^/]+)$) {
  182. }
  183. if ($http_user_agent ~* "^WordPress.*; verifying pingback") {
  184. }
  185. if ($foo = "BAR") { rewrite ^(.*)$ /bar; }
  186. '''
  187. expected = [
  188. ['http://nginx.org/ru/docs/http/ngx_http_rewrite_module.html#if'],
  189. ['if', ['$http_user_agent', '~', 'MSIE'], [
  190. ['rewrite', '^(.*)$', '/msie/$1', 'break']
  191. ]],
  192. ['if', ['$http_cookie', '~*', 'id=([^;]+)(?:;|$)'], [
  193. ['set', '$id', '$1']
  194. ]],
  195. ['if', ['$request_method', '=', 'POST'], [
  196. ['return', '405']
  197. ]],
  198. ['if', ['$slow'], [
  199. ['limit_rate', '10k']
  200. ]],
  201. ['if', ['$invalid_referer'], [
  202. ['return', '403']
  203. ]],
  204. ['if', ['!-e', '/var/data/$dataset'], [
  205. ['return', '503']
  206. ]],
  207. ['if', ['$https_or_slb', '=', '(by_\(sl\)b|https)'], [
  208. ]],
  209. ['if', ['$host', '~*', '(lori|rage2)\.yandex\.(ru|ua|com|com\.tr)'], [
  210. ['set', '$x_frame_options', 'ALLOW']
  211. ]],
  212. ['if', ['$request_filename', '~*', '^.*?/(\d+_)([^/]+)$'], [
  213. ]],
  214. ['if', ['$http_user_agent', '~*', '^WordPress.*; verifying pingback'], [
  215. ]],
  216. ['if', ['$foo', '=', 'BAR'], [
  217. ['rewrite', '^(.*)$', '/bar']
  218. ]]
  219. ]
  220. assert_config(config, expected)
  221. def test_hash_block_map():
  222. config = '''
  223. # http://nginx.org/ru/docs/http/ngx_http_map_module.html
  224. map $http_host $name {
  225. hostnames;
  226. default 0;
  227. example.com 1;
  228. *.example.com 1;
  229. example.org 2;
  230. *.example.org 2;
  231. .example.net 3;
  232. wap.* 4;
  233. }
  234. map $http_user_agent $mobile {
  235. default 0;
  236. "~Opera Mini" 1;
  237. }
  238. '''
  239. expected = [
  240. ['http://nginx.org/ru/docs/http/ngx_http_map_module.html'],
  241. ['map', ['$http_host', '$name'], [
  242. ['hostnames'],
  243. ['default', '0'],
  244. ['example.com', '1'],
  245. ['*.example.com', '1'],
  246. ['example.org', '2'],
  247. ['*.example.org', '2'],
  248. ['.example.net', '3'],
  249. ['wap.*', '4'],
  250. ]],
  251. ['map', ['$http_user_agent', '$mobile'], [
  252. ['default', '0'],
  253. ['~Opera Mini', '1'],
  254. ]]
  255. ]
  256. assert_config(config, expected)
  257. def test_upstream():
  258. config = '''
  259. # http://nginx.org/ru/docs/http/ngx_http_upstream_module.html
  260. upstream backend {
  261. server backend1.example.com weight=5;
  262. server backend2.example.com:8080;
  263. server unix:/tmp/backend3;
  264. server backup1.example.com:8080 backup;
  265. server backup2.example.com:8080 backup;
  266. }
  267. server {
  268. location / {
  269. proxy_pass http://backend;
  270. }
  271. }
  272. '''
  273. expected = [
  274. ['http://nginx.org/ru/docs/http/ngx_http_upstream_module.html'],
  275. ['upstream', ['backend'], [
  276. ['server', 'backend1.example.com', 'weight=5'],
  277. ['server', 'backend2.example.com:8080'],
  278. ['server', 'unix:/tmp/backend3'],
  279. ['server', 'backup1.example.com:8080', 'backup'],
  280. ['server', 'backup2.example.com:8080', 'backup'],
  281. ]],
  282. ['server', [], [
  283. ['location', ['/'], [
  284. ['proxy_pass', 'http://backend']
  285. ]]
  286. ]]]
  287. assert_config(config, expected)
  288. def test_issue_8():
  289. config = '''
  290. # http://nginx.org/ru/docs/http/ngx_http_upstream_module.html
  291. if ($http_referer ~* (\.(ru|ua|by|kz)/(pages/music|partners/|page-no-rights\.xml)) ) {
  292. set $temp A;
  293. }
  294. '''
  295. expected = [
  296. ['http://nginx.org/ru/docs/http/ngx_http_upstream_module.html'],
  297. ['if', ['$http_referer', '~*', '(\.(ru|ua|by|kz)/(pages/music|partners/|page-no-rights\.xml))'], [
  298. ['set', '$temp', 'A']
  299. ]]
  300. ]
  301. assert_config(config, expected)
  302. def test_issue_11():
  303. config = '''
  304. init_by_lua_block {
  305. tvm = require "nginx.tvm"
  306. }
  307. '''
  308. expected = [
  309. ['init_by_lua_block', [], ['tvm', '=', 'require', '"nginx.tvm"']]
  310. ]
  311. assert_config(config, expected)
  312. def test_lua_block():
  313. config = '''
  314. # https://github.com/openresty/lua-nginx-module#typical-uses
  315. location = /lua {
  316. # MIME type determined by default_type:
  317. default_type 'text/plain';
  318. content_by_lua_block {
  319. local res = ngx.location.capture("/some_other_location")
  320. if res then
  321. ngx.say("status: ", res.status)
  322. ngx.say("body:")
  323. ngx.print(res.body)
  324. end
  325. }
  326. }
  327. '''
  328. expected = [
  329. ['https://github.com/openresty/lua-nginx-module#typical-uses'],
  330. ['location', ['=', '/lua'], [
  331. ['MIME type determined by default_type:'],
  332. ['default_type', 'text/plain'],
  333. ['content_by_lua_block', [], [
  334. 'local', 'res', '=', 'ngx.location.capture(', '"/some_other_location"', ')',
  335. 'if', 'res', 'then',
  336. 'ngx.say(', '"status: "', ',', 'res.status)',
  337. 'ngx.say(', '"body:"', ')',
  338. 'ngx.print(res.body)',
  339. 'end']]
  340. ]]
  341. ]
  342. assert_config(config, expected)
  343. def test_lua_block_brackets():
  344. config = '''
  345. location = /foo {
  346. rewrite_by_lua_block {
  347. res = ngx.location.capture("/memc",
  348. { args = { cmd = "incr", key = ngx.var.uri } }
  349. )
  350. }
  351. proxy_pass http://blah.blah.com;
  352. }
  353. '''
  354. expected = [
  355. ['location', ['=', '/foo'], [
  356. ['rewrite_by_lua_block', [], [
  357. 'res', '=', 'ngx.location.capture(', '"/memc"', ',',
  358. ['args', '=', ['cmd', '=', '"incr"', ',', 'key', '=', 'ngx.var.uri']],
  359. ')']],
  360. ['proxy_pass', 'http://blah.blah.com']
  361. ]]
  362. ]
  363. assert_config(config, expected)
  364. def test_file_delims():
  365. config = '''
  366. # configuration file /etc/nginx/nginx.conf:
  367. http {
  368. include sites/*.conf;
  369. }
  370. # configuration file /etc/nginx/sites/default.conf:
  371. server {
  372. }
  373. '''
  374. expected = [
  375. ['/etc/nginx/nginx.conf'],
  376. ['http', [], [
  377. ['include', 'sites/*.conf']
  378. ]],
  379. ['/etc/nginx/sites/default.conf'],
  380. ['server', [], []]
  381. ]
  382. assert_config(config, expected)
  383. def test_comments():
  384. config = '''
  385. # Some comment
  386. add_header X-Some-Comment some;
  387. #
  388. # Comment with padding
  389. #
  390. add_header X-Padding-Comment padding;
  391. #
  392. add_header X-Blank-Comment blank;
  393. if (1) # Comment
  394. {
  395. add_header X-Inline blank;
  396. }
  397. '''
  398. expected = [
  399. ['Some comment'],
  400. ['add_header', 'X-Some-Comment', 'some'],
  401. [''],
  402. ['Comment with padding'],
  403. [''],
  404. ['add_header', 'X-Padding-Comment', 'padding'],
  405. [''],
  406. ['add_header', 'X-Blank-Comment', 'blank'],
  407. ['if', ['1'], [
  408. ['add_header', 'X-Inline', 'blank'],
  409. ]],
  410. ]
  411. assert_config(config, expected)
  412. def test_upstream_dot():
  413. config = '''
  414. upstream test.mysite.com {
  415. server 127.0.0.1:9009;
  416. }
  417. '''
  418. expected = [
  419. ['upstream', ['test.mysite.com'], [
  420. ['server', '127.0.0.1:9009']
  421. ]],
  422. ]
  423. assert_config(config, expected)
  424. def test_empty_config():
  425. config = '''
  426. '''
  427. expected = []
  428. assert_config(config, expected)
  429. def test_utfbom_decoding():
  430. config = b'''\xef\xbb\xbf
  431. add_header X-Test "Windows-1251";
  432. '''
  433. expected = [
  434. ['add_header', 'X-Test', 'Windows-1251']
  435. ]
  436. assert_config(config, expected)
  437. def test_national_comment_decoding():
  438. config = b'''
  439. # \xeb\xff-\xeb\xff-\xeb\xff = Lya-lya-lya
  440. add_header X-Test "Windows-1251";
  441. '''
  442. actual = RawParser().parse(config)
  443. assert_equals(len(actual.asList()), 2)
  444. def assert_config(config, expected):
  445. actual = RawParser().parse(config)
  446. assert_equals(actual.asList(), expected)