quickjs-tart

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

test_31_vsftpds.py (12221B)


      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     SUPPORTS_SSL = True
     44 
     45     @pytest.fixture(autouse=True, scope='class')
     46     def vsftpds(self, env):
     47         if not TestVsFTPD.SUPPORTS_SSL:
     48             pytest.skip('vsftpd does not seem to support SSL')
     49         vsftpds = VsFTPD(env=env, with_ssl=True)
     50         if not vsftpds.initial_start():
     51             vsftpds.stop()
     52             TestVsFTPD.SUPPORTS_SSL = False
     53             pytest.skip('vsftpd does not seem to support SSL')
     54         yield vsftpds
     55         vsftpds.stop()
     56 
     57     def _make_docs_file(self, docs_dir: str, fname: str, fsize: int):
     58         fpath = os.path.join(docs_dir, fname)
     59         data1k = 1024*'x'
     60         flen = 0
     61         with open(fpath, 'w') as fd:
     62             while flen < fsize:
     63                 fd.write(data1k)
     64                 flen += len(data1k)
     65         return flen
     66 
     67     @pytest.fixture(autouse=True, scope='class')
     68     def _class_scope(self, env, vsftpds):
     69         if os.path.exists(vsftpds.docs_dir):
     70             shutil.rmtree(vsftpds.docs_dir)
     71         if not os.path.exists(vsftpds.docs_dir):
     72             os.makedirs(vsftpds.docs_dir)
     73         self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1k', fsize=1024)
     74         self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10k', fsize=10*1024)
     75         self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1m', fsize=1024*1024)
     76         self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10m', fsize=10*1024*1024)
     77         env.make_data_file(indir=env.gen_dir, fname="upload-1k", fsize=1024)
     78         env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100*1024)
     79         env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024*1024)
     80 
     81     def test_31_01_list_dir(self, env: Env, vsftpds: VsFTPD):
     82         curl = CurlClient(env=env)
     83         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/'
     84         r = curl.ftp_ssl_get(urls=[url], with_stats=True)
     85         r.check_stats(count=1, http_status=226)
     86         lines = open(os.path.join(curl.run_dir, 'download_#1.data')).readlines()
     87         assert len(lines) == 4, f'list: {lines}'
     88 
     89     # download 1 file, no SSL
     90     @pytest.mark.parametrize("docname", [
     91         'data-1k', 'data-1m', 'data-10m'
     92     ])
     93     def test_31_02_download_1(self, env: Env, vsftpds: VsFTPD, docname):
     94         curl = CurlClient(env=env)
     95         srcfile = os.path.join(vsftpds.docs_dir, f'{docname}')
     96         count = 1
     97         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]'
     98         r = curl.ftp_ssl_get(urls=[url], with_stats=True)
     99         r.check_stats(count=count, http_status=226)
    100         self.check_downloads(curl, srcfile, count)
    101 
    102     @pytest.mark.parametrize("docname", [
    103         'data-1k', 'data-1m', 'data-10m'
    104     ])
    105     def test_31_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname):
    106         curl = CurlClient(env=env)
    107         srcfile = os.path.join(vsftpds.docs_dir, f'{docname}')
    108         count = 10
    109         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]'
    110         r = curl.ftp_ssl_get(urls=[url], with_stats=True)
    111         r.check_stats(count=count, http_status=226)
    112         self.check_downloads(curl, srcfile, count)
    113         assert r.total_connects == count + 1, 'should reuse the control conn'
    114 
    115     @pytest.mark.parametrize("docname", [
    116         'data-1k', 'data-1m', 'data-10m'
    117     ])
    118     def test_31_04_download_10_parallel(self, env: Env, vsftpds: VsFTPD, docname):
    119         curl = CurlClient(env=env)
    120         srcfile = os.path.join(vsftpds.docs_dir, f'{docname}')
    121         count = 10
    122         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]'
    123         r = curl.ftp_ssl_get(urls=[url], with_stats=True, extra_args=[
    124             '--parallel'
    125         ])
    126         r.check_stats(count=count, http_status=226)
    127         self.check_downloads(curl, srcfile, count)
    128         assert r.total_connects > count + 1, 'should have used several control conns'
    129 
    130     @pytest.mark.parametrize("docname", [
    131         'upload-1k', 'upload-100k', 'upload-1m'
    132     ])
    133     def test_31_05_upload_1(self, env: Env, vsftpds: VsFTPD, docname):
    134         curl = CurlClient(env=env)
    135         srcfile = os.path.join(env.gen_dir, docname)
    136         dstfile = os.path.join(vsftpds.docs_dir, docname)
    137         self._rmf(dstfile)
    138         count = 1
    139         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/'
    140         r = curl.ftp_ssl_upload(urls=[url], fupload=f'{srcfile}', with_stats=True)
    141         r.check_stats(count=count, http_status=226)
    142         self.check_upload(env, vsftpds, docname=docname)
    143 
    144     def _rmf(self, path):
    145         if os.path.exists(path):
    146             return os.remove(path)
    147 
    148     # check with `tcpdump` if curl causes any TCP RST packets
    149     @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available")
    150     def test_31_06_shutdownh_download(self, env: Env, vsftpds: VsFTPD):
    151         docname = 'data-1k'
    152         curl = CurlClient(env=env)
    153         count = 1
    154         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]'
    155         r = curl.ftp_ssl_get(urls=[url], with_stats=True, with_tcpdump=True)
    156         r.check_stats(count=count, http_status=226)
    157         # vsftp closes control connection without niceties,
    158         # look only at ports from DATA connection.
    159         data_ports = vsftpds.get_data_ports(r)
    160         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
    161         assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
    162 
    163     # check with `tcpdump` if curl causes any TCP RST packets
    164     @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available")
    165     def test_31_07_shutdownh_upload(self, env: Env, vsftpds: VsFTPD):
    166         docname = 'upload-1k'
    167         curl = CurlClient(env=env)
    168         srcfile = os.path.join(env.gen_dir, docname)
    169         dstfile = os.path.join(vsftpds.docs_dir, docname)
    170         self._rmf(dstfile)
    171         count = 1
    172         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/'
    173         r = curl.ftp_ssl_upload(urls=[url], fupload=f'{srcfile}', with_stats=True, with_tcpdump=True)
    174         r.check_stats(count=count, http_status=226)
    175         # vsftp closes control connection without niceties,
    176         # look only at ports from DATA connection.
    177         data_ports = vsftpds.get_data_ports(r)
    178         assert len(data_ports), f'unable to find FTP data port connected to\n{r.dump_logs()}'
    179         assert len(r.tcpdump.get_rsts(ports=data_ports)) == 0, 'Unexpected TCP RST packets'
    180 
    181     def test_31_08_upload_ascii(self, env: Env, vsftpds: VsFTPD):
    182         docname = 'upload-ascii'
    183         line_length = 21
    184         srcfile = os.path.join(env.gen_dir, docname)
    185         dstfile = os.path.join(vsftpds.docs_dir, docname)
    186         env.make_data_file(indir=env.gen_dir, fname=docname, fsize=100*1024,
    187                            line_length=line_length)
    188         srcsize = os.path.getsize(srcfile)
    189         self._rmf(dstfile)
    190         count = 1
    191         curl = CurlClient(env=env)
    192         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/'
    193         r = curl.ftp_ssl_upload(urls=[url], fupload=f'{srcfile}', with_stats=True,
    194                                 extra_args=['--use-ascii'])
    195         r.check_stats(count=count, http_status=226)
    196         # expect the uploaded file to be number of converted newlines larger
    197         dstsize = os.path.getsize(dstfile)
    198         newlines = len(open(srcfile).readlines())
    199         assert (srcsize + newlines) == dstsize, \
    200             f'expected source with {newlines} lines to be that much larger,'\
    201             f'instead srcsize={srcsize}, upload size={dstsize}, diff={dstsize-srcsize}'
    202 
    203     def test_31_08_active_download(self, env: Env, vsftpds: VsFTPD):
    204         docname = 'data-10k'
    205         curl = CurlClient(env=env)
    206         srcfile = os.path.join(vsftpds.docs_dir, f'{docname}')
    207         count = 1
    208         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]'
    209         r = curl.ftp_ssl_get(urls=[url], with_stats=True, extra_args=[
    210             '--ftp-port', '127.0.0.1'
    211         ])
    212         r.check_stats(count=count, http_status=226)
    213         self.check_downloads(curl, srcfile, count)
    214 
    215     def test_31_09_active_upload(self, env: Env, vsftpds: VsFTPD):
    216         docname = 'upload-1k'
    217         curl = CurlClient(env=env)
    218         srcfile = os.path.join(env.gen_dir, docname)
    219         dstfile = os.path.join(vsftpds.docs_dir, docname)
    220         self._rmf(dstfile)
    221         count = 1
    222         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/'
    223         r = curl.ftp_ssl_upload(urls=[url], fupload=f'{srcfile}', with_stats=True, extra_args=[
    224             '--ftp-port', '127.0.0.1'
    225         ])
    226         r.check_stats(count=count, http_status=226)
    227         self.check_upload(env, vsftpds, docname=docname)
    228 
    229     @pytest.mark.parametrize("indata", [
    230         pytest.param('1234567890', id='10-bytes'),
    231         pytest.param('', id='0-bytes'),
    232     ])
    233     def test_31_10_upload_stdin(self, env: Env, vsftpds: VsFTPD, indata):
    234         curl = CurlClient(env=env)
    235         docname = "upload_31_10"
    236         dstfile = os.path.join(vsftpds.docs_dir, docname)
    237         self._rmf(dstfile)
    238         count = 1
    239         url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}'
    240         r = curl.ftp_ssl_upload(urls=[url], updata=indata, with_stats=True)
    241         r.check_stats(count=count, http_status=226)
    242         assert os.path.exists(dstfile)
    243         destdata = open(dstfile).readlines()
    244         expdata = [indata] if len(indata) else []
    245         assert expdata == destdata, f'expected: {expdata}, got: {destdata}'
    246 
    247     def check_downloads(self, client, srcfile: str, count: int,
    248                         complete: bool = True):
    249         for i in range(count):
    250             dfile = client.download_file(i)
    251             assert os.path.exists(dfile)
    252             if complete and not filecmp.cmp(srcfile, dfile, shallow=False):
    253                 diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(),
    254                                                     b=open(dfile).readlines(),
    255                                                     fromfile=srcfile,
    256                                                     tofile=dfile,
    257                                                     n=1))
    258                 assert False, f'download {dfile} differs:\n{diff}'
    259 
    260     def check_upload(self, env, vsftpd: VsFTPD, docname):
    261         srcfile = os.path.join(env.gen_dir, docname)
    262         dstfile = os.path.join(vsftpd.docs_dir, docname)
    263         assert os.path.exists(srcfile)
    264         assert os.path.exists(dstfile)
    265         if not filecmp.cmp(srcfile, dstfile, shallow=False):
    266             diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(),
    267                                                 b=open(dstfile).readlines(),
    268                                                 fromfile=srcfile,
    269                                                 tofile=dstfile,
    270                                                 n=1))
    271             assert False, f'upload {dstfile} differs:\n{diff}'