Move special network replies to their own file.

This commit is contained in:
Florian Bruhin 2014-11-14 08:34:01 +01:00
parent c4da44e90c
commit 142e90cdd3
4 changed files with 135 additions and 110 deletions

View File

@ -31,7 +31,7 @@ else:
from qutebrowser.config import config
from qutebrowser.utils import message, log, usertypes, utils, objreg
from qutebrowser.network import qutescheme, schemehandler
from qutebrowser.network import qutescheme, networkreply
class NetworkManager(QNetworkAccessManager):
@ -148,7 +148,7 @@ class NetworkManager(QNetworkAccessManager):
"""
scheme = req.url().scheme()
if scheme == 'https' and not SSL_AVAILABLE:
return schemehandler.ErrorNetworkReply(
return networkreply.ErrorNetworkReply(
req, "SSL is not supported by the installed Qt library!",
QNetworkReply.ProtocolUnknownError)
elif scheme in self._scheme_handlers:

View File

@ -0,0 +1,128 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# Based on the Eric5 helpviewer,
# Copyright (c) 2009 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
#
# 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/>.
"""Special network replies.."""
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
from PyQt5.QtCore import pyqtSlot, QIODevice, QByteArray, QTimer
class FixedDataNetworkReply(QNetworkReply):
"""QNetworkReply subclass for fixed data."""
def __init__(self, request, fileData, mimeType, parent=None):
"""Constructor.
Args:
request: reference to the request object (QNetworkRequest)
fileData: reference to the data buffer (QByteArray)
mimeType: for the reply (string)
parent: reference to the parent object (QObject)
"""
super().__init__(parent)
self._data = fileData
self.setRequest(request)
self.setUrl(request.url())
self.setOpenMode(QIODevice.ReadOnly)
self.setHeader(QNetworkRequest.ContentTypeHeader, mimeType)
self.setHeader(QNetworkRequest.ContentLengthHeader,
QByteArray.number(len(fileData)))
self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, 'OK')
# For some reason, a segfault will be triggered if these lambdas aren't
# there.
QTimer.singleShot(0, lambda: self.metaDataChanged.emit())
QTimer.singleShot(0, lambda: self.readyRead.emit())
QTimer.singleShot(0, lambda: self.finished.emit())
@pyqtSlot()
def abort(self):
"""Abort the operation."""
pass
def bytesAvailable(self):
"""Determine the bytes available for being read.
Return:
bytes available (int)
"""
return len(self._data) + super().bytesAvailable()
def readData(self, maxlen):
"""Retrieve data from the reply object.
Args:
maxlen maximum number of bytes to read (int)
Return:
bytestring containing the data
"""
len_ = min(maxlen, len(self._data))
buf = bytes(self._data[:len_])
self._data = self._data[len_:]
return buf
def isFinished(self):
"""Check if the reply is finished."""
return True
class ErrorNetworkReply(QNetworkReply):
"""QNetworkReply which always returns an error."""
def __init__(self, req, errorstring, error, parent=None):
"""Constructor.
Args:
req: The QNetworkRequest associated with this reply.
errorstring: The error string to print.
error: The numerical error value.
parent: The parent to pass to QNetworkReply.
"""
super().__init__(parent)
self.setRequest(req)
self.setUrl(req.url())
# We don't actually want to read anything, but we still need to open
# the device to avoid getting a warning.
self.setOpenMode(QIODevice.ReadOnly)
self.setError(error, errorstring)
# For some reason, a segfault will be triggered if these lambdas aren't
# there.
QTimer.singleShot(0, lambda: self.error.emit(error))
QTimer.singleShot(0, lambda: self.finished.emit())
def abort(self):
"""Do nothing since it's a fake reply."""
pass
def bytesAvailable(self):
"""We always have 0 bytes available."""
return 0
def readData(self):
"""No data available."""
return bytes()

View File

