From b33695ef0e73581dd73135c41d2143800c0d865d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 27 Jan 2016 13:45:31 +0000 Subject: [PATCH 1/3] Include websockets by default --- coreapi/commandline.py | 26 +++++++++++++- coreapi/transports/__init__.py | 5 +-- coreapi/transports/websockets.py | 60 ++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 coreapi/transports/websockets.py diff --git a/coreapi/commandline.py b/coreapi/commandline.py index bd3674a..fcf59fe 100644 --- a/coreapi/commandline.py +++ b/coreapi/commandline.py @@ -65,7 +65,8 @@ def get_client(): credentials = get_credentials() headers = get_headers() http_transport = coreapi.transports.HTTPTransport(credentials, headers) - return coreapi.Client(transports=[http_transport]) + websocket_transport = coreapi.transports.WebSocketsTransport() + return coreapi.Client(transports=[http_transport, websocket_transport]) def get_document(): @@ -134,6 +135,28 @@ def get(url): set_history(history) +@click.command(help='Watch a document at the given URL.') +@click.argument('url') +def watch(url): + client = get_client() + history = get_history() + heading = click.style('Watching %s' % url, bold=True) + watched = client.get(url) + try: + for doc in watched: + click.clear() + click.echo(heading) + click.echo(display(doc)) + if isinstance(doc, coreapi.Document): + history = history.add(doc) + set_document(doc) + set_history(history) + except coreapi.exceptions.ErrorMessage as exc: + click.echo(display(exc.error)) + sys.exit(1) + + + @click.command(help='Load a document from disk.') @click.argument('input_file', type=click.File('rb')) @click.option('--format', default='corejson', type=click.Choice(['corejson', 'hal', 'hyperschema'])) @@ -546,6 +569,7 @@ def history_forward(): client.add_command(get) +client.add_command(watch) client.add_command(show) client.add_command(action) client.add_command(reload_document, name='reload') diff --git a/coreapi/transports/__init__.py b/coreapi/transports/__init__.py index b61b817..d6d5f68 100644 --- a/coreapi/transports/__init__.py +++ b/coreapi/transports/__init__.py @@ -3,14 +3,15 @@ from coreapi.exceptions import TransportError from coreapi.transports.base import BaseTransport from coreapi.transports.http import HTTPTransport +from coreapi.transports.websockets import WebSocketsTransport import itypes __all__ = [ - 'BaseTransport', 'HTTPTransport' + 'BaseTransport', 'HTTPTransport', 'WebSocketsTransport' ] -default_transports = itypes.List([HTTPTransport()]) +default_transports = itypes.List([HTTPTransport(), WebSocketsTransport()]) def determine_transport(url, transports=default_transports): diff --git a/coreapi/transports/websockets.py b/coreapi/transports/websockets.py new file mode 100644 index 0000000..0fd6547 --- /dev/null +++ b/coreapi/transports/websockets.py @@ -0,0 +1,60 @@ +from coreapi.codecs import negotiate_decoder, default_decoders +from coreapi.compat import force_bytes +from coreapi.transports.base import BaseTransport +from websocket import create_connection +from websocket._exceptions import WebSocketConnectionClosedException +import json +import jsonpatch + + +def _get_headers_and_body(content): + head, body = content.split('\n\n', 1) + key_value_pairs = [line.split(':', 1) for line in head.splitlines()] + headers = dict([ + (key.strip().lower(), value.strip()) + for key, value in key_value_pairs + ]) + return (headers, body) + + +def _decode_content(headers, content, decoders=None, base_url=None): + content_type = headers.get('content-type') + codec = negotiate_decoder(content_type, decoders=decoders) + return codec.load(content, base_url=base_url) + + +def _apply_diff(content, diff): + # TODO: Negotiate diff scheme. + previous = json.loads(content.decode('utf-8')) + new = jsonpatch.apply_patch(previous, diff) + return force_bytes(json.dumps(new)) + + +def _get_request(decoders=None): + # TODO: Include User-Agent, X-Accept-Diff + if decoders is None: + decoders = default_decoders + + accept = ', '.join([decoder.media_type for decoder in decoders]) + return 'Accept: %s\n\n' % accept + + +class WebSocketsTransport(BaseTransport): + schemes = ['ws', 'wss'] + + def transition(self, link, params=None, decoders=None, link_ancestors=None): + url = link.url + ws = create_connection(url) + request = _get_request(decoders) + ws.send(request) + content = ws.recv() + headers, body = _get_headers_and_body(content) + yield _decode_content(headers, body, decoders=decoders, base_url=url) + while True: + try: + diff = ws.recv() + except WebSocketConnectionClosedException: + return + patch = jsonpatch.JsonPatch.from_string(diff) + body = json.dumps(jsonpatch.apply_patch(json.loads(body), patch)) + yield _decode_content(headers, body, decoders=decoders, base_url=url) From 0c0ccce8f57911dd9883c85fab8d80a97b30b955 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 27 Jan 2016 17:04:09 +0000 Subject: [PATCH 2/3] Minor tweaks --- coreapi/transports/websockets.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/coreapi/transports/websockets.py b/coreapi/transports/websockets.py index 0fd6547..01969ad 100644 --- a/coreapi/transports/websockets.py +++ b/coreapi/transports/websockets.py @@ -23,14 +23,14 @@ def _decode_content(headers, content, decoders=None, base_url=None): return codec.load(content, base_url=base_url) -def _apply_diff(content, diff): - # TODO: Negotiate diff scheme. - previous = json.loads(content.decode('utf-8')) - new = jsonpatch.apply_patch(previous, diff) - return force_bytes(json.dumps(new)) +def _diff_content(heaaders, body, diff): + patch = jsonpatch.JsonPatch.from_string(diff) + previous_data = json.loads(body) + next_data = jsonpatch.apply_patch(previous_data, patch) + return json.dumps(next_data) -def _get_request(decoders=None): +def _generate_request(decoders=None): # TODO: Include User-Agent, X-Accept-Diff if decoders is None: decoders = default_decoders @@ -44,17 +44,18 @@ class WebSocketsTransport(BaseTransport): def transition(self, link, params=None, decoders=None, link_ancestors=None): url = link.url - ws = create_connection(url) - request = _get_request(decoders) - ws.send(request) - content = ws.recv() + connection = create_connection(url) + request = _generate_request(decoders) + connection.send(request) + content = connection.recv() headers, body = _get_headers_and_body(content) yield _decode_content(headers, body, decoders=decoders, base_url=url) while True: try: - diff = ws.recv() + diff = connection.recv() except WebSocketConnectionClosedException: return + body = _diff_content(headers, body, diff) patch = jsonpatch.JsonPatch.from_string(diff) body = json.dumps(jsonpatch.apply_patch(json.loads(body), patch)) yield _decode_content(headers, body, decoders=decoders, base_url=url) From 6bfbf927c7d2cef50a5e2c1ab061e9556941224f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 28 Jan 2016 15:12:58 +0000 Subject: [PATCH 3/3] Minor fixes --- coreapi/transports/http.py | 2 +- coreapi/transports/websockets.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/coreapi/transports/http.py b/coreapi/transports/http.py index adf5883..bafebcb 100644 --- a/coreapi/transports/http.py +++ b/coreapi/transports/http.py @@ -209,7 +209,7 @@ def transition(self, link, params=None, decoders=None, link_ancestors=None): response = _make_http_request(url, method, headers, query_params, form_params) result = _decode_result(response, decoders) - if isinstance(result, Document) and link_ancestors: + if (isinstance(result, Document) or result is None) and link_ancestors: result = _handle_inplace_replacements(result, link, link_ancestors) if isinstance(result, Error): diff --git a/coreapi/transports/websockets.py b/coreapi/transports/websockets.py index 01969ad..c1e34f2 100644 --- a/coreapi/transports/websockets.py +++ b/coreapi/transports/websockets.py @@ -23,7 +23,7 @@ def _decode_content(headers, content, decoders=None, base_url=None): return codec.load(content, base_url=base_url) -def _diff_content(heaaders, body, diff): +def _diff_content(headers, body, diff): patch = jsonpatch.JsonPatch.from_string(diff) previous_data = json.loads(body) next_data = jsonpatch.apply_patch(previous_data, patch) @@ -44,18 +44,17 @@ class WebSocketsTransport(BaseTransport): def transition(self, link, params=None, decoders=None, link_ancestors=None): url = link.url + base_url = url.replace('wss:', 'https:').replace('ws:', 'http:') connection = create_connection(url) request = _generate_request(decoders) connection.send(request) content = connection.recv() headers, body = _get_headers_and_body(content) - yield _decode_content(headers, body, decoders=decoders, base_url=url) + yield _decode_content(headers, body, decoders=decoders, base_url=base_url) while True: try: diff = connection.recv() except WebSocketConnectionClosedException: return body = _diff_content(headers, body, diff) - patch = jsonpatch.JsonPatch.from_string(diff) - body = json.dumps(jsonpatch.apply_patch(json.loads(body), patch)) - yield _decode_content(headers, body, decoders=decoders, base_url=url) + yield _decode_content(headers, body, decoders=decoders, base_url=base_url) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy