test_raw_parser.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  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 ($foo = "BAR") { rewrite ^(.*)$ /bar; }
  184. '''
  185. expected = [
  186. ['http://nginx.org/ru/docs/http/ngx_http_rewrite_module.html#if'],
  187. ['if', ['$http_user_agent', '~', 'MSIE'], [
  188. ['rewrite', '^(.*)$', '/msie/$1', 'break']
  189. ]],
  190. ['if', ['$http_cookie', '~*', 'id=([^;]+)(?:;|$)'], [
  191. ['set', '$id', '$1']
  192. ]],
  193. ['if', ['$request_method', '=', 'POST'], [
  194. ['return', '405']
  195. ]],
  196. ['if', ['$slow'], [
  197. ['limit_rate', '10k']
  198. ]],
  199. ['if', ['$invalid_referer'], [
  200. ['return', '403']
  201. ]],
  202. ['if', ['!-e', '/var/data/$dataset'], [
  203. ['return', '503']
  204. ]],
  205. ['if', ['$https_or_slb', '=', '(by_\(sl\)b|https)'], [
  206. ]],
  207. ['if', ['$host', '~*', '(lori|rage2)\.yandex\.(ru|ua|com|com\.tr)'], [
  208. ['set', '$x_frame_options', 'ALLOW']
  209. ]],
  210. ['if', ['$request_filename', '~*', '^.*?/(\d+_)([^/]+)$'], [
  211. ]],
  212. ['if', ['$foo', '=', 'BAR'], [
  213. ['rewrite', '^(.*)$', '/bar']
  214. ]]
  215. ]
  216. assert_config(config, expected)
  217. def test_hash_block_map():
  218. config = '''
  219. # http://nginx.org/ru/docs/http/ngx_http_map_module.html
  220. map $http_host $name {
  221. hostnames;
  222. default 0;
  223. example.com 1;
  224. *.example.com 1;
  225. example.org 2;
  226. *.example.org 2;
  227. .example.net 3;
  228. wap.* 4;
  229. }
  230. map $http_user_agent $mobile {
  231. default 0;
  232. "~Opera Mini" 1;
  233. }
  234. '''
  235. expected = [
  236. ['http://nginx.org/ru/docs/http/ngx_http_map_module.html'],
  237. ['map', ['$http_host', '$name'], [
  238. ['hostnames'],
  239. ['default', '0'],
  240. ['example.com', '1'],
  241. ['*.example.com', '1'],
  242. ['example.org', '2'],
  243. ['*.example.org', '2'],
  244. ['.example.net', '3'],
  245. ['wap.*', '4'],
  246. ]],
  247. ['map', ['$http_user_agent', '$mobile'], [
  248. ['default', '0'],
  249. ['~Opera Mini', '1'],
  250. ]]
  251. ]
  252. assert_config(config, expected)
  253. def test_upstream():
  254. config = '''
  255. # http://nginx.org/ru/docs/http/ngx_http_upstream_module.html
  256. upstream backend {
  257. server backend1.example.com weight=5;
  258. server backend2.example.com:8080;
  259. server unix:/tmp/backend3;
  260. server backup1.example.com:8080 backup;
  261. server backup2.example.com:8080 backup;
  262. }
  263. server {
  264. location / {
  265. proxy_pass http://backend;
  266. }
  267. }
  268. '''
  269. expected = [
  270. ['http://nginx.org/ru/docs/http/ngx_http_upstream_module.html'],
  271. ['upstream', ['backend'], [
  272. ['server', 'backend1.example.com', 'weight=5'],
  273. ['server', 'backend2.example.com:8080'],
  274. ['server', 'unix:/tmp/backend3'],
  275. ['server', 'backup1.example.com:8080', 'backup'],
  276. ['server', 'backup2.example.com:8080', 'backup'],
  277. ]],
  278. ['server', [], [
  279. ['location', ['/'], [
  280. ['proxy_pass', 'http://backend']
  281. ]]
  282. ]]]
  283. assert_config(config, expected)
  284. def test_issue_8():
  285. config = '''
  286. # http://nginx.org/ru/docs/http/ngx_http_upstream_module.html
  287. if ($http_referer ~* (\.(ru|ua|by|kz)/(pages/music|partners/|page-no-rights\.xml)) ) {
  288. set $temp A;
  289. }
  290. '''
  291. expected = [
  292. ['http://nginx.org/ru/docs/http/ngx_http_upstream_module.html'],
  293. ['if', ['$http_referer', '~*', '(\.(ru|ua|by|kz)/(pages/music|partners/|page-no-rights\.xml))'], [
  294. ['set', '$temp', 'A']
  295. ]]
  296. ]
  297. assert_config(config, expected)
  298. def test_issue_11():
  299. config = '''
  300. init_by_lua_block {
  301. tvm = require "nginx.tvm"
  302. }
  303. '''
  304. expected = [
  305. ['init_by_lua_block', [], ['tvm', '=', 'require', '"nginx.tvm"']]
  306. ]
  307. assert_config(config, expected)
  308. def test_lua_block():
  309. config = '''
  310. # https://github.com/openresty/lua-nginx-module#typical-uses
  311. location = /lua {
  312. # MIME type determined by default_type:
  313. default_type 'text/plain';
  314. content_by_lua_block {
  315. local res = ngx.location.capture("/some_other_location")
  316. if res then
  317. ngx.say("status: ", res.status)
  318. ngx.say("body:")
  319. ngx.print(res.body)
  320. end
  321. }
  322. }
  323. '''
  324. expected = [
  325. ['https://github.com/openresty/lua-nginx-module#typical-uses'],
  326. ['location', ['=', '/lua'], [
  327. ['MIME type determined by default_type:'],
  328. ['default_type', 'text/plain'],
  329. ['content_by_lua_block', [], [
  330. 'local', 'res', '=', 'ngx.location.capture(', '"/some_other_location"', ')',
  331. 'if', 'res', 'then',
  332. 'ngx.say(', '"status: "', ',', 'res.status)',
  333. 'ngx.say(', '"body:"', ')',
  334. 'ngx.print(res.body)',
  335. 'end']]
  336. ]]
  337. ]
  338. assert_config(config, expected)
  339. def test_lua_block_brackets():
  340. config = '''
  341. location = /foo {
  342. rewrite_by_lua_block {
  343. res = ngx.location.capture("/memc",
  344. { args = { cmd = "incr", key = ngx.var.uri } }
  345. )
  346. }
  347. proxy_pass http://blah.blah.com;
  348. }
  349. '''
  350. expected = [
  351. ['location', ['=', '/foo'], [
  352. ['rewrite_by_lua_block', [], [
  353. 'res', '=', 'ngx.location.capture(', '"/memc"', ',',
  354. ['args', '=', ['cmd', '=', '"incr"', ',', 'key', '=', 'ngx.var.uri']],
  355. ')']],
  356. ['proxy_pass', 'http://blah.blah.com']
  357. ]]
  358. ]
  359. assert_config(config, expected)
  360. def test_file_delims():
  361. config = '''
  362. # configuration file /etc/nginx/nginx.conf:
  363. http {
  364. include sites/*.conf;
  365. }
  366. # configuration file /etc/nginx/sites/default.conf:
  367. server {
  368. }
  369. '''
  370. expected = [
  371. ['/etc/nginx/nginx.conf'],
  372. ['http', [], [
  373. ['include', 'sites/*.conf']
  374. ]],
  375. ['/etc/nginx/sites/default.conf'],
  376. ['server', [], []]
  377. ]
  378. assert_config(config, expected)
  379. def test_comments():
  380. config = '''
  381. # Some comment
  382. add_header X-Some-Comment some;
  383. #
  384. # Comment with padding
  385. #
  386. add_header X-Padding-Comment padding;
  387. #
  388. add_header X-Blank-Comment blank;
  389. '''
  390. expected = [
  391. ['Some comment'],
  392. ['add_header', 'X-Some-Comment', 'some'],
  393. [''],
  394. ['Comment with padding'],
  395. [''],
  396. ['add_header', 'X-Padding-Comment', 'padding'],
  397. [''],
  398. ['add_header', 'X-Blank-Comment', 'blank'],
  399. ]
  400. assert_config(config, expected)
  401. def test_upstream_dot():
  402. config = '''
  403. upstream test.mysite.com {
  404. server 127.0.0.1:9009;
  405. }
  406. '''
  407. expected = [
  408. ['upstream', ['test.mysite.com'], [
  409. ['server', '127.0.0.1:9009']
  410. ]],
  411. ]
  412. assert_config(config, expected)
  413. def test_empty_config():
  414. config = '''
  415. '''
  416. expected = []
  417. assert_config(config, expected)
  418. def test_utfbom_decoding():
  419. config = b'''\xef\xbb\xbf
  420. add_header X-Test "Windows-1251";
  421. '''
  422. expected = [
  423. ['add_header', 'X-Test', 'Windows-1251']
  424. ]
  425. assert_config(config, expected)
  426. def test_national_comment_decoding():
  427. config = b'''
  428. # \xeb\xff-\xeb\xff-\xeb\xff = Lya-lya-lya
  429. add_header X-Test "Windows-1251";
  430. '''
  431. actual = RawParser().parse(config)
  432. assert_equals(len(actual.asList()), 2)
  433. def assert_config(config, expected):
  434. actual = RawParser().parse(config)
  435. assert_equals(actual.asList(), expected)