メモ代わり。てきとーに。 いや、ですからてきとーですって。 2年前ぐらいにPythonあたりでメールくれた方、ごめんなさい。メール紛失してしまい無視した形になってしまいました。。。

2008年5月20日火曜日

[libserf] libserf使ってみた

へぼいプログラムを書いてみた。
といってもtest/serf_get.cのほぼパクリ。

では、さっそく。

SSLを使うかどうかのフラグとSSLを使用する場合のSSLコンテキスト保持用に
構造体を用意する。
また、この構造体にはserf内で使用するallocatorも保持しておく。


struct __app_ctx_t {
int ssl_flag; /* SSLを使用する場合は1 */
serf_ssl_context_t *ssl_ctx; /* SSL用のコンテキスト */
serf_bucket_alloc_t *bkt_alloc; /* serf用バケットアロケータ */
};
 

な感じ。
これをアプリケーションコンテキストと名づけた。

で次、

struct __handler_ctx_t {
#if APR_MAJOR_VERSION > 0
apr_uint32_t requests_outstanding;
#else
apr_atomic_t requests_outstanding;
#endif

serf_response_acceptor_t acceptor;
app_ctx_t *acceptor_ctx;

serf_response_handler_t handler;

const char *host;
const char *method;
const char *path;

apr_status_t rv;
const char *reason;
};
 

ハンドラのコンテキスト。
リクエストが終了したかどうかの判定に使用するrequest_outstanding、
セットアップに必要な各情報(acceptor、acceptor_ctx、handler)、
あとエラー終了時のステータスを覚えておくためのrvとreason
を保持する。

aprの初期化など

void
s_init(apr_pool_t **pool)
{
apr_initialize();

apr_pool_create(pool, NULL);
apr_atomic_init(*pool);
}


void
s_term(apr_pool_t *pool)
{
apr_pool_destroy(pool);
apr_terminate();
}
 

決まり文句的なaprの初期化ルーチン。

さて、libserfを使うためには5つほどコールバックを作成する必要がある。

で、以下そのコールバック。

コネクション確立時にコールされるコールバック

static serf_bucket_t *
s_connection_setup(apr_socket_t *skt, void *setup_ctx, apr_pool_t *pool)
{
serf_bucket_t *c;
app_ctx_t *ctx = (app_ctx_t *)setup_ctx;

if (ctx->ssl_flag) {
/* sslを使用する場合、ssl用serf_bucket_tを作成する */
c = serf_bucket_ssl_decrypt_create(c, ctx->ssl_ctx, ctx->bkt_alloc);
if (!ctx->ssl_ctx) {
/* sslコンテキストも生成する */
ctx->ssl_ctx = serf_bucket_ssl_decrypt_context_get(c);
}
return c;
}
/* 通常のsef_bucket_t */
return serf_bucket_socket_create(skt, ctx->bkt_alloc);
}
 

こんな感じ。sslを使用する場合にはsslでコネクション用serf_bucket_tを作る。
またコンテキストも生成する。


コネクション解放時にコールされるコールバック

static void
s_connection_closed(serf_connection_t *conn, void *closed_baton, apr_status_t why, apr_pool_t *pool)
{
fprintf(stderr, "コネクション切断:[%d]\n", why);
}
 

特に何もする必要ないんで、こんな感じ。

実際に投げるリクエストを生成するためのコールバック

static apr_status_t
s_setup_request(serf_request_t *request,
void *setup_ctx,
serf_bucket_t **req_bkt,
serf_response_acceptor_t *acceptor,
void **acceptor_ctx,
serf_response_handler_t *handler,
void **handler_ctx,
apr_pool_t *pool)
{
handler_ctx_t *ctx = setup_ctx;
serf_bucket_t *hdrs_bkt;
serf_bucket_t *body_bkt;

*req_bkt = serf_bucket_request_create(ctx->method, ctx->path, NULL, serf_request_get_alloc(request));

hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);

