Skip to content
This repository was archived by the owner on Jun 7, 2021. It is now read-only.

Commit 3b3d29e

Browse files
committed
Add support for response generators
1 parent 227ec79 commit 3b3d29e

1 file changed

Lines changed: 68 additions & 37 deletions

File tree

webthing/server.py

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import json
55
import socket
66
import sys
7+
import typing
8+
import types
9+
import asyncio
710
import tornado.concurrent
811
import tornado.gen
912
import tornado.httpserver
@@ -56,6 +59,39 @@ def options(self, *args, **kwargs):
5659
"""Handle an OPTIONS request."""
5760
self.set_status(204)
5861

62+
async def represent_response(
63+
self, data, content_type: str = "application/json", headers: dict = None
64+
):
65+
headers = headers or {}
66+
for k, v in headers:
67+
self.set_header(k, v)
68+
69+
self.set_header("Content-Type", content_type)
70+
71+
if isinstance(data, (typing.AsyncGenerator, types.GeneratorType)):
72+
self.set_header(
73+
"Cache-Control",
74+
"no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0",
75+
)
76+
self.set_header("Pragma", "no-cache")
77+
78+
if isinstance(data, typing.AsyncGenerator):
79+
async for frame in data:
80+
# Write data to memory
81+
self.write(frame)
82+
# Write data to network
83+
await self.flush()
84+
elif isinstance(data, types.GeneratorType):
85+
for frame in data:
86+
# Write data to memory
87+
self.write(frame)
88+
# Write data to network
89+
await self.flush()
90+
else:
91+
if content_type == "application/json":
92+
data = json.dumps(data)
93+
self.write(data)
94+
5995

6096
class ThingHandler(tornado.websocket.WebSocketHandler, Subscriber):
6197
"""Handle a request to /."""
@@ -299,7 +335,7 @@ def update_event(self, event):
299335
class PropertiesHandler(BaseHandler):
300336
"""Handle a request to /properties."""
301337

302-
def get(self):
338+
async def get(self):
303339
"""
304340
Handle a GET request.
305341
"""
@@ -308,13 +344,13 @@ def get(self):
308344
return
309345

310346
self.set_header("Content-Type", "application/json")
311-
self.write(json.dumps(self.thing.get_properties()))
347+
self.write(json.dumps(await self.thing.get_properties()))
312348

313349

314350
class PropertyHandler(BaseHandler):
315351
"""Handle a request to /properties/<property>."""
316352

317-
def get(self, property_name=None):
353+
async def get(self, property_name=None):
318354
"""
319355
Handle a GET request.
320356
@@ -324,13 +360,14 @@ def get(self, property_name=None):
324360
self.set_status(404)
325361
return
326362

327-
if self.thing.has_property(property_name):
328-
self.set_header("Content-Type", "application/json")
329-
self.write(json.dumps(self.thing.get_property(property_name)))
363+
prop = self.thing.find_property(property_name)
364+
if prop:
365+
data = await self.thing.get_property(property_name)
366+
await self.represent_response(data, prop.get_content_type())
330367
else:
331368
self.set_status(404)
332369

333-
def put(self, property_name=None):
370+
async def put(self, property_name=None):
334371
"""
335372
Handle a PUT request.
336373
@@ -346,20 +383,15 @@ def put(self, property_name=None):
346383
self.set_status(400)
347384
return
348385

