Skip to content
This repository was archived by the owner on Jan 13, 2021. It is now read-only.

Commit 9700492

Browse files
committed
Added 'hyper' command as CLI
1 parent 26ae40d commit 9700492

File tree

4 files changed

+173
-1
lines changed

4 files changed

+173
-1
lines changed

hyper/cli.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
hyper/cli
4+
~~~~~~~~~
5+
6+
Command line interface for hyper.
7+
"""
8+
import argparse
9+
import logging
10+
import sys
11+
12+
from hyper import HTTP20Connection
13+
14+
log = logging.getLogger('hyper')
15+
16+
_ARGUMENT_DEFAULTS = {
17+
'encoding': 'utf-8',
18+
'host': None,
19+
'nullout': False,
20+
'method': 'GET',
21+
'path': '/',
22+
'verbose': False,
23+
}
24+
25+
26+
def parse_argument(argv=None):
27+
parser = argparse.ArgumentParser()
28+
parser.set_defaults(**_ARGUMENT_DEFAULTS)
29+
30+
# positional arguments
31+
parser.add_argument('host', help='set host to request')
32+
parser.add_argument(
33+
'path', nargs='?',
34+
help='set path to resource (default: /)')
35+
36+
# optional arguments
37+
parser.add_argument(
38+
'-e', '--encoding',
39+
help='set charset for content-type')
40+
parser.add_argument(
41+
'-n', '--nullout', action='store_true',
42+
help='do not show response data')
43+
parser.add_argument(
44+
'-m', '--method',
45+
help='set http method (default: GET)')
46+
parser.add_argument(
47+
'-v', '--verbose', action='store_true',
48+
help='set verbose mode (loglevel=DEBUG)')
49+
50+
args = parser.parse_args(sys.argv[1:] if argv is None else argv)
51+
return args
52+
53+
54+
def request(args):
55+
conn = HTTP20Connection(args.host)
56+
conn.request(args.method, args.path)
57+
response = conn.getresponse()
58+
return response.read()
59+
60+
61+
def main(argv=None):
62+
args = parse_argument(argv)
63+
if args.verbose:
64+
handler = logging.StreamHandler()
65+
handler.setLevel(logging.DEBUG)
66+
log.addHandler(handler)
67+
log.setLevel(logging.DEBUG)
68+
69+
data = request(args)
70+
if not args.nullout:
71+
print(data.decode(args.encoding))
72+
73+
74+
if __name__ == '__main__':
75+
main()

hyper/http20/tls.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
66
Contains the TLS/SSL logic for use in hyper.
77
"""
8+
import logging
89
import os.path as path
910

1011
from ..compat import ignore_missing, ssl
1112

13+
log = logging.getLogger(__name__)
1214

1315
NPN_PROTOCOL = 'h2-16'
1416
H2_NPN_PROTOCOLS = [NPN_PROTOCOL, 'h2-15', 'h2-14'] # All h2s we support.
@@ -41,7 +43,9 @@ def wrap_socket(sock, server_hostname):
4143
ssl.match_hostname(ssl_sock.getpeercert(), server_hostname)
4244

4345
with ignore_missing():
44-
assert ssl_sock.selected_npn_protocol() in H2_NPN_PROTOCOLS
46+
selected_npn = ssl_sock.selected_npn_protocol()
47+
log.debug('Selected NPN: %s' % selected_npn)
48+
assert selected_npn in H2_NPN_PROTOCOLS
4549

4650
return ssl_sock
4751

setup.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,9 @@ def resolve_install_requires():
6363
'Programming Language :: Python :: Implementation :: CPython',
6464
],
6565
install_requires=resolve_install_requires(),
66+
entry_points={
67+
'console_scripts': [
68+
'hyper = hyper.cli:main',
69+
],
70+
},
6671
)

test/test_cli.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# -*- coding: utf-8 -*-
2+
from hyper.cli import _ARGUMENT_DEFAULTS
3+
from hyper.cli import main, parse_argument
4+
5+
import pytest
6+
7+
8+
@pytest.mark.parametrize('argv', [
9+
['example.com'],
10+
['example.com', '/home'],
11+
['-v', 'example.com'],
12+
['-n', 'example.com'],
13+
], ids=[
14+
'specified host',
15+
'specified host with path',
16+
'specified host with "-v/--verbose" option',
17+
'specified host with "-n/--nullout" option',
18+
])
19+
def test_cli_normal(monkeypatch, argv):
20+
monkeypatch.setattr('hyper.cli.HTTP20Connection', DummyConnection)
21+
main(argv)
22+
assert True
23+
24+
25+
@pytest.mark.parametrize('argv', [
26+
[],
27+
['-h'],
28+
], ids=[
29+
'specified no argument',
30+
'specified "-h" option',
31+
])
32+
def test_cli_with_system_exit(argv):
33+
with pytest.raises(SystemExit):
34+
main(argv)
35+
36+
37+
@pytest.mark.parametrize('argv', [
38+
{'host': 'example.com'},
39+
{'host': 'example.com', 'path': '/home'},
40+
{'host': 'example.com', 'encoding': 'latin-1'},
41+
{'host': 'example.com', 'nullout': True},
42+
{'host': 'example.com', 'method': 'POST'},
43+
{'host': 'example.com', 'verbose': True},
44+
], ids=[
45+
'specified host',
46+
'specified host with path',
47+
'specified host with "-e/--encoding" option',
48+
'specified host with "-m/--method" option',
49+
'specified host with "-n/--nullout" option',
50+
'specified host with "-v/--verbose" option',
51+
])
52+
def test_cli_parse_argument(argv):
53+
d = _ARGUMENT_DEFAULTS.copy()
54+
d.update(argv)
55+
_argv = ['-e', d['encoding'], '-m', d['method']]
56+
for key, value in d.items():
57+
if isinstance(value, bool) and value is True:
58+
_argv.append('--%s' % key)
59+
_argv.extend([d['host'], d['path']])
60+
61+
args = parse_argument(_argv)
62+
for key in d.keys():
63+
assert getattr(args, key) == d[key]
64+
65+
66+
def test_cli_with_main(monkeypatch):
67+
monkeypatch.setattr('sys.argv', ['./hyper'])
68+
import imp
69+
import hyper.cli
70+
with pytest.raises(SystemExit):
71+
imp.load_source('__main__', hyper.cli.__file__)
72+
73+
74+
# mock for testing
75+
class DummyResponse(object):
76+
def read(self):
77+
return b'<html>dummy</html>'
78+
79+
80+
class DummyConnection(object):
81+
def __init__(self, host):
82+
self.host = host
83+
84+
def request(self, method, path):
85+
return method, path
86+
87+
def getresponse(self):
88+
return DummyResponse()

0 commit comments

Comments
 (0)