import os import socket import unittest from unit.applications.proto import TestApplicationProto class TestStatic(TestApplicationProto): prerequisites = {} def setUp(self): super().setUp() os.makedirs(self.testdir + '/assets/dir') with open(self.testdir + '/assets/index.html', 'w') as index, \ open(self.testdir + '/assets/README', 'w') as readme, \ open(self.testdir + '/assets/log.log', 'w') as log, \ open(self.testdir + '/assets/dir/file', 'w') as file: index.write('0123456789') readme.write('readme') log.write('[debug]') file.write('blah') self._load_conf( { "listeners": {"*:7080": {"pass": "routes"}}, "routes": [{"action": {"share": self.testdir + "/assets"}}], "settings": { "http": { "static": { "mime_types": {"text/plain": [".log", "README"]} } } }, } ) def test_static_index(self): self.assertEqual( self.get(url='/index.html')['body'], '0123456789', 'index' ) self.assertEqual(self.get(url='/')['body'], '0123456789', 'index 2') self.assertEqual(self.get(url='//')['body'], '0123456789', 'index 3') self.assertEqual(self.get(url='/.')['body'], '0123456789', 'index 4') self.assertEqual(self.get(url='/./')['body'], '0123456789', 'index 5') self.assertEqual( self.get(url='/?blah')['body'], '0123456789', 'index vars' ) self.assertEqual( self.get(url='/#blah')['body'], '0123456789', 'index anchor' ) self.assertEqual( self.get(url='/dir/')['status'], 404, 'index not found' ) resp = self.get(url='/index.html/') self.assertEqual(resp['status'], 404, 'index not found 2 status') self.assertEqual( resp['headers']['Content-Type'], 'text/html', 'index not found 2 Content-Type', ) def test_static_large_file(self): file_size = 32 * 1024 * 1024 with open(self.testdir + '/assets/large', 'wb') as f: f.seek(file_size - 1) f.write(b'\0') self.assertEqual( len( self.get(url='/large', read_buffer_size=1024 * 1024)['body'] ), file_size, 'large file', ) def test_static_etag(self): etag = self.get(url='/')['headers']['ETag'] etag_2 = self.get(url='/README')['headers']['ETag'] self.assertNotEqual(etag, etag_2, 'different ETag') self.assertEqual( etag, self.get(url='/')['headers']['ETag'], 'same ETag' ) with open(self.testdir + '/assets/index.html', 'w') as f: f.write('blah') self.assertNotEqual( etag, self.get(url='/')['headers']['ETag'], 'new ETag' ) def test_static_redirect(self): resp = self.get(url='/dir') self.assertEqual(resp['status'], 301, 'redirect status') self.assertEqual( resp['headers']['Location'], '/dir/', 'redirect Location' ) self.assertNotIn( 'Content-Type', resp['headers'], 'redirect Content-Type' ) def test_static_space_in_name(self): os.rename( self.testdir + '/assets/dir/file', self.testdir + '/assets/dir/fi le', ) self.waitforfiles(self.testdir + '/assets/dir/fi le') self.assertEqual( self.get(url='/dir/fi le')['body'], 'blah', 'file name' ) os.rename(self.testdir + '/assets/dir', self.testdir + '/assets/di r') self.waitforfiles(self.testdir + '/assets/di r/fi le') self.assertEqual( self.get(url='/di r/fi le')['body'], 'blah', 'dir name' ) os.rename( self.testdir + '/assets/di r', self.testdir + '/assets/ di r ' ) self.waitforfiles(self.testdir + '/assets/ di r /fi le') self.assertEqual( self.get(url='/ di r /fi le')['body'], 'blah', 'dir name enclosing' ) self.assertEqual( self.get(url='/%20di%20r%20/fi le')['body'], 'blah', 'dir encoded' ) self.assertEqual( self.get(url='/ di r %2Ffi le')['body'], 'blah', 'slash encoded' ) self.assertEqual( self.get(url='/ di r /fi%20le')['body'], 'blah', 'file encoded' ) self.assertEqual( self.get(url='/%20di%20r%20%2Ffi%20le')['body'], 'blah', 'encoded' ) self.assertEqual( self.get(url='/%20%64%69%20%72%20%2F%66%69%20%6C%65')['body'], 'blah', 'encoded 2', ) os.rename( self.testdir + '/assets/ di r /fi le', self.testdir + '/assets/ di r / fi le ', ) self.waitforfiles(self.testdir + '/assets/ di r / fi le ') self.assertEqual( self.get(url='/%20di%20r%20/%20fi%20le%20')['body'], 'blah', 'file name enclosing', ) try: print('файл') utf8 = True except: utf8 = False if utf8: os.rename( self.testdir + '/assets/ di r / fi le ', self.testdir + '/assets/ di r /фа йл', ) self.waitforfiles(self.testdir + '/assets/ di r /фа йл') self.assertEqual( self.get(url='/ di r /фа йл')['body'], 'blah', 'file name 2' ) os.rename( self.testdir + '/assets/ di r ', self.testdir + '/assets/ди ректория', ) self.waitforfiles(self.testdir + '/assets/ди ректория/фа йл') self.assertEqual( self.get(url='/ди ректория/фа йл')['body'], 'blah', 'dir name 2' ) def test_static_unix_socket(self): sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(self.testdir + '/assets/unix_socket') self.assertEqual(self.get(url='/unix_socket')['status'], 404, 'socket') sock.close() def test_static_unix_fifo(self): os.mkfifo(self.testdir + '/assets/fifo') self.assertEqual(self.get(url='/fifo')['status'], 404, 'fifo') def test_static_symlink(self): os.symlink(self.testdir + '/assets/dir', self.testdir + '/assets/link') self.assertEqual(self.get(url='/dir')['status'], 301, 'dir') self.assertEqual(self.get(url='/dir/file')['status'], 200, 'file') self.assertEqual(self.get(url='/link')['status'], 301, 'symlink dir') self.assertEqual( self.get(url='/link/file')['status'], 200, 'symlink file' ) def test_static_method(self): resp = self.head() self.assertEqual(resp['status'], 200, 'HEAD status') self.assertEqual(resp['body'], '', 'HEAD empty body') self.assertEqual(self.delete()['status'], 405, 'DELETE') self.assertEqual(self.post()['status'], 405, 'POST') self.assertEqual(self.put()['status'], 405, 'PUT') def test_static_path(self): self.assertEqual( self.get(url='/dir/../dir/file')['status'], 200, 'relative' ) self.assertEqual(self.get(url='./')['status'], 400, 'path invalid') self.assertEqual(self.get(url='../')['status'], 400, 'path invalid 2') self.assertEqual(self.get(url='/..')['status'], 400, 'path invalid 3') self.assertEqual( self.get(url='../assets/')['status'], 400, 'path invalid 4' ) self.assertEqual( self.get(url='/../assets/')['status'], 400, 'path invalid 5' ) def test_static_two_clients(self): _, sock = self.get(url='/', start=True, no_recv=True) _, sock2 = self.get(url='/', start=True, no_recv=True) self.assertEqual(sock.recv(1), b'H', 'client 1') self.assertEqual(sock2.recv(1), b'H', 'client 2') self.assertEqual(sock.recv(1), b'T', 'client 1 again') self.assertEqual(sock2.recv(1), b'T', 'client 2 again') sock.close() sock2.close() def test_static_mime_types(self): self.assertIn( 'success', self.conf( { "text/x-code/x-blah/x-blah": "readme", "text/plain": [".html", ".log", "file"], }, 'settings/http/static/mime_types', ), 'configure mime_types', ) self.assertEqual( self.get(url='/README')['headers']['Content-Type'], 'text/x-code/x-blah/x-blah', 'mime_types string case insensitive', ) self.assertEqual( self.get(url='/index.html')['headers']['Content-Type'], 'text/plain', 'mime_types html', ) self.assertEqual( self.get(url='/')['headers']['Content-Type'], 'text/plain', 'mime_types index default', ) self.assertEqual( self.get(url='/dir/file')['headers']['Content-Type'], 'text/plain', 'mime_types file in dir', ) def test_static_mime_types_partial_match(self): self.assertIn( 'success', self.conf( { "text/x-blah": ["ile", "fil", "f", "e", ".file"], }, 'settings/http/static/mime_types', ), 'configure mime_types', ) self.assertNotIn( 'Content-Type', self.get(url='/dir/file'), 'partial match' ) def test_static_mime_types_reconfigure(self): self.assertIn( 'success', self.conf( { "text/x-code": "readme", "text/plain": [".html", ".log", "file"], }, 'settings/http/static/mime_types', ), 'configure mime_types', ) self.assertEqual( self.conf_get('settings/http/static/mime_types'), {'text/x-code': 'readme', 'text/plain': ['.html', '.log', 'file']}, 'mime_types get', ) self.assertEqual( self.conf_get('settings/http/static/mime_types/text%2Fx-code'), 'readme', 'mime_types get string', ) self.assertEqual( self.conf_get('settings/http/static/mime_types/text%2Fplain'), ['.html', '.log', 'file'], 'mime_types get array', ) self.assertEqual( self.conf_get('settings/http/static/mime_types/text%2Fplain/1'), '.log', 'mime_types get array element', ) self.assertIn( 'success', self.conf_delete('settings/http/static/mime_types/text%2Fplain/2'), 'mime_types remove array element', ) self.assertNotIn( 'Content-Type', self.get(url='/dir/file')['headers'], 'mime_types removed', ) self.assertIn( 'success', self.conf_post( '"file"', 'settings/http/static/mime_types/text%2Fplain' ), 'mime_types add array element', ) self.assertEqual( self.get(url='/dir/file')['headers']['Content-Type'], 'text/plain', 'mime_types reverted', ) self.assertIn( 'success', self.conf( '"file"', 'settings/http/static/mime_types/text%2Fplain' ), 'configure mime_types update', ) self.assertEqual( self.get(url='/dir/file')['headers']['Content-Type'], 'text/plain', 'mime_types updated', ) self.assertNotIn( 'Content-Type', self.get(url='/log.log')['headers'], 'mime_types updated 2', ) self.assertIn( 'success', self.conf( '".log"', 'settings/http/static/mime_types/text%2Fblahblahblah' ), 'configure mime_types create', ) self.assertEqual( self.get(url='/log.log')['headers']['Content-Type'], 'text/blahblahblah', 'mime_types create', ) def test_static_mime_types_correct(self): self.assertIn( 'error', self.conf( {"text/x-code": "readme", "text/plain": "readme"}, 'settings/http/static/mime_types', ), 'mime_types same extensions', ) self.assertIn( 'error', self.conf( {"text/x-code": [".h", ".c"], "text/plain": ".c"}, 'settings/http/static/mime_types', ), 'mime_types same extensions array', ) self.assertIn( 'error', self.conf( { "text/x-code": [".h", ".c", "readme"], "text/plain": "README", }, 'settings/http/static/mime_types', ), 'mime_types same extensions case insensitive', ) @unittest.skip('not yet') def test_static_mime_types_invalid(self): self.assertIn( 'error', self.http( b"""PUT /config/settings/http/static/mime_types/%0%00% HTTP/1.1\r Host: localhost\r Connection: close\r Content-Length: 6\r \r \"blah\"""", raw_resp=True, raw=True, sock_type='unix', addr=self.testdir + '/control.unit.sock', ), 'mime_types invalid', ) if __name__ == '__main__': TestStatic.main()