Add a new qutebrowser.utils.http module
This commit is contained in:
parent
50604de24d
commit
be2604cacd
@ -31,6 +31,7 @@ import qutebrowser.config.config as config
|
|||||||
import qutebrowser.utils.message as message
|
import qutebrowser.utils.message as message
|
||||||
import qutebrowser.commands.utils as cmdutils
|
import qutebrowser.commands.utils as cmdutils
|
||||||
import qutebrowser.utils.misc as utils
|
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 downloads as logger
|
||||||
from qutebrowser.utils.log import fix_rfc2622
|
from qutebrowser.utils.log import fix_rfc2622
|
||||||
from qutebrowser.utils.usertypes import PromptMode, Question, Timer
|
from qutebrowser.utils.usertypes import PromptMode, Question, Timer
|
||||||
@ -375,7 +376,7 @@ class DownloadManager(QObject):
|
|||||||
Args:
|
Args:
|
||||||
reply: The QNetworkReply to download.
|
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))
|
logger.debug("fetch: {} -> {}".format(reply.url(), suggested_filename))
|
||||||
download = DownloadItem(reply, self)
|
download = DownloadItem(reply, self)
|
||||||
download.finished.connect(partial(self.on_finished, download))
|
download.finished.connect(partial(self.on_finished, download))
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""Tests for qutebrowser.utils.misc.parse_content_disposition."""
|
"""Tests for qutebrowser.utils.http.parse_content_disposition."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
import qutebrowser.utils.misc as utils
|
import qutebrowser.utils.http as httputils
|
||||||
from qutebrowser.test.stubs import FakeNetworkReply
|
from qutebrowser.test.stubs import FakeNetworkReply
|
||||||
|
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ class AttachmentTestCase(unittest.TestCase):
|
|||||||
def _check_filename(self, header, filename):
|
def _check_filename(self, header, filename):
|
||||||
"""Check if the passed header has the given filename."""
|
"""Check if the passed header has the given filename."""
|
||||||
reply = FakeNetworkReply(header)
|
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.assertIsNotNone(cd_filename)
|
||||||
self.assertEqual(cd_filename, filename)
|
self.assertEqual(cd_filename, filename)
|
||||||
self.assertFalse(cd_inline)
|
self.assertFalse(cd_inline)
|
||||||
@ -48,14 +48,14 @@ class AttachmentTestCase(unittest.TestCase):
|
|||||||
def _check_ignored(self, header):
|
def _check_ignored(self, header):
|
||||||
"""Check if the passed header is ignored."""
|
"""Check if the passed header is ignored."""
|
||||||
reply = FakeNetworkReply(header)
|
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.assertEqual(cd_filename, DEFAULT_NAME)
|
||||||
self.assertTrue(cd_inline)
|
self.assertTrue(cd_inline)
|
||||||
|
|
||||||
def _check_unnamed(self, header):
|
def _check_unnamed(self, header):
|
||||||
"""Check if the passed header results in an unnamed attachment."""
|
"""Check if the passed header results in an unnamed attachment."""
|
||||||
reply = FakeNetworkReply(header)
|
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.assertEqual(cd_filename, DEFAULT_NAME)
|
||||||
self.assertFalse(cd_inline)
|
self.assertFalse(cd_inline)
|
||||||
|
|
||||||
@ -70,14 +70,14 @@ class InlineTests(unittest.TestCase):
|
|||||||
def _check_filename(self, header, filename):
|
def _check_filename(self, header, filename):
|
||||||
"""Check if the passed header has the given filename."""
|
"""Check if the passed header has the given filename."""
|
||||||
reply = FakeNetworkReply(header)
|
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.assertEqual(cd_filename, filename)
|
||||||
self.assertTrue(cd_inline)
|
self.assertTrue(cd_inline)
|
||||||
|
|
||||||
def _check_ignored(self, header):
|
def _check_ignored(self, header):
|
||||||
"""Check if the passed header is ignored."""
|
"""Check if the passed header is ignored."""
|
||||||
reply = FakeNetworkReply(header)
|
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.assertEqual(cd_filename, DEFAULT_NAME)
|
||||||
self.assertTrue(cd_inline)
|
self.assertTrue(cd_inline)
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ class AttachmentTests(AttachmentTestCase):
|
|||||||
UA should offer to download the resource.
|
UA should offer to download the resource.
|
||||||
"""
|
"""
|
||||||
reply = FakeNetworkReply('attachment')
|
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.assertFalse(cd_inline)
|
||||||
self.assertEqual(cd_filename, DEFAULT_NAME)
|
self.assertEqual(cd_filename, DEFAULT_NAME)
|
||||||
|
|
||||||
@ -894,8 +894,8 @@ class OurTests(AttachmentTestCase):
|
|||||||
|
|
||||||
|
|
||||||
def setUpModule():
|
def setUpModule():
|
||||||
"""Mock out logging in utils."""
|
"""Mock out logging in httputils."""
|
||||||
utils.log = Mock()
|
httputils.logger = Mock()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
60
qutebrowser/utils/http.py
Normal file
60
qutebrowser/utils/http.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
|
||||||
|
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""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)
|
@ -28,13 +28,11 @@ from urllib.parse import urljoin, urlencode
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
import rfc6266
|
|
||||||
from PyQt5.QtCore import QCoreApplication, QStandardPaths, Qt
|
from PyQt5.QtCore import QCoreApplication, QStandardPaths, Qt
|
||||||
from PyQt5.QtGui import QKeySequence, QColor
|
from PyQt5.QtGui import QKeySequence, QColor
|
||||||
from pkg_resources import resource_string
|
from pkg_resources import resource_string
|
||||||
|
|
||||||
import qutebrowser
|
import qutebrowser
|
||||||
import qutebrowser.utils.log as log
|
|
||||||
from qutebrowser.utils.qt import qt_version_check, qt_ensure_valid
|
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)
|
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):
|
def key_to_string(key):
|
||||||
"""Convert a Qt::Key member to a meaningful name.
|
"""Convert a Qt::Key member to a meaningful name.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user