#ifndef _LIGHTTPD_ANGEL_CONNECTION_H_ #define _LIGHTTPD_ANGEL_CONNECTION_H_ #include #define ANGEL_CALL_MAX_STR_LEN (64*1024) /* must fit into a gint32 */ typedef struct liAngelConnection liAngelConnection; typedef struct liAngelCall liAngelCall; /* error, data and fds-array will be freed/closed by the angel api itself; if you want to use the fds set the array size to 0 */ typedef void (*liAngelCallCB)(liAngelCall *acall, gpointer ctx, gboolean timeout, GString *error, GString *data, GArray *fds); typedef void (*liAngelReceiveCallCB)(liAngelConnection *acon, const gchar *mod, gsize mod_len, const gchar *action, gsize action_len, gint32 id, GString *data); /* gets called after read/write errors */ typedef void (*liAngelCloseCB)(liAngelConnection *acon, GError *err); struct liAngelConnection { gpointer data; GMutex *mutex; struct ev_loop *loop; int fd; liIDList *call_id_list; GPtrArray *call_table; ev_io fd_watcher; ev_async out_notify_watcher; GQueue *out; liAngelBuffer in; liAngelReceiveCallCB recv_call; liAngelCloseCB close_cb; /* parse input */ struct { gboolean have_header; gint32 type, id; gint32 mod_len, action_len, error_len, data_len, missing_fds; guint body_size; GString *mod, *action, *error, *data; GArray *fds; } parse; }; /* with multi-threading you should protect the structure * containing the angel_call with a lock */ struct liAngelCall { gpointer context; liAngelCallCB callback; /* internal data */ gint32 id; /* id is -1 if there is no call pending (the callback may still be running) */ liAngelConnection *acon; ev_timer timeout_watcher; ev_tstamp timeout; }; /* error handling */ #define LI_ANGEL_CALL_ERROR li_angel_call_error_quark() LI_API GQuark li_angel_call_error_quark(); #define LI_ANGEL_CONNECTION_ERROR li_angel_connection_error_quark() LI_API GQuark li_angel_connection_error_quark(); typedef enum { LI_ANGEL_CALL_ALREADY_RUNNING, /* the angel_call struct is already in use for a call */ LI_ANGEL_CALL_OUT_OF_CALL_IDS, /* too many calls already pending */ LI_ANGEL_CALL_INVALID /* invalid params */ } liAngelCallError; typedef enum { LI_ANGEL_CONNECTION_CLOSED, /* error on socket */ LI_ANGEL_CONNECTION_RESET, /* connection closed by remote side */ LI_ANGEL_CONNECTION_INVALID_DATA /* invalid data from stream */ } liAngelConnectionError; /* create connection */ LI_API liAngelConnection* li_angel_connection_new( struct ev_loop *loop, int fd, gpointer data, liAngelReceiveCallCB recv_call, liAngelCloseCB close_cb); LI_API void li_angel_connection_free(liAngelConnection *acon); LI_API liAngelCall *li_angel_call_new(liAngelCallCB callback, ev_tstamp timeout); /* returns TRUE if a call was cancelled; make sure you don't call free while you're calling send_call */ LI_API gboolean li_angel_call_free(liAngelCall *call); /* calls */ /* the GString* parameters get stolen by the angel call (moved to chunkqueue) */ LI_API gboolean li_angel_send_simple_call( liAngelConnection *acon, const gchar *mod, gsize mod_len, const gchar *action, gsize action_len, GString *data, GError **err); LI_API gboolean li_angel_send_call( liAngelConnection *acon, const gchar *mod, gsize mod_len, const gchar *action, gsize action_len, liAngelCall *call, GString *data, GError **err); LI_API gboolean li_angel_send_result( liAngelConnection *acon, gint32 id, GString *error, GString *data, GArray *fds, GError **err); /* free temporary needed memroy; call this once in while after some activity */ LI_API void li_angel_cleanup_tables(liAngelConnection *acon); /* Usage */ #if 0 void init() { /* ... init ctx... */ angel_call_init(&ctx->call); ctx->call.context = ctx; ctx->call.callback = my_callback; } gboolean start_call(curctx) { GError *err = NULL; GString *data; lock(); ctx = get_ctx(); /* another thread may have already called angel, depending on the task (e.g. fastcgi spawn) */ if (!angel_call_is_needed(ctx)) { unlock(); return TRUE; } data = build_call_data(); if (!li_angel_send_call(acon, CONST_STR_LEN("mymod"), CONST_STR_LEN("myaction"), &ctx->call, 10, data, &err)) { unlock(); report_error(&err); return FALSE; } /* add current context (e.g. vrequest) to a wakeup list */ push_to_queue(ctx->waitqueue, curctx); unlock(); return TRUE; /* at this point the callback may be already finished */ } void my_callback(angel_call *acall, gboolean timeout, GString *error, GString *data, GArray *fds) { lock(); handle_error(); parse_data(); /* ... */ done: wakeup(acall->ctx->waitqueue); if (error) g_string_free(error, TRUE); if (data) g_string_free(error, TRUE); /* destroy fd-array? */ perhaps_free_ctx(acall->ctx); unlock(); } void stop_call() { lock(); ctx = get_ctx(); if (!angel_cancel_call(acon, ctx)) { /* callback either is already done or just to be called */ /* do _not_ destroy the context */ unlock(); return; } perhaps_free_ctx(ctx); unlock(); } #endif #endif