设为首页 收藏本站
查看: 535|回复: 0

[经验分享] Intro to the Apache table API

[复制链接]

尚未签到

发表于 2015-7-31 08:06:06 | 显示全部楼层 |阅读模式
  一篇介绍Apache table API的文章,非常不错:)
  
  Apache table API

Copyright 2002 Thomas Eibner  Tables in Apache works almost like hashes in Perl. The main difference between the two is that you can have two of the same keys in the Apache table API. The table lookups are done in a case-insensitive manner.
  This reference is structured as most of the functions are grouped and tries to make examples that work for both Apache 1.3.x and 2.0.x.
  The implementation of tables in the Apache API is made with the array API that Apache makes use of. A table is really just a struct that contains an array header. The array consists of table_entry structs.

typedef struct {

char *key;

char *val;

} table_entry;


  Because of the chosen hash algorithm, lookups will take longer as more key/value pairs are added to the table. This is because of the fact that for every lookup the algorithm needs to go through each of the array elements until it finds a matching key or it runs out of elements to check against. In general though, the speed is more than sufficient for the applications where these tables are used; namely header-parsing.
  This initial description is valid for both 1.3 and 2.0 versions of Apache. The following descriptions of each of the functions will contain examples for both 1.3 and 2.0 module writers.

Creating a Table
  To create a table we use the ap_make_table and apr_make_table functions for Apache 1.3 and 2.0 respectively.

table * ap_make_table(pool *p, int nelts);              /* 1.3 */

apr_table_t * apr_make_table(apr_pool_t *p, int nelts); /* 2.0 */


  Arguments to the functions are the same; a pool to allocate the memory for the table from and the number of elements it will allocate for initially. If the number of elements is set to zero it will defer allocating memory until you use the table the first time. All the table functions will automatically allocate more memory as needed.

/* 1.3 example */

table *my_table;

my_table = ap_make_table(r->pool, 10);

/* 2.0 example */

apr_table_t *my_table;

my_table = apr_table_make(r->pool, 10);


  In the above example we declare a new table and initialize the table with ap_make_table and allocate memory for 10 initial key/value pairs.

Setting a key/value pair in a table
  Setting a key/value pair in a table is done with the ap_table_set and apr_table_set functions. The function expects a pointer to a table, the key, and the value of the entry. Internally the keys and values are copied using ap_pstrdup. If one or more key/value pairs already exists in the table the first one will be overwritten and the rest deleted.

void ap_table_set(table *t, const char *key, const char *val);         /* 1.3 */

void ap_table_setn(table *t, const char *key, const char *val);        /* 1.3 */

void apr_table_set(apr_table_t *t, const char *key, const char *val);  /* 2.0 */

void apr_table_setn(apr_table_t *t, const char *key, const char *val); /* 2.0 */


  If you choose to use the setn version of the function you will have to ensure that the value part of the arguments passed with persist for the duration of which the table is to be used, since it wont be copied internally.

/* 1.3 example */

ap_table_setn(my_table, "MyKey", "MyValue");

ap_table_setn(my_table, "MyKey", "MyOtherValue");

/* 2.0 example */

apr_table_setn(my_table, "MyKey", "MyValue");

apr_table_setn(my_table, "MyKey", "MyOtherValue");


  In the above example the table my_table will end up having one element where the key is "MyKey" and the value is "MyOtherValue" since the first key/value pair was overwritten by the second set with the same key.

Adding a key/value pair to a table
  Instead of setting a key/value pair and maybe overwriting existing key/value pairs with the same key you can use ap_table_add and apr_table_add variants. These functions essentially adds the key/value pair at the end of the array of which the table is made.

void ap_table_add(table *t, const char *key, const char *val);         /* 1.3 */

void ap_table_addn(table *t, const char *key, const char *val);        /* 1.3 */

void apr_table_add(apr_table_t *t, const char *key, const char *val);  /* 2.0 */

void apr_table_addn(apr_table_t *t, const char *key, const char *val); /* 2.0 */


  Like with the set functions there is a addn variant for adding elements without doing the internal copying and the same cautions apply here.

/* 1.3 example */

ap_table_addn(my_table, "MyKey", "MyValue");

/* 2.0 example */

apr_table_addn(my_table, "MyKey", "MyValue");


  With this example we will now have two key/value pairs in the my_table table with the same key.

