You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils.py 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import importlib
  2. from twisted.web.http_headers import Headers
  3. def import_by_path(path):
  4. """
  5. Given a dotted/colon path, like project.module:ClassName.callable,
  6. returns the object at the end of the path.
  7. """
  8. module_path, object_path = path.split(":", 1)
  9. target = importlib.import_module(module_path)
  10. for bit in object_path.split("."):
  11. target = getattr(target, bit)
  12. return target
  13. def header_value(headers, header_name):
  14. value = headers[header_name]
  15. if isinstance(value, list):
  16. value = value[0]
  17. return value.decode("utf-8")
  18. def parse_x_forwarded_for(
  19. headers,
  20. address_header_name="X-Forwarded-For",
  21. port_header_name="X-Forwarded-Port",
  22. proto_header_name="X-Forwarded-Proto",
  23. original_addr=None,
  24. original_scheme=None,
  25. ):
  26. """
  27. Parses an X-Forwarded-For header and returns a host/port pair as a list.
  28. @param headers: The twisted-style object containing a request's headers
  29. @param address_header_name: The name of the expected host header
  30. @param port_header_name: The name of the expected port header
  31. @param proto_header_name: The name of the expected proto header
  32. @param original_addr: A host/port pair that should be returned if the headers are not in the request
  33. @param original_scheme: A scheme that should be returned if the headers are not in the request
  34. @return: A list containing a host (string) as the first entry and a port (int) as the second.
  35. """
  36. if not address_header_name:
  37. return original_addr, original_scheme
  38. # Convert twisted-style headers into dicts
  39. if isinstance(headers, Headers):
  40. headers = dict(headers.getAllRawHeaders())
  41. # Lowercase all header names in the dict
  42. headers = {name.lower(): values for name, values in headers.items()}
  43. # Make sure header names are bytes (values are checked in header_value)
  44. assert all(isinstance(name, bytes) for name in headers.keys())
  45. address_header_name = address_header_name.lower().encode("utf-8")
  46. result_addr = original_addr
  47. result_scheme = original_scheme
  48. if address_header_name in headers:
  49. address_value = header_value(headers, address_header_name)
  50. if "," in address_value:
  51. address_value = address_value.split(",")[0].strip()
  52. result_addr = [address_value, 0]
  53. if port_header_name:
  54. # We only want to parse the X-Forwarded-Port header if we also parsed the X-Forwarded-For
  55. # header to avoid inconsistent results.
  56. port_header_name = port_header_name.lower().encode("utf-8")
  57. if port_header_name in headers:
  58. port_value = header_value(headers, port_header_name)
  59. try:
  60. result_addr[1] = int(port_value)
  61. except ValueError:
  62. pass
  63. if proto_header_name:
  64. proto_header_name = proto_header_name.lower().encode("utf-8")
  65. if proto_header_name in headers:
  66. result_scheme = header_value(headers, proto_header_name)
  67. return result_addr, result_scheme