11import os
22import json
3+ import asyncio
34import secrets
45import inspect
56import pathlib
1011from functools import partial
1112from dataclasses import dataclass
1213from contextlib import AsyncExitStack
13- from typing import List , Union , AsyncIterator , Type , NamedTuple , Dict
14+ from typing import List , Union , AsyncIterator , Type , NamedTuple , Dict , Any
1415
15- from aiohttp import web
1616import aiohttp_cors
17+ from aiohttp import web
18+
1719
1820from dffml import Sources , MemorySource
1921from dffml .record import Record
@@ -176,7 +178,21 @@ class HTTPChannelConfig(NamedTuple):
176178 - text:OUTPUT_KEYS
177179 - json
178180 - output of dataflow (Dict) is passes as json
179-
181+ immediate_response: Dict[str,Any]
182+ If provided with a reponse, server responds immediatly with
183+ it, whilst scheduling to run the dataflow.
184+ Expected keys:
185+ - status: HTTP status code for the response
186+ - content_type: MIME type.If not given, determined
187+ from the presence of body/text/json
188+ - body/text/json: One of this according to content_type
189+ - headers
190+ eg:
191+ {
192+ "status": 200,
193+ "content_type": "application/json",
194+ "data": {"text": "ok"},
195+ }
180196 """
181197
182198 path : str
@@ -185,6 +201,7 @@ class HTTPChannelConfig(NamedTuple):
185201 asynchronous : bool = False
186202 input_mode : str = "default"
187203 forward_headers : str = None
204+ immediate_response : Dict [str , Any ] = None
188205
189206 @classmethod
190207 def _fromdict (cls , ** kwargs ):
@@ -199,7 +216,7 @@ class Routes(BaseMultiCommContext):
199216 async def get_registered_handler (self , request ):
200217 return self .app ["multicomm_routes" ].get (request .path , None )
201218
202- async def multicomm_dataflow (self , config , request ):
219+ async def _multicomm_dataflow (self , config , request ):
203220 # Seed the network with inputs given by caller
204221 # TODO(p0,security) allowlist of valid definitions to seed (set
205222 # Input.origin to something other than seed)
@@ -222,6 +239,7 @@ async def multicomm_dataflow(self, config, request):
222239 },
223240 status = HTTPStatus .NOT_FOUND ,
224241 )
242+
225243 inputs .append (
226244 MemoryInputSet (
227245 MemoryInputSetConfig (
@@ -251,17 +269,19 @@ async def multicomm_dataflow(self, config, request):
251269 )
252270 )
253271 elif ":" in config .input_mode :
254- preprocess_mode , input_def = config .input_mode .split (":" )
272+ preprocess_mode , * input_def = config .input_mode .split (":" )
273+ input_def = ":" .join (input_def )
255274 if input_def not in config .dataflow .definitions :
256275 return web .json_response (
257276 {
258277 "error" : f"Missing definition for { input_def } in dataflow"
259278 },
260279 status = HTTPStatus .NOT_FOUND ,
261280 )
281+
262282 if preprocess_mode == "json" :
263283 value = await request .json ()
264- elif preprocess_mode == "str " :
284+ elif preprocess_mode == "text " :
265285 value = await request .text ()
266286 elif preprocess_mode == "bytes" :
267287 value = await request .read ()
@@ -270,10 +290,11 @@ async def multicomm_dataflow(self, config, request):
270290 else :
271291 return web .json_response (
272292 {
273- "error" : f"preprocess tag must be one of { IO_MODES } , got { preprocess_mode } "
293+ "error" : f"preprocess tag must be one of { self . IO_MODES } , got { preprocess_mode } "
274294 },
275295 status = HTTPStatus .NOT_FOUND ,
276296 )
297+
277298 inputs .append (
278299 MemoryInputSet (
279300 MemoryInputSetConfig (
@@ -301,6 +322,7 @@ async def multicomm_dataflow(self, config, request):
301322 )
302323 )
303324 )
325+
304326 else :
305327 raise NotImplementedError (
306328 "Input modes other than default,preprocess:definition_name not yet implemented"
@@ -314,6 +336,7 @@ async def multicomm_dataflow(self, config, request):
314336 results = {
315337 str (ctx ): result async for ctx , result in octx .run (* inputs )
316338 }
339+
317340 if config .output_mode == "json" :
318341 return web .json_response (results )
319342
@@ -342,6 +365,38 @@ async def multicomm_dataflow(self, config, request):
342365 status = HTTPStatus .NOT_FOUND ,
343366 )
344367
368+ async def multicomm_dataflow (self , config , request ):
369+ if config .immediate_response :
370+ asyncio .create_task (self ._multicomm_dataflow (config , request ))
371+ ir = config .immediate_response
372+ content_type = None
373+ if "content_type" in ir :
374+ content_type = ir ["content_type" ]
375+ else :
376+ if "data" in ir :
377+ content_type = "application/json"
378+ elif "text" in ir :
379+ content_type = "text/plain"
380+ elif "body" in ir :
381+ content_type = "application/octet-stream"
382+
383+ if content_type == "application/json" :
384+ return web .json_response (
385+ data = {} if not "data" in ir else ir ["data" ],
386+ status = 200 if not "status" in ir else ir ["status" ],
387+ headers = None if not "headers" in ir else ir ["headers" ],
388+ )
389+ else :
390+ return web .Response (
391+ body = None if not "body" in ir else ir ["body" ],
392+ text = None if not "text" in ir else ir ["text" ],
393+ status = 200 if not "status" in ir else ir ["status" ],
394+ headers = None if not "headers" in ir else ir ["headers" ],
395+ content_type = content_type ,
396+ )
397+ else :
398+ return await self ._multicomm_dataflow (config , request )
399+
345400 async def multicomm_dataflow_asynchronous (self , config , request ):
346401 # TODO allow list of valid definitions to seed
347402 raise NotImplementedError (
0 commit comments