Merging values in a table
  If you want to append something to the value of an already existing key/value pair you could go through the hassle of having to retrieve the current value, then merge the two, and finally set the value in the table again. Instead of going through this we can use the ap_table_merge and apr_table_merge functions.

void ap_table_merge(table *t, const char *key, const char *val);         /* 1.3 */

void ap_table_mergen(table *t, const char *key, const char *val);        /* 1.3 */

void apr_table_merge(apr_table_t *t, const char *key, const char *val);  /* 2.0 */

void apr_table_mergen(apr_table_t *t, const char *key, const char *val); /* 2.0 */


  The function merges the two values by appending a comma and a space (", ") after the first value and then append the contents of the passed value afterwards. The real advantage of using these functions are that if the key/value pair is not previously set in the table these functions will do this internally.

/* 1.3 example */

ap_table_mergen(my_table, "NewKey", "NewValue1");

ap_table_mergen(my_table, "NewKey", "NewValue2");

/* 2.0 example */

apr_table_mergen(my_table, "NewKey", "NewValue1");

apr_table_mergen(my_table, "NewKey", "NewValue2");


  In our example we add a key/value pair that does not previously exist in the table. We then call the merge function once more and the value part of the key/value pair is appended to the key/value pair in the table. In the end the value field referenced by the "NewKey" key contains "NewValue1, NewValue2". Remember seeing fields ordered like this? Exactly, in header fields of an HTTP request.

Retrieving values from a table
  Getting the value of an entry in a table is done with ap_table_get and apr_table_get. The first key will retrieved in case there are more than one entry in the table for the key. If you intend on modifying the retrieved value you have to make sure to copy it with ap_pstrdup before changing it.

const char * ap_table_get(const table *t, const char *key);        /* 1.3 */

const char * apr_table_get(const apr_table_t *t, const char *key); /* 2.0 */


  If you need to get all the values for a single key you need to use the ap_table_do function.

/* 1.3 example */

char *host = ap_pstrdup(r->pool, ap_table_get(r->headers_in, "Host"));

/* 2.0 example */

char *host = apr_pstrdup(r->pool, apr_table_get(r->headers_in, "Host"));


  In this example we extract the "Host:" header value from the incoming headers table. We copy the string over in the host variable so we are free to manipulate it afterwards since we are no longer working on a const char type.

Removing entries from a table
  To delete every key/value pair associated with a key in the table you should use the ap_table_unset and apr_table_unset functions.

void ap_table_unset(table *t, const char *key);        /* 1.3 */

void apr_table_unset(apr_table_t *t, const char *key); /* 2.0 */


  As a useful example we can make an anonymizing module that deletes the User-Agent and Cookie field your browser sends. It will delete the headers Via, Cookie, User-Agent, and X-Forwarded-For.

/* 1.3 Example */

#include "httpd.h"

#include "http_config.h"

#include "http_core.h"



module MODULE_VAR_EXPORT anon_proxy_module;



static int anonymize_request(request_rec *r) {

if (r->proxyreq) {

ap_table_unset(r->headers_in, "Via");

ap_table_unset(r->headers_in, "Cookie");

ap_table_unset(r->headers_in, "User-Agent");

ap_table_unset(r->headers_in, "X-Forwarded-For");

return OK;

}

return DECLINED;

}



module MODULE_VAR_EXPORT anon_proxy_module = {

STANDARD_MODULE_STUFF,

NULL,                       /* initializer */

NULL,                       /* dir config creater */

NULL,                       /* dir merger --- default is to override */

NULL,                       /* server config */

NULL,                       /* merge server configs */

NULL,                       /* command table */

NULL,                       /* handlers */

NULL,                       /* filename translation */

NULL,                       /* check_user_id */

NULL,                       /* check auth */

NULL,                       /* check access */

NULL,                       /* type_checker */

anonymize_request,          /* fixups */

NULL,                       /* logger */

NULL,                       /* header parser */

NULL,                       /* child_init */

NULL,                       /* child_exit */

NULL                        /* post read-request */

};


/* 2.0 Example */

#include "httpd.h"

#include "http_config.h"

#include "http_core.h"



module AP_MODULE_DECLARE_DATA anon_proxy_module;



