__init__.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. # Copyright 2018 New Vector Ltd
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. import re
  17. from typing import Union
  18. from twisted.internet import address, task
  19. from twisted.web.client import FileBodyProducer
  20. from twisted.web.iweb import IRequest
  21. from synapse.api.errors import SynapseError
  22. class RequestTimedOutError(SynapseError):
  23. """Exception representing timeout of an outbound request"""
  24. def __init__(self, msg):
  25. super().__init__(504, msg)
  26. ACCESS_TOKEN_RE = re.compile(r"(\?.*access(_|%5[Ff])token=)[^&]*(.*)$")
  27. CLIENT_SECRET_RE = re.compile(r"(\?.*client(_|%5[Ff])secret=)[^&]*(.*)$")
  28. def redact_uri(uri):
  29. """Strips sensitive information from the uri replaces with <redacted>"""
  30. uri = ACCESS_TOKEN_RE.sub(r"\1<redacted>\3", uri)
  31. return CLIENT_SECRET_RE.sub(r"\1<redacted>\3", uri)
  32. class QuieterFileBodyProducer(FileBodyProducer):
  33. """Wrapper for FileBodyProducer that avoids CRITICAL errors when the connection drops.
  34. Workaround for https://github.com/matrix-org/synapse/issues/4003 /
  35. https://twistedmatrix.com/trac/ticket/6528
  36. """
  37. def stopProducing(self):
  38. try:
  39. FileBodyProducer.stopProducing(self)
  40. except task.TaskStopped:
  41. pass
  42. def get_request_uri(request: IRequest) -> bytes:
  43. """Return the full URI that was requested by the client"""
  44. return b"%s://%s%s" % (
  45. b"https" if request.isSecure() else b"http",
  46. _get_requested_host(request),
  47. # despite its name, "request.uri" is only the path and query-string.
  48. request.uri,
  49. )
  50. def _get_requested_host(request: IRequest) -> bytes:
  51. hostname = request.getHeader(b"host")
  52. if hostname:
  53. return hostname
  54. # no Host header, use the address/port that the request arrived on
  55. host = request.getHost() # type: Union[address.IPv4Address, address.IPv6Address]
  56. hostname = host.host.encode("ascii")
  57. if request.isSecure() and host.port == 443:
  58. # default port for https
  59. return hostname
  60. if not request.isSecure() and host.port == 80:
  61. # default port for http
  62. return hostname
  63. return b"%s:%i" % (
  64. hostname,
  65. host.port,
  66. )
  67. def get_request_user_agent(request: IRequest, default: str = "") -> str:
  68. """Return the last User-Agent header, or the given default."""
  69. # There could be raw utf-8 bytes in the User-Agent header.
  70. # N.B. if you don't do this, the logger explodes cryptically
  71. # with maximum recursion trying to log errors about
  72. # the charset problem.
  73. # c.f. https://github.com/matrix-org/synapse/issues/3471
  74. h = request.getHeader(b"User-Agent")
  75. return h.decode("ascii", "replace") if h else default