├── README.md ├── nginx.conf ├── nl.robwu.pdfjs.conf ├── pdfjs-logrotate └── testserver.py /README.md: -------------------------------------------------------------------------------- 1 | This repository contains the configuration files for pdfjs.robwu.nl. This site 2 | collects anonymous statistics from the [PDF Viewer Chrome extension](https://github.com/mozilla/pdf.js/wiki/PDF-Viewer-%28Chrome-extension%29) 3 | ("telemetry") since version 1.5.285. 4 | 5 | [extensions/chromium/telemetry.js](https://github.com/mozilla/pdf.js/blob/master/extensions/chromium/telemetry.js) 6 | shows the extension code that sends such requests. 7 | 8 | ## Privacy policy 9 | 10 | ### What information is collected? 11 | 12 | The PDF Viewer extension sends at most two pings a day to the server, 13 | and the following data is saved: 14 | 15 | - The server's local time (year, month, day). 16 | - User agent string (browser version, Operating System). 17 | - PDF Viewer extension version. 18 | - A random constant identifier. The value is refreshed at every major browser 19 | update (roughly every six weeks). When the identifier is refreshed, it is not 20 | possible to relate the ID to any previously generated ID. 21 | 22 | The exact time and IP address is *not* saved, so the collected information 23 | cannot be traced back to an individual. 24 | 25 | 26 | ### Why is the information being collected? 27 | 28 | The user agent string is used to determine how many users are still using an 29 | old version of Chrome. This data guides decisions about updating the extension 30 | to replace old technologies (APIs) with new ones. 31 | 32 | The random identifier is only used to remove duplicate entries when the log data 33 | is aggregated. The identifier is not included in any public log data. 34 | 35 | 36 | ### Who can access the data? 37 | 38 | The server is operated by [Rob Wu](https://robwu.nl) (the main developer of the 39 | PDF Viewer Chrome extension) and hosted in The Netherlands on servers from a 40 | Dutch provider (https://www.liteserver.nl). 41 | 42 | The data is automatically erased after one year. 43 | 44 | 45 | ### How can I disable telemetry? 46 | 47 | Telemetry is enabled by default. Visit the options page of the extension and 48 | tick the "Disable telemetry" checkbox. This value can also be set through 49 | enterprise policies via the "disableTelemetry" setting. 50 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | # Note: This file is intended for local testing. 2 | # The actual server may have different parameters 3 | # (e.g. OCSP stapling enabled) 4 | pid nginx.pid; 5 | 6 | events { 7 | } 8 | 9 | http { 10 | include nl.robwu.pdfjs.conf; 11 | client_body_temp_path temp; 12 | fastcgi_temp_path temp; 13 | uwsgi_temp_path temp; 14 | scgi_temp_path temp; 15 | 16 | ssl_session_timeout 1d; 17 | ssl_session_cache shared:SSL:50m; 18 | ssl_session_tickets off; 19 | 20 | ssl_protocols TLSv1.2; 21 | ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; 22 | ssl_prefer_server_ciphers on; 23 | } 24 | 25 | # vim: syntax=nginx smartindent 26 | -------------------------------------------------------------------------------- /nl.robwu.pdfjs.conf: -------------------------------------------------------------------------------- 1 | # This configuration creates a server that does nothing else besides logging 2 | # some data as explained in https://github.com/mozilla/pdf.js/issues/7312. 3 | # 4 | # Method: POST 5 | # Path : /logpdfjs 6 | # Request headers: 7 | # - Deduplication-ID: hexadecimal string of length 10. 8 | # - User-Agent: At most 1000 characters. 9 | # - Extension-Version: 1 - 24 characters (up to 4 uint16_t separated by dot). 10 | # 11 | # The following data is logged: 12 | # - The above request headers. 13 | # 14 | # 15 | # This design was chosen for the following reasons: 16 | # 17 | # - Third-party web pages cannot forge the request, because of the custom header 18 | # requirement. This header can thus only be set through the XMLHttpRequest or 19 | # fetch API. However, that is blocked unless the request is allowed via CORS. 20 | # A custom header is a non-simple request, so the POST request will always be 21 | # preceded by an OPTIONS request. This is rejected by the server. 22 | # 23 | # - The logged information is minimal to avoid any privacy risks. 24 | # 25 | # - Note that fake data (i.e. requests made via curl, etc.) are tolerated. 26 | 27 | # First the fixed-format values, then the arbitrary-value UA string. 28 | log_format pdfjs 29 | '$http_deduplication_id ' 30 | '$http_extension_version ' 31 | '"$http_user_agent"'; 32 | 33 | map $http_user_agent $has_valid_headers_2 { 34 | '~^.{1,1000}$' 1; 35 | } 36 | map $http_deduplication_id $has_valid_headers_1 { 37 | '~^[0-9a-f]{10}$' $has_valid_headers_2; 38 | } 39 | map $http_extension_version $has_valid_headers { 40 | # The number of parts must be between 1 and 4 (inclusive) and each part 41 | # is separated by a dot. Each part must fit in a 16-bit integer, i.e. 42 | # between 0 and 65535 (inclusive). 43 | '~^(([0-5]?[0-9]{1,4}|6([0-4][0-9]{3}|5([0-4][0-9]{2}|5([0-2][0-9]|3[0-5]))))(\.(?!$)|$)){1,4}$' $has_valid_headers_1; 44 | } 45 | 46 | server { 47 | listen 5.2.64.236:80; 48 | listen [2a04:52c0:101:da::7312]:80; 49 | listen 5.2.64.236:443 ssl; 50 | listen [2a04:52c0:101:da::7312]:443 ssl; 51 | 52 | server_name pdfjs.robwu.nl; 53 | ssl_certificate /home/letsencrypt/keys/pdfjs.robwu.nl.crt; 54 | ssl_certificate_key /home/letsencrypt/keys/pdfjs.robwu.nl.key; 55 | ssl_trusted_certificate /home/letsencrypt/keys/pdfjs.robwu.nl.crt; 56 | 57 | access_log off; 58 | log_not_found off; 59 | log_subrequest off; 60 | max_ranges 0; 61 | 62 | # Restrict length of headers. Only the User-Agent header has to fit in here. 63 | client_header_buffer_size 1k; 64 | large_client_header_buffers 4 1k; 65 | 66 | # We don't expect a request body, so reject any value. 67 | client_max_body_size 1; 68 | keepalive_timeout 0; 69 | 70 | location = /logpdfjs { 71 | if ($request_method != POST) { 72 | return 405; 73 | } 74 | if ($has_valid_headers) { 75 | access_log /var/log/pdfjs/pdfjs.log pdfjs; 76 | return 204; 77 | } 78 | return 400; 79 | } 80 | 81 | location = /robots.txt { 82 | return 200 'User-Agent: * 83 | Disallow: / 84 | '; 85 | } 86 | 87 | # For Let's encrypt. 88 | location ^~ /.well-known/acme-challenge/ { 89 | root /home/letsencrypt/public_html; 90 | } 91 | 92 | location = / { 93 | return 302 'https://github.com/mozilla/pdf.js/wiki/PDF-Viewer-(Chrome-extension)'; 94 | } 95 | 96 | location / { 97 | return 404; 98 | } 99 | } 100 | 101 | # vim: syntax=nginx smartindent 102 | -------------------------------------------------------------------------------- /pdfjs-logrotate: -------------------------------------------------------------------------------- 1 | /var/log/pdfjs/pdfjs.log { 2 | # file name: pdfjs.log-YYYYMMDD 3 | daily 4 | dateext 5 | maxage 366 6 | 7 | compress 8 | delaycompress 9 | create 0640 root adm 10 | sharedscripts 11 | postrotate 12 | [ ! -f /run/nginx.pid ] || kill -USR1 "$(cat /run/nginx.pid)" 13 | # TODO: aggregate results. E.g. To get the stats for the past 7 days (extension version + UA), use: 14 | # find /var/log/pdfjs/ -maxdepth 1 -name 'pdfjs.log-*' -type f | sort | tail -n 7 | zcat | sort | uniq | cut 12- 15 | endscript 16 | } 17 | -------------------------------------------------------------------------------- /testserver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This file provides unit tests to verify the correctness of the server at 4 | # pdfjs.robwu.nl. To run local tests, Nginx must be installed. 5 | # The local test expects localhost.crt and localhost.key to exist in the 6 | # directory containing this script. If these are unavailable, they are 7 | # generated on the fly using openssl (which should then be installed). 8 | # 9 | # To run the tests: 10 | # ./testserver.py TestHttp TestHttps # Run test against local Nginx server. 11 | # ./testserver.py TestProd # Run test against pdfjs.robwu.nl 12 | 13 | import atexit 14 | import os 15 | import re 16 | import signal 17 | import ssl 18 | import shutil 19 | import subprocess 20 | import tempfile 21 | import time 22 | import unittest 23 | 24 | try: 25 | # Py3 26 | from http.client import BadStatusLine 27 | from urllib.request import HTTPHandler, HTTPSHandler, UnknownHandler 28 | from urllib.request import OpenerDirector, Request 29 | from urllib.error import URLError 30 | except ImportError: 31 | # Py2 32 | from httplib import BadStatusLine 33 | from urllib2 import HTTPHandler, HTTPSHandler, UnknownHandler 34 | from urllib2 import OpenerDirector, Request 35 | from urllib2 import URLError 36 | 37 | 38 | def get_http_status(url, **kwargs): 39 | return get_http_response(url, **kwargs).getcode() 40 | 41 | 42 | def get_http_response(url, **kwargs): 43 | req = Request(url, 44 | data=kwargs.get('data', None), 45 | headers=kwargs.get('headers', {})) 46 | context = None 47 | if url.startswith('https://localhost'): 48 | if get_http_response._shouldWarnAboutCertValidation: 49 | get_http_response._shouldWarnAboutCertValidation = False 50 | print('Warning: Disabling certificate validation for localhost') 51 | context = ssl._create_unverified_context() 52 | # Use OpenerDirector instead of urlopen(req, context=context) to make sure 53 | # that we can send requests without automatically following redirects, and 54 | # that non-2xx status codes won't raise HTTPError. 55 | opener = OpenerDirector() 56 | opener.add_handler(HTTPHandler()) 57 | opener.add_handler(HTTPSHandler(context=context)) 58 | opener.add_handler(UnknownHandler()) 59 | return opener.open(req) 60 | 61 | 62 | get_http_response._shouldWarnAboutCertValidation = True 63 | 64 | 65 | def good_headers(): 66 | '''A dictionary of expected good headers.''' 67 | return { 68 | 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36 = testserver.py', # NOQA 69 | 'deduplication-id': '0123456789', 70 | 'extension-version': '0', 71 | } 72 | 73 | 74 | class LocalServer: 75 | instance = None 76 | # Let's hard-code them for now. 77 | http_port = 8088 78 | https_port = 8443 79 | 80 | server_proc = None 81 | nginx_root_path = '' 82 | nginx_prefix_path = '' 83 | nginx_log_path = '' 84 | 85 | @staticmethod 86 | def Get(): 87 | if not LocalServer.instance: 88 | LocalServer.instance = LocalServer() 89 | LocalServer.instance.start_server() 90 | return LocalServer.instance 91 | 92 | def _create_nginx_root_content(self): 93 | ''' 94 | Create a temporary directory containing the the Nginx server's config. 95 | ''' 96 | def src_path(x): return os.path.join(os.path.dirname(__file__), x) 97 | 98 | with open(src_path('nl.robwu.pdfjs.conf')) as f: 99 | server_conf = f.read() 100 | 101 | for count, needle, replacement in [ 102 | # Listen on localhost only. 103 | [0, '(?= listen )', ' #'], 104 | [1, '(?=# listen .*80;)', 105 | 'listen 127.0.0.1:%d; ' % self.http_port], 106 | [1, '(?=# listen .*443 ssl;)', 107 | 'listen 127.0.0.1:%d ssl; ' % self.https_port], 108 | 109 | # Self-signed test certificates, e.g. via 110 | # openssl req -x509 -newkey rsa:2048 -keyout localhost.key -out localhost.crt -nodes -sha256 -subj '/CN=localhost' # NOQA 111 | [1, '( ssl_certificate) .+;', r'\1 localhost.crt;'], 112 | [1, '( ssl_certificate_key) .+;', r'\1 localhost.key;'], 113 | # Not really needed because the test server doesn't have OCSP, 114 | # but Nginx checks whether the file exists, so rename it. 115 | [1, '( ssl_trusted_certificate) .+;', r'\1 localhost.crt;'], 116 | 117 | # Write to a temporary access log instead of a system destination. 118 | [1, '( access_log) /[^ ]+', r'\1 localhost.log'], 119 | 120 | # Without this, it takes 5 seconds before the logs are flushed. 121 | # Also, when worker processes are used, this also slows down 122 | # termination by 5s (because workers wait until the sockets are 123 | # closed upon a graceful shutdown). 124 | [1, 'server {', 'server { lingering_close off;'], 125 | ]: 126 | rneedle = re.compile(needle) 127 | found = rneedle.findall(server_conf) 128 | if not found: 129 | raise ValueError('Not found in source: %s' % needle) 130 | if count and len(found) < count: 131 | raise ValueError('Expected %d, but got %d for: %s' % ( 132 | count, len(found), needle)) 133 | server_conf = re.sub( 134 | rneedle, replacement, server_conf, count=count) 135 | 136 | # Generation succeeded, create files. 137 | 138 | nginx_root = tempfile.mkdtemp(prefix='nginx_test_server') 139 | with open(os.path.join(nginx_root, 'nl.robwu.pdfjs.conf'), 'w') as f: 140 | f.write(server_conf) 141 | 142 | if not os.path.isfile(src_path('localhost.key')): 143 | subprocess.call([ 144 | 'openssl', 'req', '-x509', 145 | '-newkey', 'rsa:2048', 146 | '-keyout', 'localhost.key', 147 | '-out', 'localhost.crt', 148 | '-nodes', 149 | '-sha256', 150 | '-subj', '/CN=localhost' 151 | ], cwd=src_path('.')) 152 | 153 | for filename in [ 154 | 'nginx.conf', 155 | # For testing, these are actually self-signed certificates. 156 | 'localhost.crt', 157 | 'localhost.key', 158 | ]: 159 | shutil.copyfile(src_path(filename), 160 | os.path.join(nginx_root, filename)) 161 | 162 | prefix_path = os.path.join(nginx_root, 'prefix') 163 | os.mkdir(prefix_path) 164 | # Required by nginx, "temp" as specific in nginx.conf 165 | os.mkdir(os.path.join(prefix_path, 'temp')) 166 | 167 | self.nginx_root_path = nginx_root 168 | self.nginx_prefix_path = prefix_path 169 | 170 | def start_server(self): 171 | ''' 172 | Start a Nginx server at the given ports. 173 | ''' 174 | 175 | if not self.nginx_root_path: 176 | self._create_nginx_root_content() 177 | 178 | self.nginx_log_path = '%s/localhost.log' % self.nginx_prefix_path 179 | 180 | print('Starting nginx server at %s' % self.nginx_root_path) 181 | self.server_proc = subprocess.Popen([ 182 | 'nginx', 183 | '-p', self.nginx_prefix_path, 184 | '-c', os.path.join(self.nginx_root_path, 'nginx.conf'), 185 | '-g', 'daemon off; master_process off;', 186 | ]) 187 | 188 | atexit.register(self.stop_server) 189 | 190 | # Wait until the server has started (at most a few seconds) 191 | for i in range(0, 10): 192 | try: 193 | get_http_status('http://localhost:%d' % self.http_port) 194 | return # Request succeeded, server started. 195 | except URLError: 196 | time.sleep(0.2) 197 | 198 | def stop_server(self): 199 | self.server_proc.send_signal(signal.SIGQUIT) 200 | self.server_proc.wait() 201 | self.server_proc = None 202 | 203 | def get_http_base_url(self): 204 | return 'http://localhost:%d' % self.http_port 205 | 206 | def get_https_base_url(self): 207 | return 'https://localhost:%d' % self.https_port 208 | 209 | def get_log_content(self): 210 | # Assume that the logs have been written. 211 | # We have set lingering_close to "off", and from Python's side (urllib) 212 | # the request has ended, so the log should immediately be flushed. 213 | with open(self.nginx_log_path, 'r') as f: 214 | return f.read() 215 | 216 | 217 | class TestHttpBase(object): 218 | ''' 219 | These tests check whether the response from the logging server is OK. 220 | If the response is 204, it's assumed that an entry is written to the log, 221 | but the tests do NOT check whether the log is actually written. 222 | ''' 223 | 224 | @classmethod 225 | def setUpClass(cls): 226 | cls.base_url = cls.get_base_url() 227 | # Check whether we can connect before running all other tests. 228 | get_http_status(cls.base_url) 229 | 230 | def assertStatus(self, expected_status, path, **kwargs): 231 | http_method = 'POST' if 'data' in kwargs else 'GET' 232 | status = get_http_status(self.base_url + path, **kwargs) 233 | msg = 'Expected %d but got %d for %s %s' % ( 234 | expected_status, status, http_method, path) 235 | self.assertEqual(expected_status, status, msg) 236 | 237 | def test_non_existing_404(self): 238 | self.assertStatus(404, '/.well-known/') 239 | self.assertStatus(404, '/favicon.ico') 240 | # Actually, robots.txt is supported to avoid getting crawled. 241 | self.assertStatus(200, '/robots.txt') 242 | 243 | self.assertStatus(404, '/.well-known/', data=b'') 244 | 245 | def test_root_index_response(self): 246 | self.assertStatus(302, '/') 247 | res = get_http_response(self.base_url + '/') 248 | self.assertEqual( 249 | res.headers['location'], 250 | 'https://github.com/mozilla/pdf.js/wiki/PDF-Viewer-(Chrome-extension)', # NOQA 251 | 'Expected redirect target at /' 252 | ) 253 | 254 | # Redirect is independent of HTTP method, also for POST: 255 | self.assertStatus(302, '/', data=b'') 256 | 257 | def test_logging_invalid_method(self): 258 | self.assertStatus(405, '/logpdfjs') 259 | 260 | def test_logging_invalid_body(self): 261 | # Nginx doesn't allow us to disable the body, so the config accepts 262 | # length 1. Requests with bodies of size 2 should be rejected though. 263 | self.assertStatus(413, '/logpdfjs', data=b'12') 264 | 265 | def test_logging_valid_headers(self): 266 | self.assertStatus(204, '/logpdfjs', data=b'', headers=good_headers()) 267 | 268 | def test_logging_invalid_headers(self): 269 | self.assertStatus(400, '/logpdfjs', data=b'', headers={}) 270 | 271 | headers = good_headers() 272 | del headers['deduplication-id'] 273 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 274 | 275 | # Can't test a missing User-Agent header because urllib always adds it. 276 | # Let's assume that Nginx doesn't blow up when it is missing... 277 | 278 | headers = good_headers() 279 | del headers['extension-version'] 280 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 281 | 282 | def test_logging_valid_deduplication_id(self): 283 | headers = good_headers() 284 | headers['deduplication-id'] = '0123abcdef' 285 | self.assertStatus(204, '/logpdfjs', data=b'', headers=headers) 286 | 287 | def test_logging_invalid_deduplication_id(self): 288 | # Note that the last character in Deduplication-Id is an uppercase 'F'. 289 | headers = good_headers() 290 | headers['deduplication-id'] = '012345678F' 291 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 292 | 293 | headers = good_headers() 294 | headers['deduplication-id'] = '012345678g' 295 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 296 | 297 | # Too short 298 | headers = good_headers() 299 | headers['deduplication-id'] = '012345678' 300 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 301 | 302 | # Too long 303 | headers = good_headers() 304 | headers['deduplication-id'] = '0123456789a' 305 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 306 | 307 | def test_logging_valid_user_agent(self): 308 | # Minimal allowed 309 | headers = good_headers() 310 | headers['user-agent'] = 'a' 311 | self.assertStatus(204, '/logpdfjs', data=b'', headers=headers) 312 | 313 | # Maximum allowed. 314 | headers = good_headers() 315 | headers['user-agent'] = 'a' * 1000 316 | self.assertStatus(204, '/logpdfjs', data=b'', headers=headers) 317 | 318 | def test_logging_invalid_user_agent(self): 319 | # Too short 320 | headers = good_headers() 321 | headers['user-agent'] = '' 322 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 323 | 324 | # Too long 325 | headers = good_headers() 326 | headers['user-agent'] = 'a' * 1001 327 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 328 | 329 | headers = good_headers() 330 | headers['user-agent'] = 'x\x00' 331 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 332 | 333 | def test_logging_valid_extension_version(self): 334 | headers = good_headers() 335 | headers['extension-version'] = '0' 336 | self.assertStatus(204, '/logpdfjs', data=b'', headers=headers) 337 | 338 | headers = good_headers() 339 | headers['extension-version'] = '0.0.0.0' 340 | self.assertStatus(204, '/logpdfjs', data=b'', headers=headers) 341 | 342 | headers = good_headers() 343 | headers['extension-version'] = '65535.65535.65535.65535' 344 | self.assertStatus(204, '/logpdfjs', data=b'', headers=headers) 345 | 346 | def test_logging_invalid_extension_version(self): 347 | headers = good_headers() 348 | headers['extension-version'] = '' 349 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 350 | 351 | headers = good_headers() 352 | headers['extension-version'] = '.' 353 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 354 | 355 | headers = good_headers() 356 | headers['extension-version'] = '0.' 357 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 358 | 359 | headers = good_headers() 360 | headers['extension-version'] = '.0' 361 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 362 | 363 | headers = good_headers() 364 | headers['extension-version'] = '65536' 365 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 366 | 367 | def test_extension_version_pattern(self): 368 | # Copy-paste of the numeric regex in the nginx config. 369 | regex = '^([0-5]?[0-9]{1,4}|6([0-4][0-9]{3}|5([0-4][0-9]{2}|5([0-2][0-9]|3[0-5]))))$' # NOQA 370 | pattern = re.compile(regex, re.DOTALL) 371 | for i in range(0, 0xFFFF + 1): 372 | self.assertTrue(re.match(pattern, str(i)), 373 | '%d should match the version pattern!' % i) 374 | 375 | self.assertFalse(re.match(pattern, str(0xFFFF + 1)), 376 | '0xFFFF+1 should not match the version pattern!') 377 | 378 | 379 | class TestLocalBase(object): 380 | ''' 381 | Tests specific to a local Nginx instance. 382 | ''' 383 | def test_did_write_log(self): 384 | old_log = LocalServer.Get().get_log_content() 385 | 386 | headers = good_headers() 387 | headers['extension-version'] = '1337' 388 | self.assertStatus(204, '/logpdfjs', data=b'', headers=headers) 389 | 390 | new_log = LocalServer.Get().get_log_content() 391 | 392 | new_log = new_log[len(old_log):] 393 | self.assertNotEqual(new_log, '', 'Expected a new log entry.') 394 | self.assertEqual(new_log, '0123456789 1337 "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36 = testserver.py"\n') # NOQA 395 | 396 | def test_did_not_write_log(self): 397 | old_log = LocalServer.Get().get_log_content() 398 | 399 | headers = good_headers() 400 | headers['extension-version'] = '' 401 | self.assertStatus(400, '/logpdfjs', data=b'', headers=headers) 402 | 403 | new_log = LocalServer.Get().get_log_content() 404 | 405 | new_log = new_log[len(old_log):] 406 | self.assertEqual(new_log, '', 'Expected a new log entry.') 407 | 408 | 409 | class TestHttp(TestHttpBase, TestLocalBase, unittest.TestCase): 410 | @staticmethod 411 | def get_base_url(): 412 | return LocalServer.Get().get_http_base_url() 413 | 414 | 415 | class TestHttps(TestHttpBase, TestLocalBase, unittest.TestCase): 416 | @staticmethod 417 | def get_base_url(): 418 | return LocalServer.Get().get_https_base_url() 419 | 420 | 421 | class TestProd(TestHttpBase, unittest.TestCase): 422 | @staticmethod 423 | def get_base_url(): 424 | return 'https://pdfjs.robwu.nl' 425 | 426 | def test_bad_host(self): 427 | headers = good_headers() 428 | headers['host'] = 'not.pdfjs.robwu.nl' 429 | try: 430 | status = get_http_status(self.base_url + '/logpdfjs', data=b'', 431 | headers=headers) 432 | self.assertNotEqual(204, status) 433 | except BadStatusLine: 434 | pass # Connection closed, OK! 435 | 436 | def test_http(self): 437 | status = get_http_status('http://pdfjs.robwu.nl/logpdfjs', 438 | data=b'', headers=good_headers()) 439 | self.assertEqual(204, status) 440 | 441 | def test_http_bad_host(self): 442 | headers = good_headers() 443 | headers['host'] = 'not.pdfjs.robwu.nl' 444 | try: 445 | status = get_http_status('http://pdfjs.robwu.nl/logpdfjs', 446 | data=b'', headers=headers) 447 | self.assertNotEqual(204, status) 448 | except BadStatusLine: 449 | pass # Connection closed, OK! 450 | 451 | 452 | if __name__ == '__main__': 453 | unittest.main() 454 | --------------------------------------------------------------------------------