349-
if self.thing.has_property(property_name):
386+
prop = self.thing.find_property(property_name)
387+
if prop:
350388
try:
351-
self.thing.set_property(property_name, args)
389+
await self.thing.set_property(property_name, args)
352390
except PropertyError:
353391
self.set_status(400)
354392
return
355-
356-
self.set_header("Content-Type", "application/json")
357-
self.write(
358-
json.dumps(
359-
{
360-
property_name: self.thing.get_property(property_name),
361-
}
362-
)
393+
await self.represent_response(
394+
await self.thing.get_property(property_name), prop.get_content_type()
363395
)
364396
else:
365397
self.set_status(404)
@@ -368,18 +400,17 @@ def put(self, property_name=None):
368400
class ActionsHandler(BaseHandler):
369401
"""Handle a request to /actions."""
370402

371-
def get(self):
403+
async def get(self):
372404
"""
373405
Handle a GET request.
374406
"""
375407
if self.thing is None:
376408
self.set_status(404)
377409
return
378410

379-
self.set_header("Content-Type", "application/json")
380-
self.write(json.dumps(self.thing.get_action_descriptions()))
411+
await self.represent_response(self.thing.get_action_descriptions())
381412

382-
def post(self):
413+
async def post(self):
383414
"""
384415
Handle a POST request.
385416
"""
@@ -412,15 +443,15 @@ def post(self):
412443
tornado.ioloop.IOLoop.current().spawn_callback(action.start)
413444

414445
self.set_status(201)
415-
self.write(json.dumps(response))
446+
await self.represent_response(response)
416447
else:
417448
self.set_status(400)
418449

419450

420451
class ActionHandler(BaseHandler):
421452
"""Handle a request to /actions/<action_name>."""
422453

423-
def get(self, action_name=None):
454+
async def get(self, action_name=None):
424455
"""
425456
Handle a GET request.
426457
@@ -430,10 +461,11 @@ def get(self, action_name=None):
430461
self.set_status(404)
431462
return
432463

433-
self.set_header("Content-Type", "application/json")
434-
self.write(json.dumps(self.thing.get_action_descriptions(action_name=action_name)))
464+
await self.represent_response(
465+
self.thing.get_action_descriptions(action_name=action_name)
466+
)
435467

436-
def post(self, action_name=None):
468+
async def post(self, action_name=None):
437469
"""
438470
Handle a POST request.
439471
"""
@@ -455,15 +487,15 @@ def post(self, action_name=None):
455487
tornado.ioloop.IOLoop.current().spawn_callback(action.start)
456488

457489
self.set_status(201)
458-
self.write(json.dumps(response))
490+
await self.represent_response(response)
459491
else:
460492
self.set_status(400)
461493

462494

463495
class ActionIDHandler(BaseHandler):
464496
"""Handle a request to /actions/<action_name>/<action_id>."""
465497

466-
def get(self, action_name=None, action_id=None):
498+
async def get(self, action_name=None, action_id=None):
467499
"""
468500
Handle a GET request.
469501
@@ -479,10 +511,9 @@ def get(self, action_name=None, action_id=None):
479511
self.set_status(404)
480512
return
481513

482-
self.set_header("Content-Type", "application/json")
483-
self.write(json.dumps(action.as_action_description()))
514+
await self.represent_response(action.as_action_description())
484515

485-
def put(self, action_name=None, action_id=None):
516+
def put(self, action_name=None, action_id=None):
486517
"""
487518
Handle a PUT request.
488519
@@ -517,22 +548,21 @@ def delete(self, action_name=None, action_id=None):
517548
class EventsHandler(BaseHandler):
518549
"""Handle a request to /events."""
519550

520-
def get(self):
551+
async def get(self):
521552
"""
522553
Handle a GET request.
523554
"""
524555
if self.thing is None:
525556
self.set_status(404)
526557
return
527558

528-
self.set_header("Content-Type", "application/json")
529-
self.write(json.dumps(self.thing.get_event_descriptions()))
559+
await self.represent_response(self.thing.get_event_descriptions())
530560

531561

532562
class EventHandler(BaseHandler):
533563
"""Handle a request to /events/<event_name>."""
534564

535-
def get(self, event_name=None):
565+
async def get(self, event_name=None):
536566
"""
537567
Handle a GET request.
538568
@@ -542,8 +572,9 @@ def get(self, event_name=None):
542572
self.set_status(404)
543573
return
544574

545-
self.set_header("Content-Type", "application/json")
546-
self.write(json.dumps(self.thing.get_event_descriptions(event_name=event_name)))
575+
await self.represent_response(
576+
self.thing.get_event_descriptions(event_name=event_name)
577+
)
547578

548579

549580
class WebThingServer:

0 commit comments

Comments
 (0)