Format: text/x-python
⬇ Download File (3.9 KB)
   1  import json
   2  import os.path
   3  import socket
   4  import socketserver
   5  import threading
   6  from contextlib import closing, contextmanager
   7  from http.server import SimpleHTTPRequestHandler
   8  from typing import Callable, Generator
   9  from urllib.request import urlopen
  10  
  11  import h11
  12  
  13  
  14  @contextmanager
  15  def socket_server(
  16      handler: Callable[..., socketserver.BaseRequestHandler]
  17  ) -> Generator[socketserver.TCPServer, None, None]:
  18      httpd = socketserver.TCPServer(("127.0.0.1", 0), handler)
  19      thread = threading.Thread(
  20          target=httpd.serve_forever, kwargs={"poll_interval": 0.01}
  21      )
  22      thread.daemon = True
  23      try:
  24          thread.start()
  25          yield httpd
  26      finally:
  27          httpd.shutdown()
  28  
  29  
  30  test_file_path = os.path.join(os.path.dirname(__file__), "data/test-file")
  31  with open(test_file_path, "rb") as f:
  32      test_file_data = f.read()
  33  
  34  
  35  class SingleMindedRequestHandler(SimpleHTTPRequestHandler):
  36      def translate_path(self, path: str) -> str:
  37          return test_file_path
  38  
  39  
  40  def test_h11_as_client() -> None:
  41      with socket_server(SingleMindedRequestHandler) as httpd:
  42          with closing(socket.create_connection(httpd.server_address)) as s:
  43              c = h11.Connection(h11.CLIENT)
  44  
  45              s.sendall(
  46                  c.send(  # type: ignore[arg-type]
  47                      h11.Request(
  48                          method="GET", target="/foo", headers=[("Host", "localhost")]
  49                      )
  50                  )
  51              )
  52              s.sendall(c.send(h11.EndOfMessage()))  # type: ignore[arg-type]
  53  
  54              data = bytearray()
  55              while True:
  56                  event = c.next_event()
  57                  print(event)
  58                  if event is h11.NEED_DATA:
  59                      # Use a small read buffer to make things more challenging
  60                      # and exercise more paths :-)
  61                      c.receive_data(s.recv(10))
  62                      continue
  63                  if type(event) is h11.Response:
  64                      assert event.status_code == 200
  65                  if type(event) is h11.Data:
  66                      data += event.data
  67                  if type(event) is h11.EndOfMessage:
  68                      break
  69              assert bytes(data) == test_file_data
  70  
  71  
  72  class H11RequestHandler(socketserver.BaseRequestHandler):
  73      def handle(self) -> None:
  74          with closing(self.request) as s:
  75              c = h11.Connection(h11.SERVER)
  76              request = None
  77              while True:
  78                  event = c.next_event()
  79                  if event is h11.NEED_DATA:
  80                      # Use a small read buffer to make things more challenging
  81                      # and exercise more paths :-)
  82                      c.receive_data(s.recv(10))
  83                      continue
  84                  if type(event) is h11.Request:
  85                      request = event
  86                  if type(event) is h11.EndOfMessage:
  87                      break
  88              assert request is not None
  89              info = json.dumps(
  90                  {
  91                      "method": request.method.decode("ascii"),
  92                      "target": request.target.decode("ascii"),
  93                      "headers": {
  94                          name.decode("ascii"): value.decode("ascii")
  95                          for (name, value) in request.headers
  96                      },
  97                  }
  98              )
  99              s.sendall(c.send(h11.Response(status_code=200, headers=[])))  # type: ignore[arg-type]
 100              s.sendall(c.send(h11.Data(data=info.encode("ascii"))))
 101              s.sendall(c.send(h11.EndOfMessage()))
 102  
 103  
 104  def test_h11_as_server() -> None:
 105      with socket_server(H11RequestHandler) as httpd:
 106          host, port = httpd.server_address
 107          url = "http://{}:{}/some-path".format(host, port)
 108          with closing(urlopen(url)) as f:
 109              assert f.getcode() == 200
 110              data = f.read()
 111      info = json.loads(data.decode("ascii"))
 112      print(info)
 113      assert info["method"] == "GET"
 114      assert info["target"] == "/some-path"
 115      assert "urllib" in info["headers"]["user-agent"]
 116