test_http.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #!/usr/bin/env python3
  2. # Allow direct execution
  3. import os
  4. import sys
  5. import unittest
  6. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  7. import http.server
  8. import ssl
  9. import threading
  10. import urllib.request
  11. from test.helper import http_server_port
  12. from hypervideo_dl import YoutubeDL
  13. TEST_DIR = os.path.dirname(os.path.abspath(__file__))
  14. class HTTPTestRequestHandler(http.server.BaseHTTPRequestHandler):
  15. def log_message(self, format, *args):
  16. pass
  17. def do_GET(self):
  18. if self.path == '/video.html':
  19. self.send_response(200)
  20. self.send_header('Content-Type', 'text/html; charset=utf-8')
  21. self.end_headers()
  22. self.wfile.write(b'<html><video src="/vid.mp4" /></html>')
  23. elif self.path == '/vid.mp4':
  24. self.send_response(200)
  25. self.send_header('Content-Type', 'video/mp4')
  26. self.end_headers()
  27. self.wfile.write(b'\x00\x00\x00\x00\x20\x66\x74[video]')
  28. elif self.path == '/%E4%B8%AD%E6%96%87.html':
  29. self.send_response(200)
  30. self.send_header('Content-Type', 'text/html; charset=utf-8')
  31. self.end_headers()
  32. self.wfile.write(b'<html><video src="/vid.mp4" /></html>')
  33. else:
  34. assert False
  35. class FakeLogger:
  36. def debug(self, msg):
  37. pass
  38. def warning(self, msg):
  39. pass
  40. def error(self, msg):
  41. pass
  42. class TestHTTP(unittest.TestCase):
  43. def setUp(self):
  44. self.httpd = http.server.HTTPServer(
  45. ('127.0.0.1', 0), HTTPTestRequestHandler)
  46. self.port = http_server_port(self.httpd)
  47. self.server_thread = threading.Thread(target=self.httpd.serve_forever)
  48. self.server_thread.daemon = True
  49. self.server_thread.start()
  50. class TestHTTPS(unittest.TestCase):
  51. def setUp(self):
  52. certfn = os.path.join(TEST_DIR, 'testcert.pem')
  53. self.httpd = http.server.HTTPServer(
  54. ('127.0.0.1', 0), HTTPTestRequestHandler)
  55. sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
  56. sslctx.load_cert_chain(certfn, None)
  57. self.httpd.socket = sslctx.wrap_socket(self.httpd.socket, server_side=True)
  58. self.port = http_server_port(self.httpd)
  59. self.server_thread = threading.Thread(target=self.httpd.serve_forever)
  60. self.server_thread.daemon = True
  61. self.server_thread.start()
  62. def test_nocheckcertificate(self):
  63. ydl = YoutubeDL({'logger': FakeLogger()})
  64. self.assertRaises(
  65. Exception,
  66. ydl.extract_info, 'https://127.0.0.1:%d/video.html' % self.port)
  67. ydl = YoutubeDL({'logger': FakeLogger(), 'nocheckcertificate': True})
  68. r = ydl.extract_info('https://127.0.0.1:%d/video.html' % self.port)
  69. self.assertEqual(r['url'], 'https://127.0.0.1:%d/vid.mp4' % self.port)
  70. class TestClientCert(unittest.TestCase):
  71. def setUp(self):
  72. certfn = os.path.join(TEST_DIR, 'testcert.pem')
  73. self.certdir = os.path.join(TEST_DIR, 'testdata', 'certificate')
  74. cacertfn = os.path.join(self.certdir, 'ca.crt')
  75. self.httpd = http.server.HTTPServer(('127.0.0.1', 0), HTTPTestRequestHandler)
  76. sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
  77. sslctx.verify_mode = ssl.CERT_REQUIRED
  78. sslctx.load_verify_locations(cafile=cacertfn)
  79. sslctx.load_cert_chain(certfn, None)
  80. self.httpd.socket = sslctx.wrap_socket(self.httpd.socket, server_side=True)
  81. self.port = http_server_port(self.httpd)
  82. self.server_thread = threading.Thread(target=self.httpd.serve_forever)
  83. self.server_thread.daemon = True
  84. self.server_thread.start()
  85. def _run_test(self, **params):
  86. ydl = YoutubeDL({
  87. 'logger': FakeLogger(),
  88. # Disable client-side validation of unacceptable self-signed testcert.pem
  89. # The test is of a check on the server side, so unaffected
  90. 'nocheckcertificate': True,
  91. **params,
  92. })
  93. r = ydl.extract_info('https://127.0.0.1:%d/video.html' % self.port)
  94. self.assertEqual(r['url'], 'https://127.0.0.1:%d/vid.mp4' % self.port)
  95. def test_certificate_combined_nopass(self):
  96. self._run_test(client_certificate=os.path.join(self.certdir, 'clientwithkey.crt'))
  97. def test_certificate_nocombined_nopass(self):
  98. self._run_test(client_certificate=os.path.join(self.certdir, 'client.crt'),
  99. client_certificate_key=os.path.join(self.certdir, 'client.key'))
  100. def test_certificate_combined_pass(self):
  101. self._run_test(client_certificate=os.path.join(self.certdir, 'clientwithencryptedkey.crt'),
  102. client_certificate_password='foobar')
  103. def test_certificate_nocombined_pass(self):
  104. self._run_test(client_certificate=os.path.join(self.certdir, 'client.crt'),
  105. client_certificate_key=os.path.join(self.certdir, 'clientencrypted.key'),
  106. client_certificate_password='foobar')
  107. def _build_proxy_handler(name):
  108. class HTTPTestRequestHandler(http.server.BaseHTTPRequestHandler):
  109. proxy_name = name
  110. def log_message(self, format, *args):
  111. pass
  112. def do_GET(self):
  113. self.send_response(200)
  114. self.send_header('Content-Type', 'text/plain; charset=utf-8')
  115. self.end_headers()
  116. self.wfile.write(f'{self.proxy_name}: {self.path}'.encode())
  117. return HTTPTestRequestHandler
  118. class TestProxy(unittest.TestCase):
  119. def setUp(self):
  120. self.proxy = http.server.HTTPServer(
  121. ('127.0.0.1', 0), _build_proxy_handler('normal'))
  122. self.port = http_server_port(self.proxy)
  123. self.proxy_thread = threading.Thread(target=self.proxy.serve_forever)
  124. self.proxy_thread.daemon = True
  125. self.proxy_thread.start()
  126. self.geo_proxy = http.server.HTTPServer(
  127. ('127.0.0.1', 0), _build_proxy_handler('geo'))
  128. self.geo_port = http_server_port(self.geo_proxy)
  129. self.geo_proxy_thread = threading.Thread(target=self.geo_proxy.serve_forever)
  130. self.geo_proxy_thread.daemon = True
  131. self.geo_proxy_thread.start()
  132. def test_proxy(self):
  133. geo_proxy = f'127.0.0.1:{self.geo_port}'
  134. ydl = YoutubeDL({
  135. 'proxy': f'127.0.0.1:{self.port}',
  136. 'geo_verification_proxy': geo_proxy,
  137. })
  138. url = 'http://foo.com/bar'
  139. response = ydl.urlopen(url).read().decode()
  140. self.assertEqual(response, f'normal: {url}')
  141. req = urllib.request.Request(url)
  142. req.add_header('Ytdl-request-proxy', geo_proxy)
  143. response = ydl.urlopen(req).read().decode()
  144. self.assertEqual(response, f'geo: {url}')
  145. def test_proxy_with_idn(self):
  146. ydl = YoutubeDL({
  147. 'proxy': f'127.0.0.1:{self.port}',
  148. })
  149. url = 'http://中文.tw/'
  150. response = ydl.urlopen(url).read().decode()
  151. # b'xn--fiq228c' is '中文'.encode('idna')
  152. self.assertEqual(response, 'normal: http://xn--fiq228c.tw/')
  153. if __name__ == '__main__':
  154. unittest.main()