Skip to content

Commit

Permalink
Merge pull request #5 from iamdual/ch
Browse files Browse the repository at this point in the history
Improvements on Client Hints
  • Loading branch information
iamdual authored Mar 10, 2024
2 parents a871c8e + 802b0d2 commit 44da3ef
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 7 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ device = ('desktop', 'mobile')
platform = ('windows', 'macos', 'ios', 'linux', 'android')
browser = ('chrome', 'edge', 'firefox', 'safari')
```

_All of the parameters are optional, and the types can be set more than one by using a tuple._

## Customized user-agent generation:

```python
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = ua-generator
version = 0.3.0
version = 0.4.0
author = Ekin Karadeniz
author_email = iamdual@icloud.com
description = A random user-agent generator
Expand Down
25 changes: 24 additions & 1 deletion src/ua_generator/client_hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Copyright: 2022-2024 Ekin Karadeniz (github.com/iamdual)
License: Apache License 2.0
"""
from . import formats, serialization
from . import formats, serialization, utils
from .data import platforms_mobile
from .data import generator

Expand All @@ -17,6 +17,8 @@ class ClientHints:
platform_version: str
brands: str
brands_full_version_list: str
bitness: str
architecture: str

def __init__(self, gen: generator.Generator):
self.__generator = gen
Expand All @@ -38,6 +40,9 @@ def __platform(self):
return platform

def __platform_version(self):
if self.__generator.platform == 'windows' and formats.major_version(self.__generator.platform_version) == '10':
return utils.choice(('10.0.0', '13.0.0'))

return formats.version(self.__generator.platform_version)

def __brands(self, full_version_list: bool = False):
Expand All @@ -57,6 +62,20 @@ def __brands(self, full_version_list: bool = False):

return brand_list

def __bitness(self):
if self.__generator.platform == 'android':
return utils.choice(('32', '64', '32', '32'))

return '64'

def __architecture(self):
if self.__generator.platform == 'android' or self.__generator.platform == 'ios':
return 'arm'
elif self.__generator.platform == 'macos':
return utils.choice(('arm', 'x86', 'arm', 'arm'))

return 'x86'

def __getattr__(self, name):
if name in self.__cache:
return self.__cache[name]
Expand All @@ -71,6 +90,10 @@ def __getattr__(self, name):
self.__cache[name] = serialization.ch_brand_list(self.__brands())
elif name == 'brands_full_version_list':
self.__cache[name] = serialization.ch_brand_list(self.__brands(full_version_list=True))
elif name == 'bitness':
self.__cache[name] = serialization.ch_string(self.__bitness())
elif name == 'architecture':
self.__cache[name] = serialization.ch_string(self.__architecture())

return self.__cache[name]

Expand Down
2 changes: 1 addition & 1 deletion src/ua_generator/data/platforms/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@


def get_version():
return {'major': random.choice(versions)}
return {'major': random.choice(versions), 'minor': '0'}
13 changes: 11 additions & 2 deletions src/ua_generator/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ class Headers:
def __init__(self, gen: Generator, ch: ClientHints):
self.__generator = gen
self.__client_hints = ch
self.__headers = {}
self.reset()

def reset(self):
self.__headers = {
'user-agent': gen.user_agent,
'user-agent': self.__generator.user_agent,
}

# https://developer.mozilla.org/en-US/docs/Web/HTTP/Client_hints#low_entropy_hints
Expand All @@ -28,17 +32,22 @@ def add(self, key: str):
self.__headers[key] = self.__client_hints.brands
elif key == 'sec-ch-ua-full-version-list':
self.__headers[key] = self.__client_hints.brands_full_version_list
if key == 'sec-ch-ua-platform':
elif key == 'sec-ch-ua-platform':
self.__headers[key] = self.__client_hints.platform
elif key == 'sec-ch-ua-platform-version':
self.__headers[key] = self.__client_hints.platform_version
elif key == 'sec-ch-ua-mobile':
self.__headers[key] = self.__client_hints.mobile
elif key == 'sec-ch-ua-bitness':
self.__headers[key] = self.__client_hints.bitness
elif key == 'sec-ch-ua-arch':
self.__headers[key] = self.__client_hints.architecture

def accept_ch(self, val: str):
if self.__generator.browser not in ('chrome', 'edge'):
return

self.reset()
requested_hints = val.split(',')
for hint in requested_hints:
self.add(hint.strip().lower())
Expand Down
19 changes: 19 additions & 0 deletions tests/test_client_hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ def test_ch_platform_version(self):
self.assertTrue(type(ua.ch.platform_version) is str)
self.assertTrue(len(ua.ch.platform_version) > 0)

def test_ch_platform_version_windows(self):
for i in range(0, 100):
ua = ua_generator.generate(platform='windows')
self.assertIsNotNone(ua.ch)
self.assertTrue(type(ua.ch.platform_version) is str)
self.assertEqual(len(ua.ch.platform_version.split('.')), 3)

def test_ch_mobile(self):
for i in range(0, 100):
ua = ua_generator.generate(browser=('chrome', 'edge'), platform='android')
Expand Down Expand Up @@ -56,6 +63,18 @@ def test_ch_brands_full_version_list(self):
self.assertTrue('Chromium' in ua.ch.brands_full_version_list)
self.assertTrue('Microsoft Edge' in ua.ch.brands_full_version_list)

def test_ch_bitness(self):
for i in range(0, 100):
ua = ua_generator.generate(browser=('chrome', 'edge'))
self.assertIsNotNone(ua.ch)
self.assertIn(ua.ch.bitness, ('"32"', '"64"'))

def test_ch_architecture(self):
for i in range(0, 100):
ua = ua_generator.generate(browser=('chrome', 'edge'))
self.assertIsNotNone(ua.ch)
self.assertIn(ua.ch.architecture, ('"arm"', '"x86"'))


if __name__ == '__main__':
unittest.main()
21 changes: 21 additions & 0 deletions tests/test_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,31 @@ def test_client_hints_not_exists(self):
def test_accept_ch(self):
for i in range(0, 100):
ua = ua_generator.generate(browser=('chrome', 'edge'))

ua.headers.accept_ch('Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List')
self.assertTrue('sec-ch-ua-platform-version' in ua.headers.get())
self.assertTrue('sec-ch-ua-full-version-list' in ua.headers.get())

ua.headers.accept_ch('Sec-CH-UA-Bitness, Sec-CH-UA-Arch')
self.assertTrue('sec-ch-ua-bitness' in ua.headers.get())
self.assertTrue('sec-ch-ua-arch' in ua.headers.get())
self.assertFalse('sec-ch-ua-platform-version' in ua.headers.get())
self.assertFalse('sec-ch-ua-full-version-list' in ua.headers.get())

def test_reset(self):
for i in range(0, 100):
ua = ua_generator.generate(browser=('chrome', 'edge'))

ua.headers.add('sec-ch-ua-bitness')
self.assertTrue('user-agent' in ua.headers.get())
self.assertTrue('sec-ch-ua' in ua.headers.get())
self.assertTrue('sec-ch-ua-bitness' in ua.headers.get())

ua.headers.reset()
self.assertTrue('user-agent' in ua.headers.get())
self.assertTrue('sec-ch-ua' in ua.headers.get())
self.assertFalse('sec-ch-ua-bitness' in ua.headers.get())

def test_accept_ch_not_exists(self):
for i in range(0, 100):
ua = ua_generator.generate(browser=('chrome', 'edge'))
Expand Down

0 comments on commit 44da3ef

Please sign in to comment.