From be2604cacdd5372a4af6fbf2ebe3d2d8244c9412 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 11 Aug 2014 15:56:18 +0200 Subject: [PATCH] Add a new qutebrowser.utils.http module --- qutebrowser/browser/downloads.py | 3 +- .../test_content_disposition.py} | 20 +++---- qutebrowser/utils/http.py | 60 +++++++++++++++++++ qutebrowser/utils/misc.py | 37 ------------ 4 files changed, 72 insertions(+), 48 deletions(-) rename qutebrowser/test/utils/{test_misc_content_disposition.py => http/test_content_disposition.py} (98%) create mode 100644 qutebrowser/utils/http.py diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 7132f6932..9af04661b 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -31,6 +31,7 @@ import qutebrowser.config.config as config import qutebrowser.utils.message as message import qutebrowser.commands.utils as cmdutils import qutebrowser.utils.misc as utils +from qutebrowser.utils.http import parse_content_disposition from qutebrowser.utils.log import downloads as logger from qutebrowser.utils.log import fix_rfc2622 from qutebrowser.utils.usertypes import PromptMode, Question, Timer @@ -375,7 +376,7 @@ class DownloadManager(QObject): Args: reply: The QNetworkReply to download. """ - _inline, suggested_filename = utils.parse_content_disposition(reply) + _inline, suggested_filename = parse_content_disposition(reply) logger.debug("fetch: {} -> {}".format(reply.url(), suggested_filename)) download = DownloadItem(reply, self) download.finished.connect(partial(self.on_finished, download)) diff --git a/qutebrowser/test/utils/test_misc_content_disposition.py b/qutebrowser/test/utils/http/test_content_disposition.py similarity index 98% rename from qutebrowser/test/utils/test_misc_content_disposition.py rename to qutebrowser/test/utils/http/test_content_disposition.py index 2ee793f7b..08c705ef9 100644 --- a/qutebrowser/test/utils/test_misc_content_disposition.py +++ b/qutebrowser/test/utils/http/test_content_disposition.py @@ -17,13 +17,13 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -"""Tests for qutebrowser.utils.misc.parse_content_disposition.""" +"""Tests for qutebrowser.utils.http.parse_content_disposition.""" import os import unittest from unittest.mock import Mock -import qutebrowser.utils.misc as utils +import qutebrowser.utils.http as httputils from qutebrowser.test.stubs import FakeNetworkReply @@ -40,7 +40,7 @@ class AttachmentTestCase(unittest.TestCase): def _check_filename(self, header, filename): """Check if the passed header has the given filename.""" reply = FakeNetworkReply(header) - cd_inline, cd_filename = utils.parse_content_disposition(reply) + cd_inline, cd_filename = httputils.parse_content_disposition(reply) self.assertIsNotNone(cd_filename) self.assertEqual(cd_filename, filename) self.assertFalse(cd_inline) @@ -48,14 +48,14 @@ class AttachmentTestCase(unittest.TestCase): def _check_ignored(self, header): """Check if the passed header is ignored.""" reply = FakeNetworkReply(header) - cd_inline, cd_filename = utils.parse_content_disposition(reply) + cd_inline, cd_filename = httputils.parse_content_disposition(reply) self.assertEqual(cd_filename, DEFAULT_NAME) self.assertTrue(cd_inline) def _check_unnamed(self, header): """Check if the passed header results in an unnamed attachment.""" reply = FakeNetworkReply(header) - cd_inline, cd_filename = utils.parse_content_disposition(reply) + cd_inline, cd_filename = httputils.parse_content_disposition(reply) self.assertEqual(cd_filename, DEFAULT_NAME) self.assertFalse(cd_inline) @@ -70,14 +70,14 @@ class InlineTests(unittest.TestCase): def _check_filename(self, header, filename): """Check if the passed header has the given filename.""" reply = FakeNetworkReply(header) - cd_inline, cd_filename = utils.parse_content_disposition(reply) + cd_inline, cd_filename = httputils.parse_content_disposition(reply) self.assertEqual(cd_filename, filename) self.assertTrue(cd_inline) def _check_ignored(self, header): """Check if the passed header is ignored.""" reply = FakeNetworkReply(header) - cd_inline, cd_filename = utils.parse_content_disposition(reply) + cd_inline, cd_filename = httputils.parse_content_disposition(reply) self.assertEqual(cd_filename, DEFAULT_NAME) self.assertTrue(cd_inline) @@ -135,7 +135,7 @@ class AttachmentTests(AttachmentTestCase): UA should offer to download the resource. """ reply = FakeNetworkReply('attachment') - cd_inline, cd_filename = utils.parse_content_disposition(reply) + cd_inline, cd_filename = httputils.parse_content_disposition(reply) self.assertFalse(cd_inline) self.assertEqual(cd_filename, DEFAULT_NAME) @@ -894,8 +894,8 @@ class OurTests(AttachmentTestCase): def setUpModule(): - """Mock out logging in utils.""" - utils.log = Mock() + """Mock out logging in httputils.""" + httputils.logger = Mock() if __name__ == '__main__': diff --git a/qutebrowser/utils/http.py b/qutebrowser/utils/http.py new file mode 100644 index 000000000..a5683cd4e --- /dev/null +++ b/qutebrowser/utils/http.py @@ -0,0 +1,60 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + +"""Other utilities which don't fit anywhere else. """ + + +import os.path +import rfc6266 +from qutebrowser.utils.log import misc as logger + + +def parse_content_disposition(reply): + """Parse a content_disposition header. + + Args: + reply: The QNetworkReply to get a filename for. + + Return: + A (is_inline, filename) tuple. + """ + is_inline = True + filename = None + # First check if the Content-Disposition header has a filename + # attribute. + if reply.hasRawHeader('Content-Disposition'): + # We use the unsafe variant of the filename as we sanitize it via + # os.path.basename later. + try: + content_disposition = rfc6266.parse_headers( + bytes(reply.rawHeader('Content-Disposition')), relaxed=True) + filename = content_disposition.filename_unsafe + except UnicodeDecodeError as e: + logger.warning("Error while getting filename: {}: {}".format( + e.__class__.__name__, e)) + filename = None + else: + is_inline = content_disposition.is_inline + # Then try to get filename from url + if not filename: + filename = reply.url().path() + # If that fails as well, use a fallback + if not filename: + filename = 'qutebrowser-download' + return is_inline, os.path.basename(filename) diff --git a/qutebrowser/utils/misc.py b/qutebrowser/utils/misc.py index 76361beb2..887b25c7b 100644 --- a/qutebrowser/utils/misc.py +++ b/qutebrowser/utils/misc.py @@ -28,13 +28,11 @@ from urllib.parse import urljoin, urlencode from collections import OrderedDict from functools import reduce -import rfc6266 from PyQt5.QtCore import QCoreApplication, QStandardPaths, Qt from PyQt5.QtGui import QKeySequence, QColor from pkg_resources import resource_string import qutebrowser -import qutebrowser.utils.log as log from qutebrowser.utils.qt import qt_version_check, qt_ensure_valid @@ -304,41 +302,6 @@ def format_size(size, base=1024, suffix=''): return '{:.02f}{}{}'.format(size, prefixes[-1], suffix) -def parse_content_disposition(reply): - """Parse a content_disposition header. - - Args: - reply: The QNetworkReply to get a filename for. - - Return: - A (is_inline, filename) tuple. - """ - is_inline = True - filename = None - # First check if the Content-Disposition header has a filename - # attribute. - if reply.hasRawHeader('Content-Disposition'): - # We use the unsafe variant of the filename as we sanitize it via - # os.path.basename later. - try: - content_disposition = rfc6266.parse_headers( - bytes(reply.rawHeader('Content-Disposition')), relaxed=True) - filename = content_disposition.filename_unsafe - except UnicodeDecodeError as e: - log.misc.warning("Error while getting filename: {}: {}".format( - e.__class__.__name__, e)) - filename = None - else: - is_inline = content_disposition.is_inline - # Then try to get filename from url - if not filename: - filename = reply.url().path() - # If that fails as well, use a fallback - if not filename: - filename = 'qutebrowser-download' - return is_inline, os.path.basename(filename) - - def key_to_string(key): """Convert a Qt::Key member to a meaningful name.