static int anonymize_request(request_rec *r) {

if (r->proxyreq) {

apr_table_unset(r->headers_in, "Via");

apr_table_unset(r->headers_in, "Cookie");

apr_table_unset(r->headers_in, "User-Agent");

apr_table_unset(r->headers_in, "X-Forwarded-For");

return OK;

}

return DECLINED;

}



static void anonproxy_register_hooks(apr_pool_t *p) {

ap_hook_fixups(anonymize_request, NULL, NULL, APR_HOOK_MIDDLE);

}



module AP_MODULE_DECLARE_DATA anon_proxy_module = {

STANDARD20_MODULE_STUFF,

NULL,                     /* per-directory config creator */

NULL,                     /* dir config merger */

NULL,                     /* server config creator */

NULL,                     /* server config merger */

NULL,                     /* command table */

anonproxy_register_hooks, /* set up other request processing hooks */

};


  The module is pretty straight forward. We start by including the necessary header files and then declare our module name to be anon_proxy_module. We then have a function that if it is a proxy request will unset the fields we specified and return OK. In the last part of the module we register our function for the fixup phase which is right before the proxy sets in and requests the resource the user requested.

Clearing a Table
  To remove all key/value pairs from the table you use the ap_clear_table and apr_table_clear functions.

void ap_clear_table(table *t);        /* 1.3 */

void apr_table_clear(apr_table_t *t); /* 2.0 */


  The only argument this function takes is the table in which it sets the number of elements in the table to zero.

Copying a Table
  Sometimes it is necessary to copy the contents of a table into another table for manipulation and the ap_copy_table and apr_table_copy functions are made just for that.

table * ap_copy_table(pool *p, const table *t);                    /* 1.3 */

apr_table_t * apr_table_copy(apr_pool_t *p, const apr_table_t *t); /* 2.0 */


  If any data existed in the table from previous operations these will be wiped from the table.
  mod_proxy uses ap_copy_table to make a copy of the incoming headers to manipulate before requesting the proxy-resource. This makes it possible for the rest of the request phases to still use the original data.

Overlaying Tables
  If you need to overlay two tables you can use the ap_overlay_tables or apr_table_overlay.

table * ap_overlay_tables(pool *p, const table *overlay, const table *base);                         /* 1.3 */

apr_table_t * apr_table_overlay(apr_pool_t *p, const apr_table_t *overlay, const apr_table_t *base); /* 2.0 */


  The function will return a table where all of the key/value pairs from the overlay table is transfered to and all of the key/value pairs from the base table where the key doesn't exist in the overlay table will be transfered too.

Overlapping Tables
  The functionality of the overlapping functions ap_overlap_tables and apr_table_overlap is determined by the flag that is passed to it. There are two different flags for each of the 1.3 and 2.0 versions. For 1.3 the flags are AP_OVERLAP_TABLES_SET and AP_OVERLAP_TABLES_MERGE and for 2.0 the flags are APR_OVERLAP_TABLES_SET and APR_OVERLAP_TABLES_MERGE.

void ap_overlap_tables(table *a, const table *b, unsigned flags);             /* 1.3 */

void apr_table_overlap(apr_table_t *a, const apr_table_t *b, unsigned flags); /* 2.0 */


  If you pass the SET flag to the function it behaves like ap_overlay_tables and apr_table_overlay. If you pass the MERGE flag to the function it will merge the overlapping key/value pairs from table b into table a that is passed to it. For the merging it uses the before descriped merging functions.

Iterating over key/value pairs from a table
  To iterate over key/value pairs in a table we use the ap_table_do, apr_table_do, or apr_table_vdo functions. The function takes a pointer to a function which will be executed for each key/value pair that is to be iterated over.

int function(void *rec, const char *key, const char *value);


  The function should follow the above prototype and take key and value as second and third argument. The first argument is a pointer to a structure or variable you passed to the actual ap_table_do function. This is useful if you want to pass a configuration structure to the iteration function, just remember to cast it to the right type.

void ap_table_do(int (*comp) (void *, const char *, const char *), void *rec, const table *t,...);             /* 1.3 */

void apr_table_do(int (*comp)(void *, const char *, const char *), void *rec, const apr_table_t *t, ...);      /* 2.0 */

