archiving community contributions on YouTube: unpublished captions, title and description translations and caption credits
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 

158 líneas
4.9 KiB

  1. import asyncio
  2. import logging
  3. import time
  4. from collections import deque
  5. from typing import Deque, Dict, Optional
  6. from urllib.parse import urlparse
  7. import aioquic
  8. from aioquic.asyncio.protocol import QuicConnectionProtocol
  9. from aioquic.h3.connection import H3Connection
  10. from aioquic.h3.events import (
  11. DataReceived,
  12. H3Event,
  13. HeadersReceived,
  14. PushPromiseReceived,
  15. )
  16. from aioquic.quic.events import QuicEvent
  17. logger = logging.getLogger("client")
  18. USER_AGENT = "aioquic/" + aioquic.__version__
  19. class URL:
  20. def __init__(self, url: str) -> None:
  21. parsed = urlparse(url)
  22. self.authority = parsed.netloc
  23. self.full_path = parsed.path
  24. if parsed.query:
  25. self.full_path += "?" + parsed.query
  26. self.scheme = parsed.scheme
  27. class HttpRequest:
  28. def __init__(
  29. self, method: str, url: URL, content: bytes = b"", headers: Dict = {}
  30. ) -> None:
  31. self.content = content
  32. self.headers = headers
  33. self.method = method
  34. self.url = url
  35. class HttpClient(QuicConnectionProtocol):
  36. def __init__(self, *args, **kwargs) -> None:
  37. super().__init__(*args, **kwargs)
  38. self.pushes: Dict[int, Deque[H3Event]] = {}
  39. self._request_events: Dict[int, Deque[H3Event]] = {}
  40. self._request_waiter: Dict[int, asyncio.Future[Deque[H3Event]]] = {}
  41. self._http = H3Connection(self._quic)
  42. async def get(self, url: str, headers: Dict = {}) -> Deque[H3Event]:
  43. """
  44. Perform a GET request.
  45. """
  46. return await self._request(
  47. HttpRequest(method="GET", url=URL(url), headers=headers)
  48. )
  49. async def post(self, url: str, data: bytes, headers: Dict = {}) -> Deque[H3Event]:
  50. """
  51. Perform a POST request.
  52. """
  53. return await self._request(
  54. HttpRequest(method="POST", url=URL(url), content=data, headers=headers)
  55. )
  56. def http_event_received(self, event: H3Event) -> None:
  57. if isinstance(event, (HeadersReceived, DataReceived)):
  58. stream_id = event.stream_id
  59. if stream_id in self._request_events:
  60. # http
  61. self._request_events[event.stream_id].append(event)
  62. if event.stream_ended:
  63. request_waiter = self._request_waiter.pop(stream_id)
  64. request_waiter.set_result(self._request_events.pop(stream_id))
  65. elif event.push_id in self.pushes:
  66. # push
  67. self.pushes[event.push_id].append(event)
  68. elif isinstance(event, PushPromiseReceived):
  69. self.pushes[event.push_id] = deque()
  70. self.pushes[event.push_id].append(event)
  71. def quic_event_received(self, event: QuicEvent) -> None:
  72. # pass event to the HTTP layer
  73. if self._http is not None:
  74. for http_event in self._http.handle_event(event):
  75. self.http_event_received(http_event)
  76. async def _request(self, request: HttpRequest) -> Deque[H3Event]:
  77. stream_id = self._quic.get_next_available_stream_id()
  78. self._http.send_headers(
  79. stream_id=stream_id,
  80. headers=[
  81. (b":method", request.method.encode()),
  82. (b":scheme", request.url.scheme.encode()),
  83. (b":authority", request.url.authority.encode()),
  84. (b":path", request.url.full_path.encode()),
  85. (b"user-agent", USER_AGENT.encode()),
  86. ]
  87. + [(k.lower().encode(), v.encode()) for (k, v) in request.headers.items()],
  88. )
  89. self._http.send_data(stream_id=stream_id, data=request.content, end_stream=True)
  90. waiter = self._loop.create_future()
  91. self._request_events[stream_id] = deque()
  92. self._request_waiter[stream_id] = waiter
  93. self.transmit()
  94. return await asyncio.shield(waiter)
  95. async def perform_http_request(
  96. client: HttpClient,
  97. url: str,
  98. headers: Optional[dict]
  99. ) -> Dict[int, Deque[H3Event]] :
  100. # perform request
  101. start = time.time()
  102. if headers:
  103. http_events = await client.get(url, headers=headers)
  104. else:
  105. http_events = await client.get(url)
  106. method = "GET"
  107. elapsed = time.time() - start
  108. # print speed
  109. octets = 0
  110. for http_event in http_events:
  111. if isinstance(http_event, DataReceived):
  112. octets += len(http_event.data)
  113. logger.info(
  114. "Response received for %s %s : %d bytes in %.1f s (%.3f Mbps)"
  115. % (method, urlparse(url).path, octets, elapsed, octets * 8 / elapsed / 1000000)
  116. )
  117. return http_events
  118. def prepare_response(
  119. http_events: Deque[H3Event]
  120. ) -> str:
  121. byteslist = []
  122. headers = {}
  123. for http_event in http_events:
  124. if isinstance(http_event, HeadersReceived):
  125. headers.update(http_event.headers)
  126. elif isinstance(http_event, DataReceived):
  127. byteslist.append(http_event.data)
  128. return headers, b''.join(byteslist)