serf_bucket_headers_setn(hdrs_bkt, "Host", ctx->host);
serf_bucket_headers_setn(hdrs_bkt, "User-Agent", "Wget2/1.0");

/* 処理中 */
apr_atomic_inc32(&(ctx->requests_outstanding));

/* SSLなら */
if (ctx->acceptor_ctx->ssl_flag) {
serf_bucket_alloc_t *req_alloc;
app_ctx_t *app_ctx = ctx->acceptor_ctx;

req_alloc = serf_request_get_alloc(request);

if (app_ctx->ssl_ctx == NULL) {
*req_bkt = serf_bucket_ssl_encrypt_create(*req_bkt, NULL, app_ctx->bkt_alloc);
app_ctx->ssl_ctx = serf_bucket_ssl_encrypt_context_get(*req_bkt);
}
else {
*req_bkt = serf_bucket_ssl_encrypt_create(*req_bkt, app_ctx->ssl_ctx, app_ctx->bkt_alloc);
}
}

*acceptor = ctx->acceptor;
*acceptor_ctx = ctx->acceptor_ctx;
*handler = ctx->handler;
*handler_ctx = ctx;

return APR_SUCCESS;
}
 

とりあえず、body部はいらないんで、こんな感じ。
ここでrequest_outstandingを1にしている。1の間は「処理中」を表す。
で、acceptorとhandlerも設定。


レスポンスをacceptしたときにコールされるコールバック

static serf_bucket_t *
s_accept_response(serf_request_t *request, serf_bucket_t *stream, void *acceptor_baton, apr_pool_t *pool)
{
serf_bucket_alloc_t *bkt_alloc;
serf_bucket_t *c;

/* 入力バケットをwrapする */
bkt_alloc = serf_request_get_alloc(request);
c = serf_bucket_barrier_create(stream, bkt_alloc);
return serf_bucket_response_create(c, bkt_alloc);
}
 

acceptorはこんな感じ。wrapして返すだけ。


レスポンスの内容を処理するコールバック

static apr_status_t
s_handle_response(serf_request_t *request, serf_bucket_t *response, void *handler_ctx, apr_pool_t *pool)
{
const char *data;
apr_size_t len;
serf_status_line sl;
apr_status_t rv;
handler_ctx_t *ctx = handler_ctx;

/* レスポンスのステータスライン取得 */
rv = serf_bucket_response_status(response, &sl);
if (rv != APR_SUCCESS) {
if (APR_STATUS_IS_EAGAIN(rv)) {
return rv;
}
ctx->rv = rv;
apr_atomic_dec32(&ctx->requests_outstanding); /* 終了通知 */
return rv;
}
ctx->reason = sl.reason;

while (1) {
rv = serf_bucket_read(response, 2048, &data, &len);
if (SERF_BUCKET_READ_ERROR(rv)) {
ctx->rv;
apr_atomic_dec32(&ctx->requests_outstanding);
return rv;
}

fwrite(data, 1, len, stdout);

if (APR_STATUS_IS_EOF(rv)) {
apr_atomic_dec32(&ctx->requests_outstanding);
return APR_EOF;
}

if (APR_STATUS_IS_EAGAIN(rv))
return rv;
}
}
 

ステータスラインを調べて、レスポンスを読み込む。
読み込み終わったら、

apr_atomic_dec32(&ctx->requests_outstanding);
 

として、終了を通知。


で、しょぼしょぼメインはこんな感じ。