void apr_table_vdo(int (*comp)(void *, const char *, const char *), void *rec, const apr_table_t *t, va_list); /* 2.0 */


  The iterating function takes as the first argument the function you want called for each iteration that we described above. The second argument is the pointer to a structure or a variable you want passed to the iterating function. Third argument is the table you want processed. The last argument is a NULL-terminated list of values you want processed from the table or NULL. In the case that you pass NULL to the function the iterating function will be called for all key/value pairs.
  Below is an example use of ap_table_do to dump information about the request to the browser.

/* 1.3 Example */

#include "httpd.h"

#include "http_config.h"

#include "http_main.h"

#include "http_protocol.h"

#include "http_core.h"



module MODULE_VAR_EXPORT dumpreq_module;



int iterate_func(void *req, const char *key, const char *value) {

int stat;

char *line;

request_rec *r = (request_rec *)req;

if (key == NULL || value == NULL || value[0] == '"0')

return 1;



line = ap_psprintf(r->pool, "%s => %s"n", key, value);

stat = ap_rputs(line, r);



return 1;

}



static int dump_request(request_rec *r) {

r->content_type = "text/plain";

ap_send_http_header(r);

if (r->header_only)

return OK;



ap_table_do(iterate_func, r, r->headers_in, NULL);



return OK;

}



static const handler_rec dumpreq_handlers[] = {

{"dumpreq-handler", dump_request},

{NULL}

};



module MODULE_VAR_EXPORT dumpreq_module = {

STANDARD_MODULE_STUFF,

NULL,                       /* initializer */

NULL,                       /* dir config creater */

NULL,                       /* dir merger --- default is to override */

NULL,                       /* server config */

NULL,                       /* merge server configs */

NULL,                       /* command table */

dumpreq_handlers,           /* handlers */

NULL,                       /* filename translation */

NULL,                       /* check_user_id */

NULL,                       /* check auth */

NULL,                       /* check access */

NULL,                       /* type_checker */

NULL,                       /* fixups */

NULL,                       /* logger */

NULL,                       /* header parser */

NULL,                       /* child_init */

NULL,                       /* child_exit */

NULL                        /* post read-request */

};


/* 2.0 Example */

#include "httpd.h"

#include "http_config.h"

#include "http_main.h"

#include "http_protocol.h"

#include "http_core.h"



module AP_MODULE_DECLARE_DATA dumpreq_module;



int iterate_func(void *req, const char *key, const char *value) {

int stat;

char *line;

request_rec *r = (request_rec *)req;

if (key == NULL || value == NULL || value[0] == '"0')

return 1;



line = apr_psprintf(r->pool, "%s => %s"n", key, value);

stat = ap_rputs(line, r);



return 1;

}



static int dump_request(request_rec *r) {



if (strcmp(r->handler, "dumpreq-handler"))

return DECLINED;



ap_set_content_type(r, "text/plain");



if (r->header_only)

return OK;



apr_table_do(iterate_func, r, r->headers_in, NULL);



return OK;

}



static void dumpreq_register_hooks(apr_pool_t *p) {

ap_hook_handler(dump_request, NULL, NULL, APR_HOOK_MIDDLE);

}



module AP_MODULE_DECLARE_DATA dumpreq_module = {

STANDARD20_MODULE_STUFF,

NULL,                     /* per-directory config creator */

NULL,                     /* dir config merger */

NULL,                     /* server config creator */

NULL,                     /* server config merger */

NULL,                     /* command table */

dumpreq_register_hooks, /* set up other request processing hooks */

};


Other table functions
  There is two more functions that we are going to show here, these are actually simple defines that will tell you wheter the table is empty and the number of elements in the table.

#define ap_table_elts(t) ((array_header *)(t))                                             /* 1.3 */

#define ap_is_empty_table(t) (((t) == NULL)||(((array_header *)(t))->nelts == 0))          /* 1.3 */

#define apr_table_elts(t) ((const apr_array_header_t *)(t))                                /* 2.0 */

#define apr_is_empty_table(t) (((t) == NULL) || (((apr_array_header_t *)(t))->nelts == 0)) /* 2.0 */


  The use of these functions should be pretty straightforward and they both only take table as arguments.

More info
  The Table functions for 1.3 are declared in "src/include/ap_alloc.h" and they are implemented in "src/main/alloc.c". For 2.0 you should look in "srclib/apr/include/apr_tables.h".

Copyright 2002 Thomas Eibner

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-92444-1-1.html 上篇帖子: Apache Storm技术实战之1 下篇帖子: Apache Spark源码走读之15
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表