Merge pull request #345 from sangeeths/xep_0332
XEP-0332: HTTP over XMPP transport
This commit is contained in:
commit
192b7e0349
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@ sleekxmpp.egg-info/
|
|||||||
*~
|
*~
|
||||||
.baboon/
|
.baboon/
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
.idea/
|
||||||
|
@ -161,7 +161,7 @@ item itself, and the JID and node that will own the item.
|
|||||||
In this case, the owning JID and node are provided with the
|
In this case, the owning JID and node are provided with the
|
||||||
parameters ``ijid`` and ``node``.
|
parameters ``ijid`` and ``node``.
|
||||||
|
|
||||||
Peforming Disco Queries
|
Performing Disco Queries
|
||||||
-----------------------
|
-----------------------
|
||||||
The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs
|
The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs
|
||||||
and their nodes for disco information. Since these methods are wrappers for
|
and their nodes for disco information. Since these methods are wrappers for
|
||||||
|
101
examples/http_over_xmpp.py
Normal file
101
examples/http_over_xmpp.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Implementation of HTTP over XMPP transport
|
||||||
|
http://xmpp.org/extensions/xep-0332.html
|
||||||
|
Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp import ClientXMPP
|
||||||
|
|
||||||
|
from optparse import OptionParser
|
||||||
|
import logging
|
||||||
|
import getpass
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPOverXMPPClient(ClientXMPP):
|
||||||
|
def __init__(self, jid, password):
|
||||||
|
ClientXMPP.__init__(self, jid, password)
|
||||||
|
self.register_plugin('xep_0332') # HTTP over XMPP Transport
|
||||||
|
self.add_event_handler(
|
||||||
|
'session_start', self.session_start, threaded=True
|
||||||
|
)
|
||||||
|
self.add_event_handler('http_request', self.http_request_received)
|
||||||
|
self.add_event_handler('http_response', self.http_response_received)
|
||||||
|
|
||||||
|
def http_request_received(self, iq):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def http_response_received(self, iq):
|
||||||
|
print 'HTTP Response Received : ', iq
|
||||||
|
print 'From : ', iq['from']
|
||||||
|
print 'To : ', iq['to']
|
||||||
|
print 'Type : ', iq['type']
|
||||||
|
print 'Headers : ', iq['resp']['headers']
|
||||||
|
print 'Code : ', iq['resp']['code']
|
||||||
|
print 'Message : ', iq['resp']['message']
|
||||||
|
print 'Data : ', iq['resp']['data']
|
||||||
|
|
||||||
|
def session_start(self, event):
|
||||||
|
# TODO: Fill in the blanks
|
||||||
|
self['xep_0332'].send_request(
|
||||||
|
to='?', method='?', resource='?', headers={}
|
||||||
|
)
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
#
|
||||||
|
# NOTE: To run this example, fill up the blanks in session_start() and
|
||||||
|
# use the following command.
|
||||||
|
#
|
||||||
|
# ./http_over_xmpp.py -J <jid> -P <pwd> -i <ip> -p <port> [-v]
|
||||||
|
#
|
||||||
|
|
||||||
|
parser = OptionParser()
|
||||||
|
|
||||||
|
# Output verbosity options.
|
||||||
|
parser.add_option(
|
||||||
|
'-v', '--verbose', help='set logging to DEBUG', action='store_const',
|
||||||
|
dest='loglevel', const=logging.DEBUG, default=logging.ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
# JID and password options.
|
||||||
|
parser.add_option('-J', '--jid', dest='jid', help='JID')
|
||||||
|
parser.add_option('-P', '--password', dest='password', help='Password')
|
||||||
|
|
||||||
|
# XMPP server ip and port options.
|
||||||
|
parser.add_option(
|
||||||
|
'-i', '--ipaddr', dest='ipaddr',
|
||||||
|
help='IP Address of the XMPP server', default=None
|
||||||
|
)
|
||||||
|
parser.add_option(
|
||||||
|
'-p', '--port', dest='port',
|
||||||
|
help='Port of the XMPP server', default=None
|
||||||
|
)
|
||||||
|
|
||||||
|
opts, args = parser.parse_args()
|
||||||
|
|
||||||
|
# Setup logging.
|
||||||
|
logging.basicConfig(level=opts.loglevel,
|
||||||
|
format='%(levelname)-8s %(message)s')
|
||||||
|
|
||||||
|
if opts.jid is None:
|
||||||
|
opts.jid = raw_input('Username: ')
|
||||||
|
if opts.password is None:
|
||||||
|
opts.password = getpass.getpass('Password: ')
|
||||||
|
|
||||||
|
xmpp = HTTPOverXMPPClient(opts.jid, opts.password)
|
||||||
|
if xmpp.connect((opts.ipaddr, int(opts.port))):
|
||||||
|
print 'Connected!'
|
||||||
|
xmpp.process(block=True)
|
||||||
|
else:
|
||||||
|
print 'Not connected!'
|
||||||
|
print 'Goodbye....'
|
||||||
|
|
2
setup.py
2
setup.py
@ -123,6 +123,8 @@ packages = [ 'sleekxmpp',
|
|||||||
'sleekxmpp/plugins/xep_0323/stanza',
|
'sleekxmpp/plugins/xep_0323/stanza',
|
||||||
'sleekxmpp/plugins/xep_0325',
|
'sleekxmpp/plugins/xep_0325',
|
||||||
'sleekxmpp/plugins/xep_0325/stanza',
|
'sleekxmpp/plugins/xep_0325/stanza',
|
||||||
|
'sleekxmpp/plugins/xep_0332',
|
||||||
|
'sleekxmpp/plugins/xep_0332/stanza',
|
||||||
'sleekxmpp/plugins/google',
|
'sleekxmpp/plugins/google',
|
||||||
'sleekxmpp/plugins/google/gmail',
|
'sleekxmpp/plugins/google/gmail',
|
||||||
'sleekxmpp/plugins/google/auth',
|
'sleekxmpp/plugins/google/auth',
|
||||||
|
@ -83,4 +83,5 @@ __all__ = [
|
|||||||
'xep_0319', # Last User Interaction in Presence
|
'xep_0319', # Last User Interaction in Presence
|
||||||
'xep_0323', # IoT Systems Sensor Data
|
'xep_0323', # IoT Systems Sensor Data
|
||||||
'xep_0325', # IoT Systems Control
|
'xep_0325', # IoT Systems Control
|
||||||
|
'xep_0332', # HTTP Over XMPP Transport
|
||||||
]
|
]
|
||||||
|
17
sleekxmpp/plugins/xep_0332/__init__.py
Normal file
17
sleekxmpp/plugins/xep_0332/__init__.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Implementation of HTTP over XMPP transport
|
||||||
|
http://xmpp.org/extensions/xep-0332.html
|
||||||
|
Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.xep_0332 import stanza
|
||||||
|
from sleekxmpp.plugins.xep_0332.http import XEP_0332
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0332)
|
143
sleekxmpp/plugins/xep_0332/http.py
Normal file
143
sleekxmpp/plugins/xep_0332/http.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Implementation of HTTP over XMPP transport
|
||||||
|
http://xmpp.org/extensions/xep-0332.html
|
||||||
|
Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp import Iq
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||||
|
from sleekxmpp.xmlstream.handler import Callback
|
||||||
|
from sleekxmpp.xmlstream.matcher import StanzaPath
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.base import BasePlugin
|
||||||
|
from sleekxmpp.plugins.xep_0332.stanza import Request, Response, Data
|
||||||
|
from sleekxmpp.plugins.xep_0131.stanza import Headers
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0332(BasePlugin):
|
||||||
|
"""
|
||||||
|
XEP-0332: HTTP over XMPP transport
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = 'xep_0332'
|
||||||
|
description = 'XEP-0332: HTTP over XMPP transport'
|
||||||
|
|
||||||
|
#: xep_0047 not included.
|
||||||
|
#: xep_0001, 0137 and 0166 are missing
|
||||||
|
dependencies = set(['xep_0030', 'xep_0131'])
|
||||||
|
|
||||||
|
#: TODO: Do we really need to mention the supported_headers?!
|
||||||
|
default_config = {
|
||||||
|
'supported_headers': set([
|
||||||
|
'Content-Length', 'Transfer-Encoding', 'DateTime',
|
||||||
|
'Accept-Charset', 'Location', 'Content-ID', 'Description',
|
||||||
|
'Content-Language', 'Content-Transfer-Encoding', 'Timestamp',
|
||||||
|
'Expires', 'User-Agent', 'Host', 'Proxy-Authorization', 'Date',
|
||||||
|
'WWW-Authenticate', 'Accept-Encoding', 'Server', 'Error-Info',
|
||||||
|
'Identifier', 'Content-Location', 'Content-Encoding', 'Distribute',
|
||||||
|
'Accept', 'Proxy-Authenticate', 'ETag', 'Expect', 'Content-Type'
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
self.xmpp.register_handler(Callback(
|
||||||
|
'HTTP Request', StanzaPath('iq/req'), self._handle_request
|
||||||
|
))
|
||||||
|
self.xmpp.register_handler(Callback(
|
||||||
|
'HTTP Response', StanzaPath('iq/resp'), self._handle_response
|
||||||
|
))
|
||||||
|
register_stanza_plugin(Iq, Request, iterable=True)
|
||||||
|
register_stanza_plugin(Iq, Response, iterable=True)
|
||||||
|
register_stanza_plugin(Request, Headers, iterable=True)
|
||||||
|
register_stanza_plugin(Request, Data, iterable=True)
|
||||||
|
register_stanza_plugin(Response, Headers, iterable=True)
|
||||||
|
register_stanza_plugin(Response, Data, iterable=True)
|
||||||
|
# TODO: Should we register any api's here? self.api.register()
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('HTTP Request')
|
||||||
|
self.xmpp.remove_handler('HTTP Response')
|
||||||
|
self.xmpp['xep_0030'].del_feature('urn:xmpp:http')
|
||||||
|
for header in self.supported_headers:
|
||||||
|
self.xmpp['xep_0030'].del_feature(
|
||||||
|
feature='%s#%s' % (Headers.namespace, header)
|
||||||
|
)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
self.xmpp['xep_0030'].add_feature('urn:xmpp:http')
|
||||||
|
for header in self.supported_headers:
|
||||||
|
self.xmpp['xep_0030'].add_feature(
|
||||||
|
'%s#%s' % (Headers.namespace, header)
|
||||||
|
)
|
||||||
|
# TODO: Do we need to add the supported headers to xep_0131?
|
||||||
|
# self.xmpp['xep_0131'].supported_headers.add(header)
|
||||||
|
|
||||||
|
def _handle_request(self, iq):
|
||||||
|
self.xmpp.event('http_request', iq)
|
||||||
|
|
||||||
|
def _handle_response(self, iq):
|
||||||
|
self.xmpp.event('http_response', iq)
|
||||||
|
|
||||||
|
def send_request(self, to=None, method=None, resource=None, headers=None,
|
||||||
|
data=None, **kwargs):
|
||||||
|
iq = self.xmpp.Iq()
|
||||||
|
iq['from'] = self.xmpp.boundjid
|
||||||
|
iq['to'] = to
|
||||||
|
iq['type'] = 'set'
|
||||||
|
iq['req']['headers'] = headers
|
||||||
|
iq['req']['method'] = method
|
||||||
|
iq['req']['resource'] = resource
|
||||||
|
iq['req']['version'] = '1.1' # TODO: set this implicitly
|
||||||
|
if data is not None:
|
||||||
|
iq['req']['data'] = data
|
||||||
|
return iq.send(
|
||||||
|
timeout=kwargs.get('timeout', None),
|
||||||
|
block=kwargs.get('block', True),
|
||||||
|
callback=kwargs.get('callback', None),
|
||||||
|
timeout_callback=kwargs.get('timeout_callback', None)
|
||||||
|
)
|
||||||
|
|
||||||
|
def send_response(self, to=None, code=None, message=None, headers=None,
|
||||||
|
data=None, **kwargs):
|
||||||
|
iq = self.xmpp.Iq()
|
||||||
|
iq['from'] = self.xmpp.boundjid
|
||||||
|
iq['to'] = to
|
||||||
|
iq['type'] = 'result'
|
||||||
|
iq['resp']['headers'] = headers
|
||||||
|
iq['resp']['code'] = code
|
||||||
|
iq['resp']['message'] = message
|
||||||
|
iq['resp']['version'] = '1.1' # TODO: set this implicitly
|
||||||
|
if data is not None:
|
||||||
|
iq['resp']['data'] = data
|
||||||
|
return iq.send(
|
||||||
|
timeout=kwargs.get('timeout', None),
|
||||||
|
block=kwargs.get('block', True),
|
||||||
|
callback=kwargs.get('callback', None),
|
||||||
|
timeout_callback=kwargs.get('timeout_callback', None)
|
||||||
|
)
|
||||||
|
|
||||||
|
def send_error(self, to=None, ecode=500, etype='wait',
|
||||||
|
econd='internal-server-error', **kwargs):
|
||||||
|
iq = self.xmpp.Iq()
|
||||||
|
iq['type'] = 'error'
|
||||||
|
iq['from'] = self.xmpp.boundjid
|
||||||
|
iq['to'] = to
|
||||||
|
iq['error']['code'] = ecode
|
||||||
|
iq['error']['type'] = etype
|
||||||
|
iq['error']['condition'] = econd
|
||||||
|
return iq.send(
|
||||||
|
timeout=kwargs.get('timeout', None),
|
||||||
|
block=kwargs.get('block', True),
|
||||||
|
callback=kwargs.get('callback', None),
|
||||||
|
timeout_callback=kwargs.get('timeout_callback', None)
|
||||||
|
)
|
13
sleekxmpp/plugins/xep_0332/stanza/__init__.py
Normal file
13
sleekxmpp/plugins/xep_0332/stanza/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Implementation of HTTP over XMPP transport
|
||||||
|
http://xmpp.org/extensions/xep-0332.html
|
||||||
|
Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.xep_0332.stanza.request import Request
|
||||||
|
from sleekxmpp.plugins.xep_0332.stanza.response import Response
|
||||||
|
from sleekxmpp.plugins.xep_0332.stanza.data import Data
|
30
sleekxmpp/plugins/xep_0332/stanza/data.py
Normal file
30
sleekxmpp/plugins/xep_0332/stanza/data.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Implementation of HTTP over XMPP transport
|
||||||
|
http://xmpp.org/extensions/xep-0332.html
|
||||||
|
Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import ElementBase
|
||||||
|
|
||||||
|
|
||||||
|
class Data(ElementBase):
|
||||||
|
"""
|
||||||
|
The data element.
|
||||||
|
"""
|
||||||
|
name = 'data'
|
||||||
|
namespace = 'urn:xmpp:http'
|
||||||
|
interfaces = set(['data'])
|
||||||
|
plugin_attrib = 'data'
|
||||||
|
is_extension = True
|
||||||
|
|
||||||
|
def get_data(self, encoding='text'):
|
||||||
|
data = self._get_sub_text(encoding, None)
|
||||||
|
return str(data) if data is not None else data
|
||||||
|
|
||||||
|
def set_data(self, data, encoding='text'):
|
||||||
|
self._set_sub_text(encoding, text=data)
|
||||||
|
|
65
sleekxmpp/plugins/xep_0332/stanza/request.py
Normal file
65
sleekxmpp/plugins/xep_0332/stanza/request.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Implementation of HTTP over XMPP transport
|
||||||
|
http://xmpp.org/extensions/xep-0332.html
|
||||||
|
Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import ElementBase
|
||||||
|
|
||||||
|
|
||||||
|
class Request(ElementBase):
|
||||||
|
|
||||||
|
"""
|
||||||
|
All HTTP communication is done using the `Request`/`Response` paradigm.
|
||||||
|
Each HTTP Request is made sending an `iq` stanza containing a `req` element
|
||||||
|
to the server. Each `iq` stanza sent is of type `set`.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
<iq type='set' from='a@b.com/browser' to='x@y.com' id='1'>
|
||||||
|
<req xmlns='urn:xmpp:http' method='GET' resource='/api/users' version='1.1'>
|
||||||
|
<headers xmlns='http://jabber.org/protocol/shim'>
|
||||||
|
<header name='Host'>b.com</header>
|
||||||
|
</headers>
|
||||||
|
</req>
|
||||||
|
</iq>
|
||||||
|
|
||||||
|
<iq type='set' from='a@b.com/browser' to='x@y.com' id='2'>
|
||||||
|
<req xmlns='urn:xmpp:http' method='PUT' resource='/api/users' version='1.1'>
|
||||||
|
<headers xmlns='http://jabber.org/protocol/shim'>
|
||||||
|
<header name='Host'>b.com</header>
|
||||||
|
<header name='Content-Type'>text/html</header>
|
||||||
|
<header name='Content-Length'>...</header>
|
||||||
|
</headers>
|
||||||
|
<data>
|
||||||
|
<text>...</text>
|
||||||
|
</data>
|
||||||
|
</req>
|
||||||
|
</iq>
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = 'request'
|
||||||
|
namespace = 'urn:xmpp:http'
|
||||||
|
interfaces = set(['method', 'resource', 'version'])
|
||||||
|
plugin_attrib = 'req'
|
||||||
|
|
||||||
|
def get_method(self):
|
||||||
|
return self._get_attr('method', None)
|
||||||
|
|
||||||
|
def set_method(self, method):
|
||||||
|
self._set_attr('method', method)
|
||||||
|
|
||||||
|
def get_resource(self):
|
||||||
|
return self._get_attr('resource', None)
|
||||||
|
|
||||||
|
def set_resource(self, resource):
|
||||||
|
self._set_attr('resource', resource)
|
||||||
|
|
||||||
|
def get_version(self):
|
||||||
|
return self._get_attr('version', None)
|
||||||
|
|
||||||
|
def set_version(self, version='1.1'):
|
||||||
|
self._set_attr('version', version)
|
61
sleekxmpp/plugins/xep_0332/stanza/response.py
Normal file
61
sleekxmpp/plugins/xep_0332/stanza/response.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Implementation of HTTP over XMPP transport
|
||||||
|
http://xmpp.org/extensions/xep-0332.html
|
||||||
|
Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import ElementBase
|
||||||
|
|
||||||
|
|
||||||
|
class Response(ElementBase):
|
||||||
|
|
||||||
|
"""
|
||||||
|
When the HTTP Server responds, it does so by sending an `iq` stanza
|
||||||
|
response (type=`result`) back to the client containing the `resp` element.
|
||||||
|
Since response are asynchronous, and since multiple requests may be active
|
||||||
|
at the same time, responses may be returned in a different order than the
|
||||||
|
in which the original requests were made.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
<iq type='result' from='httpserver@clayster.com' to='httpclient@clayster.com/browser' id='2'>
|
||||||
|
<resp xmlns='urn:xmpp:http' version='1.1' statusCode='200' statusMessage='OK'>
|
||||||
|
<headers xmlns='http://jabber.org/protocol/shim'>
|
||||||
|
<header name='Date'>Fri, 03 May 2013 16:39:54GMT-4</header>
|
||||||
|
<header name='Server'>Clayster</header>
|
||||||
|
<header name='Content-Type'>text/turtle</header>
|
||||||
|
<header name='Content-Length'>...</header>
|
||||||
|
<header name='Connection'>Close</header>
|
||||||
|
</headers>
|
||||||
|
<data>
|
||||||
|
<text>
|
||||||
|
...
|
||||||
|
</text>
|
||||||
|
</data>
|
||||||
|
</resp>
|
||||||
|
</iq>
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = 'response'
|
||||||
|
namespace = 'urn:xmpp:http'
|
||||||
|
interfaces = set(['code', 'message', 'version'])
|
||||||
|
plugin_attrib = 'resp'
|
||||||
|
|
||||||
|
def get_code(self):
|
||||||
|
code = self._get_attr('statusCode', None)
|
||||||
|
return int(code) if code is not None else code
|
||||||
|
|
||||||
|
def set_code(self, code):
|
||||||
|
self._set_attr('statusCode', str(code))
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
return self._get_attr('statusMessage', '')
|
||||||
|
|
||||||
|
def set_message(self, message):
|
||||||
|
self._set_attr('statusMessage', message)
|
||||||
|
|
||||||
|
def set_version(self, version='1.1'):
|
||||||
|
self._set_attr('version', version)
|
Loading…
x
Reference in New Issue
Block a user