mirror of
https://github.com/element-hq/synapse.git
synced 2026-01-11 19:56:31 +00:00
Some checks are pending
Build docker images / Build and push image for linux/amd64 (push) Waiting to run
Build docker images / Build and push image for linux/arm64 (push) Waiting to run
Build docker images / Push merged images to docker.io/matrixdotorg/synapse (push) Blocked by required conditions
Build docker images / Push merged images to ghcr.io/element-hq/synapse (push) Blocked by required conditions
Deploy the documentation / Calculate variables for GitHub Pages deployment (push) Waiting to run
Deploy the documentation / GitHub Pages (push) Blocked by required conditions
Build release artifacts / Calculate list of debian distros (push) Waiting to run
Build release artifacts / Build .deb packages (push) Blocked by required conditions
Build release artifacts / Build wheels on macos-14 (push) Waiting to run
Build release artifacts / Build wheels on macos-15-intel (push) Waiting to run
Build release artifacts / Build wheels on ubuntu-24.04 (push) Waiting to run
Build release artifacts / Build wheels on ubuntu-24.04-arm (push) Waiting to run
Build release artifacts / Build sdist (push) Waiting to run
Build release artifacts / Attach assets to release (push) Blocked by required conditions
Schema / Ensure Synapse config schema is valid (push) Waiting to run
Schema / Ensure generated documentation is up-to-date (push) Waiting to run
Tests / lint (push) Blocked by required conditions
Tests / lint-readme (push) Blocked by required conditions
Tests / linting-done (push) Blocked by required conditions
Tests / calculate-test-jobs (push) Blocked by required conditions
Tests / changes (push) Waiting to run
Tests / check-sampleconfig (push) Blocked by required conditions
Tests / check-schema-delta (push) Blocked by required conditions
Tests / check-lockfile (push) Waiting to run
Tests / Typechecking (push) Blocked by required conditions
Tests / lint-crlf (push) Waiting to run
Tests / lint-newsfile (push) Waiting to run
Tests / lint-clippy (push) Blocked by required conditions
Tests / lint-clippy-nightly (push) Blocked by required conditions
Tests / lint-rust (push) Blocked by required conditions
Tests / lint-rustfmt (push) Blocked by required conditions
Tests / trial (push) Blocked by required conditions
Tests / trial-olddeps (push) Blocked by required conditions
Tests / trial-pypy (all, pypy-3.10) (push) Blocked by required conditions
Tests / sytest (push) Blocked by required conditions
Tests / export-data (push) Blocked by required conditions
Tests / portdb (13, 3.10) (push) Blocked by required conditions
Tests / portdb (17, 3.14) (push) Blocked by required conditions
Tests / complement (monolith, Postgres) (push) Blocked by required conditions
Tests / complement (monolith, SQLite) (push) Blocked by required conditions
Tests / complement (workers, Postgres) (push) Blocked by required conditions
Tests / cargo-test (push) Blocked by required conditions
Tests / cargo-bench (push) Blocked by required conditions
Tests / tests-done (push) Blocked by required conditions
aka PEP 604, added in Python 3.10
216 lines
6.5 KiB
Python
216 lines
6.5 KiB
Python
#
|
|
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
|
#
|
|
# Copyright 2014-2016 OpenMarket Ltd
|
|
# Copyright (C) 2023 New Vector, Ltd
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# See the GNU Affero General Public License for more details:
|
|
# <https://www.gnu.org/licenses/agpl-3.0.html>.
|
|
#
|
|
# Originally licensed under the Apache License, Version 2.0:
|
|
# <http://www.apache.org/licenses/LICENSE-2.0>.
|
|
#
|
|
# [This file includes modifications made by New Vector Limited]
|
|
#
|
|
#
|
|
|
|
import json
|
|
import urllib
|
|
from pprint import pformat
|
|
|
|
from twisted.internet import defer, reactor
|
|
from twisted.web.client import Agent, readBody
|
|
from twisted.web.http_headers import Headers
|
|
|
|
|
|
class HttpClient:
|
|
"""Interface for talking json over http"""
|
|
|
|
def put_json(self, url, data):
|
|
"""Sends the specifed json data using PUT
|
|
|
|
Args:
|
|
url (str): The URL to PUT data to.
|
|
data (dict): A dict containing the data that will be used as
|
|
the request body. This will be encoded as JSON.
|
|
|
|
Returns:
|
|
Deferred: Succeeds when we get a 2xx HTTP response. The result
|
|
will be the decoded JSON body.
|
|
"""
|
|
|
|
def get_json(self, url, args=None):
|
|
"""Gets some json from the given host homeserver and path
|
|
|
|
Args:
|
|
url (str): The URL to GET data from.
|
|
args (dict): A dictionary used to create query strings, defaults to
|
|
None.
|
|
**Note**: The value of each key is assumed to be an iterable
|
|
and *not* a string.
|
|
|
|
Returns:
|
|
Deferred: Succeeds when we get a 2xx HTTP response. The result
|
|
will be the decoded JSON body.
|
|
"""
|
|
|
|
|
|
class TwistedHttpClient(HttpClient):
|
|
"""Wrapper around the twisted HTTP client api.
|
|
|
|
Attributes:
|
|
agent (twisted.web.client.Agent): The twisted Agent used to send the
|
|
requests.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.agent = Agent(reactor)
|
|
|
|
@defer.inlineCallbacks
|
|
def put_json(self, url, data):
|
|
response = yield self._create_put_request(
|
|
url, data, headers_dict={"Content-Type": ["application/json"]}
|
|
)
|
|
body = yield readBody(response)
|
|
return response.code, body
|
|
|
|
@defer.inlineCallbacks
|
|
def get_json(self, url, args=None):
|
|
if args:
|
|
# generates a list of strings of form "k=v".
|
|
qs = urllib.urlencode(args, True)
|
|
url = "%s?%s" % (url, qs)
|
|
response = yield self._create_get_request(url)
|
|
body = yield readBody(response)
|
|
return json.loads(body)
|
|
|
|
def _create_put_request(self, url, json_data, headers_dict: dict | None = None):
|
|
"""Wrapper of _create_request to issue a PUT request"""
|
|
headers_dict = headers_dict or {}
|
|
|
|
if "Content-Type" not in headers_dict:
|
|
raise defer.error(RuntimeError("Must include Content-Type header for PUTs"))
|
|
|
|
return self._create_request(
|
|
"PUT", url, producer=_JsonProducer(json_data), headers_dict=headers_dict
|
|
)
|
|
|
|
def _create_get_request(self, url, headers_dict: dict | None = None):
|
|
"""Wrapper of _create_request to issue a GET request"""
|
|
return self._create_request("GET", url, headers_dict=headers_dict or {})
|
|
|
|
@defer.inlineCallbacks
|
|
def do_request(
|
|
self,
|
|
method,
|
|
url,
|
|
data=None,
|
|
qparams=None,
|
|
jsonreq=True,
|
|
headers: dict | None = None,
|
|
):
|
|
headers = headers or {}
|
|
|
|
if qparams:
|
|
url = "%s?%s" % (url, urllib.urlencode(qparams, True))
|
|
|
|
if jsonreq:
|
|
prod = _JsonProducer(data)
|
|
headers["Content-Type"] = ["application/json"]
|
|
else:
|
|
prod = _RawProducer(data)
|
|
|
|
if method in ["POST", "PUT"]:
|
|
response = yield self._create_request(
|
|
method, url, producer=prod, headers_dict=headers
|
|
)
|
|
else:
|
|
response = yield self._create_request(method, url)
|
|
|
|
body = yield readBody(response)
|
|
return json.loads(body)
|
|
|
|
@defer.inlineCallbacks
|
|
def _create_request(
|
|
self, method, url, producer=None, headers_dict: dict | None = None
|
|
):
|
|
"""Creates and sends a request to the given url"""
|
|
headers_dict = headers_dict or {}
|
|
|
|
headers_dict["User-Agent"] = ["Synapse Cmd Client"]
|
|
|
|
retries_left = 5
|
|
print("%s to %s with headers %s" % (method, url, headers_dict))
|
|
if self.verbose and producer:
|
|
if "password" in producer.data:
|
|
temp = producer.data["password"]
|
|
producer.data["password"] = "[REDACTED]"
|
|
print(json.dumps(producer.data, indent=4))
|
|
producer.data["password"] = temp
|
|
else:
|
|
print(json.dumps(producer.data, indent=4))
|
|
|
|
while True:
|
|
try:
|
|
response = yield self.agent.request(
|
|
method, url.encode("UTF8"), Headers(headers_dict), producer
|
|
)
|
|
break
|
|
except Exception as e:
|
|
print("uh oh: %s" % e)
|
|
if retries_left:
|
|
yield self.sleep(2 ** (5 - retries_left))
|
|
retries_left -= 1
|
|
else:
|
|
raise e
|
|
|
|
if self.verbose:
|
|
print("Status %s %s" % (response.code, response.phrase))
|
|
print(pformat(list(response.headers.getAllRawHeaders())))
|
|
return response
|
|
|
|
def sleep(self, seconds):
|
|
d = defer.Deferred()
|
|
reactor.callLater(seconds, d.callback, seconds)
|
|
return d
|
|
|
|
|
|
class _RawProducer:
|
|
def __init__(self, data):
|
|
self.data = data
|
|
self.body = data
|
|
self.length = len(self.body)
|
|
|
|
def startProducing(self, consumer):
|
|
consumer.write(self.body)
|
|
return defer.succeed(None)
|
|
|
|
def pauseProducing(self):
|
|
pass
|
|
|
|
def stopProducing(self):
|
|
pass
|
|
|
|
|
|
class _JsonProducer:
|
|
"""Used by the twisted http client to create the HTTP body from json"""
|
|
|
|
def __init__(self, jsn):
|
|
self.data = jsn
|
|
self.body = json.dumps(jsn).encode("utf8")
|
|
self.length = len(self.body)
|
|
|
|
def startProducing(self, consumer):
|
|
consumer.write(self.body)
|
|
return defer.succeed(None)
|
|
|
|
def pauseProducing(self):
|
|
pass
|
|
|
|
def stopProducing(self):
|
|
pass
|