]> jfr.im git - yt-dlp.git/blame - test/test_networking_utils.py
[ie/brightcove] Upgrade requests to HTTPS (#10202)
[yt-dlp.git] / test / test_networking_utils.py
CommitLineData
227bf1a3 1#!/usr/bin/env python3
2
3# Allow direct execution
4import os
5import sys
6
7import pytest
8
9sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
10
11import io
227bf1a3 12import random
13import ssl
227bf1a3 14
15from yt_dlp.cookies import YoutubeDLCookieJar
16from yt_dlp.dependencies import certifi
17from yt_dlp.networking import Response
18from yt_dlp.networking._helper import (
19 InstanceStoreMixin,
20 add_accept_encoding_header,
21 get_redirect_method,
22 make_socks_proxy_opts,
23 select_proxy,
24 ssl_load_certs,
25)
26from yt_dlp.networking.exceptions import (
27 HTTPError,
28 IncompleteRead,
227bf1a3 29)
30from yt_dlp.socks import ProxyType
31from yt_dlp.utils.networking import HTTPHeaderDict
32
33TEST_DIR = os.path.dirname(os.path.abspath(__file__))
34
35
36class TestNetworkingUtils:
37
38 def test_select_proxy(self):
39 proxies = {
40 'all': 'socks5://example.com',
41 'http': 'http://example.com:1080',
add96eb9 42 'no': 'bypass.example.com,yt-dl.org',
227bf1a3 43 }
44
45 assert select_proxy('https://example.com', proxies) == proxies['all']
46 assert select_proxy('http://example.com', proxies) == proxies['http']
47 assert select_proxy('http://bypass.example.com', proxies) is None
48 assert select_proxy('https://yt-dl.org', proxies) is None
49
50 @pytest.mark.parametrize('socks_proxy,expected', [
51 ('socks5h://example.com', {
52 'proxytype': ProxyType.SOCKS5,
53 'addr': 'example.com',
54 'port': 1080,
55 'rdns': True,
56 'username': None,
add96eb9 57 'password': None,
227bf1a3 58 }),
59 ('socks5://user:@example.com:5555', {
60 'proxytype': ProxyType.SOCKS5,
61 'addr': 'example.com',
62 'port': 5555,
63 'rdns': False,
64 'username': 'user',
add96eb9 65 'password': '',
227bf1a3 66 }),
67 ('socks4://u%40ser:pa%20ss@127.0.0.1:1080', {
68 'proxytype': ProxyType.SOCKS4,
69 'addr': '127.0.0.1',
70 'port': 1080,
71 'rdns': False,
72 'username': 'u@ser',
add96eb9 73 'password': 'pa ss',
227bf1a3 74 }),
75 ('socks4a://:pa%20ss@127.0.0.1', {
76 'proxytype': ProxyType.SOCKS4A,
77 'addr': '127.0.0.1',
78 'port': 1080,
79 'rdns': True,
80 'username': '',
add96eb9 81 'password': 'pa ss',
82 }),
227bf1a3 83 ])
84 def test_make_socks_proxy_opts(self, socks_proxy, expected):
85 assert make_socks_proxy_opts(socks_proxy) == expected
86
87 def test_make_socks_proxy_unknown(self):
88 with pytest.raises(ValueError, match='Unknown SOCKS proxy version: socks'):
89 make_socks_proxy_opts('socks://127.0.0.1')
90
91 @pytest.mark.skipif(not certifi, reason='certifi is not installed')
92 def test_load_certifi(self):
de20687e 93 context_certifi = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
94 context_certifi.load_verify_locations(cafile=certifi.where())
227bf1a3 95 context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
227bf1a3 96 ssl_load_certs(context, use_certifi=True)
de20687e 97 assert context.get_ca_certs() == context_certifi.get_ca_certs()
98
99 context_default = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
100 context_default.load_default_certs()
101 context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
102 ssl_load_certs(context, use_certifi=False)
103 assert context.get_ca_certs() == context_default.get_ca_certs()
104
105 if context_default.get_ca_certs() == context_certifi.get_ca_certs():
106 pytest.skip('System uses certifi as default. The test is not valid')
227bf1a3 107
108 @pytest.mark.parametrize('method,status,expected', [
109 ('GET', 303, 'GET'),
110 ('HEAD', 303, 'HEAD'),
111 ('PUT', 303, 'GET'),
112 ('POST', 301, 'GET'),
113 ('HEAD', 301, 'HEAD'),
114 ('POST', 302, 'GET'),
115 ('HEAD', 302, 'HEAD'),
116 ('PUT', 302, 'PUT'),
117 ('POST', 308, 'POST'),
118 ('POST', 307, 'POST'),
119 ('HEAD', 308, 'HEAD'),
120 ('HEAD', 307, 'HEAD'),
121 ])
122 def test_get_redirect_method(self, method, status, expected):
123 assert get_redirect_method(method, status) == expected
124
125 @pytest.mark.parametrize('headers,supported_encodings,expected', [
126 ({'Accept-Encoding': 'br'}, ['gzip', 'br'], {'Accept-Encoding': 'br'}),
127 ({}, ['gzip', 'br'], {'Accept-Encoding': 'gzip, br'}),
128 ({'Content-type': 'application/json'}, [], {'Content-type': 'application/json', 'Accept-Encoding': 'identity'}),
129 ])
130 def test_add_accept_encoding_header(self, headers, supported_encodings, expected):
131 headers = HTTPHeaderDict(headers)
132 add_accept_encoding_header(headers, supported_encodings)
133 assert headers == HTTPHeaderDict(expected)
134
135
136class TestInstanceStoreMixin:
137
138 class FakeInstanceStoreMixin(InstanceStoreMixin):
139 def _create_instance(self, **kwargs):
140 return random.randint(0, 1000000)
141
142 def _close_instance(self, instance):
143 pass
144
145 def test_mixin(self):
146 mixin = self.FakeInstanceStoreMixin()
147 assert mixin._get_instance(d={'a': 1, 'b': 2, 'c': {'d', 4}}) == mixin._get_instance(d={'a': 1, 'b': 2, 'c': {'d', 4}})
148
149 assert mixin._get_instance(d={'a': 1, 'b': 2, 'c': {'e', 4}}) != mixin._get_instance(d={'a': 1, 'b': 2, 'c': {'d', 4}})
150
151 assert mixin._get_instance(d={'a': 1, 'b': 2, 'c': {'d', 4}} != mixin._get_instance(d={'a': 1, 'b': 2, 'g': {'d', 4}}))
152
153 assert mixin._get_instance(d={'a': 1}, e=[1, 2, 3]) == mixin._get_instance(d={'a': 1}, e=[1, 2, 3])
154
155 assert mixin._get_instance(d={'a': 1}, e=[1, 2, 3]) != mixin._get_instance(d={'a': 1}, e=[1, 2, 3, 4])
156
157 cookiejar = YoutubeDLCookieJar()
158 assert mixin._get_instance(b=[1, 2], c=cookiejar) == mixin._get_instance(b=[1, 2], c=cookiejar)
159
160 assert mixin._get_instance(b=[1, 2], c=cookiejar) != mixin._get_instance(b=[1, 2], c=YoutubeDLCookieJar())
161
162 # Different order
163 assert mixin._get_instance(c=cookiejar, b=[1, 2]) == mixin._get_instance(b=[1, 2], c=cookiejar)
164
165 m = mixin._get_instance(t=1234)
166 assert mixin._get_instance(t=1234) == m
167 mixin._clear_instances()
168 assert mixin._get_instance(t=1234) != m
169
170
171class TestNetworkingExceptions:
172
173 @staticmethod
174 def create_response(status):
175 return Response(fp=io.BytesIO(b'test'), url='http://example.com', headers={'tesT': 'test'}, status=status)
176
811d298b 177 def test_http_error(self):
227bf1a3 178
179 response = self.create_response(403)
811d298b 180 error = HTTPError(response)
227bf1a3 181
182 assert error.status == 403
183 assert str(error) == error.msg == 'HTTP Error 403: Forbidden'
184 assert error.reason == response.reason
185 assert error.response is response
186
187 data = error.response.read()
188 assert data == b'test'
189 assert repr(error) == '<HTTPError 403: Forbidden>'
190
811d298b 191 def test_redirect_http_error(self):
227bf1a3 192 response = self.create_response(301)
811d298b 193 error = HTTPError(response, redirect_loop=True)
227bf1a3 194 assert str(error) == error.msg == 'HTTP Error 301: Moved Permanently (redirect loop detected)'
195 assert error.reason == 'Moved Permanently'
196
227bf1a3 197 def test_incomplete_read_error(self):
5ca095cb 198 error = IncompleteRead(4, 3, cause='test')
227bf1a3 199 assert isinstance(error, IncompleteRead)
200 assert repr(error) == '<IncompleteRead: 4 bytes read, 3 more expected>'
201 assert str(error) == error.msg == '4 bytes read, 3 more expected'
5ca095cb 202 assert error.partial == 4
227bf1a3 203 assert error.expected == 3
204 assert error.cause == 'test'
205
5ca095cb 206 error = IncompleteRead(3)
227bf1a3 207 assert repr(error) == '<IncompleteRead: 3 bytes read>'
208 assert str(error) == '3 bytes read'