int
main(int argc, char *argv[])
{
apr_pool_t *pool;
apr_uri_t url;
apr_status_t rv;
apr_sockaddr_t *address = NULL;

serf_context_t *context;
serf_connection_t *connection;

/* serf用アロケータ、sslのctxなどを保持する */
app_ctx_t app_ctx;
/* handler用ctx */
handler_ctx_t handler_ctx;


if (argc != 2) {
fprintf(stderr, "usage: %s url\n", argv[0]);
return -1;
}

s_init(&pool);
apr_uri_parse(pool, argv[1], &url);
if (!url.port) {
url.port = apr_uri_port_of_scheme(url.scheme);
}
if (!url.port) {
url.port = 80;
}
if (!url.path) {
url.path = "/";
}
if (!url.hostname) {
url.hostname = "localhost";
}

/* addressの取得 */
rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
if (rv != APR_SUCCESS) {
fprintf(stderr, "apr_sockaddr_info_get()失敗: %d\n", rv);
return -2;
}
fprintf(stderr, "url.hostname:[%s] url.port:[%d]\n", url.hostname, url.port);

/* アプリケーション用コンテキスト領域初期化 */
memset(&app_ctx, 0, sizeof(app_ctx_t));

/* serf用アロケータの設定 */
app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
if (strcasecmp(url.scheme, "https") == 0) {
app_ctx.ssl_flag = 1;
}

/* serfコンテキストの生成 */
context = serf_context_create(pool);

/* serfコネクションの生成 */
connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);

memset(&handler_ctx, 0, sizeof(handler_ctx_t));
handler_ctx.requests_outstanding = 0;
handler_ctx.host = url.hostinfo;
handler_ctx.method = "GET";
handler_ctx.path = url.path;

handler_ctx.acceptor = s_accept_response;
handler_ctx.acceptor_ctx = &app_ctx;
handler_ctx.handler = s_handle_response;

/* リクエスト生成 */
serf_connection_request_create(connection, s_setup_request, &handler_ctx);

/* 処理ループ */
while (1) {
rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
if (APR_STATUS_IS_TIMEUP(rv))
continue;
if (rv) {
char buf[200];
fprintf(stderr, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
break;
}
if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
if (handler_ctx.rv != APR_SUCCESS) {
char buf[200];
fprintf(stderr, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
}
break;
}
}


serf_connection_close(connection);
fprintf(stderr, "%s\n", handler_ctx.reason);
s_term(pool);
return 0;
}
 


はい。お粗末さまでした。

.

2008年5月19日月曜日

[libserf] libserfを調べてみた

css変換部からHTTPリクエストを発行したいので、Javaで言う、
httpclientのようなライブラリを探してみた。
有名どころとしては、libneonとlibserfらしい。
libneonはGPL。libserfはApache Licence 2.0。なので、できればlibserfを使いたい。

ということでlibserfを調べてみた。

処理の大きな流れは以下のとおり。

  1. serf用のpoolを用意する。
  2. apr_sockaddr_info_get()を使ってapr_socketaddr_tを取得する。
  3. serf_context_create()を使ってserf_context_tを生成。
  4. serf_connection_create()でserf_connection_tを生成。
  5. serf_connection_request_create()でserf_request_tを生成。
  6. serf_context_run()でループする。
  7. 終了を何かしらの方法で検知したら、ループを終了する。
  8. serf_connection_close()でコネクションを終了。
  9. serf用poolの解放。

このほかに、
  1. コネクション確立時にコールされるコールバック
  2. コネクション解放時にコールされるコールバック
  3. 実際に投げるリクエストを生成するためのコールバック
  4. レスポンスをacceptしたときにコールされるコールバック
  5. レスポンスの内容を処理するコールバック
を用意する必要がある。

コネクション確立時のコールバック関数

typedef serf_bucket_t * (*serf_connection_setup_t)(apr_socket_t *skt,
void *setup_baton,
apr_pool_t *pool);
 

と定義されているので、このインタフェースを満たす巻数を用意する。
やることは、コネクション確立時にapr_socket_tをserf用socketにwrapすること。
setup_batonはserf_connection_create()時にsetup_batonに渡されたものがわたってくる。

コネクション解放時にコールされるコールバック

typedef void (*serf_connection_closed_t)(serf_connection_t *conn,
void *closed_baton,
apr_status_t why,
apr_pool_t *pool);
 

と定義されている。
目的はコネクション切断を通知するもの。
切断時のステータスがwhyに入ってくる。
コネクション切断時にエラーが起きているなら、ここで何か処理をすることができる。

実際に投げるリクエストを生成するためのコールバック