@ -30,7 +30,7 @@ Module attributes:
from PyQt5.QtNetwork import QNetworkReply
import qutebrowser
from qutebrowser.network import schemehandler
from qutebrowser.network import schemehandler, networkreply
from qutebrowser.utils import version, utils, jinja, log, message, docutils
@ -65,16 +65,16 @@ class QuteSchemeHandler(schemehandler.SchemeHandler):
except KeyError:
errorstr = "No handler found for {}!".format(
request.url().toDisplayString())
return schemehandler.ErrorNetworkReply(
return networkreply.ErrorNetworkReply(
request, errorstr, QNetworkReply.ContentNotFoundError,
self.parent())
try:
data = handler(self._win_id, request)
except IOError as e:
return schemehandler.ErrorNetworkReply(
return networkreply.ErrorNetworkReply(
request, str(e), QNetworkReply.ContentNotFoundError,
self.parent())
return schemehandler.SpecialNetworkReply(
return networkreply.FixedDataNetworkReply(
request, data, 'text/html', self.parent())

View File

@ -22,8 +22,7 @@
"""Base class for custom scheme handlers."""
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
from PyQt5.QtCore import pyqtSlot, QObject, QIODevice, QByteArray, QTimer
from PyQt5.QtCore import QObject
class SchemeHandler(QObject):
@ -50,105 +49,3 @@ class SchemeHandler(QObject):
A QNetworkReply.
"""
raise NotImplementedError
class SpecialNetworkReply(QNetworkReply):
"""QNetworkReply subclass for special data."""
def __init__(self, request, fileData, mimeType, parent=None):
"""Constructor.
Args:
request: reference to the request object (QNetworkRequest)
fileData: reference to the data buffer (QByteArray)
mimeType: for the reply (string)
parent: reference to the parent object (QObject)
"""
super().__init__(parent)
self._data = fileData
self.setRequest(request)
self.setUrl(request.url())
self.setOpenMode(QIODevice.ReadOnly)
self.setHeader(QNetworkRequest.ContentTypeHeader, mimeType)
self.setHeader(QNetworkRequest.ContentLengthHeader,
QByteArray.number(len(fileData)))
self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, 'OK')
# For some reason, a segfault will be triggered if these lambdas aren't
# there.
QTimer.singleShot(0, lambda: self.metaDataChanged.emit())
QTimer.singleShot(0, lambda: self.readyRead.emit())
QTimer.singleShot(0, lambda: self.finished.emit())
@pyqtSlot()
def abort(self):
"""Abort the operation."""
pass
def bytesAvailable(self):
"""Determine the bytes available for being read.
Return:
bytes available (int)
"""
return len(self._data) + super().bytesAvailable()
def readData(self, maxlen):
"""Retrieve data from the reply object.
Args:
maxlen maximum number of bytes to read (int)
Return:
bytestring containing the data
"""
len_ = min(maxlen, len(self._data))
buf = bytes(self._data[:len_])
self._data = self._data[len_:]
return buf
def isFinished(self):
"""Check if the reply is finished."""
return True
class ErrorNetworkReply(QNetworkReply):
"""QNetworkReply which always returns an error."""
def __init__(self, req, errorstring, error, parent=None):
"""Constructor.
Args:
req: The QNetworkRequest associated with this reply.
errorstring: The error string to print.
error: The numerical error value.
parent: The parent to pass to QNetworkReply.
"""
super().__init__(parent)
self.setRequest(req)
self.setUrl(req.url())
# We don't actually want to read anything, but we still need to open
# the device to avoid getting a warning.
self.setOpenMode(QIODevice.ReadOnly)
self.setError(error, errorstring)
# For some reason, a segfault will be triggered if these lambdas aren't
# there.
QTimer.singleShot(0, lambda: self.error.emit(error))
QTimer.singleShot(0, lambda: self.finished.emit())
def abort(self):
"""Do nothing since it's a fake reply."""
pass
def bytesAvailable(self):
"""We always have 0 bytes available."""
return 0
def readData(self):
"""No data available."""
return bytes()