adn framework - loltarudesh/adn-warp GitHub Wiki

The ADN project aims at handling flows of packet by building "pipelines". The execution monad, called ADN, is an instance of MonadIO, with a few handy tools. it is simply imported with:

import ADN

Pipelines

A pipeline is a chain of modules dealing with packets, between the network layer up to the application layer. By convention, the start of a pipeline is the network, and the end the application. The different modules in a pipeline communicate together through Channels.

Channel

the type Channel represent a both-way communication channel, with send/recv function similar to a standard network socket.

type Sink p = p -> ADN ()
type Source p = ADN p

data Channel p = Channel {
    send :: Sink p
    recv :: Source p
}

the type parameter p represent the type of packets exchanged.

WARNING: just like a socket, a Channel cannot be shared! If a thread reads from it, data will not be available to other thread. If you wan't to duplicate packets, you have to find another way!

PipeStart

The beginning of a pipeline does not have a lower layer, but simply an upper one. It is represented by the type PipeStart:

type PipeStart p = ADN (Channel p)
class PipeStartBuilder a p | a -> p where pipeStart :: a -> PipeStart p

where p is the type of packets produced. the parameter a is intended to be the configuration data structure of the module. The class method pipeStart produces, with the configuration, a Channel to be used by the upper layer.

PipeEnd

The end of a pipeline only have a lower layer, but no upper one. It is represented by the type:

type PipeEnd p = Channel p -> ADN ()
class PipeEndBuilder a p | a -> p where pipeEnd :: a -> PipeEnd p

where p is the type of packet processed and a is the configuration. The pipeEnd method receive a Channel from the lower layer, and process it in a monadic action.

Remarque: PipeEnd and PipeStart are very similar class, and can be easily converted one to the other:

pipeStartToEnd :: PipeStartBuider a p => a -> PipeEnd p
pipeEndToStart :: PipeEndBuilder a p => a -> PipeStart p

the existence of both type class is intended for 2 purposes:

  • to provide a direction to the pipeline and prevent easy mistakes, like connecting modules backward.
  • to make the linking of module simple and intuitive, and limit the threading madness.

Pipe

A pipe is a module located in the middle of a pipeline, and communicate with both a lower and an upper layer. It is represented by the type:

type Pipe p p' = Channel p -> ADN Channel p'
class PipeBuilder a p p' | a -> p, a -> p' where pipe :: a -> Pipe p p'

A Pipe exchange packet of type p with the lower layer, and of type p' with the upper layer. The method pipe receive a Channel from the lower layer, and produces one for the upper layer.

Building a pipeline

Instances of the three class, PipeStart, Pipe and PipeEnd, can be linked together with a simple "bind":

pipeStart RawSocket >>= pipe TCP-IP >>= pipeEnd MyApplication

keep in mind that it is absolutely possible to build multiple pipelines, and have them communicate. It is also possible to create, alter and close pipelines during the runtime, and even inside the send/recv functions of a Channel, thanks to the ADN monad.

ADN Monad

The ADN monad is a MonadIO instance, with a few handy tools to help build and manage pipelines.

Block

One key feature is the encapsulation of a part of execution inside a named "Block":

type BlockName = String
block :: BlockName -> ADN a -> ADN a

The encapsulated code become a sub-block of the caller's block, and can create sub-blocks of itselfs.

Close

It is possible to close the current block, or a sub-block, with the functions:

closeBlock :: ADN () 
closeSubBlock :: BlockName -> ADN ()

This functions close the targeted block and its sub-blocks by calling the functions registered with:

registerClose :: ADN () -> ADN ()  -- called before closing sub-blocks
registerCloseAfer ADN () -> ADN () -- called after closing sub-blocks

Log

ADN also export a rudimentary logging system:

debug :: String -> ADN ()
info :: String -> ADN ()
warning :: String -> ADN ()
error :: String -> ADN ()

logs are flagged with the origin block, to help debugging.

CLI

Any part of the pipeline can register simple control commands:

type CLI = String -> ADN String 
registerCLI :: String -> String -> CLI -> ADN ()
registerCLI_ :: String -> String -> ADN String -> ADN () -- same as registerCLI without arguments

arguments are:

  • name of the command
  • help message
  • the function to execute

reserved commands this commands are implemented by every block:

  • help -> print this help, and block-specific commands
  • exit -> exit the CLI
  • close-> close the block, and it's sub-blocks
  • list -> list the availabke sub-blocks (only 1 level)
  • tree -> show the whole sub-block tree