typedef apr_status_t (*serf_request_setup_t)(serf_request_t *request,
void *setup_baton,
serf_bucket_t **req_bkt,
serf_response_acceptor_t *acceptor,
void **acceptor_baton,
serf_response_handler_t *handler,
void **handler_baton,
apr_pool_t *pool);
 

と定義されている。
1リクエストを生成し、設定することが目的。生成したリクエストはreq_bktに設定してやる。
また、レスポンスをacceptしたときのコールバック、レスポンスを処理するコールバックも
本関数で設定する。レスポンスをacceptしたときのコールバックは、acceptorに、
レスポンスを処理するコールバックは、handlerにそれぞれセットする。
本処理に渡したいものがあれば、serf_connection_request_create()の第三引数として渡して
あげれば、本関数のsetup_batonパラメータにセットされてわたってくる。

レスポンスをacceptしたときにコールされるコールバック

typedef serf_bucket_t * (*serf_response_acceptor_t)(serf_request_t *request,
serf_bucket_t *stream,
void *acceptor_baton,
apr_pool_t *pool);
 

目的は、パラメータのrequestに対応するレスポンス用serf_bucket_t領域の生成や確保。
acceptor_batonはserf_request_setup_tの方でセットしたものが渡される。
streamをwrapしてやったりする(serf_bucket_barrier_create()でできる)。


レスポンスの内容を処理するコールバック

typedef apr_status_t (*serf_response_handler_t)(serf_request_t *request,
serf_bucket_t *response,
void *handler_baton,
apr_pool_t *pool);
 

と定義されている。
レスポンスデータが届いたときに行うべき処理をここで書く。
handler_batonはserf_request_setup_tの中で設定した値がわたってくる。



ということで大体わかった。
次に実際にコードを書いてみるつもり。

.

2008年5月18日日曜日

[mod_chxj] CSS変換部考え中

@importルールやlinkタグがあった場合、cssファイルを取りにいかなきゃならない
のを忘れていた。

modules-devを見ていたら、libserfというものを見つけた。


a high-performance asynchronous HTTP client library
 

だそうで。
svnのra_serfで使われている??

ということでlibserfを調査してみることにした。

.

[携帯] TypePadの絵文字アイコン画像と、携帯コンテンツ表示モジュールをフリー(自由)ライセンスで公開

>TypePadの絵文字アイコン画像と、携帯コンテンツ表示モジュールをフリー(自由)ライセンスで公開
とのこと。
をを!

これを使えばPCサイトにも絵文字が表示できるっすね。
ちなみにクリエイティブ・コモンズ・ライセンスだそうで。
.

[mod_chxj] CSS変換部データ構造考え中

個人的メモ。

CSS変換部のデータ構造は


/**
* CSS property.
*/
typedef struct __css_property_t {
struct __css_property_t *next;
struct __css_property_t **ref;
char *name;
char *value;
} css_property_t;


/**
* CSS selector.
*/
typedef struct __css_selector_t {
struct __css_selector_t *next;
struct __css_selector_t **ref;
/* has tag or/and class or/and id */
char *name;
css_property_t *head;
css_property_t *tail;
} css_selector_t;

/**
* CSS stylesheet.
* Manager of css_selector_t.
*/
typedef struct __css_stylesheet_t {
css_selector_t *head;
css_selector_t *tail;
} css_stylesheet_t;

/**
* CSS current_stylesheet.
*/
typedef struct __css_current_stylesheet_t {
struct __css_property_t *head;
struct __css_property_t *tail;
struct __css_current_stylesheet_t *next;
struct __css_current_stylesheet_t **ref;
} css_current_stylesheet_t;

/**
* CSS current_stylesheet_stack_t.
*/
typedef struct __css_current_stylesheet_stack_t {
css_current_stylesheet_t *head;
css_current_stylesheet_t *tail;
} css_current_stylesheet_stack_t;

 


な感じかな。
足りないところがありそう。
ちなみに**refは馬鹿の一つ覚えの例。

.