XEP-0363: Types, docs, and new-style kwargs
This commit is contained in:
parent
a7d690813c
commit
ea2d851a93
3 changed files with 90 additions and 32 deletions
|
@ -8,6 +8,12 @@ XEP-0363: HTTP File Upload
|
|||
:members:
|
||||
:exclude-members: session_bind, plugin_init, plugin_end
|
||||
|
||||
.. autoclass:: UploadServiceNotFound
|
||||
|
||||
.. autoclass:: FileTooBig
|
||||
|
||||
.. autoclass:: HTTPError
|
||||
|
||||
|
||||
Stanza elements
|
||||
---------------
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
# slixmpp: The Slick XMPP Library
|
||||
# Copyright (C) 2018 Emmanuel Gil Peyrot
|
||||
# This file is part of slixmpp.
|
||||
|
@ -6,6 +5,12 @@
|
|||
from slixmpp.plugins.base import register_plugin
|
||||
|
||||
from slixmpp.plugins.xep_0363.stanza import Request, Slot, Put, Get, Header
|
||||
from slixmpp.plugins.xep_0363.http_upload import XEP_0363
|
||||
from slixmpp.plugins.xep_0363.http_upload import (
|
||||
XEP_0363,
|
||||
UploadServiceNotFound,
|
||||
FileTooBig,
|
||||
HTTPError,
|
||||
FileUploadError,
|
||||
)
|
||||
|
||||
register_plugin(XEP_0363)
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
"""
|
||||
slixmpp: The Slick XMPP Library
|
||||
Copyright (C) 2018 Emmanuel Gil Peyrot
|
||||
This file is part of slixmpp.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
# slixmpp: The Slick XMPP Library
|
||||
# Copyright (C) 2018 Emmanuel Gil Peyrot
|
||||
# This file is part of slixmpp.
|
||||
# See the file LICENSE for copying permission.
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from asyncio import Future
|
||||
from mimetypes import guess_type
|
||||
from typing import (
|
||||
Optional,
|
||||
IO,
|
||||
)
|
||||
|
||||
from slixmpp import Iq, __version__
|
||||
from slixmpp import JID, __version__
|
||||
from slixmpp.stanza import Iq
|
||||
from slixmpp.plugins import BasePlugin
|
||||
from slixmpp.xmlstream import register_stanza_plugin
|
||||
from slixmpp.xmlstream.handler import Callback
|
||||
|
@ -25,19 +28,39 @@ class FileUploadError(Exception):
|
|||
pass
|
||||
|
||||
class UploadServiceNotFound(FileUploadError):
|
||||
pass
|
||||
"""
|
||||
Raised if no upload service can be found.
|
||||
"""
|
||||
|
||||
class FileTooBig(FileUploadError):
|
||||
"""
|
||||
Raised if the file size is above advertised server limits.
|
||||
|
||||
args:
|
||||
|
||||
- size of the file
|
||||
- max file size allowed
|
||||
"""
|
||||
def __str__(self):
|
||||
return 'File size too large: {} (max: {} bytes)' \
|
||||
.format(self.args[0], self.args[1])
|
||||
|
||||
class HTTPError(FileUploadError):
|
||||
"""
|
||||
Raised when we receive an HTTP error response during upload.
|
||||
|
||||
args:
|
||||
|
||||
- HTTP Error code
|
||||
- Content of the HTTP response
|
||||
"""
|
||||
def __str__(self):
|
||||
return 'Could not upload file: %d (%s)' % (self.args[0], self.args[1])
|
||||
|
||||
class XEP_0363(BasePlugin):
|
||||
''' This plugin only supports Python 3.5+ '''
|
||||
"""
|
||||
XEP-0363: HTTP File Upload
|
||||
"""
|
||||
|
||||
name = 'xep_0363'
|
||||
description = 'XEP-0363: HTTP File Upload'
|
||||
|
@ -62,9 +85,7 @@ class XEP_0363(BasePlugin):
|
|||
self._handle_request))
|
||||
|
||||
def plugin_end(self):
|
||||
self._http_session.close()
|
||||
self.xmpp.remove_handler('HTTP Upload Request')
|
||||
self.xmpp.remove_handler('HTTP Upload Slot')
|
||||
self.xmpp['xep_0030'].del_feature(feature=Request.namespace)
|
||||
|
||||
def session_bind(self, jid):
|
||||
|
@ -73,9 +94,14 @@ class XEP_0363(BasePlugin):
|
|||
def _handle_request(self, iq):
|
||||
self.xmpp.event('http_upload_request', iq)
|
||||
|
||||
async def find_upload_service(self, domain=None, timeout=None):
|
||||
async def find_upload_service(self, domain: Optional[JID] = None, **iqkwargs) -> Optional[Iq]:
|
||||
"""Find an upload service on a domain (our own by default).
|
||||
|
||||
:param domain: Domain to disco to find a service.
|
||||
"""
|
||||
results = await self.xmpp['xep_0030'].get_info_from_domain(
|
||||
domain=domain, timeout=timeout)
|
||||
domain=domain, **iqkwargs
|
||||
)
|
||||
|
||||
candidates = []
|
||||
for info in results:
|
||||
|
@ -87,26 +113,49 @@ class XEP_0363(BasePlugin):
|
|||
if feature == Request.namespace:
|
||||
return info
|
||||
|
||||
def request_slot(self, jid, filename, size, content_type=None, ifrom=None,
|
||||
timeout=None, callback=None, timeout_callback=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['to'] = jid
|
||||
iq['from'] = ifrom
|
||||
iq['type'] = 'get'
|
||||
def request_slot(self, jid: JID, filename: str, size: int,
|
||||
content_type: Optional[str] = None, *,
|
||||
ifrom: Optional[JID] = None, **iqkwargs) -> Future:
|
||||
"""Request an HTTP upload slot from a service.
|
||||
|
||||
:param jid: Service to request the slot from.
|
||||
:param filename: Name of the file that will be uploaded.
|
||||
:param size: size of the file in bytes.
|
||||
:param content_type: Type of the file that will be uploaded.
|
||||
"""
|
||||
iq = self.xmpp.make_iq_get(ito=jid, ifrom=ifrom)
|
||||
request = iq['http_upload_request']
|
||||
request['filename'] = filename
|
||||
request['size'] = str(size)
|
||||
request['content-type'] = content_type or self.default_content_type
|
||||
return iq.send(timeout=timeout, callback=callback,
|
||||
timeout_callback=timeout_callback)
|
||||
return iq.send(**iqkwargs)
|
||||
|
||||
async def upload_file(self, filename, size=None, content_type=None, *,
|
||||
input_file=None, ifrom=None, domain=None, timeout=None,
|
||||
callback=None, timeout_callback=None):
|
||||
''' Helper function which does all of the uploading process. '''
|
||||
async def upload_file(self, filename: str, size: Optional[int] = None,
|
||||
content_type: Optional[str] = None, *,
|
||||
input_file: Optional[IO[bytes]]=None,
|
||||
domain: Optional[JID] = None,
|
||||
**iqkwargs) -> str:
|
||||
'''Helper function which does all of the uploading discovery and
|
||||
process.
|
||||
|
||||
:param filename: Path to the file to upload (or only the name if
|
||||
``input_file`` is provided.
|
||||
:param size: size of the file in bytes.
|
||||
:param content_type: Type of the file that will be uploaded.
|
||||
:param input_file: Binary file stream on the file.
|
||||
:param domain: Domain to query to find an HTTP upload service.
|
||||
:raises .UploadServiceNotFound: If slixmpp is unable to find an
|
||||
an available upload service.
|
||||
:raises .FileTooBig: If the filesize is above what is accepted by
|
||||
the service.
|
||||
:raises .HTTPError: If there is an error in the HTTP operation.
|
||||
:returns: The URL of the uploaded file.
|
||||
'''
|
||||
timeout = iqkwargs.get('timeout', None)
|
||||
if self.upload_service is None:
|
||||
info_iq = await self.find_upload_service(
|
||||
domain=domain, timeout=timeout)
|
||||
domain=domain, **iqkwargs
|
||||
)
|
||||
if info_iq is None:
|
||||
raise UploadServiceNotFound()
|
||||
self.upload_service = info_iq['from']
|
||||
|
@ -137,9 +186,7 @@ class XEP_0363(BasePlugin):
|
|||
|
||||
basename = os.path.basename(filename)
|
||||
slot_iq = await self.request_slot(self.upload_service, basename, size,
|
||||
content_type, ifrom, timeout,
|
||||
callback=callback,
|
||||
timeout_callback=timeout_callback)
|
||||
content_type, **iqkwargs)
|
||||
slot = slot_iq['http_upload_slot']
|
||||
|
||||
headers = {
|
||||
|
|
Loading…
Reference in a new issue