-
Notifications
You must be signed in to change notification settings - Fork 188
Conversation
So I may be a bit out of my role as a user, but this seems best as a 3rd party package, something that is built with hyper but does not need to be included in hyper itself. I see this as being something like what httpie is in relation to requests. Perhaps you could even implement this in httpie. @jakubroztocil care to comment as to whether httpie would like this? |
Thank you for information. I didn't know |
Holy wow @t2y, this is fantastic! I was briefly working on a tool like this myself but never got around to finishing or committing it. I think having httpie grab this function would be interesting, and I'm happy to work with @jakubroztocil to make that happen, but in the meantime I think this is a great value-add for hyper. It should make it much easier for people to give HTTP/2 a try and to report bugs with the library. With that said, I think we should emulate the CLI of httpie. Not all of it, but the initial feature set should use the same arguments and flags as httpie. Basically, don't add any new features (in this pull request), but do change the argument parsing to match httpie. |
Thank you for your advice.
I see. I'll investigate httpie more and more.
I agree with you. My original motivation is to be able to use hyper quickly and simply for new users. |
@t2y 👍 for emulating HTTPie's CLI for these reasons:
@Lukasa I see there is already a |
@jakubroztocil Doing it as a plugin is definitely the way forward, because right now we can't do upgrade very well, so you'd have to specify HTTP/2 ahead of time. Let me know if you want some help with the work. |
@Lukasa I've updated the HTTPie plugin API to support transport adapters and created
$ http --debug https://www.twitter.com
HTTPie 1.0.0-dev
HTTPie data: /Users/jakub/.httpie
Requests 2.5.1
Pygments 2.0.2
Python 3.4.2 (default, Oct 17 2014, 12:53:14)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.51)] darwin
>>> requests.request({'allow_redirects': False,
'auth': None,
'cert': None,
'data': OrderedDict(),
'files': DataDict(),
'headers': {'User-Agent': b'HTTPie/1.0.0-dev'},
'method': 'get',
'params': ParamsDict(),
'proxies': {},
'stream': True,
'timeout': 30,
'url': 'https://www.twitter.com',
'verify': True})
Traceback (most recent call last):
File "/usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/runpy.py", line 170, in _run_module_as_main
"__main__", mod_spec)
File "/usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/Users/jakub/Code/httpie/httpie/httpie/__main__.py", line 10, in <module>
sys.exit(main())
File "/Users/jakub/Code/httpie/httpie/httpie/core.py", line 112, in main
response = get_response(args, config_dir=env.config.directory)
File "/Users/jakub/Code/httpie/httpie/httpie/client.py", line 36, in get_response
response = requests_session.request(**kwargs)
File "/Users/jakub/.virtualenvs/httpie-py34/lib/python3.4/site-packages/requests/sessions.py", line 461, in request
resp = self.send(prep, **send_kwargs)
File "/Users/jakub/.virtualenvs/httpie-py34/lib/python3.4/site-packages/requests/sessions.py", line 573, in send
r = adapter.send(request, **kwargs)
File "/Users/jakub/.virtualenvs/httpie-py34/lib/python3.4/site-packages/hyper/contrib.py", line 59, in send
request.headers
File "/Users/jakub/.virtualenvs/httpie-py34/lib/python3.4/site-packages/hyper/http20/connection.py", line 149, in request
self.endheaders(message_body=body, final=True, stream_id=stream_id)
File "/Users/jakub/.virtualenvs/httpie-py34/lib/python3.4/site-packages/hyper/http20/connection.py", line 297, in endheaders
self.connect()
File "/Users/jakub/.virtualenvs/httpie-py34/lib/python3.4/site-packages/hyper/http20/connection.py", line 204, in connect
sock = wrap_socket(sock, self.host)
File "/Users/jakub/.virtualenvs/httpie-py34/lib/python3.4/site-packages/hyper/http20/tls.py", line 43, in wrap_socket
assert ssl_sock.selected_npn_protocol() == NPN_PROTOCOL
AssertionError
Any idea how to fix that? ( Python 2.7 gives me this error: $ http --debug https://twitter.com
HTTPie 1.0.0-dev
HTTPie data: /Users/jakub/.httpie
Requests 2.5.1
Pygments 2.0.2
Python 2.7.9 (default, Dec 15 2014, 10:01:34)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] darwin
>>> requests.request({'allow_redirects': False,
'auth': None,
'cert': None,
'data': OrderedDict(),
'files': DataDict(),
'headers': {'User-Agent': 'HTTPie/1.0.0-dev'},
'method': 'get',
'params': ParamsDict(),
'proxies': {},
'stream': True,
'timeout': 30,
'url': u'https://twitter.com',
'verify': True})
Traceback (most recent call last):
File "/Users/jakub/.virtualenvs/httpie/bin/http", line 9, in <module>
load_entry_point('httpie==1.0.0-dev', 'console_scripts', 'http')()
File "/Users/jakub/Code/httpie/httpie/httpie/core.py", line 112, in main
response = get_response(args, config_dir=env.config.directory)
File "/Users/jakub/Code/httpie/httpie/httpie/client.py", line 36, in get_response
response = requests_session.request(**kwargs)
File "/Users/jakub/.virtualenvs/httpie/lib/python2.7/site-packages/requests/sessions.py", line 461, in request
resp = self.send(prep, **send_kwargs)
File "/Users/jakub/.virtualenvs/httpie/lib/python2.7/site-packages/requests/sessions.py", line 573, in send
r = adapter.send(request, **kwargs)
File "/Users/jakub/.virtualenvs/httpie/lib/python2.7/site-packages/hyper/contrib.py", line 59, in send
request.headers
File "/Users/jakub/.virtualenvs/httpie/lib/python2.7/site-packages/hyper/http20/connection.py", line 149, in request
self.endheaders(message_body=body, final=True, stream_id=stream_id)
File "/Users/jakub/.virtualenvs/httpie/lib/python2.7/site-packages/hyper/http20/connection.py", line 297, in endheaders
self.connect()
File "/Users/jakub/.virtualenvs/httpie/lib/python2.7/site-packages/hyper/http20/connection.py", line 204, in connect
sock = wrap_socket(sock, self.host)
File "/Users/jakub/.virtualenvs/httpie/lib/python2.7/site-packages/hyper/http20/tls.py", line 32, in wrap_socket
_context = _init_context()
File "/Users/jakub/.virtualenvs/httpie/lib/python2.7/site-packages/hyper/http20/tls.py", line 52, in _init_context
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
AttributeError: 'NoneType' object has no attribute 'SSLContext' Also, is there a way to enable |
@jakubroztocil Yeah, that assertion error is not as clean as it should be. Essentially, we're not negotiating the correct HTTP/2 draft with Twitter. The released version of hyper only claims support for h2-14, despite the fact that h2-16 and h2-15 should be binary-compatible with h2-14. Twitter advertises h2-15, so we need to advertise support for it if necessary. I can push a release that causes hyper to do that. That said, my code seems to be having some trouble talking to Twitter at the moment (error code PROTOCOL_ERROR), so let me dig into that a bit before I do so. Currently As for enabling |
Suspect Twitter doesn't like one of my SETTINGS values, because nghttp2 has no complaints. |
Yup, definitely a settings issue. From the logs:
|
So Twitter doesn't like us sending a value for ENABLE_PUSH. Weird. Setting other settings values seems to work just fine. |
Note that it doesn't matter what value I send for ENABLE_PUSH, either 0 or 1, Twitter seems to barf on it. |
Ok, so I'm going to push the branch that advertises h2-15 and h2-16 support as well as h2-14, but note that Twitter is still misbehaving. @jakubroztocil, try testing against |
Interesting, I'm not getting it locally unless I update to a newer @tatsuhiro-t: With a recent Mozilla certificate bundle we're getting cert verification errors on nghttp2.org. Any thoughts as to why that might be? |
Confusingly, if I use the PEM file with openssl s_client it connects just fine. |
@Lukasa I'm using the defaults: $ mkvirtualenv hyper-test -p `which python3`
Running virtualenv with interpreter /usr/local/bin/python3
Using base prefix '/usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4'
[…]
(hyper-test)~ $ pip install hyper
Downloading/unpacking hyper
Downloading hyper-0.1.0-py2.py3-none-any.whl (212kB): 212kB downloaded
Installing collected packages: hyper
Successfully installed hyper
Cleaning up...
(hyper-test)~ $ python
>>> from hyper import HTTP20Connection
>>> conn = HTTP20Connection('nghttp2.org:443')
>>> conn.request('GET', '/')
---------------------------------------------------------------------------
SSLError Traceback (most recent call last)
<ipython-input-3-685be65690da> in <module>()
----> 1 conn.request('GET', '/')
/Users/jakub/.virtualenvs/hyper-test/lib/python3.4/site-packages/hyper/http20/connection.py in request(self, method, url, body, headers)
147 body = body.encode('utf-8')
148
--> 149 self.endheaders(message_body=body, final=True, stream_id=stream_id)
150
151 return stream_id
/Users/jakub/.virtualenvs/hyper-test/lib/python3.4/site-packages/hyper/http20/connection.py in endheaders(self, message_body, final, stream_id)
295 :returns: Nothing.
296 """
--> 297 self.connect()
298
299 stream = self._get_stream(stream_id)
/Users/jakub/.virtualenvs/hyper-test/lib/python3.4/site-packages/hyper/http20/connection.py in connect(self)
202 if self._sock is None:
203 sock = socket.create_connection((self.host, self.port), 5)
--> 204 sock = wrap_socket(sock, self.host)
205 self._sock = BufferedSocket(sock, self.network_buffer_size)
206
/Users/jakub/.virtualenvs/hyper-test/lib/python3.4/site-packages/hyper/http20/tls.py in wrap_socket(sock, server_hostname)
33
34 # the spec requires SNI support
---> 35 ssl_sock = _context.wrap_socket(sock, server_hostname=server_hostname)
36 # Setting SSLContext.check_hostname to True only verifies that the
37 # post-handshake servername matches that of the certificate. We also need to
/usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/ssl.py in wrap_socket(self, sock, server_side, do_handshake_on_connect, suppress_ragged_eofs, server_hostname)
362 suppress_ragged_eofs=suppress_ragged_eofs,
363 server_hostname=server_hostname,
--> 364 _context=self)
365
366 def set_npn_protocols(self, npn_protocols):
/usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/ssl.py in __init__(self, sock, keyfile, certfile, server_side, cert_reqs, ssl_version, ca_certs, do_handshake_on_connect, family, type, proto, fileno, suppress_ragged_eofs, npn_protocols, ciphers, server_hostname, _context)
576 # non-blocking
577 raise ValueError("do_handshake_on_connect should not be specified for non-blocking sockets")
--> 578 self.do_handshake()
579
580 except (OSError, ValueError):
/usr/local/Cellar/python3/3.4.2_1/Frameworks/Python.framework/Versions/3.4/lib/python3.4/ssl.py in do_handshake(self, block)
803 if timeout == 0.0 and block:
804 self.settimeout(None)
--> 805 self._sslobj.do_handshake()
806 finally:
807 self.settimeout(timeout)
SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600) |
Aha, no. That's with old OpenSSL. With 1.0.2 I get the following:
|
Hello. nghttp2.org certificate expires coming April, but it should be ok until then. |
Arg I'm really confused. It seems to be a bug in OpenSSL 1.0.2. @jakubroztocil, are you running OpenSSL 1.0.2 as installed from Homebrew by any chance? Observe:
|
@Lukasa Yep:
|
Interesting stuff...@jakubroztocil can you reproduce the test I just ran to confirm you see the same difference in behaviour between the system OpenSSL and 1.0.2? If you can, I think we may have stumbled into an OpenSSL 1.0.2 bug (or at least a Homebrew bug). |
~ $ openssl version
OpenSSL 0.9.8zc 15 Oct 2014
~ $ openssl s_client -CAfile ~/.virtualenvs/hyper-test/lib/python3.4/site-packages/hyper/certs.pem -connect nghttp2.org:443
CONNECTED(00000003)
depth=2 /C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
verify return:1
depth=1 /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2
verify return:1
depth=0 /OU=Domain Control Validated/CN=*.nghttp2.org
verify return:1
---
Certificate chain
0 s:/OU=Domain Control Validated/CN=*.nghttp2.org
i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2
1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2
i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
--- ~ $ /usr/local/Cellar/openssl/1.0.2/bin/openssl version
OpenSSL 1.0.2 22 Jan 2015
~ $
~ $ /usr/local/Cellar/openssl/1.0.2/bin/openssl s_client -CAfile ~/.virtualenvs/hyper-test/lib/python3.4/site-packages/hyper/certs.pem -connect nghttp2.org:443
CONNECTED(00000003)
depth=2 C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
verify return:1
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Domain Validation CA - SHA256 - G2
verify return:1
depth=0 OU = Domain Control Validated, CN = *.nghttp2.org
verify return:1
---
Certificate chain
0 s:/OU=Domain Control Validated/CN=*.nghttp2.org
i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2
1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2
i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
--- |
Ah, interesting, so you don't reproduce it. Clearly my one is busted, I'll reinstall. |
Actually, sorry @jakubroztocil, but can you test using the |
$ /usr/local/Cellar/openssl/1.0.2/bin/openssl s_client -CAfile certs.pem -connect nghttp2.org:443
CONNECTED(00000003)
depth=2 C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
verify return:1
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Domain Validation CA - SHA256 - G2
verify return:1
depth=0 OU = Domain Control Validated, CN = *.nghttp2.org
verify return:1
---
Certificate chain
0 s:/OU=Domain Control Validated/CN=*.nghttp2.org
i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2
1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2
i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
--- |
Interesting, so this is only happening to me, but it's very consistently happening to me. I've done a reinstall and that hasn't helped at all: neither has refreshing the certificate file. |
Additionally, if I print out the certificate itself from my 1.0.2 there's clearly no problem with it:
|
Unsurprisingly, that didn't work out... |
Hmm. I'm going to ignore this for the moment. @jakubroztocil Care to try testing against nghttp2, but replacing the hyper |
@Lukasa The certificate you are seeing did exist: https://2014.globalsign.com/ |
|
That said, the GlobalSign root CA I have in my keychain seems to match the working one. |
Here is the pem for grepping: https://www.tbs-certificates.co.uk/FAQ/en/545.html |
And: https://metaclassical.com/how-expired-root-certificates-broke-my-python-app-build/ |
Hurrah, that worked @t-8ch! Once again I owe you big-time: if we're ever at the same conference I really have to buy you dinner. |
Alrighty, so I'm running a travis build now that will hopefully pass most of its release tests (it won't pass Twitter because of silly Twitter-ness, but I'll try to find someone to talk to about that). If/when that happens I'll release a new |
@Lukasa awesome |
Thanks. It's really exciting. I tried, but an error happened. |
I changed to emulate the CLI of Httpie in a limited way. $ hyper -h
usage: hyper [-h] [--version] [--debug]
[METHOD] URL [REQUEST_ITEM [REQUEST_ITEM ...]]
positional arguments:
METHOD
The HTTP method to be used for the request
(GET, POST, PUT, DELETE, ...).
URL
The scheme defaults to 'https://' if the URL does not include one.
REQUEST_ITEM
Optional key-value pairs to be included in the request.
The separator used determines the type:
':' HTTP headers:
Referer:http://httpie.org Cookie:foo=bar User-Agent:bacon/1.0
'==' URL parameters to be appended to the request URI:
search==hyper
'=' Data fields to be serialized into a JSON object:
name=Hyper language=Python description='CLI HTTP client'
optional arguments:
-h, --help show this help message and exit
--version Show version and exit.
--debug Show debugging information (loglevel=DEBUG) This
NOTE: I haven't implemented the Httpie's escaping process since it's a bit of complexity. For example,
$ hyper GET https://nghttp2.org/httpbin/get x-test1:header1 x-test2:header2 p1==test1 p2==test2
{'args': {'p1': 'test1', 'p2': 'test2'},
'headers': {'Connection': 'close',
'Host': 'httpbin',
'Via': '2.0 nghttpx',
'X-Test1': 'header1',
'X-Test2': 'header2'},
'origin': '118.22.213.163',
'url': 'https://httpbin/get?p2=test2&p1=test1'}
Note: The postdata is serialized into a JSON object only. For simplicity, it doesn't have Content Types options ( $ hyper POST https://nghttp2.org/httpbin/post name=hyper version=2.0
{'args': {},
'data': '{"version": "2.0", "name": "hyper"}',
'files': {},
'form': {},
'headers': {'Connection': 'close',
'Content-Length': '35',
'Content-Type': 'application/json; charset=utf-8',
'Host': 'httpbin',
'Via': '2.0 nghttpx'},
'json': {'name': 'hyper', 'version': '2.0'},
'origin': '118.22.213.163',
'url': 'https://httpbin/post'}
$ hyper --version
0.2.0
$ hyper --debug ...
...
(show log.debug() info)
... |
Fantastic! I'll grab this branch and give it a play, confirm everything looks good. |
setattr(info, attr, value) | ||
|
||
if info.scheme == 'http': | ||
info.port = 80 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will override a port set by the user by hand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that's a bug. I'll fix it.
Alright, a couple of tiny nits, but this is almost ready to go! |
@Lukasa is ready to merge? just ping. |
Sorry, you're quite right, I lost track of this. =D |
Add hyper as a Command Line Interface
LGTM, thanks so much @t2y! 🍰 |
No problem. Thank you, too! 😄 |
I made the
hyper
command to run hyper quickly.To use cli, install again hyper.
For example, it's easy to run for simple debugging.
Other options are here. They're encoding or do not show response data, and so on.
I'll implement if you want additional function or typical use. I have no idea what kind of functions is useful.