httpsclient.py 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. # Copyright 2014 OpenMarket Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import json
  15. import logging
  16. from io import BytesIO
  17. from typing import TYPE_CHECKING, Any, Dict, Optional
  18. from twisted.internet.defer import Deferred
  19. from twisted.internet.ssl import optionsForClientTLS
  20. from twisted.web.client import Agent, FileBodyProducer
  21. from twisted.web.http_headers import Headers
  22. from twisted.web.iweb import IPolicyForHTTPS
  23. from zope.interface import implementer
  24. if TYPE_CHECKING:
  25. from sydent.sydent import Sydent
  26. logger = logging.getLogger(__name__)
  27. class ReplicationHttpsClient:
  28. """
  29. An HTTPS client specifically for talking replication to other Matrix Identity Servers
  30. (ie. presents our replication SSL certificate and validates peer SSL certificates as we would in the
  31. replication HTTPS server)
  32. """
  33. def __init__(self, sydent: "Sydent") -> None:
  34. self.sydent = sydent
  35. self.agent = None
  36. if self.sydent.sslComponents.myPrivateCertificate:
  37. # We will already have logged a warn if this is absent, so don't do it again
  38. # cert = self.sydent.sslComponents.myPrivateCertificate
  39. # self.certOptions = twisted.internet.ssl.CertificateOptions(privateKey=cert.privateKey.original,
  40. # certificate=cert.original,
  41. # trustRoot=self.sydent.sslComponents.trustRoot)
  42. self.agent = Agent(self.sydent.reactor, SydentPolicyForHTTPS(self.sydent))
  43. def postJson(self, uri: str, jsonObject: Dict[Any, Any]) -> Optional[Deferred]:
  44. """
  45. Sends an POST request over HTTPS.
  46. :param uri: The URI to send the request to.
  47. :param jsonObject: The request's body.
  48. :return: The request's response.
  49. :rtype: twisted.internet.defer.Deferred[twisted.web.iweb.IResponse]
  50. """
  51. logger.debug("POSTing request to %s", uri)
  52. if not self.agent:
  53. logger.error("HTTPS post attempted but HTTPS is not configured")
  54. return None
  55. headers = Headers(
  56. {"Content-Type": ["application/json"], "User-Agent": ["Sydent"]}
  57. )
  58. json_bytes = json.dumps(jsonObject).encode("utf8")
  59. reqDeferred = self.agent.request(
  60. b"POST", uri.encode("utf8"), headers, FileBodyProducer(BytesIO(json_bytes))
  61. )
  62. return reqDeferred
  63. @implementer(IPolicyForHTTPS)
  64. class SydentPolicyForHTTPS:
  65. def __init__(self, sydent: "Sydent") -> None:
  66. self.sydent = sydent
  67. def creatorForNetloc(self, hostname, port):
  68. return optionsForClientTLS(
  69. hostname.decode("ascii"),
  70. trustRoot=self.sydent.sslComponents.trustRoot,
  71. clientCertificate=self.sydent.sslComponents.myPrivateCertificate,
  72. )