Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[![Build Status](https://travis-ci.org/Tanganelli/CoAPthon.svg?branch=master)](https://travis-ci.org/Tanganelli/CoAPthon)
[![Coverage Status](https://coveralls.io/repos/Tanganelli/CoAPthon/badge.svg?branch=master&service=github)](https://coveralls.io/github/Tanganelli/CoAPthon?branch=master)
[![Documentation Status](https://readthedocs.org/projects/coapthon/badge/?version=latest)](http://coapthon.readthedocs.org/en/latest/?badge=latest)
[![BuyMeACoffe](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/jeo)

CoAPthon
========
Expand Down Expand Up @@ -408,4 +407,3 @@ $ make html
```

The documentation will be build in CoAPthon/docs/build/html. Let's start from index.html to have an overview of the library.

35 changes: 34 additions & 1 deletion coapclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ def main(): # pragma: no cover
sys.exit(2)

host, port, path = parse_uri(path)

""" Idee sul controllo finale dell'url

if path and not path.endswith("coap://"):
print "errore nel passaggio"
usage()
sys.exit(2)
"""

try:
tmp = socket.gethostbyname(host)
host = tmp
Expand All @@ -124,7 +133,7 @@ def main(): # pragma: no cover
usage()
sys.exit(2)
client.observe(path, client_callback_observe)

elif op == "DELETE":
if path is None:
print "Path cannot be empty for a DELETE request"
Expand Down Expand Up @@ -161,6 +170,30 @@ def main(): # pragma: no cover
response = client.discover()
print response.pretty_print()
client.stop()
"""test con implementazione in request layer"""
elif op == "FETCH":
print "FETCH CALLBACK OK"
if path is None:
print "Path cannot be empty for FETCH"
usage()
sys.exit(2)
##AGGIUNTA DEL PAYLOAD DA DOVE ARRIVA LA RICHIESTA
if payload is None:
print "Payload cannot be empty for a FETCH request"
usage()
sys.exit(2)
response = client.fetch(path, payload, proxy_uri=proxy_uri)
print response.pretty_print()
client.stop()
elif op == "PATCH":
print "PATCH CALLBACK OK"
if path is None:
print "Path cannot be empty for FETCH"
usage()
sys.exit(2)
response = client.patch(path, payload, proxy_uri=proxy_uri)
print response.pretty_print()
client.stop()
else:
print "Operation not recognized"
usage()
Expand Down
137 changes: 69 additions & 68 deletions coapserver.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,69 @@
#!/usr/bin/env python

import getopt
import sys
from coapthon.server.coap import CoAP
from exampleresources import BasicResource, Long, Separate, Storage, Big, voidResource, XMLResource, ETAGResource, \
Child, \
MultipleEncodingResource, AdvancedResource, AdvancedResourceSeparate, DynamicResource

__author__ = 'Giacomo Tanganelli'


class CoAPServer(CoAP):
def __init__(self, host, port, multicast=False):
CoAP.__init__(self, (host, port), multicast)
self.add_resource('basic/', BasicResource())
self.add_resource('storage/', Storage())
self.add_resource('separate/', Separate())
self.add_resource('long/', Long())
self.add_resource('big/', Big())
self.add_resource('void/', voidResource())
self.add_resource('xml/', XMLResource())
self.add_resource('encoding/', MultipleEncodingResource())
self.add_resource('etag/', ETAGResource())
self.add_resource('child/', Child())
self.add_resource('advanced/', AdvancedResource())
self.add_resource('advancedSeparate/', AdvancedResourceSeparate())
self.add_resource('dynamic/', DynamicResource())

print "CoAP Server start on " + host + ":" + str(port)
print self.root.dump()


def usage(): # pragma: no cover
print "coapserver.py -i <ip address> -p <port>"


def main(argv): # pragma: no cover
ip = "0.0.0.0"
port = 5683
multicast = False
try:
opts, args = getopt.getopt(argv, "hi:p:m", ["ip=", "port=", "multicast"])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
usage()
sys.exit()
elif opt in ("-i", "--ip"):
ip = arg
elif opt in ("-p", "--port"):
port = int(arg)
elif opt in ("-m", "--multicast"):
multicast = True

server = CoAPServer(ip, port, multicast)
try:
server.listen(10)
except KeyboardInterrupt:
print "Server Shutdown"
server.close()
print "Exiting..."


if __name__ == "__main__": # pragma: no cover
main(sys.argv[1:])
#!/usr/bin/env python

import getopt
import sys
from coapthon.server.coap import CoAP
from exampleresources import BasicResource, Long, Separate, Storage, Big, voidResource, XMLResource, ETAGResource, \
Child, \
MultipleEncodingResource, AdvancedResource, AdvancedResourceSeparate, FetchResource

__author__ = 'Giacomo Tanganelli'


class CoAPServer(CoAP):
def __init__(self, host, port, multicast=False):
CoAP.__init__(self, (host, port), multicast)
self.add_resource('basic/', BasicResource())
self.add_resource('storage/', Storage())
self.add_resource('separate/', Separate())
self.add_resource('long/', Long())
self.add_resource('big/', Big())
self.add_resource('void/', voidResource())
self.add_resource('xml/', XMLResource())
self.add_resource('encoding/', MultipleEncodingResource())
self.add_resource('etag/', ETAGResource())
self.add_resource('child/', Child())
self.add_resource('advanced/', AdvancedResource())
self.add_resource('advancedSeparate/', AdvancedResourceSeparate())
##Resource to test FETCH
self.add_resource('fetchTest/', FetchResource())

print "CoAP Server start on " + host + ":" + str(port)
print self.root.dump()


def usage(): # pragma: no cover
print "coapserver.py -i <ip address> -p <port>"


def main(argv): # pragma: no cover
ip = "0.0.0.0"
port = 5683
multicast = False
try:
opts, args = getopt.getopt(argv, "hi:p:m", ["ip=", "port=", "multicast"])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
usage()
sys.exit()
elif opt in ("-i", "--ip"):
ip = arg
elif opt in ("-p", "--port"):
port = int(arg)
elif opt in ("-m", "--multicast"):
multicast = True

server = CoAPServer(ip, port, multicast)
try:
server.listen(10)
except KeyboardInterrupt:
print "Server Shutdown"
server.close()
print "Exiting..."


if __name__ == "__main__": # pragma: no cover
main(sys.argv[1:])
48 changes: 48 additions & 0 deletions coapthon/client/helperclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,54 @@ def get(self, path, proxy_uri=None, callback=None, timeout=None, **kwargs): # p

return self.send_request(request, callback, timeout)

def fetch(self, path, payload, proxy_uri=None, callback=None, timeout=None, **kwargs): # pragma: no cover
"""
Perform a FETCH on a certain path.

:param path: the path
:param payload: the request payload
:param proxy_uri: Proxy-Uri option of a request
:param callback: the callback function to invoke upon response
:param timeout: the timeout of the request
:return: the response
"""
request = self.mk_request(defines.Codes.FETCH, path)
request.token = generate_random_token(2)
if proxy_uri:
request.proxy_uri = proxy_uri
request.payload = payload
request.content_type = defines.Content_types["application/map-keys+json"]

for k, v in kwargs.iteritems():
if hasattr(request, k):
setattr(request, k, v)

return self.send_request(request, callback, timeout)

def patch(self, path, payload, proxy_uri=None, callback=None, timeout=None, **kwargs): # pragma: no cover
"""
Perform a PATCH on a certain path.

:param path: the path
:param payload: the request payload
:param proxy_uri: Proxy-Uri option of a request
:param callback: the callback function to invoke upon response
:param timeout: the timeout of the request
:return: the response
"""
request = self.mk_request(defines.Codes.PATCH, path)
request.token = generate_random_token(2)
request.payload = payload
request.content_type = defines.Content_types["application/json-patch+json"]
if proxy_uri:
request.proxy_uri = proxy_uri

for k, v in kwargs.iteritems():
if hasattr(request, k):
setattr(request, k, v)

return self.send_request(request, callback, timeout)

def observe(self, path, callback, timeout=None, **kwargs): # pragma: no cover
"""
Perform a GET with observe on a certain path.
Expand Down
19 changes: 16 additions & 3 deletions coapthon/defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,16 @@ class Codes(object):
"""
CoAP codes. Every code is represented as (NUMBER, NAME)
"""

ERROR_LOWER_BOUND = 128

EMPTY = CodeItem(0, 'EMPTY')
GET = CodeItem(1, 'GET')
POST = CodeItem(2, 'POST')
PUT = CodeItem(3, 'PUT')
DELETE = CodeItem(4, 'DELETE')
FETCH = CodeItem(5, 'FETCH')
PATCH = CodeItem(6, 'PATCH')

CREATED = CodeItem(65, 'CREATED')
DELETED = CodeItem(66, 'DELETED')
Expand All @@ -253,13 +256,17 @@ class Codes(object):
SERVICE_UNAVAILABLE = CodeItem(163, 'SERVICE_UNAVAILABLE')
GATEWAY_TIMEOUT = CodeItem(164, 'GATEWAY_TIMEOUT')
PROXY_NOT_SUPPORTED = CodeItem(165, 'PROXY_NOT_SUPPORTED')
UNPROCESSABLE_ENTITY = CodeItem(166, 'UNPROCESSABLE_ENTITY')
INCONSISTENT_STATE = CodeItem(167, 'INCOSISTENT_STATE')

LIST = {
0: EMPTY,
1: GET,
2: POST,
3: PUT,
4: DELETE,
5: FETCH,
6: PATCH,

65: CREATED,
66: DELETED,
Expand All @@ -283,7 +290,9 @@ class Codes(object):
162: BAD_GATEWAY,
163: SERVICE_UNAVAILABLE,
164: GATEWAY_TIMEOUT,
165: PROXY_NOT_SUPPORTED
165: PROXY_NOT_SUPPORTED,
166: UNPROCESSABLE_ENTITY,
167: INCONSISTENT_STATE

}

Expand All @@ -295,7 +304,9 @@ class Codes(object):
"application/octet-stream": 42,
"application/exi": 47,
"application/json": 50,
"application/cbor": 60
"application/cbor": 60,
"application/map-keys+json": 51,
"application/json-patch+json": 52
}

COAP_PREFACE = "coap://"
Expand Down Expand Up @@ -328,6 +339,8 @@ class Codes(object):
"BAD_GATEWAY": "502",
"SERVICE_UNAVAILABLE": "503",
"GATEWAY_TIMEOUT": "504",
"PROXY_NOT_SUPPORTED": "502"
"PROXY_NOT_SUPPORTED": "502",
"INCONSISTENT_STATE": "409",
"UNPROCESSABLE_ENTITY": "422"

}
58 changes: 58 additions & 0 deletions coapthon/layers/requestlayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ def receive_request(self, transaction):
transaction = self._handle_put(transaction)
elif method == defines.Codes.DELETE.number:
transaction = self._handle_delete(transaction)
elif method == defines.Codes.FETCH.number:
transaction = self._handle_fetch(transaction)
elif method == defines.Codes.PATCH.number:
transaction = self._handle_patch(transaction)
else:
transaction.response = None
return transaction
Expand Down Expand Up @@ -140,3 +144,57 @@ def _handle_delete(self, transaction):
transaction = self._server.resourceLayer.delete_resource(transaction, path)
return transaction

def _handle_fetch(self, transaction):
"""
Handle FETCH requests

:type transaction: Transaction
:param transaction: the transaction that owns the request
:rtype : Transaction
:return: the edited transaction with the response to the request
"""
path = str("/" + transaction.request.uri_path)
transaction.response = Response()
transaction.response.destination = transaction.request.source
transaction.response.token = transaction.request.token
if path == defines.DISCOVERY_URL:
transaction = self._server.resourceLayer.discover(transaction)
else:
try:
resource = self._server.root[path]
except KeyError:
resource = None
if resource is None or path == '/':
# Not Found
transaction.response.code = defines.Codes.NOT_FOUND.number
else:
transaction.resource = resource
transaction = self._server.resourceLayer.fetch_resource(transaction)

return transaction

def _handle_patch(self, transaction):
"""
Handle PATCH requests

:type transaction: Transaction
:param transaction: the transaction that owns the request
:rtype : Transaction
:return: the edited transaction with the response to the request
"""
path = str("/" + transaction.request.uri_path)
transaction.response = Response()
transaction.response.destination = transaction.request.source
transaction.response.token = transaction.request.token
try:
resource = self._server.root[path]
except KeyError:
resource = None
if resource is None:
transaction.response.code = defines.Codes.NOT_FOUND.number
else:
transaction.resource = resource
# Update request
transaction = self._server.resourceLayer.patch_resource(transaction)

return transaction
Loading