[Notes] [Git][BuildStream/buildstream][master] 3 commits: Add support for .netrc in remote/tar/zip plugins



Title: GitLab

Valentin David pushed to branch master at BuildStream / buildstream

Commits:

9 changed files:

Changes:

  • .gitlab-ci.yml
    1
    -image: buildstream/testsuite-debian:9-master-119-552f5fc6
    
    1
    +image: buildstream/testsuite-debian:9-master-123-7ce6581b
    
    2 2
     
    
    3 3
     cache:
    
    4 4
       key: "$CI_JOB_NAME-"
    
    ... ... @@ -140,7 +140,7 @@ tests-unix:
    140 140
     
    
    141 141
     tests-fedora-missing-deps:
    
    142 142
       # Ensure that tests behave nicely while missing bwrap and ostree
    
    143
    -  image: buildstream/testsuite-fedora:28-master-119-552f5fc6
    
    143
    +  image: buildstream/testsuite-fedora:28-master-123-7ce6581b
    
    144 144
       <<: *tests
    
    145 145
     
    
    146 146
       script:
    

  • buildstream/plugins/sources/_downloadablefilesource.py
    ... ... @@ -5,16 +5,77 @@ import urllib.request
    5 5
     import urllib.error
    
    6 6
     import contextlib
    
    7 7
     import shutil
    
    8
    +import netrc
    
    8 9
     
    
    9 10
     from buildstream import Source, SourceError, Consistency
    
    10 11
     from buildstream import utils
    
    11 12
     
    
    12 13
     
    
    14
    +class _NetrcFTPOpener(urllib.request.FTPHandler):
    
    15
    +
    
    16
    +    def __init__(self, netrc_config):
    
    17
    +        self.netrc = netrc_config
    
    18
    +
    
    19
    +    def _split(self, netloc):
    
    20
    +        userpass, hostport = urllib.parse.splituser(netloc)
    
    21
    +        host, port = urllib.parse.splitport(hostport)
    
    22
    +        if userpass:
    
    23
    +            user, passwd = urllib.parse.splitpasswd(userpass)
    
    24
    +        else:
    
    25
    +            user = None
    
    26
    +            passwd = None
    
    27
    +        return host, port, user, passwd
    
    28
    +
    
    29
    +    def _unsplit(self, host, port, user, passwd):
    
    30
    +        if port:
    
    31
    +            host = '{}:{}'.format(host, port)
    
    32
    +        if user:
    
    33
    +            if passwd:
    
    34
    +                user = '{}:{}'.format(user, passwd)
    
    35
    +            host = '{}@{}'.format(user, host)
    
    36
    +
    
    37
    +        return host
    
    38
    +
    
    39
    +    def ftp_open(self, req):
    
    40
    +        host, port, user, passwd = self._split(req.host)
    
    41
    +
    
    42
    +        if user is None and self.netrc:
    
    43
    +            entry = self.netrc.authenticators(host)
    
    44
    +            if entry:
    
    45
    +                user, _, passwd = entry
    
    46
    +
    
    47
    +        req.host = self._unsplit(host, port, user, passwd)
    
    48
    +
    
    49
    +        return super().ftp_open(req)
    
    50
    +
    
    51
    +
    
    52
    +class _NetrcPasswordManager:
    
    53
    +
    
    54
    +    def __init__(self, netrc_config):
    
    55
    +        self.netrc = netrc_config
    
    56
    +
    
    57
    +    def add_password(self, realm, uri, user, passwd):
    
    58
    +        pass
    
    59
    +
    
    60
    +    def find_user_password(self, realm, authuri):
    
    61
    +        if not self.netrc:
    
    62
    +            return None, None
    
    63
    +        parts = urllib.parse.urlsplit(authuri)
    
    64
    +        entry = self.netrc.authenticators(parts.hostname)
    
    65
    +        if not entry:
    
    66
    +            return None, None
    
    67
    +        else:
    
    68
    +            login, _, password = entry
    
    69
    +            return login, password
    
    70
    +
    
    71
    +
    
    13 72
     class DownloadableFileSource(Source):
    
    14 73
         # pylint: disable=attribute-defined-outside-init
    
    15 74
     
    
    16 75
         COMMON_CONFIG_KEYS = Source.COMMON_CONFIG_KEYS + ['url', 'ref', 'etag']
    
    17 76
     
    
    77
    +    __urlopener = None
    
    78
    +
    
    18 79
         def configure(self, node):
    
    19 80
             self.original_url = self.node_get_member(node, str, 'url')
    
    20 81
             self.ref = self.node_get_member(node, str, 'ref', None)
    
    ... ... @@ -118,7 +179,8 @@ class DownloadableFileSource(Source):
    118 179
                         if etag and self.get_consistency() == Consistency.CACHED:
    
    119 180
                             request.add_header('If-None-Match', etag)
    
    120 181
     
    
    121
    -                with contextlib.closing(urllib.request.urlopen(request)) as response:
    
    182
    +                opener = self.__get_urlopener()
    
    183
    +                with contextlib.closing(opener.open(request)) as response:
    
    122 184
                         info = response.info()
    
    123 185
     
    
    124 186
                         etag = info['ETag'] if 'ETag' in info else None
    
    ... ... @@ -164,3 +226,19 @@ class DownloadableFileSource(Source):
    164 226
     
    
    165 227
         def _get_mirror_file(self, sha=None):
    
    166 228
             return os.path.join(self._get_mirror_dir(), sha or self.ref)
    
    229
    +
    
    230
    +    def __get_urlopener(self):
    
    231
    +        if not DownloadableFileSource.__urlopener:
    
    232
    +            try:
    
    233
    +                netrc_config = netrc.netrc()
    
    234
    +            except FileNotFoundError:
    
    235
    +                DownloadableFileSource.__urlopener = urllib.request.build_opener()
    
    236
    +            except netrc.NetrcParseError as e:
    
    237
    +                self.warn('{}: While reading .netrc: {}'.format(self, e))
    
    238
    +                return urllib.request.build_opener()
    
    239
    +            else:
    
    240
    +                netrc_pw_mgr = _NetrcPasswordManager(netrc_config)
    
    241
    +                http_auth = urllib.request.HTTPBasicAuthHandler(netrc_pw_mgr)
    
    242
    +                ftp_handler = _NetrcFTPOpener(netrc_config)
    
    243
    +                DownloadableFileSource.__urlopener = urllib.request.build_opener(http_auth, ftp_handler)
    
    244
    +        return DownloadableFileSource.__urlopener

  • dev-requirements.txt
    ... ... @@ -9,3 +9,4 @@ pytest-pep8
    9 9
     pytest-pylint
    
    10 10
     pytest-xdist
    
    11 11
     pytest-timeout
    
    12
    +pyftpdlib

  • tests/sources/remote.py
    ... ... @@ -5,6 +5,7 @@ import pytest
    5 5
     from buildstream._exceptions import ErrorDomain
    
    6 6
     from buildstream import _yaml
    
    7 7
     from tests.testutils import cli
    
    8
    +from tests.testutils.file_server import create_file_server
    
    8 9
     
    
    9 10
     DATA_DIR = os.path.join(
    
    10 11
         os.path.dirname(os.path.realpath(__file__)),
    
    ... ... @@ -22,6 +23,16 @@ def generate_project(project_dir, tmpdir):
    22 23
         }, project_file)
    
    23 24
     
    
    24 25
     
    
    26
    +def generate_project_file_server(server, project_dir):
    
    27
    +    project_file = os.path.join(project_dir, "project.conf")
    
    28
    +    _yaml.dump({
    
    29
    +        'name': 'foo',
    
    30
    +        'aliases': {
    
    31
    +            'tmpdir': server.base_url()
    
    32
    +        }
    
    33
    +    }, project_file)
    
    34
    +
    
    35
    +
    
    25 36
     # Test that without ref, consistency is set appropriately.
    
    26 37
     @pytest.mark.datafiles(os.path.join(DATA_DIR, 'no-ref'))
    
    27 38
     def test_no_ref(cli, tmpdir, datafiles):
    
    ... ... @@ -164,3 +175,35 @@ def test_executable(cli, tmpdir, datafiles):
    164 175
         assert (mode & stat.S_IEXEC)
    
    165 176
         # Assert executable by anyone
    
    166 177
         assert(mode & (stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH))
    
    178
    +
    
    179
    +
    
    180
    +@pytest.mark.parametrize('server_type', ('FTP', 'HTTP'))
    
    181
    +@pytest.mark.datafiles(os.path.join(DATA_DIR, 'single-file'))
    
    182
    +def test_use_netrc(cli, datafiles, server_type, tmpdir):
    
    183
    +    fake_home = os.path.join(str(tmpdir), 'fake_home')
    
    184
    +    os.makedirs(fake_home, exist_ok=True)
    
    185
    +    project = str(datafiles)
    
    186
    +    checkoutdir = os.path.join(str(tmpdir), 'checkout')
    
    187
    +
    
    188
    +    os.environ['HOME'] = fake_home
    
    189
    +    with open(os.path.join(fake_home, '.netrc'), 'wb') as f:
    
    190
    +        os.fchmod(f.fileno(), 0o700)
    
    191
    +        f.write(b'machine 127.0.0.1\n')
    
    192
    +        f.write(b'login testuser\n')
    
    193
    +        f.write(b'password 12345\n')
    
    194
    +
    
    195
    +    with create_file_server(server_type) as server:
    
    196
    +        server.add_user('testuser', '12345', project)
    
    197
    +        generate_project_file_server(server, project)
    
    198
    +
    
    199
    +        server.start()
    
    200
    +
    
    201
    +        result = cli.run(project=project, args=['fetch', 'target.bst'])
    
    202
    +        result.assert_success()
    
    203
    +        result = cli.run(project=project, args=['build', 'target.bst'])
    
    204
    +        result.assert_success()
    
    205
    +        result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
    
    206
    +        result.assert_success()
    
    207
    +
    
    208
    +        checkout_file = os.path.join(checkoutdir, 'file')
    
    209
    +        assert(os.path.exists(checkout_file))

  • tests/sources/tar.py
    ... ... @@ -3,11 +3,13 @@ import pytest
    3 3
     import tarfile
    
    4 4
     import tempfile
    
    5 5
     import subprocess
    
    6
    +import urllib.parse
    
    6 7
     from shutil import copyfile, rmtree
    
    7 8
     
    
    8 9
     from buildstream._exceptions import ErrorDomain
    
    9 10
     from buildstream import _yaml
    
    10 11
     from tests.testutils import cli
    
    12
    +from tests.testutils.file_server import create_file_server
    
    11 13
     from tests.testutils.site import HAVE_LZIP
    
    12 14
     from . import list_dir_contents
    
    13 15
     
    
    ... ... @@ -49,6 +51,16 @@ def generate_project(project_dir, tmpdir):
    49 51
         }, project_file)
    
    50 52
     
    
    51 53
     
    
    54
    +def generate_project_file_server(base_url, project_dir):
    
    55
    +    project_file = os.path.join(project_dir, "project.conf")
    
    56
    +    _yaml.dump({
    
    57
    +        'name': 'foo',
    
    58
    +        'aliases': {
    
    59
    +            'tmpdir': base_url
    
    60
    +        }
    
    61
    +    }, project_file)
    
    62
    +
    
    63
    +
    
    52 64
     # Test that without ref, consistency is set appropriately.
    
    53 65
     @pytest.mark.datafiles(os.path.join(DATA_DIR, 'no-ref'))
    
    54 66
     def test_no_ref(cli, tmpdir, datafiles):
    
    ... ... @@ -302,3 +314,77 @@ def test_read_only_dir(cli, tmpdir, datafiles):
    302 314
                 else:
    
    303 315
                     os.remove(path)
    
    304 316
             rmtree(str(tmpdir), onerror=make_dir_writable)
    
    317
    +
    
    318
    +
    
    319
    +@pytest.mark.parametrize('server_type', ('FTP', 'HTTP'))
    
    320
    +@pytest.mark.datafiles(os.path.join(DATA_DIR, 'fetch'))
    
    321
    +def test_use_netrc(cli, datafiles, server_type, tmpdir):
    
    322
    +    file_server_files = os.path.join(str(tmpdir), 'file_server')
    
    323
    +    fake_home = os.path.join(str(tmpdir), 'fake_home')
    
    324
    +    os.makedirs(file_server_files, exist_ok=True)
    
    325
    +    os.makedirs(fake_home, exist_ok=True)
    
    326
    +    project = str(datafiles)
    
    327
    +    checkoutdir = os.path.join(str(tmpdir), 'checkout')
    
    328
    +
    
    329
    +    os.environ['HOME'] = fake_home
    
    330
    +    with open(os.path.join(fake_home, '.netrc'), 'wb') as f:
    
    331
    +        os.fchmod(f.fileno(), 0o700)
    
    332
    +        f.write(b'machine 127.0.0.1\n')
    
    333
    +        f.write(b'login testuser\n')
    
    334
    +        f.write(b'password 12345\n')
    
    335
    +
    
    336
    +    with create_file_server(server_type) as server:
    
    337
    +        server.add_user('testuser', '12345', file_server_files)
    
    338
    +        generate_project_file_server(server.base_url(), project)
    
    339
    +
    
    340
    +        src_tar = os.path.join(file_server_files, 'a.tar.gz')
    
    341
    +        _assemble_tar(os.path.join(str(datafiles), 'content'), 'a', src_tar)
    
    342
    +
    
    343
    +        server.start()
    
    344
    +
    
    345
    +        result = cli.run(project=project, args=['track', 'target.bst'])
    
    346
    +        result.assert_success()
    
    347
    +        result = cli.run(project=project, args=['fetch', 'target.bst'])
    
    348
    +        result.assert_success()
    
    349
    +        result = cli.run(project=project, args=['build', 'target.bst'])
    
    350
    +        result.assert_success()
    
    351
    +        result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
    
    352
    +        result.assert_success()
    
    353
    +
    
    354
    +        original_dir = os.path.join(str(datafiles), 'content', 'a')
    
    355
    +        original_contents = list_dir_contents(original_dir)
    
    356
    +        checkout_contents = list_dir_contents(checkoutdir)
    
    357
    +        assert(checkout_contents == original_contents)
    
    358
    +
    
    359
    +
    
    360
    +@pytest.mark.parametrize('server_type', ('FTP', 'HTTP'))
    
    361
    +@pytest.mark.datafiles(os.path.join(DATA_DIR, 'fetch'))
    
    362
    +def test_netrc_already_specified_user(cli, datafiles, server_type, tmpdir):
    
    363
    +    file_server_files = os.path.join(str(tmpdir), 'file_server')
    
    364
    +    fake_home = os.path.join(str(tmpdir), 'fake_home')
    
    365
    +    os.makedirs(file_server_files, exist_ok=True)
    
    366
    +    os.makedirs(fake_home, exist_ok=True)
    
    367
    +    project = str(datafiles)
    
    368
    +    checkoutdir = os.path.join(str(tmpdir), 'checkout')
    
    369
    +
    
    370
    +    os.environ['HOME'] = fake_home
    
    371
    +    with open(os.path.join(fake_home, '.netrc'), 'wb') as f:
    
    372
    +        os.fchmod(f.fileno(), 0o700)
    
    373
    +        f.write(b'machine 127.0.0.1\n')
    
    374
    +        f.write(b'login testuser\n')
    
    375
    +        f.write(b'password 12345\n')
    
    376
    +
    
    377
    +    with create_file_server(server_type) as server:
    
    378
    +        server.add_user('otheruser', '12345', file_server_files)
    
    379
    +        parts = urllib.parse.urlsplit(server.base_url())
    
    380
    +        base_url = urllib.parse.urlunsplit([parts[0]] + ['otheruser@{}'.format(parts[1])] + list(parts[2:]))
    
    381
    +        generate_project_file_server(base_url, project)
    
    382
    +
    
    383
    +        src_tar = os.path.join(file_server_files, 'a.tar.gz')
    
    384
    +        _assemble_tar(os.path.join(str(datafiles), 'content'), 'a', src_tar)
    
    385
    +
    
    386
    +        server.start()
    
    387
    +
    
    388
    +        result = cli.run(project=project, args=['track', 'target.bst'])
    
    389
    +        result.assert_main_error(ErrorDomain.STREAM, None)
    
    390
    +        result.assert_task_error(ErrorDomain.SOURCE, None)

  • tests/sources/zip.py
    ... ... @@ -5,6 +5,7 @@ import zipfile
    5 5
     from buildstream._exceptions import ErrorDomain
    
    6 6
     from buildstream import _yaml
    
    7 7
     from tests.testutils import cli
    
    8
    +from tests.testutils.file_server import create_file_server
    
    8 9
     from . import list_dir_contents
    
    9 10
     
    
    10 11
     DATA_DIR = os.path.join(
    
    ... ... @@ -35,6 +36,16 @@ def generate_project(project_dir, tmpdir):
    35 36
         }, project_file)
    
    36 37
     
    
    37 38
     
    
    39
    +def generate_project_file_server(server, project_dir):
    
    40
    +    project_file = os.path.join(project_dir, "project.conf")
    
    41
    +    _yaml.dump({
    
    42
    +        'name': 'foo',
    
    43
    +        'aliases': {
    
    44
    +            'tmpdir': server.base_url()
    
    45
    +        }
    
    46
    +    }, project_file)
    
    47
    +
    
    48
    +
    
    38 49
     # Test that without ref, consistency is set appropriately.
    
    39 50
     @pytest.mark.datafiles(os.path.join(DATA_DIR, 'no-ref'))
    
    40 51
     def test_no_ref(cli, tmpdir, datafiles):
    
    ... ... @@ -176,3 +187,44 @@ def test_stage_explicit_basedir(cli, tmpdir, datafiles):
    176 187
         original_contents = list_dir_contents(original_dir)
    
    177 188
         checkout_contents = list_dir_contents(checkoutdir)
    
    178 189
         assert(checkout_contents == original_contents)
    
    190
    +
    
    191
    +
    
    192
    +@pytest.mark.parametrize('server_type', ('FTP', 'HTTP'))
    
    193
    +@pytest.mark.datafiles(os.path.join(DATA_DIR, 'fetch'))
    
    194
    +def test_use_netrc(cli, datafiles, server_type, tmpdir):
    
    195
    +    file_server_files = os.path.join(str(tmpdir), 'file_server')
    
    196
    +    fake_home = os.path.join(str(tmpdir), 'fake_home')
    
    197
    +    os.makedirs(file_server_files, exist_ok=True)
    
    198
    +    os.makedirs(fake_home, exist_ok=True)
    
    199
    +    project = str(datafiles)
    
    200
    +    checkoutdir = os.path.join(str(tmpdir), 'checkout')
    
    201
    +
    
    202
    +    os.environ['HOME'] = fake_home
    
    203
    +    with open(os.path.join(fake_home, '.netrc'), 'wb') as f:
    
    204
    +        os.fchmod(f.fileno(), 0o700)
    
    205
    +        f.write(b'machine 127.0.0.1\n')
    
    206
    +        f.write(b'login testuser\n')
    
    207
    +        f.write(b'password 12345\n')
    
    208
    +
    
    209
    +    with create_file_server(server_type) as server:
    
    210
    +        server.add_user('testuser', '12345', file_server_files)
    
    211
    +        generate_project_file_server(server, project)
    
    212
    +
    
    213
    +        src_zip = os.path.join(file_server_files, 'a.zip')
    
    214
    +        _assemble_zip(os.path.join(str(datafiles), 'content'), src_zip)
    
    215
    +
    
    216
    +        server.start()
    
    217
    +
    
    218
    +        result = cli.run(project=project, args=['track', 'target.bst'])
    
    219
    +        result.assert_success()
    
    220
    +        result = cli.run(project=project, args=['fetch', 'target.bst'])
    
    221
    +        result.assert_success()
    
    222
    +        result = cli.run(project=project, args=['build', 'target.bst'])
    
    223
    +        result.assert_success()
    
    224
    +        result = cli.run(project=project, args=['checkout', 'target.bst', checkoutdir])
    
    225
    +        result.assert_success()
    
    226
    +
    
    227
    +        original_dir = os.path.join(str(datafiles), 'content', 'a')
    
    228
    +        original_contents = list_dir_contents(original_dir)
    
    229
    +        checkout_contents = list_dir_contents(checkoutdir)
    
    230
    +        assert(checkout_contents == original_contents)

  • tests/testutils/file_server.py
    1
    +from contextlib import contextmanager
    
    2
    +
    
    3
    +from .ftp_server import SimpleFtpServer
    
    4
    +from .http_server import SimpleHttpServer
    
    5
    +
    
    6
    +
    
    7
    +@contextmanager
    
    8
    +def create_file_server(file_server_type):
    
    9
    +    if file_server_type == 'FTP':
    
    10
    +        server = SimpleFtpServer()
    
    11
    +    elif file_server_type == 'HTTP':
    
    12
    +        server = SimpleHttpServer()
    
    13
    +    else:
    
    14
    +        assert False
    
    15
    +
    
    16
    +    try:
    
    17
    +        yield server
    
    18
    +    finally:
    
    19
    +        server.stop()

  • tests/testutils/ftp_server.py
    1
    +import multiprocessing
    
    2
    +
    
    3
    +from pyftpdlib.authorizers import DummyAuthorizer
    
    4
    +from pyftpdlib.handlers import FTPHandler
    
    5
    +from pyftpdlib.servers import FTPServer
    
    6
    +
    
    7
    +
    
    8
    +class SimpleFtpServer(multiprocessing.Process):
    
    9
    +    def __init__(self):
    
    10
    +        super().__init__()
    
    11
    +        self.authorizer = DummyAuthorizer()
    
    12
    +        handler = FTPHandler
    
    13
    +        handler.authorizer = self.authorizer
    
    14
    +        self.server = FTPServer(('127.0.0.1', 0), handler)
    
    15
    +
    
    16
    +    def run(self):
    
    17
    +        self.server.serve_forever()
    
    18
    +
    
    19
    +    def stop(self):
    
    20
    +        self.server.close_all()
    
    21
    +        self.server.close()
    
    22
    +        self.terminate()
    
    23
    +        self.join()
    
    24
    +
    
    25
    +    def allow_anonymous(self, cwd):
    
    26
    +        self.authorizer.add_anonymous(cwd)
    
    27
    +
    
    28
    +    def add_user(self, user, password, cwd):
    
    29
    +        self.authorizer.add_user(user, password, cwd, perm='elradfmwMT')
    
    30
    +
    
    31
    +    def base_url(self):
    
    32
    +        return 'ftp://127.0.0.1:{}'.format(self.server.address[1])

  • tests/testutils/http_server.py
    1
    +import multiprocessing
    
    2
    +import os
    
    3
    +import posixpath
    
    4
    +import html
    
    5
    +import threading
    
    6
    +import base64
    
    7
    +from http.server import SimpleHTTPRequestHandler, HTTPServer, HTTPStatus
    
    8
    +
    
    9
    +
    
    10
    +class Unauthorized(Exception):
    
    11
    +    pass
    
    12
    +
    
    13
    +
    
    14
    +class RequestHandler(SimpleHTTPRequestHandler):
    
    15
    +
    
    16
    +    def get_root_dir(self):
    
    17
    +        authorization = self.headers.get('authorization')
    
    18
    +        if not authorization:
    
    19
    +            if not self.server.anonymous_dir:
    
    20
    +                raise Unauthorized('unauthorized')
    
    21
    +            return self.server.anonymous_dir
    
    22
    +        else:
    
    23
    +            authorization = authorization.split()
    
    24
    +            if len(authorization) != 2 or authorization[0].lower() != 'basic':
    
    25
    +                raise Unauthorized('unauthorized')
    
    26
    +            try:
    
    27
    +                decoded = base64.decodebytes(authorization[1].encode('ascii'))
    
    28
    +                user, password = decoded.decode('ascii').split(':')
    
    29
    +                expected_password, directory = self.server.users[user]
    
    30
    +                if password == expected_password:
    
    31
    +                    return directory
    
    32
    +            except:
    
    33
    +                raise Unauthorized('unauthorized')
    
    34
    +            return None
    
    35
    +
    
    36
    +    def unauthorized(self):
    
    37
    +        shortmsg, longmsg = self.responses[HTTPStatus.UNAUTHORIZED]
    
    38
    +        self.send_response(HTTPStatus.UNAUTHORIZED, shortmsg)
    
    39
    +        self.send_header('Connection', 'close')
    
    40
    +
    
    41
    +        content = (self.error_message_format % {
    
    42
    +            'code': HTTPStatus.UNAUTHORIZED,
    
    43
    +            'message': html.escape(longmsg, quote=False),
    
    44
    +            'explain': html.escape(longmsg, quote=False)
    
    45
    +        })
    
    46
    +        body = content.encode('UTF-8', 'replace')
    
    47
    +        self.send_header('Content-Type', self.error_content_type)
    
    48
    +        self.send_header('Content-Length', str(len(body)))
    
    49
    +        self.send_header('WWW-Authenticate', 'Basic realm="{}"'.format(self.server.realm))
    
    50
    +        self.end_headers()
    
    51
    +        self.end_headers()
    
    52
    +
    
    53
    +        if self.command != 'HEAD' and body:
    
    54
    +            self.wfile.write(body)
    
    55
    +
    
    56
    +    def do_GET(self):
    
    57
    +        try:
    
    58
    +            super().do_GET()
    
    59
    +        except Unauthorized:
    
    60
    +            self.unauthorized()
    
    61
    +
    
    62
    +    def do_HEAD(self):
    
    63
    +        try:
    
    64
    +            super().do_HEAD()
    
    65
    +        except Unauthorized:
    
    66
    +            self.unauthorized()
    
    67
    +
    
    68
    +    def translate_path(self, path):
    
    69
    +        path = path.split('?', 1)[0]
    
    70
    +        path = path.split('#', 1)[0]
    
    71
    +        path = posixpath.normpath(path)
    
    72
    +        assert(posixpath.isabs(path))
    
    73
    +        path = posixpath.relpath(path, '/')
    
    74
    +        return os.path.join(self.get_root_dir(), path)
    
    75
    +
    
    76
    +
    
    77
    +class AuthHTTPServer(HTTPServer):
    
    78
    +    def __init__(self, *args, **kwargs):
    
    79
    +        self.users = {}
    
    80
    +        self.anonymous_dir = None
    
    81
    +        self.realm = 'Realm'
    
    82
    +        super().__init__(*args, **kwargs)
    
    83
    +
    
    84
    +
    
    85
    +class SimpleHttpServer(multiprocessing.Process):
    
    86
    +    def __init__(self):
    
    87
    +        self.__stop = multiprocessing.Queue()
    
    88
    +        super().__init__()
    
    89
    +        self.server = AuthHTTPServer(('127.0.0.1', 0), RequestHandler)
    
    90
    +        self.started = False
    
    91
    +
    
    92
    +    def start(self):
    
    93
    +        self.started = True
    
    94
    +        super().start()
    
    95
    +
    
    96
    +    def run(self):
    
    97
    +        t = threading.Thread(target=self.server.serve_forever)
    
    98
    +        t.start()
    
    99
    +        self.__stop.get()
    
    100
    +        self.server.shutdown()
    
    101
    +        t.join()
    
    102
    +
    
    103
    +    def stop(self):
    
    104
    +        if not self.started:
    
    105
    +            return
    
    106
    +        self.__stop.put(None)
    
    107
    +        self.terminate()
    
    108
    +        self.join()
    
    109
    +
    
    110
    +    def allow_anonymous(self, cwd):
    
    111
    +        self.server.anonymous_dir = cwd
    
    112
    +
    
    113
    +    def add_user(self, user, password, cwd):
    
    114
    +        self.server.users[user] = (password, cwd)
    
    115
    +
    
    116
    +    def base_url(self):
    
    117
    +        return 'http://127.0.0.1:{}'.format(self.server.server_port)



  • [Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]