Package asycamore :: Module servicemodel

Module servicemodel



service level connection management and dispatcher integration.

Each model may completely control the life cycle of the connection state. The api is structured to enable each service to interpose itself between the sockets used during the listen, accept, handle cycle and the dispatch_events for each of those sockets.

An IServiceModel implementation is typically closely coupled with one or more connection state classes derived from asycamore.dispatcher.IDispatchTarget. An additional auxiliary class is usually associated with the connection state instance to deal with the application level aspects of a serviceable connection.

Implementations are free to operate at either end, or both ends, of the wire. This api can support client side requesting connections and server side responding connections.

For the asycamore.asycwsgi server implementation the corresponding classes are:

A minimal asynchronous http client, capable of fetching urls concurrently on many connections is implemented by:

Typical service model use:

The dispatch context is made available to the service model implementation via its first_dispatch if it defines one.

connection establishment and event dispatching

The implementation of IServiceModel is the instance that defines how the work required for different applications and services is scheduled and by what means those applications must interact with the outside world.

In general service models provide connect and listen style entry points as is appropriate to the model. These form the connection initiation api of the service model implementation. The these methods share a common theme: They provide the bridge between the service model and the specific instances that produce or consume the connection content. The service model decides when and how often each connection gets to produce or consume content and the application instances perform the actual work.

This separation of concerns is the mechanism that allows the service model to take care of things like http request pipelining and other connection level performance issues that enable the service model to balance connect/response latency and over all throughput.

In order to document the precise details of the connection initiation api the IServiceModel interface provides the following definitions for connect and listen:

The critical aspects for bridging are the callback arguments defined by those methods. Namely: connection_factory and connection_started.

connection_factory is called to produce an instance that provides the IDispatchTarget interface. Its implementation will be registered with the dispatcher by the service model whenever the service model is interested in events for that target. As events arrive at the dispatcher they will propagate through the dispatch target and in to the service model. (See IDispatchContext and asycamore.dispatcher.iterate)

The connection_factory is responsible for selecting the appropriate start_connection and continue_connection entry points from the service model and providing them to the dispatch target instance. Typically they will be methods implemented directly by the service model. The factory implementation MUST chose entry points that are appropriate for the kind of connection it represents (client/server/other) from those offered by the service model implementation. The dispatch target, produced by the factory, then uses those entry points to propagate dispatch events into the service model.

All implementations of start_connection must arrange to call the connection_started callback provided, along with the connection_factory, in the parameters to the connection initiation api. connection_started is the last step of connection initiation. It is required to produce an application context instance that is compatible with the continue_connection implementation implied by the connection_factory.

IServiceModel defines the method signatures and documents the requirements:

The service model may use any names it desires for these entry points. However, regardless of the actual connection flavour in question or the method names that are used, the corresponding implementations of start_connection and continue_connection MUST honour the semantics of the above definitions.

HTTPServiceModel provides both client and server flavours of these entry points. The continue_connection server implementation is particularly interesting because it implements a busy update, for the benefit of connections that aggressively pipeline their requests:

The corresponding connection_factory definitions are:

In summary:

For a given class of dispatch target that implements IDispatchTarget the corresponding implementation of IServiceModel.dispatch_continue_connection is determined by the connection_factory passed in to the connection initiation api defined by the service model. This entry point then becomes the single point of entry, from the dispatcher, for all work carried out for a particular connection in direct response to a dispatcher event. The factory will provide the dispatch target with a start_connection callback which the dispatch target will call exactly once with the first event it receives. The target will not call start_connection if the first event indicates the connection is broken or closed. The target will call start_connection BEFORE any call to continue_connection. The first call to continue_connection is made immediately after start_connection, with the same event, provided that start_connection does not close the connection.

The service model start_connection implementation is responsible for invoking the connection_started callback, provided to the connection setup api by the calling application. The implementation of connection_started MUST produce an instance that is compatible with the continue_connection implementation implied by the connection_factory that it accompanies.

The event tuple

Both the IServiceModel interface and the IDispatchContext interface honour the same semantics for events. event is always a tuple with at least 3 elements. The first three elements are required, respectively, to reflect:

Irrespective of the actual values used for each field if bool(event[i]) is True then the implementation of IServiceModel.dispatch_continue_connection should assume the connection is in the appropriate state to consume that kind of event. The typical idiom is:

R, W, E = event
if E:
  o.close() # but this is usualy handled by `o`
if R:
  d = o.socket.recv(8192)
  # do something with d
if W and o.pending:
  nsent = o.socket.send(o.pending)
  o.pending = o.pending[nsent:]

It is rare to access the socket api directly in this manner, typically a generator-iterator focused on a particular application level protocol is used. For example see asycamore.tcpsocket.write_iterable, asycamore.chunkedtransfer or asycamore.requestresponse.http_request_reader

The IDispatchTarget instances, and all the protocol level helpers, deal appropriately with the socket exceptions that occur when socket.send or socket.recv is called on a (NON BLOCKING) socket that is not read for the requested operation. Exceptional events on socket.send or socket.recv are always translated to raise StopIterration. A recv of 0 bytes is always translated to raise StopIteration.

At the service model level StopIterration is the one way to flag that a connection should be closed. StopIteration from a read operation implies, at least, read close (half close support). Similarly from a read operation StopIteration implies at least "write close". For the HTTP implementation StopIteration on write always implies a complete socket.shutdown.



Classes
  IServiceModel
Schedules all work performed by applications in servicing connections.