##// END OF EJS Templates
wireprotov2: implement commands as a generator of objects...
wireprotov2: implement commands as a generator of objects Previously, wire protocol version 2 inherited version 1's model of having separate types to represent the results of different wire protocol commands. As I implemented more powerful commands in future commits, I found I was using a common pattern of returning a special type to hold a generator. This meant the command function required a closure to do most of the work. That made logic flow more difficult to follow. I also noticed that many commands were effectively a sequence of objects to be CBOR encoded. I think it makes sense to define version 2 commands as generators. This way, commands can simply emit the data structures they wish to send to the client. This eliminates the need for a closure in command functions and removes encoding from the bodies of commands. As part of this commit, the handling of response objects has been moved into the serverreactor class. This puts the reactor in the driver's seat with regards to CBOR encoding and error handling. Having error handling in the function that emits frames is particularly important because exceptions in that function can lead to things getting in a bad state: I'm fairly certain that uncaught exceptions in the frame generator were causing deadlocks. I also introduced a dedicated error type for explicit error reporting in command handlers. This will be used in subsequent commits. There's still a bit of work to be done here, especially around formalizing the error handling "protocol." I've added yet another TODO to track this so we don't forget. Test output changed because we're using generators and no longer know we are at the end of the data until we hit the end of the generator. This means we can't emit the end-of-stream flag until we've exhausted the generator. Hence the introduction of 0-sized end-of-stream frames. Differential Revision: https://phab.mercurial-scm.org/D4472

File last commit:

