def redirect(http_request, factory_name, encrypted_password, request_id): """ Redirect the browser to the requested URL for the screenshot, and save the browser in the database. """ try: factory = get_object_or_404(Factory, name=factory_name) nonces.verify(http_request, factory, encrypted_password) request = get_object_or_404(Request, id=request_id) request.check_factory_lock(factory) user_agent = http_request.META['HTTP_USER_AGENT'] try: browser = Browser.objects.get( factory=factory, user_agent=user_agent, active=True) except Browser.DoesNotExist: raise Fault(404, u"Unknown user agent: %s." % user_agent) # Check that the browser matches the request if (request.browser_group_id is not None and request.browser_group_id != browser.browser_group_id): raise Fault(409, u"Requested browser %s but got %s." % (request.browser_group.name, browser.browser_group.name)) if ((request.major is not None and request.major != browser.major) or (request.minor is not None and request.minor != browser.minor)): raise Fault(409, u"Requested browser version %s.%s but got %s.%s." % (request.major, request.minor, browser.major, browser.minor)) # Update request with browser and redirect timestamp request.update_fields(browser=browser, redirected=datetime.now()) return HttpResponseRedirect(request.request_group.website.url) except Fault, fault: FactoryError.objects.create(factory=factory, code=fault.faultCode, message=fault.faultString) return error_page(http_request, "redirect error", fault.faultString)
def poll(http_request, factory, encrypted_password): """ Try to find a matching screenshot request for a given factory. Arguments ~~~~~~~~~ * factory_name string (lowercase, normally from hostname) * encrypted_password string (lowercase hexadecimal, length 32) See nonces.verify for how to encrypt your password. Return value ~~~~~~~~~~~~ * options dict (screenshot request configuration) If successful, the options dict will have the following keys: * request int (for redirect and screenshots.upload) * browser string (browser name) * version string (browser version) * major int (major browser version number) * minor int (minor browser version number) * command string (browser command to run, empty for default) * width int (screen width in pixels) * height int (screen height in pixels) * bpp int (color depth in bits per pixel) * javascript string (javascript version) * java string (java version) * flash string (flash version) Locking ~~~~~~~ The matching screenshot request is locked for five minutes. This is to make sure that no requests are processed by two factories at the same time. If your factory takes longer to process a request, it is possible that somebody else will lock it. In this case, your upload will fail. """ # Verify authentication nonces.verify(http_request, factory, encrypted_password) # Update last_poll timestamp factory.update_fields(last_poll=datetime.now(), ip=http_request.META['REMOTE_ADDR']) if hasattr(settings, 'FACTORY_THROTTLE_INTERVAL'): if factory.name in settings.FACTORY_THROTTLE_INTERVAL: interval = settings.FACTORY_THROTTLE_INTERVAL[factory.name] if datetime.now() - factory.last_upload < interval: raise Fault( 205, ' '.join(( "Sorry, your screenshot factory is blocked for a few minutes.", "Please check your email for error messages from Browsershots." ))) # Check server load randomized_load = max(os.getloadavg()) * random.random() if factory.operating_system.platform_id in PRIORITY_PLATFORMS: randomized_load /= 2 if randomized_load > ACCEPTABLE_SERVER_LOAD: raise Fault( 503, "The server is currently overloaded. Please try again in a minute." ) # Get matching request request = find_and_lock_request(factory, factory.features_q()) # Get matching browser filters = { 'factory': factory, 'browser_group': request.browser_group, 'active': True } add_version(filters, request.major, 'major', exact=True) add_version(filters, request.minor, 'minor', exact=True) add_version(filters, request.request_group.javascript, 'javascript__id') add_version(filters, request.request_group.java, 'java__id') add_version(filters, request.request_group.flash, 'flash__id') try: browser = Browser.objects.select_related().get(**filters) except Browser.DoesNotExist: raise Fault(404, "No matching browser for selected request.") # Build result dict screen_size = select_screen_size(factory, request) color_depth = select_color_depth(factory, request) return { 'request': request.id, 'browser': browser.browser_group.name, 'version': browser.version, 'major': browser.major, 'minor': browser.minor, 'command': browser.command, 'width': screen_size.width, 'height': screen_size.height, 'bpp': color_depth.bits_per_pixel, 'javascript': version_or_empty(request.request_group.javascript), 'java': version_or_empty(request.request_group.java), 'flash': version_or_empty(request.request_group.flash), }
def poll(http_request, factory, encrypted_password): """ Try to find a matching screenshot request for a given factory. Arguments ~~~~~~~~~ * factory_name string (lowercase, normally from hostname) * encrypted_password string (lowercase hexadecimal, length 32) See nonces.verify for how to encrypt your password. Return value ~~~~~~~~~~~~ * options dict (screenshot request configuration) If successful, the options dict will have the following keys: * request int (for redirect and screenshots.upload) * browser string (browser name) * version string (browser version) * major int (major browser version number) * minor int (minor browser version number) * command string (browser command to run, empty for default) * width int (screen width in pixels) * height int (screen height in pixels) * bpp int (color depth in bits per pixel) * javascript string (javascript version) * java string (java version) * flash string (flash version) Locking ~~~~~~~ The matching screenshot request is locked for five minutes. This is to make sure that no requests are processed by two factories at the same time. If your factory takes longer to process a request, it is possible that somebody else will lock it. In this case, your upload will fail. """ # Verify authentication nonces.verify(http_request, factory, encrypted_password) # Update last_poll timestamp factory.update_fields(last_poll=datetime.now(), ip=http_request.META['REMOTE_ADDR']) if hasattr(settings, 'FACTORY_THROTTLE_INTERVAL'): if factory.name in settings.FACTORY_THROTTLE_INTERVAL: interval = settings.FACTORY_THROTTLE_INTERVAL[factory.name] if datetime.now() - factory.last_upload < interval: raise Fault(205, ' '.join(( "Sorry, your screenshot factory is blocked for a few minutes.", "Please check your email for error messages from Browsershots."))) # Check server load randomized_load = max(os.getloadavg()) * random.random() if factory.operating_system.platform_id in PRIORITY_PLATFORMS: randomized_load /= 2 if randomized_load > ACCEPTABLE_SERVER_LOAD: raise Fault(503, "The server is currently overloaded. Please try again in a minute.") # Get matching request request = find_and_lock_request(factory, factory.features_q()) # Get matching browser filters = {'factory': factory, 'browser_group': request.browser_group, 'active': True} add_version(filters, request.major, 'major', exact=True) add_version(filters, request.minor, 'minor', exact=True) add_version(filters, request.request_group.javascript, 'javascript__id') add_version(filters, request.request_group.java, 'java__id') add_version(filters, request.request_group.flash, 'flash__id') try: browser = Browser.objects.select_related().get(**filters) except Browser.DoesNotExist: raise Fault(404, "No matching browser for selected request.") # Build result dict screen_size = select_screen_size(factory, request) color_depth = select_color_depth(factory, request) return { 'request': request.id, 'browser': browser.browser_group.name, 'version': browser.version, 'major': browser.major, 'minor': browser.minor, 'command': browser.command, 'width': screen_size.width, 'height': screen_size.height, 'bpp': color_depth.bits_per_pixel, 'javascript': version_or_empty(request.request_group.javascript), 'java': version_or_empty(request.request_group.java), 'flash': version_or_empty(request.request_group.flash), }
def upload(http_request, factory, encrypted_password, request, screenshot): """ Submit a multi-page screenshot as a lossless PNG file. Arguments ~~~~~~~~~ * factory_name string (lowercase, normally from hostname) * encrypted_password string (lowercase hexadecimal, length 32) * request int (from requests.poll) * screenshot binary (BASE64-encoded PNG file) See nonces.verify for how to encrypt your password. Return value ~~~~~~~~~~~~ * hashkey string (lowercase hexadecimal, length 32) Users can see the resulting uploaded screenshot at http://browsershots.org/screenshots/hashkey/ """ # Verify authentication nonces.verify(http_request, factory, encrypted_password) request_id = request request = get_or_fault(Request, pk=request_id) request_group = request.request_group # Make sure the request was locked by this factory request.check_factory_lock(factory) # Store and check screenshot file hashkey = storage.save_upload(screenshot) bytes = storage.png_filesize(hashkey) # Make sure the request was redirected by the browser browser = request.browser if browser is None or browser.factory_id != factory.id: guessed = factory.browser_set.filter(active=True, browser_group=request.browser_group, major=request.major, minor=request.minor)[:1] if not len(guessed): guessed = [None] raise ExtraFault(406, u"The browser has not visited the requested website.", request=request, hashkey=hashkey, browser=guessed[0]) # Unpack PNG file and run more checks ppmname = storage.pngtoppm(hashkey) try: magic, width, height = storage.read_pnm_header(ppmname) if request_group.width and request_group.width != width: raise ExtraFault(412, u"The screenshot is %d pixels wide, not %d as requested." % (width, request_group.width), request=request, hashkey=hashkey, browser=browser) if height > width * 4: raise ExtraFault(413, u"The screenshot is too tall (more than 4 times the width).", request=request, hashkey=hashkey, browser=browser) if height < width / 2: raise ExtraFault(414, u"The screenshot is too short (less than half the width).", request=request, hashkey=hashkey, browser=browser) if os.path.exists('/usr/local/etc/pbmgrep'): check_ppm_problems(ppmname, request, hashkey, browser) # Make smaller preview images for size in PREVIEW_SIZES: storage.scale(ppmname, size, hashkey) finally: # Delete temporary PPM file os.unlink(ppmname) # Upload screenshots to Amazon S3 (but not for anonymous users) if hasattr(settings, 'S3_BUCKETS') and request_group.user_id is not None: storage.s3_upload(hashkey) # size='original' for size in PREVIEW_SIZES: storage.s3_upload(hashkey, size) # Save screenshot in database screenshot = Screenshot(hashkey=hashkey, user=request_group.user, website=request_group.website, factory=factory, browser=browser, width=width, height=height, bytes=bytes) screenshot.save() # Close the request close_request(request_id, factory, screenshot) # Update timestamps and estimates now = datetime.now() if request.priority == 0: factory.update_fields(last_upload=now, queue_estimate=(now - request_group.submitted).seconds) else: factory.update_fields(last_upload=now) browser.update_fields(last_upload=now) return hashkey
def upload(http_request, factory, encrypted_password, request, screenshot): """ Submit a multi-page screenshot as a lossless PNG file. Arguments ~~~~~~~~~ * factory_name string (lowercase, normally from hostname) * encrypted_password string (lowercase hexadecimal, length 32) * request int (from requests.poll) * screenshot binary (BASE64-encoded PNG file) See nonces.verify for how to encrypt your password. Return value ~~~~~~~~~~~~ * hashkey string (lowercase hexadecimal, length 32) Users can see the resulting uploaded screenshot at http://browsershots.org/screenshots/hashkey/ """ # Verify authentication nonces.verify(http_request, factory, encrypted_password) request_id = request request = get_or_fault(Request, pk=request_id) request_group = request.request_group # Make sure the request was locked by this factory request.check_factory_lock(factory) # Store and check screenshot file hashkey = storage.save_upload(screenshot) bytes = storage.png_filesize(hashkey) # Make sure the request was redirected by the browser browser = request.browser if browser is None or browser.factory_id != factory.id: guessed = factory.browser_set.filter( active=True, browser_group=request.browser_group, major=request.major, minor=request.minor)[:1] if not len(guessed): guessed = [None] raise ExtraFault(406, u"The browser has not visited the requested website.", request=request, hashkey=hashkey, browser=guessed[0]) # Unpack PNG file and run more checks ppmname = storage.pngtoppm(hashkey) try: magic, width, height = storage.read_pnm_header(ppmname) if request_group.width and request_group.width != width: raise ExtraFault( 412, u"The screenshot is %d pixels wide, not %d as requested." % (width, request_group.width), request=request, hashkey=hashkey, browser=browser) if height > width * 4: raise ExtraFault( 413, u"The screenshot is too tall (more than 4 times the width).", request=request, hashkey=hashkey, browser=browser) if height < width / 2: raise ExtraFault( 414, u"The screenshot is too short (less than half the width).", request=request, hashkey=hashkey, browser=browser) if os.path.exists('/usr/local/etc/pbmgrep'): check_ppm_problems(ppmname, request, hashkey, browser) # Make smaller preview images for size in PREVIEW_SIZES: storage.scale(ppmname, size, hashkey) finally: # Delete temporary PPM file os.unlink(ppmname) # Upload screenshots to Amazon S3 (but not for anonymous users) if hasattr(settings, 'S3_BUCKETS') and request_group.user_id is not None: storage.s3_upload(hashkey) # size='original' for size in PREVIEW_SIZES: storage.s3_upload(hashkey, size) # Save screenshot in database screenshot = Screenshot(hashkey=hashkey, user=request_group.user, website=request_group.website, factory=factory, browser=browser, width=width, height=height, bytes=bytes) screenshot.save() # Close the request close_request(request_id, factory, screenshot) # Update timestamps and estimates now = datetime.now() if request.priority == 0: factory.update_fields(last_upload=now, queue_estimate=(now - request_group.submitted).seconds) else: factory.update_fields(last_upload=now) browser.update_fields(last_upload=now) return hashkey