mirror of
https://mau.dev/maunium/synapse.git
synced 2024-10-01 01:36:05 -04:00
Split PlainHttpClient into separate clients for talking to Identity servers and talking to Capatcha servers
This commit is contained in:
parent
2d55d43d40
commit
4f11518934
@ -25,7 +25,7 @@ from twisted.web.static import File
|
|||||||
from twisted.web.server import Site
|
from twisted.web.server import Site
|
||||||
from synapse.http.server import JsonResource, RootRedirect
|
from synapse.http.server import JsonResource, RootRedirect
|
||||||
from synapse.http.content_repository import ContentRepoResource
|
from synapse.http.content_repository import ContentRepoResource
|
||||||
from synapse.http.client import TwistedHttpClient
|
from synapse.http.client import MatrixHttpClient
|
||||||
from synapse.api.urls import (
|
from synapse.api.urls import (
|
||||||
CLIENT_PREFIX, FEDERATION_PREFIX, WEB_CLIENT_PREFIX, CONTENT_REPO_PREFIX
|
CLIENT_PREFIX, FEDERATION_PREFIX, WEB_CLIENT_PREFIX, CONTENT_REPO_PREFIX
|
||||||
)
|
)
|
||||||
@ -47,7 +47,7 @@ logger = logging.getLogger(__name__)
|
|||||||
class SynapseHomeServer(HomeServer):
|
class SynapseHomeServer(HomeServer):
|
||||||
|
|
||||||
def build_http_client(self):
|
def build_http_client(self):
|
||||||
return TwistedHttpClient(self)
|
return MatrixHttpClient(self)
|
||||||
|
|
||||||
def build_resource_for_client(self):
|
def build_resource_for_client(self):
|
||||||
return JsonResource()
|
return JsonResource()
|
||||||
|
@ -18,7 +18,7 @@ from twisted.internet import defer
|
|||||||
from ._base import BaseHandler
|
from ._base import BaseHandler
|
||||||
|
|
||||||
from synapse.api.errors import SynapseError
|
from synapse.api.errors import SynapseError
|
||||||
from synapse.http.client import HttpClient
|
from synapse.http.client import MatrixHttpClient
|
||||||
from synapse.api.events.room import RoomAliasesEvent
|
from synapse.api.events.room import RoomAliasesEvent
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -98,7 +98,7 @@ class DirectoryHandler(BaseHandler):
|
|||||||
query_type="directory",
|
query_type="directory",
|
||||||
args={
|
args={
|
||||||
"room_alias": room_alias.to_string(),
|
"room_alias": room_alias.to_string(),
|
||||||
HttpClient.RETRY_DNS_LOOKUP_FAILURES: False
|
MatrixHttpClient.RETRY_DNS_LOOKUP_FAILURES: False
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ from twisted.internet import defer
|
|||||||
|
|
||||||
from ._base import BaseHandler
|
from ._base import BaseHandler
|
||||||
from synapse.api.errors import LoginError, Codes
|
from synapse.api.errors import LoginError, Codes
|
||||||
from synapse.http.client import PlainHttpClient
|
from synapse.http.client import IdentityServerHttpClient
|
||||||
from synapse.util.emailutils import EmailException
|
from synapse.util.emailutils import EmailException
|
||||||
import synapse.util.emailutils as emailutils
|
import synapse.util.emailutils as emailutils
|
||||||
|
|
||||||
@ -97,10 +97,10 @@ class LoginHandler(BaseHandler):
|
|||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _query_email(self, email):
|
def _query_email(self, email):
|
||||||
httpCli = PlainHttpClient(self.hs)
|
httpCli = IdentityServerHttpClient(self.hs)
|
||||||
data = yield httpCli.get_json(
|
data = yield httpCli.get_json(
|
||||||
'matrix.org:8090', # TODO FIXME This should be configurable.
|
'matrix.org:8090', # TODO FIXME This should be configurable.
|
||||||
"/_matrix/identity/api/v1/lookup?medium=email&address=" +
|
"/_matrix/identity/api/v1/lookup?medium=email&address=" +
|
||||||
"%s" % urllib.quote(email)
|
"%s" % urllib.quote(email)
|
||||||
)
|
)
|
||||||
defer.returnValue(data)
|
defer.returnValue(data)
|
||||||
|
@ -22,7 +22,8 @@ from synapse.api.errors import (
|
|||||||
)
|
)
|
||||||
from ._base import BaseHandler
|
from ._base import BaseHandler
|
||||||
import synapse.util.stringutils as stringutils
|
import synapse.util.stringutils as stringutils
|
||||||
from synapse.http.client import PlainHttpClient
|
from synapse.http.client import IdentityServerHttpClient
|
||||||
|
from synapse.http.client import CaptchaServerHttpClient
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import bcrypt
|
import bcrypt
|
||||||
@ -154,7 +155,9 @@ class RegistrationHandler(BaseHandler):
|
|||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _threepid_from_creds(self, creds):
|
def _threepid_from_creds(self, creds):
|
||||||
httpCli = PlainHttpClient(self.hs)
|
# TODO: get this from the homeserver rather than creating a new one for
|
||||||
|
# each request
|
||||||
|
httpCli = IdentityServerHttpClient(self.hs)
|
||||||
# XXX: make this configurable!
|
# XXX: make this configurable!
|
||||||
trustedIdServers = ['matrix.org:8090']
|
trustedIdServers = ['matrix.org:8090']
|
||||||
if not creds['idServer'] in trustedIdServers:
|
if not creds['idServer'] in trustedIdServers:
|
||||||
@ -203,7 +206,9 @@ class RegistrationHandler(BaseHandler):
|
|||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _submit_captcha(self, ip_addr, private_key, challenge, response):
|
def _submit_captcha(self, ip_addr, private_key, challenge, response):
|
||||||
client = PlainHttpClient(self.hs)
|
# TODO: get this from the homeserver rather than creating a new one for
|
||||||
|
# each request
|
||||||
|
client = CaptchaServerHttpClient(self.hs)
|
||||||
data = yield client.post_urlencoded_get_raw(
|
data = yield client.post_urlencoded_get_raw(
|
||||||
"www.google.com:80",
|
"www.google.com:80",
|
||||||
"/recaptcha/api/verify",
|
"/recaptcha/api/verify",
|
||||||
|
@ -36,49 +36,6 @@ import urllib
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HttpClient(object):
|
|
||||||
""" Interface for talking json over http
|
|
||||||
"""
|
|
||||||
RETRY_DNS_LOOKUP_FAILURES = "__retry_dns"
|
|
||||||
|
|
||||||
def put_json(self, destination, path, data):
|
|
||||||
""" Sends the specifed json data using PUT
|
|
||||||
|
|
||||||
Args:
|
|
||||||
destination (str): The remote server to send the HTTP request
|
|
||||||
to.
|
|
||||||
path (str): The HTTP path.
|
|
||||||
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. On a 4xx or 5xx error response a
|
|
||||||
CodeMessageException is raised.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_json(self, destination, path, args=None):
|
|
||||||
""" Get's some json from the given host homeserver and path
|
|
||||||
|
|
||||||
Args:
|
|
||||||
destination (str): The remote server to send the HTTP request
|
|
||||||
to.
|
|
||||||
path (str): The HTTP path.
|
|
||||||
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 *any* HTTP response.
|
|
||||||
|
|
||||||
The result of the deferred is a tuple of `(code, response)`,
|
|
||||||
where `response` is a dict representing the decoded JSON body.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MatrixHttpAgent(_AgentBase):
|
class MatrixHttpAgent(_AgentBase):
|
||||||
|
|
||||||
def __init__(self, reactor, pool=None):
|
def __init__(self, reactor, pool=None):
|
||||||
@ -102,113 +59,14 @@ class MatrixHttpAgent(_AgentBase):
|
|||||||
parsed_URI.originForm)
|
parsed_URI.originForm)
|
||||||
|
|
||||||
|
|
||||||
class TwistedHttpClient(HttpClient):
|
class BaseHttpClient(object):
|
||||||
""" Wrapper around the twisted HTTP client api.
|
"""Base class for HTTP clients using twisted.
|
||||||
|
|
||||||
Attributes:
|
|
||||||
agent (twisted.web.client.Agent): The twisted Agent used to send the
|
|
||||||
requests.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.agent = MatrixHttpAgent(reactor)
|
self.agent = MatrixHttpAgent(reactor)
|
||||||
self.hs = hs
|
self.hs = hs
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def put_json(self, destination, path, data, on_send_callback=None):
|
|
||||||
if destination in _destination_mappings:
|
|
||||||
destination = _destination_mappings[destination]
|
|
||||||
|
|
||||||
response = yield self._create_request(
|
|
||||||
destination.encode("ascii"),
|
|
||||||
"PUT",
|
|
||||||
path.encode("ascii"),
|
|
||||||
producer=_JsonProducer(data),
|
|
||||||
headers_dict={"Content-Type": ["application/json"]},
|
|
||||||
on_send_callback=on_send_callback,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug("Getting resp body")
|
|
||||||
body = yield readBody(response)
|
|
||||||
logger.debug("Got resp body")
|
|
||||||
|
|
||||||
defer.returnValue((response.code, body))
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def get_json(self, destination, path, args={}):
|
|
||||||
if destination in _destination_mappings:
|
|
||||||
destination = _destination_mappings[destination]
|
|
||||||
|
|
||||||
logger.debug("get_json args: %s", args)
|
|
||||||
|
|
||||||
retry_on_dns_fail = True
|
|
||||||
if HttpClient.RETRY_DNS_LOOKUP_FAILURES in args:
|
|
||||||
# FIXME: This isn't ideal, but the interface exposed in get_json
|
|
||||||
# isn't comprehensive enough to give caller's any control over
|
|
||||||
# their connection mechanics.
|
|
||||||
retry_on_dns_fail = args.pop(HttpClient.RETRY_DNS_LOOKUP_FAILURES)
|
|
||||||
|
|
||||||
query_bytes = urllib.urlencode(args, True)
|
|
||||||
logger.debug("Query bytes: %s Retry DNS: %s", args, retry_on_dns_fail)
|
|
||||||
|
|
||||||
response = yield self._create_request(
|
|
||||||
destination.encode("ascii"),
|
|
||||||
"GET",
|
|
||||||
path.encode("ascii"),
|
|
||||||
query_bytes=query_bytes,
|
|
||||||
retry_on_dns_fail=retry_on_dns_fail
|
|
||||||
)
|
|
||||||
|
|
||||||
body = yield readBody(response)
|
|
||||||
|
|
||||||
defer.returnValue(json.loads(body))
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def post_urlencoded_get_json(self, destination, path, args={}):
|
|
||||||
if destination in _destination_mappings:
|
|
||||||
destination = _destination_mappings[destination]
|
|
||||||
|
|
||||||
logger.debug("post_urlencoded_get_json args: %s", args)
|
|
||||||
query_bytes = urllib.urlencode(args, True)
|
|
||||||
|
|
||||||
response = yield self._create_request(
|
|
||||||
destination.encode("ascii"),
|
|
||||||
"POST",
|
|
||||||
path.encode("ascii"),
|
|
||||||
producer=FileBodyProducer(StringIO(urllib.urlencode(args))),
|
|
||||||
headers_dict={"Content-Type": ["application/x-www-form-urlencoded"]}
|
|
||||||
)
|
|
||||||
|
|
||||||
body = yield readBody(response)
|
|
||||||
|
|
||||||
defer.returnValue(json.loads(body))
|
|
||||||
|
|
||||||
# XXX FIXME : I'm so sorry.
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def post_urlencoded_get_raw(self, destination, path, accept_partial=False, args={}):
|
|
||||||
if destination in _destination_mappings:
|
|
||||||
destination = _destination_mappings[destination]
|
|
||||||
|
|
||||||
query_bytes = urllib.urlencode(args, True)
|
|
||||||
|
|
||||||
response = yield self._create_request(
|
|
||||||
destination.encode("ascii"),
|
|
||||||
"POST",
|
|
||||||
path.encode("ascii"),
|
|
||||||
producer=FileBodyProducer(StringIO(urllib.urlencode(args))),
|
|
||||||
headers_dict={"Content-Type": ["application/x-www-form-urlencoded"]}
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
body = yield readBody(response)
|
|
||||||
defer.returnValue(body)
|
|
||||||
except PartialDownloadError as e:
|
|
||||||
if accept_partial:
|
|
||||||
defer.returnValue(e.response)
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _create_request(self, destination, method, path_bytes, param_bytes=b"",
|
def _create_request(self, destination, method, path_bytes, param_bytes=b"",
|
||||||
query_bytes=b"", producer=None, headers_dict={},
|
query_bytes=b"", producer=None, headers_dict={},
|
||||||
@ -232,7 +90,6 @@ class TwistedHttpClient(HttpClient):
|
|||||||
|
|
||||||
retries_left = 5
|
retries_left = 5
|
||||||
|
|
||||||
# TODO: setup and pass in an ssl_context to enable TLS
|
|
||||||
endpoint = self._getEndpoint(reactor, destination);
|
endpoint = self._getEndpoint(reactor, destination);
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@ -283,6 +140,92 @@ class TwistedHttpClient(HttpClient):
|
|||||||
|
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixHttpClient(BaseHttpClient):
|
||||||
|
""" Wrapper around the twisted HTTP client api. Implements
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
agent (twisted.web.client.Agent): The twisted Agent used to send the
|
||||||
|
requests.
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETRY_DNS_LOOKUP_FAILURES = "__retry_dns"
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def put_json(self, destination, path, data, on_send_callback=None):
|
||||||
|
""" Sends the specifed json data using PUT
|
||||||
|
|
||||||
|
Args:
|
||||||
|
destination (str): The remote server to send the HTTP request
|
||||||
|
to.
|
||||||
|
path (str): The HTTP path.
|
||||||
|
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. On a 4xx or 5xx error response a
|
||||||
|
CodeMessageException is raised.
|
||||||
|
"""
|
||||||
|
response = yield self._create_request(
|
||||||
|
destination.encode("ascii"),
|
||||||
|
"PUT",
|
||||||
|
path.encode("ascii"),
|
||||||
|
producer=_JsonProducer(data),
|
||||||
|
headers_dict={"Content-Type": ["application/json"]},
|
||||||
|
on_send_callback=on_send_callback,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug("Getting resp body")
|
||||||
|
body = yield readBody(response)
|
||||||
|
logger.debug("Got resp body")
|
||||||
|
|
||||||
|
defer.returnValue((response.code, body))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_json(self, destination, path, args={}):
|
||||||
|
""" Get's some json from the given host homeserver and path
|
||||||
|
|
||||||
|
Args:
|
||||||
|
destination (str): The remote server to send the HTTP request
|
||||||
|
to.
|
||||||
|
path (str): The HTTP path.
|
||||||
|
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 *any* HTTP response.
|
||||||
|
|
||||||
|
The result of the deferred is a tuple of `(code, response)`,
|
||||||
|
where `response` is a dict representing the decoded JSON body.
|
||||||
|
"""
|
||||||
|
logger.debug("get_json args: %s", args)
|
||||||
|
|
||||||
|
retry_on_dns_fail = True
|
||||||
|
if HttpClient.RETRY_DNS_LOOKUP_FAILURES in args:
|
||||||
|
# FIXME: This isn't ideal, but the interface exposed in get_json
|
||||||
|
# isn't comprehensive enough to give caller's any control over
|
||||||
|
# their connection mechanics.
|
||||||
|
retry_on_dns_fail = args.pop(HttpClient.RETRY_DNS_LOOKUP_FAILURES)
|
||||||
|
|
||||||
|
query_bytes = urllib.urlencode(args, True)
|
||||||
|
logger.debug("Query bytes: %s Retry DNS: %s", args, retry_on_dns_fail)
|
||||||
|
|
||||||
|
response = yield self._create_request(
|
||||||
|
destination.encode("ascii"),
|
||||||
|
"GET",
|
||||||
|
path.encode("ascii"),
|
||||||
|
query_bytes=query_bytes,
|
||||||
|
retry_on_dns_fail=retry_on_dns_fail
|
||||||
|
)
|
||||||
|
|
||||||
|
body = yield readBody(response)
|
||||||
|
|
||||||
|
defer.returnValue(json.loads(body))
|
||||||
|
|
||||||
|
|
||||||
def _getEndpoint(self, reactor, destination):
|
def _getEndpoint(self, reactor, destination):
|
||||||
return matrix_endpoint(
|
return matrix_endpoint(
|
||||||
reactor, destination, timeout=10,
|
reactor, destination, timeout=10,
|
||||||
@ -290,10 +233,69 @@ class TwistedHttpClient(HttpClient):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PlainHttpClient(TwistedHttpClient):
|
class IdentityServerHttpClient(BaseHttpClient):
|
||||||
|
"""Separate HTTP client for talking to the Identity servers since they
|
||||||
|
don't use SRV records and talk x-www-form-urlencoded rather than JSON.
|
||||||
|
"""
|
||||||
|
def _getEndpoint(self, reactor, destination):
|
||||||
|
#TODO: This should be talking TLS
|
||||||
|
return matrix_endpoint(reactor, destination, timeout=10)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def post_urlencoded_get_json(self, destination, path, args={}):
|
||||||
|
if destination in _destination_mappings:
|
||||||
|
destination = _destination_mappings[destination]
|
||||||
|
|
||||||
|
logger.debug("post_urlencoded_get_json args: %s", args)
|
||||||
|
query_bytes = urllib.urlencode(args, True)
|
||||||
|
|
||||||
|
response = yield self._create_request(
|
||||||
|
destination.encode("ascii"),
|
||||||
|
"POST",
|
||||||
|
path.encode("ascii"),
|
||||||
|
producer=FileBodyProducer(StringIO(urllib.urlencode(args))),
|
||||||
|
headers_dict={
|
||||||
|
"Content-Type": ["application/x-www-form-urlencoded"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
body = yield readBody(response)
|
||||||
|
|
||||||
|
defer.returnValue(json.loads(body))
|
||||||
|
|
||||||
|
|
||||||
|
class CaptchaServerHttpClient(MatrixHttpClient):
|
||||||
|
"""Separate HTTP client for talking to google's captcha servers"""
|
||||||
|
|
||||||
def _getEndpoint(self, reactor, destination):
|
def _getEndpoint(self, reactor, destination):
|
||||||
return matrix_endpoint(reactor, destination, timeout=10)
|
return matrix_endpoint(reactor, destination, timeout=10)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def post_urlencoded_get_raw(self, destination, path, accept_partial=False,
|
||||||
|
args={}):
|
||||||
|
if destination in _destination_mappings:
|
||||||
|
destination = _destination_mappings[destination]
|
||||||
|
|
||||||
|
query_bytes = urllib.urlencode(args, True)
|
||||||
|
|
||||||
|
response = yield self._create_request(
|
||||||
|
destination.encode("ascii"),
|
||||||
|
"POST",
|
||||||
|
path.encode("ascii"),
|
||||||
|
producer=FileBodyProducer(StringIO(urllib.urlencode(args))),
|
||||||
|
headers_dict={
|
||||||
|
"Content-Type": ["application/x-www-form-urlencoded"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
body = yield readBody(response)
|
||||||
|
defer.returnValue(body)
|
||||||
|
except PartialDownloadError as e:
|
||||||
|
if accept_partial:
|
||||||
|
defer.returnValue(e.response)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
def _print_ex(e):
|
def _print_ex(e):
|
||||||
if hasattr(e, "reasons") and e.reasons:
|
if hasattr(e, "reasons") and e.reasons:
|
||||||
|
@ -20,7 +20,7 @@ from twisted.internet import defer
|
|||||||
from mock import Mock
|
from mock import Mock
|
||||||
|
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
from synapse.http.client import HttpClient
|
from synapse.http.client import MatrixHttpClient
|
||||||
from synapse.handlers.directory import DirectoryHandler
|
from synapse.handlers.directory import DirectoryHandler
|
||||||
from synapse.storage.directory import RoomAliasMapping
|
from synapse.storage.directory import RoomAliasMapping
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ class DirectoryTestCase(unittest.TestCase):
|
|||||||
query_type="directory",
|
query_type="directory",
|
||||||
args={
|
args={
|
||||||
"room_alias": "#another:remote",
|
"room_alias": "#another:remote",
|
||||||
HttpClient.RETRY_DNS_LOOKUP_FAILURES: False
|
MatrixHttpClient.RETRY_DNS_LOOKUP_FAILURES: False
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user