r36594:cfd0c1df default
r39595:07b58266 default
Show More
get-with-headers.py
112 lines | 3.2 KiB | text/x-python | PythonLexer
/ tests / get-with-headers.py
Eric Hopper
Add a test for getting raw files via the web UI.
r2532 #!/usr/bin/env python
Martin Geisler
tests: fix doc string in get-with-headers.py
r8447 """This does HTTP GET requests given a host:port and path and returns
Eric Hopper
Add a test for getting raw files via the web UI.
r2532 a subset of the headers plus the body of the result."""
Yuya Nishihara
get-with-headers: use bytes stdout thoroughly...
r36594 from __future__ import absolute_import
Gregory Szorc
tests: use absolute_import in /get-with-headers.py...
r27296
Gregory Szorc
tests: use argparse in get-with-headers.py...
r35798 import argparse
Gregory Szorc
tests: use absolute_import in /get-with-headers.py...
r27296 import json
import os
import sys
Patrick Mezard
get-with-headers: fix stream modes under Windows
r7054
Pulkit Goyal
py3: conditionalize httplib import...
r29455 from mercurial import (
util,
)
httplib = util.httplib
Patrick Mezard
get-with-headers: fix stream modes under Windows
r7054 try:
Gregory Szorc
tests: use absolute_import in /get-with-headers.py...
r27296 import msvcrt
Patrick Mezard
get-with-headers: fix stream modes under Windows
r7054 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
except ImportError:
pass
Yuya Nishihara
get-with-headers: use bytes stdout thoroughly...
r36594 stdout = getattr(sys.stdout, 'buffer', sys.stdout)
Gregory Szorc
tests: use argparse in get-with-headers.py...
r35798 parser = argparse.ArgumentParser()
parser.add_argument('--twice', action='store_true')
parser.add_argument('--headeronly', action='store_true')
parser.add_argument('--json', action='store_true')
parser.add_argument('--hgproto')
Gregory Szorc
tests: teach get-with-headers.py some new tricks...
r35799 parser.add_argument('--requestheader', nargs='*', default=[],
help='Send an additional HTTP request header. Argument '
'value is <header>=<value>')
parser.add_argument('--bodyfile',
help='Write HTTP response body to a file')
Gregory Szorc
tests: use argparse in get-with-headers.py...
r35798 parser.add_argument('host')
parser.add_argument('path')
parser.add_argument('show', nargs='*')
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182
Gregory Szorc
tests: use argparse in get-with-headers.py...
r35798 args = parser.parse_args()
twice = args.twice
headeronly = args.headeronly
formatjson = args.json
hgproto = args.hgproto
Gregory Szorc
tests: teach get-with-headers.py some new tricks...
r35799 requestheaders = args.requestheader
Gregory Szorc
protocol: send application/mercurial-0.2 responses to capable clients...
r30764
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182 tag = None
def request(host, path, show):
Mads Kiilerich
tests: prepare get-with-headers.py for MSYS...
r17017 assert not path.startswith('/'), path
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182 global tag
headers = {}
if tag:
headers['If-None-Match'] = tag
Gregory Szorc
protocol: send application/mercurial-0.2 responses to capable clients...
r30764 if hgproto:
headers['X-HgProto-1'] = hgproto
Bryan O'Sullivan
hgweb: return meaningful HTTP status codes instead of nonsense
r5561
Gregory Szorc
tests: teach get-with-headers.py some new tricks...
r35799 for header in requestheaders:
key, value = header.split('=', 1)
headers[key] = value
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182 conn = httplib.HTTPConnection(host)
Mads Kiilerich
tests: prepare get-with-headers.py for MSYS...
r17017 conn.request("GET", '/' + path, None, headers)
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182 response = conn.getresponse()
Yuya Nishihara
get-with-headers: use bytes stdout thoroughly...
r36594 stdout.write(b'%d %s\n' % (response.status,
response.reason.encode('ascii')))
Mads Kiilerich
serve: don't send any content headers with 304 responses...
r18380 if show[:1] == ['-']:
Mads Kiilerich
tests: make test-hgweb.t output stable...
r18393 show = sorted(h for h, v in response.getheaders()
if h.lower() not in show)
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182 for h in [h.lower() for h in show]:
if response.getheader(h, None) is not None:
Yuya Nishihara
get-with-headers: use bytes stdout thoroughly...
r36594 stdout.write(b"%s: %s\n" % (h.encode('ascii'),
response.getheader(h).encode('ascii')))
Pierre-Yves David
get-with-headers: add a --headeronly switch...
r18400 if not headeronly:
Yuya Nishihara
get-with-headers: use bytes stdout thoroughly...
r36594 stdout.write(b'\n')
Gregory Szorc
hgweb: send proper HTTP response after uncaught exception...
r23409 data = response.read()
Gregory Szorc
get-with-headers: support parsing and pretty printing JSON...
r24543
Gregory Szorc
tests: teach get-with-headers.py some new tricks...
r35799 if args.bodyfile:
bodyfh = open(args.bodyfile, 'wb')
else:
Yuya Nishihara
get-with-headers: use bytes stdout thoroughly...
r36594 bodyfh = stdout
Gregory Szorc
tests: teach get-with-headers.py some new tricks...
r35799
Gregory Szorc
get-with-headers: support parsing and pretty printing JSON...
r24543 # Pretty print JSON. This also has the beneficial side-effect
# of verifying emitted JSON is well-formed.
if formatjson:
# json.dumps() will print trailing newlines. Eliminate them
# to make tests easier to write.
data = json.loads(data)
lines = json.dumps(data, sort_keys=True, indent=2).splitlines()
for line in lines:
Gregory Szorc
tests: teach get-with-headers.py some new tricks...
r35799 bodyfh.write(line.rstrip())
bodyfh.write(b'\n')
Gregory Szorc
get-with-headers: support parsing and pretty printing JSON...
r24543 else:
Gregory Szorc
tests: teach get-with-headers.py some new tricks...
r35799 bodyfh.write(data)
if args.bodyfile:
bodyfh.close()
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182
Gregory Szorc
tests: store ETag when using --headeronly...
r31791 if twice and response.getheader('ETag', None):
tag = response.getheader('ETag')
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182
return response.status
Gregory Szorc
tests: use argparse in get-with-headers.py...
r35798 status = request(args.host, args.path, args.show)
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182 if twice:
Gregory Szorc
tests: use argparse in get-with-headers.py...
r35798 status = request(args.host, args.path, args.show)
Dirkjan Ochtman
tests: extend get-with-headers to support cache testing
r12182
if 200 <= status <= 305:
Bryan O'Sullivan
hgweb: return meaningful HTTP status codes instead of nonsense
r5561 sys.exit(0)
sys.exit(1)