quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

test_30_vsftpd.py (10661B)


      1 #!/usr/bin/env python3
      2 # -*- coding: utf-8 -*-
      3 #***************************************************************************
      4 #                                  _   _ ____  _
      5 #  Project                     ___| | | |  _ \| |
      6 #                             / __| | | | |_) | |
      7 #                            | (__| |_| |  _ <| |___
      8 #                             \___|\___/|_| \_\_____|
      9 #
     10 # Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
     11 #
     12 # This software is licensed as described in the file COPYING, which
     13 # you should have received as part of this distribution. The terms
     14 # are also available at https://curl.se/docs/copyright.html.
     15 #
     16 # You may opt to use, copy, modify, merge, publish, distribute and/or sell
     17 # copies of the Software, and permit persons to whom the Software is
     18 # furnished to do so, under the terms of the COPYING file.
     19 #
     20 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     21 # KIND, either express or implied.
     22 #
     23 # SPDX-License-Identifier: curl
     24 #
     25 ###########################################################################
     26 #
     27 import difflib
     28 import filecmp
     29 import logging
     30 import os
     31 import shutil
     32 import pytest
     33 
     34 from testenv import Env, CurlClient, VsFTPD
     35 
     36 
     37 log = logging.getLogger(__name__)
     38 
     39 
     40 @pytest.mark.skipif(condition=not Env.has_vsftpd(), reason="missing vsftpd")
     41 class TestVsFTPD:
     42 
     43     @pytest.fixture(autouse=True, scope='class')
     44     def vsftpd(self, env):
     45         vsftpd = VsFTPD(env=env)
     46         assert vsftpd.initial_start()
     47         yield vsftpd
     48         vsftpd.stop()
     49 
     50     def _make_docs_file(self, docs_dir: str, fname: str, fsize: int):
     51         fpath = os.path.join(docs_dir, fname)
     52         data1k = 1024*'x'
     53         flen = 0
     54         with open(fpath, 'w') as fd:
     55             while flen < fsize:
     56                 fd.write(data1k)
     57                 flen += len(data1k)
     58         return flen
     59 
     60     @pytest.fixture(autouse=True, scope='class')
     61     def _class_scope(self, env, vsftpd):
     62         if os.path.exists(vsftpd.docs_dir):
     63             shutil.rmtree(vsftpd.docs_dir)
     64         if not os.path.exists(vsftpd.docs_dir):
     65             os.makedirs(vsftpd.docs_dir)
     66         self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-1k', fsize=1024)
     67         self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10k', fsize=10*1024)
     68         self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-1m', fsize=1024*1024)
     69         self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10m', fsize=10*1024*1024)
     70         env.make_data_file(indir=env.gen_dir, fname="upload-1k", fsize=1024)
     71         env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100*1024)
     72         env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024*1024)
     73 
     74     def test_30_01_list_dir(self, env: Env, vsftpd: VsFTPD):
     75         curl = CurlClient(env=env)
     76         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/'
     77         r = curl.ftp_get(urls=[url], with_stats=True)
     78         r.check_stats(count=1, http_status=226)
     79         lines = open(os.path.join(curl.run_dir, 'download_#1.data')).readlines()
     80         assert len(lines) == 4, f'list: {lines}'
     81 
     82     # download 1 file, no SSL
     83     @pytest.mark.parametrize("docname", [
     84         'data-1k', 'data-1m', 'data-10m'
     85     ])
     86     def test_30_02_download_1(self, env: Env, vsftpd: VsFTPD, docname):
     87         curl = CurlClient(env=env)
     88         srcfile = os.path.join(vsftpd.docs_dir, f'{docname}')
     89         count = 1
     90         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/{docname}?[0-{count-1}]'
     91         r = curl.ftp_get(urls=[url], with_stats=True)
     92         r.check_stats(count=count, http_status=226)
     93         self.check_downloads(curl, srcfile, count)
     94 
     95     @pytest.mark.parametrize("docname", [
     96         'data-1k', 'data-1m', 'data-10m'
     97     ])
     98     def test_30_03_download_10_serial(self, env: Env, vsftpd: VsFTPD, docname):
     99         curl = CurlClient(env=env)
    100         srcfile = os.path.join(vsftpd.docs_dir, f'{docname}')
    101         count = 10
    102         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/{docname}?[0-{count-1}]'
    103         r = curl.ftp_get(urls=[url], with_stats=True)
    104         r.check_stats(count=count, http_status=226)
    105         self.check_downloads(curl, srcfile, count)
    106         assert r.total_connects == count + 1, 'should reuse the control conn'
    107 
    108     @pytest.mark.parametrize("docname", [
    109         'data-1k', 'data-1m', 'data-10m'
    110     ])
    111     def test_30_04_download_10_parallel(self, env: Env, vsftpd: VsFTPD, docname):
    112         curl = CurlClient(env=env)
    113         srcfile = os.path.join(vsftpd.docs_dir, f'{docname}')
    114         count = 10
    115         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/{docname}?[0-{count-1}]'
    116         r = curl.ftp_get(urls=[url], with_stats=True, extra_args=[
    117             '--parallel'
    118         ])
    119         r.check_stats(count=count, http_status=226)
    120         self.check_downloads(curl, srcfile, count)
    121         assert r.total_connects > count + 1, 'should have used several control conns'
    122 
    123     @pytest.mark.parametrize("docname", [
    124         'upload-1k', 'upload-100k', 'upload-1m'
    125     ])
    126     def test_30_05_upload_1(self, env: Env, vsftpd: VsFTPD, docname):
    127         curl = CurlClient(env=env)
    128         srcfile = os.path.join(env.gen_dir, docname)
    129         dstfile = os.path.join(vsftpd.docs_dir, docname)
    130         self._rmf(dstfile)
    131         count = 1
    132         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/'
    133         r = curl.ftp_upload(urls=[url], fupload=f'{srcfile}', with_stats=True)
    134         r.check_stats(count=count, http_status=226)
    135         self.check_upload(env, vsftpd, docname=docname)
    136 
    137     def _rmf(self, path):
    138         if os.path.exists(path):
    139             return os.remove(path)
    140 
    141     # check with `tcpdump` if curl causes any TCP RST packets
    142     @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available")
    143     def test_30_06_shutdownh_download(self, env: Env, vsftpd: VsFTPD):
    144         docname = 'data-1k'
    145         curl = CurlClient(env=env)
    146         count = 1
    147         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/{docname}?[0-{count-1}]'
    148         r = curl.ftp_get(urls=[url], with_stats=True, with_tcpdump=True)
    149         r.check_stats(count=count, http_status=226)
    150         assert r.tcpdump
    151         # vsftp closes control connection without niceties,
    152         # look only at ports from DATA connection.
    153         data_ports = vsftpd.get_data_ports(r)
    154         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
    155         assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
    156 
    157     # check with `tcpdump` if curl causes any TCP RST packets
    158     @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available")
    159     def test_30_07_shutdownh_upload(self, env: Env, vsftpd: VsFTPD):
    160         docname = 'upload-1k'
    161         curl = CurlClient(env=env)
    162         srcfile = os.path.join(env.gen_dir, docname)
    163         dstfile = os.path.join(vsftpd.docs_dir, docname)
    164         self._rmf(dstfile)
    165         count = 1
    166         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/'
    167         r = curl.ftp_upload(urls=[url], fupload=f'{srcfile}', with_stats=True, with_tcpdump=True)
    168         r.check_stats(count=count, http_status=226)
    169         assert r.tcpdump
    170         # vsftp closes control connection without niceties,
    171         # look only at ports from DATA connection.
    172         data_ports = vsftpd.get_data_ports(r)
    173         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
    174         assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
    175 
    176     def test_30_08_active_download(self, env: Env, vsftpd: VsFTPD):
    177         docname = 'data-10k'
    178         curl = CurlClient(env=env)
    179         srcfile = os.path.join(vsftpd.docs_dir, f'{docname}')
    180         count = 1
    181         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/{docname}?[0-{count-1}]'
    182         r = curl.ftp_get(urls=[url], with_stats=True, extra_args=[
    183             '--ftp-port', '127.0.0.1'
    184         ])
    185         r.check_stats(count=count, http_status=226)
    186         self.check_downloads(curl, srcfile, count)
    187 
    188     def test_30_09_active_up_file(self, env: Env, vsftpd: VsFTPD):
    189         docname = 'upload-1k'
    190         curl = CurlClient(env=env)
    191         srcfile = os.path.join(env.gen_dir, docname)
    192         dstfile = os.path.join(vsftpd.docs_dir, docname)
    193         self._rmf(dstfile)
    194         count = 1
    195         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/'
    196         r = curl.ftp_upload(urls=[url], fupload=f'{srcfile}', with_stats=True, extra_args=[
    197             '--ftp-port', '127.0.0.1'
    198         ])
    199         r.check_stats(count=count, http_status=226)
    200         self.check_upload(env, vsftpd, docname=docname)
    201 
    202     def test_30_10_active_up_ascii(self, env: Env, vsftpd: VsFTPD):
    203         docname = 'upload-1k'
    204         curl = CurlClient(env=env)
    205         srcfile = os.path.join(env.gen_dir, docname)
    206         dstfile = os.path.join(vsftpd.docs_dir, docname)
    207         self._rmf(dstfile)
    208         count = 1
    209         url = f'ftp://{env.ftp_domain}:{vsftpd.port}/'
    210         r = curl.ftp_upload(urls=[url], fupload=f'{srcfile}', with_stats=True, extra_args=[
    211             '--ftp-port', '127.0.0.1', '--use-ascii'
    212         ])
    213         r.check_stats(count=count, http_status=226)
    214         self.check_upload(env, vsftpd, docname=docname, binary=False)
    215 
    216     def check_downloads(self, client, srcfile: str, count: int,
    217                         complete: bool = True):
    218         for i in range(count):
    219             dfile = client.download_file(i)
    220             assert os.path.exists(dfile)
    221             if complete and not filecmp.cmp(srcfile, dfile, shallow=False):
    222                 diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(),
    223                                                     b=open(dfile).readlines(),
    224                                                     fromfile=srcfile,
    225                                                     tofile=dfile,
    226                                                     n=1))
    227                 assert False, f'download {dfile} differs:\n{diff}'
    228 
    229     def check_upload(self, env, vsftpd: VsFTPD, docname, binary=True):
    230         srcfile = os.path.join(env.gen_dir, docname)
    231         dstfile = os.path.join(vsftpd.docs_dir, docname)
    232         assert os.path.exists(srcfile)
    233         assert os.path.exists(dstfile)
    234         if not filecmp.cmp(srcfile, dstfile, shallow=False):
    235             diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(),
    236                                                 b=open(dstfile).readlines(),
    237                                                 fromfile=srcfile,
    238                                                 tofile=dstfile,
    239                                                 n=1))
    240             assert not binary and len(diff) == 0, f'upload {dstfile} differs:\n{diff}'