FTN6: FutoIn Executor Concept
Version: 1.5
Date: 2015-03-08
Copyright: 2014 FutoIn Project (http://futoin.org)
Authors: Andrey Galkin

CHANGES

Warning

This specification IS NOT mandatory. It is just a reference model. Any implementation IS ALLOWED to provide own architecture with drawback of breaking easy migration from one to another.

1. Concept

There must be a generic object type, which can represent both request and response message data and/or communication channels.

There must be a wrapper, which holds current request-response info and potentially a set of standard utilities.

There must be a generic request Executor, which knows all registered interfaces and their implementations.

Executor implementation is not limited in internal behavior, but should have standardized interface even for helper tools, like FutoIn interface compilers and/or converters.

Executor is responsible for (actions are done in AsyncSteps):

  1. converting request from transport-level representation to internal message format
  2. gathering basic request-response info
  3. checking interface constraints
  4. checking message security (HMAC) or authenticating user by credentials
  5. passing control to implementation of specific interfaces with required major version
  6. catching exceptions or normal result
  7. converting response to transport-level representation
  8. maintaining persistent communication channel, if needed
                Firewall
    Client          ||  Executor           Implementation
       .            || [Register]                 .
       .            ||      |                     .
       |----- Request ----->|                     .
       |            ||  [Unpack]                  .
       |            ||   [Info]                   .
       |            || [Constraints]              .
       |            ||  [Security]                .
       |            ||      |---- Invoke -------->|
       |            ||      |<-- Except/Result ---|
       |            ||   [Pack]                   .
       |<---- Response -----|                     .
       .            ||      |                     .
       .            ||      |                     .
    

Executor should be tighly integrated with MasterService implementation, if supported. General FutoIn message verification should be based on HMAC checking.

Executor should also integrate with AuthService as consumer, if real human users expected to use the service.

Note: Executor is allowed to pass control to implementation only if requested major version of interfaces exactly matches implemented version and minor version is greater than or equal to requested minor version.

All actions are implemented through AsyncSteps interface (FTN12: Async API). For backward compatibility with pre-FutoIn code and/or complex logic, it is possible to make blocking implementation. Such implementations run in dedicated worker threads/processes and receive only RequestInfo object reference.

All true asynchronous implementations must implement special FutoIn AsyncImplementation interface to clearly distinguish more advanced one.

Method signatures:

void AsyncMethod( AsyncSteps as, RequestInfo reqinfo );
Result BlockingMethod( RequestInfo reqinfo );

Note: if BlockingMethod or AsyncMethod returns result then its fields are added to already existing result fields in reqinfo object.

1.1. FutoIn interfaces

Interfaces must get converted according to language/platform-specific convention into native interfaces, which can depend only on standard runtime and native language/platform-specific interfaces of Executor and related objects.

1.2. Executor security

Besides standard authentication and authorization mechanisms, peer with executor capabilities must create separate Executor objects to minimize risk of security flaws in such mechanisms. Example:

1.3. HMAC generation

See HMAC for details

1.3.1 Rules of HMAC generation for payload

1.3.2. Request "sec" field coding with HMAC data

The "sec" field is normally used for Basic Auth in "{user}:{password}" format. However, a special "-hmac" user name is reserved for HMAC message signature.

The HMAC signature has the following format: "-hmac:{user}:{algo}:{signature}"

Where:

1.3.3. Predefined HMAC algorithms

Note: MD5 and SHA2 are mandatory to be implemented on server, SHA3 - if supported by runtime. However, server may reject unsupported algorithms through configuration

1.3.4. Response "sec" field with HMAC

If request comes signed with HMAC then response must also be signed with HMAC using exactly the same secret key and hashing algorithm.

Only Base64-encoded signature in sent back in the "sec" field.

2. Native Executor interface requirements

Language/platform should support runtime introspection and exceptions. For other cases, platform/language-specific workarounds are assumed.

2.1. FutoIn interface

  1. Each FutoIn interfaces is represented as simple native interface type with only abstract methods for each FutoIn interface function
  2. Each abstract method should return no value and take exactly one Request Info object as argument for blocking implementation. Or AsyncSteps and RequestInfo objects as arguments for asynchronous implementation.
  3. Implementation method can assume that all request parameters defined in spec can be accessed from request data
  4. Access to unexpected request and/or response parameters should raise InternalError
  5. Throw of unexpected error should raise InternalError
  6. Each implementation method should have public access
  7. There must be no public method which is not part of the specific FutoIn interface definition
  8. All native interfaces should inherit from single native interface with no public abstract methods

2.2. Request Info

  1. constants:
  2. map params() - return reference to request parameter map
  3. map result() - return reference to response parameter map
  4. map info() - return reference to info parameter map, keys (defined as const with INFO_ prefix):
  5. stream rawInput() - return raw input stream or throws error
  6. stream rawOutput() - return raw output stream (no result variables are expected) or throws error
  7. Executor executor() - get reference to Executor
  8. ChannelContext channel() - get reference to ChannelContext
  9. void cancelAfter( timeout_ms ) - set to abort request after specified timeout_ms from the moment of call. It must override any previous cancelAfter() call. Note: it is different from as.setTimeout() as inner step timeout does not override outer step timeout.
  10. Language-specic get accessor for info properties

2.3. User info

  1. string localID() - get user ID as seen by trusted AuthService
  2. string globalID() - get globally unique user ID
  3. void details( AsyncSteps as, array user_field_identifiers )

2.4. Source Address

  1. string host() - numeric, no name lookup
  2. string port() - IP port or local path/identifier
  3. string type() - IPv4, IPv6, LOCAL
  4. string asString() "Type:Host:Port" or "Type:Port"

2.5. Derived Key

  1. string baseID()
  2. string sequenceID()
  3. void encrypt( AsyncSteps as, data ) - return Base64 data
  4. void decrypt( AsyncSteps as, data ) - decrypt Base64 data

2.6. General Async Step interface

See FTN12 Async API

2.7. Async completion interface

There is little use of extended AsyncSteps to provide additional API. Instead, by convention, "reqinfo" AsyncSteps state field must point to associated RequestInfo instance.

2.8. Executor

  1. AdvancedCCM ccm() - get reference to Invoker CCM, if any
  2. void register( AsyncSteps as, ifacever, impl ) - add interface implementation
  3. void process( AsyncSteps as ) - do full cycle of request processing, including all security checks
  4. void onEndpointRequest( info, ftnreq, send_executor_rsp )
  5. void onInternalRequest( as, info, ftnreq )
  6. void checkAccess( AsyncSteps as, acd ) - shortcut to check access through #acl interface
  7. void initFromCache( AsyncSteps as )
  8. void cacheInit( AsyncSteps as )
  9. void close()

2.9. Interface Implementation

No public members, except for members of the implemented spec.

Each call can set result variables the following way:

  1. through reqinfo.result() map
  2. by returning a map from the function
  3. by returning a map through as.success() call

Note: Executor implementation must merge all possible ways to set result variables in the strict order as listed above.

2.10. Channel Context

ChannelContext interface

Various specification can extend this interface with additional functionality.

=END OF SPEC=