catalog

  1. . 引言
  2. . windows下开发apache模块
  3. . mod进阶: 接收客户端数据的 echo 模块
  4. . mod进阶: 可配置的 echo 模块
  5. . mod进阶: 过滤器

0. 引言

Apache httpd 从 2.0 之后,已经不仅仅局限于一个 http 的服务器,更是一个完善而强大,灵活而健壮,且容易扩展的开发平台。开发人员通过定制 Apache 模块,可以几乎无限制的扩展 Apache httpd,使其更好的与实际应用场景匹配,而又无需考虑底层的网络传输细节。这样既可以提高开发效率,节省开发成本,又能充分利用 Apache 本身的健壮性及可靠性

0x1: Apache httpd 开发平台简介

Apache httpd 自 2.0 之后,针对 1.0 做了大量的改进,包括对自身内核的改造,扩展机制的改进,APR(Apache Portable Runtime) 的剥离(使得 Apache 成为一个真正意义的跨平台服务器)。Apache 2.0 成为一个很容易扩展的开发平台

Apache 中包含了大量的扩展模块(module),如 mod_cgi 用以处理 cgi 脚本、mod_perl 用以处理 perl 脚本,将 perl 的能力与 Apache httpd 结合起来等等。用户可以通过一定的开发标准(接口规范)来开发符合自己业务场景的模块,并动态的加载到 Apache 中,Apache 会根据配置文件中的规则来定位,调用模块,完成客户端请求,简单来说,apache的扩展编写可以分类一下几类

  1. . apache httpd 模块: 编写模块往往要比编写过滤器要复杂一点,可以看成是一种原始的apache扩展编写方式,但是灵活性更高
  2. . 输出过滤器: apache在模块的基础上进行了API封装,使得过滤器的代码编写变得更简单
  3. . 输入过滤器

0x2: Apache httpd 模块机制

Apache httpd 由一个内核和大量的模块组成,包括用以加载其他模块的功能单元自身也是一个模块,模块是Apache扩展的基础机制(理论上模块可以实现任何功能)。一般而言,一个 HTTP 服务器的工作序列是这样的

  1. . 接受客户端请求
  2. 可能是请求一个部署在 HTTP 服务器程序可访问的文件,读取该文件作为响应返回,我们在浏览器的地址栏中输入类似这样的 URLhttp://host/index.html,浏览器将会尝试与 host 指定的 HTTP 服务器的 80 端口建立连接,如果成功,则发送 HTTP 请求,获取 index.html 页面。如果成功,则在浏览器中解析该 HTML 文件
  3. 这种工作方式在静态页面的场景下没有任何问题。但是实际应用往往会与数据库交互,动态生成页面内容。如服务端较为流行的 cgi/php 脚本等。这就需要更高级,更灵活的内容生成器做支持
  4.  
  5. . 预处理
  6. ) 权限校验
  7. ) HTTP 头信息识别等
  8.  
  9. . 内容生成
  10. 通过与操作系统其他资源交互 ( 如文件读写,数据库访问等 ) 来完成动态内容的生成
  11.  
  12. . 其他善后操作等
  13. 进行日志记录,资源释放等操作

通常注册模块以处理配置文件中的特殊文件类型或其它此类标准

  1. AddHandler cgi-script .cgi
  2. //php、python都是通过配置指定特定后缀扩展名的处理模块

Apache 为每个请求调用所有处理程序,因此每个处理程序应该迅速决定请求是否是冲着它来的。因此,大多数头文件都从类似下面的语句开始

  1. if (!req->handler || strcmp(req->handler, "target-module"))
  2. return DECLINED;

0x3: Apache 2.0之后的过滤器机制

在大多数时候,apache扩展开发者并不需要很复杂的收包/发包逻辑,而仅仅需要对HTTP Header、Body进行检测,为了提高效率,apache开发者对模块代码进行了API封装
Apache 2.0 有专门的 API 用于开发模块,这些模块只需修改对用户响应的内容,或者只需修改用户的 HTTP 请求的详细信息。这些 API 分别被称为

  1. . 输出过滤器
  2. 输出过滤器最为常见,一个好的示例是标准 Apache 2.0 模块,它被用于计算返回给用户的内容的长度以便更新适当的头和日志项。另一个示例是用于对出站内容进行自动拼写检查的模块
  3.  
  4. . 输入过滤器
  5. 典型的WEB WAF

从严格意义上来说,基于WEB SERVER的Mod(扩展模块)WAF是一种HTTP全生命周期的检测/过滤/拦截/记录机制,它需要综合利用"输出过滤器"、"输入过滤器"

Relevant Link:

  1. http://www.ibm.com/developerworks/cn/opensource/os-cn-apachehttpd/

1. windows下开发apache模块

0x1: windows下安装apache

  1. http://apache.dataguru.cn//httpd/binaries/win32/
  2. //一定要custom全部安装,否则就不会有include和lib目录

0x2: 安装Perl

将要使用的apx包要用到perl解析编译,所以,需先安装perl

  1. http://www.activestate.com/activeperl

0x3: 安装apxs

  1. . http://www.apachelounge.com/download/apxs_win32.zip
  2. . 解压到: D:\apxs
  3. . pushd D:\apxs
  4. . perl Configure.pl --with-apache2=D:\wamp\bin\apache\apache2.4.9 --with-apache-prog=httpd.exe
  5. /*
  6. apxs.bat has been created under D:\wamp\bin\apache\APACHE~1.9\bin.
  7. apr-1-config.pl.bat has been created under D:\wamp\bin\apache\APACHE~1.9\bin.
  8. apu-1-config.pl.bat has been created under D:\wamp\bin\apache\APACHE~1.9\bin.
  9. */
  10.  
  11. . pushd D:\wamp\bin\apache\apache2.4.9\bin
  12. . apxs(出现下列提示则说明安装成功)
  13. /*
  14. Use of assignment to $[ is deprecated at apxs.bat line 120.
  15. Usage: apxs -g [-S <var>=<val>] -n <modname>
  16. apxs -q [-v] [-S <var>=<val>] <query> ...
  17. apxs -c [-S <var>=<val>] [-o <dsofile>] [-D <name>[=<value>]]
  18. [-I <incdir>] [-L <libdir>] [-l <libname>] [-Wc,<flags>]
  19. [-Wl,<flags>] [-p] <files> ...
  20. apxs -i [-S <var>=<val>] [-a] [-A] [-n <modname>] <dsofile> ...
  21. apxs -e [-S <var>=<val>] [-a] [-A] [-n <modname>] <dsofile> ...
  22. */
  23.  
  24. . 配置apxs编译环境
  25. D:\wamp\bin\apache\apache2.4.9\build\config_vars.mk
  26. CC = D:\CODEBL~\MinGW\bin\gcc.exe 改为: CC = cl.exe
  27. LD = D:\CODEBL~\MinGW\bin\g++.exe 改为: LD = link.exe
  28. CPP = gcc -E 改为: CPP =
  29. LDFLAGS = kernel32.lib /nologo /subsystem:windows /dll /machine:I386 /libpath:"D:\wamp\bin\apache\APACHE~1.9\lib" 改为: LDFLAGS = kernel32.lib /nologo /subsystem:windows /dll /machine:X64 /libpath:"D:\wamp\bin\apache\APACHE~1.9\lib"
  30.  
  31. . 使用apxs生成mod框架模版
  32. Visual Studio 命令提示()
  33. pushd D:\安全部工作\服务器waf模块mod研究
  34. D:\wamp\bin\apache\apache2.4.9\bin\apxs -g -n helloworld
  35. /*
  36. Use of assignment to $[ is deprecated at D:\wamp\bin\apache\apache2.4.9\bin\apxs.bat line 120.
  37. Creating [DIR] helloworld
  38. Creating [FILE] helloworld/Makefile
  39. Creating [FILE] helloworld/mod_helloworld.c
  40. Creating [FILE] helloworld/.deps
  41. */
  42.  
  43. . 进入helloworld目录,编辑mod_helloworld.c(这就是我们要开发的内容)
  44. cd helloworld
  45. D:\wamp\bin\apache\apache2.4.9\bin\apxs -c -i -a mod_helloworld.c libapr-.lib libaprutil-.lib libapriconv-.lib libhttpd.lib
  46.  
  47. . mod_helloworld.so拷贝到Apache2.\modules
  48. . 打开conf文件夹下的httpd.conf文件
  49. /*
  50. LoadModule helloworld_module "D:/wamp/bin/apache/APACHE~1.9/modules/mod_helloworld.so"
  51. <Location /helloworld>
  52. SetHandler helloworld
  53. </Location>
  54. */
  55.  
  56. . 重启apache
  57. . http://localhost/helloworld

aaarticlea/png;base64,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" alt="" />

0x4: mod通用模板代码框架

  1. #include "httpd.h"
  2. #include "http_config.h"
  3. #include "http_protocol.h"
  4. #include "ap_config.h"
  5.  
  6. /*
  7. The sample content handler
  8. 首先需要一个实际处理客户端请求的函数 (handler),命名方式一般为”模块名 _handler”,接收一个 request_rec 类型的指针,并返回一个 int 类型的状态值
  9. request_rec 指针中包括所有的客户端连接信息及 Apache 内部的指针,如连接信息表,内存池等,这个结构类似于 J2EE 开发中 servlet 的 HttpRequest 对象及 HttpResponse 对象。通过 request_rec,我们可以读取客户端请求数据 / 写入响应数据,获取请求中的信息 ( 如客户端浏览器类型,编码方式等 )
  10. */
  11. static int helloworld_handler(request_rec *r)
  12. {
  13. if (strcmp(r->handler, "helloworld")) {
  14. return DECLINED;
  15. }
  16. r->content_type = "text/html";
  17.  
  18. if (!r->header_only)
  19. ap_rputs("The sample page from mod_helloworld.c\n", r);
  20. return OK;
  21. }
  22.  
  23. //注册函数,一般命名为”模块名 _register_hooks”,传入参数为 Apache 的内存池指针。这个函数用于通知 Apache 在何时,以何种方式注册响应函数 (handler)
  24. static void helloworld_register_hooks(apr_pool_t *p)
  25. {
  26. ap_hook_handler(helloworld_handler, NULL, NULL, APR_HOOK_MIDDLE);
  27. }
  28.  
  29. /*
  30. Dispatch list for API hooks
  31. 模块的定义,Apache 模块加载器通过这个结构体中的定义来在适当的时刻调用适当的函数以处理响应。应该注意的是,第一个成员默认填写为 STANDARD20_MODULE_STUFF,最后一个成员为注册函数
  32. */
  33. module AP_MODULE_DECLARE_DATA helloworld_module = {
  34. STANDARD20_MODULE_STUFF,
  35. NULL, /* create per-dir config structures */
  36. NULL, /* merge per-dir config structures */
  37. NULL, /* create per-server config structures */
  38. NULL, /* merge per-server config structures */
  39. NULL, /* table of config file commands */
  40. helloworld_register_hooks /* register hooks */
  41. };

0x5: apache mod核心数据结构: request_rec

The request_rec request record is the heart and soul of the Apache API. It contains everything you could ever want to know about the current request and then some.
\apache2.4.9\include\httpd.h

  1. /**
  2. * @brief A structure that represents the current request
  3. */
  4. struct request_rec
  5. {
  6. /**
  7. The pool associated with the request
  8. This is a resource pool that is valid for the lifetime of the request. Your request-time handlers should allocate memory from this pool.
  9. */
  10. apr_pool_t *pool;
  11.  
  12. /**
  13. The connection to the client
  14. This is a pointer to the connection record for the current request,
  15. from which you can derive information about the local and remote host addresses, as well as the username used during authentication
  16. */
  17. conn_rec *connection;
  18.  
  19. /**
  20. The virtual host for this request
  21. This is a pointer to a server record server_rec structure, from which you can gather information about the current server.
  22. */
  23. server_rec *server;
  24.  
  25. /*
  26. Under various circumstances, including subrequests and internal redirects,
  27. Apache will generate one or more subrequests that are identical in all respects to an ordinary request.
  28. When this happens, these fields are used to chain the subrequests into a linked list.
  29. 1. The next field points to the more recent request (or NULL, if there is none),
  30. 2. and the prev field points to the immediate ancestor of the request.
  31. 3. main points back to the top-level request.
  32. */
  33. /** Pointer to the redirected request if this is an external redirect */
  34. request_rec *next;
  35. /** Pointer to the previous request if this is an internal redirect */
  36. request_rec *prev;
  37. /** Pointer to the main request if this is a sub-request
  38. * (see http_request.h) */
  39. request_rec *main;
  40.  
  41. /* Info about the request itself... we begin with stuff that only
  42. * protocol.c should ever touch...
  43. */
  44. /** First line of request This contains the first line of the request, for logging purposes. */
  45. char *the_request;
  46.  
  47. /** HTTP/0.9, "simple" request (e.g. GET /foo\n w/no headers) */
  48. int assbackwards;
  49.  
  50. /**
  51. A proxy request (calculated during post_read_request/translate_name) possible values PROXYREQ_NONE, PROXYREQ_PROXY, PROXYREQ_REVERSE, PROXYREQ_RESPONSE
  52. If the current request is a proxy request, then this field will be set to a true (nonzero) value. Note that mod_proxy or mod_perl must be configured with the server for automatic proxy request detection. You can also set it yourself in order to activate Apache's proxy mechanism
  53. */
  54. int proxyreq;
  55.  
  56. /**
  57. HEAD request, as opposed to GET
  58. This field will be true if the remote client made a head-only request (i.e., HEAD). You should not change the value of this field.
  59. */
  60. int header_only;
  61.  
  62. /** Protocol version number of protocol; 1.1 = 1001 */
  63. int proto_num;
  64. /**
  65. Protocol string, as given to us, or HTTP/0.9
  66. This field contains the name and version number of the protocol requested by the browser, for example HTTP/1.0.
  67. */
  68. char *protocol;
  69.  
  70. /**
  71. Host, as set by full URI or Host:
  72. This contains the name of the host requested by the client, either within the URI (during proxy requests) or in the Host header.
  73. The value of this field may not correspond to the canonical name of your server or the current virtual host but can be any of its DNS aliases.
  74. For this reason, it is better to use the ap_get_server_name() API function call described under "Processing Requests."
  75. hostname访问可能直接DNS域名访问
  76. */
  77. const char *hostname;
  78.  
  79. /**
  80. Time when the request started
  81. This is the time that the request started as a C time_t structure.
  82. */
  83. apr_time_t request_time;
  84.  
  85. /**
  86. Status line, if set by script
  87. This field holds the full text of the status line returned from Apache to the remote browser, for example 200 OK.
  88. Ordinarily you will not want to change this directly but will allow Apache to set it based on the return value from your handler.
  89. However, you can change it directly in the rare instance that you want your handler to lie to Apache about its intentions
  90. (e.g., tell Apache that the handler processed the transaction OK, but send an error message to the browser).
  91. */
  92. const char *status_line;
  93. /** Status line */
  94. int status;
  95.  
  96. /* Request method, two ways; also, protocol, etc.. Outside of protocol.c,
  97. * look, but don't touch.
  98. */
  99.  
  100. /** M_GET, M_POST, etc. */
  101. int method_number;
  102. /** Request method (eg. GET, HEAD, POST, etc.) */
  103. const char *method;
  104.  
  105. /**
  106. * 'allowed' is a bitvector of the allowed methods.
  107. *
  108. * A handler must ensure that the request method is one that
  109. * it is capable of handling. Generally modules should DECLINE
  110. * any request methods they do not handle. Prior to aborting the
  111. * handler like this the handler should set r->allowed to the list
  112. * of methods that it is willing to handle. This bitvector is used
  113. * to construct the "Allow:" header required for OPTIONS requests,
  114. * and HTTP_METHOD_NOT_ALLOWED and HTTP_NOT_IMPLEMENTED status codes.
  115. *
  116. * Since the default_handler deals with OPTIONS, all modules can
  117. * usually decline to deal with OPTIONS. TRACE is always allowed,
  118. * modules don't need to set it explicitly.
  119. *
  120. * Since the default_handler will always handle a GET, a
  121. * module which does *not* implement GET should probably return
  122. * HTTP_METHOD_NOT_ALLOWED. Unfortunately this means that a Script GET
  123. * handler can't be installed by mod_actions.
  124. */
  125. apr_int64_t allowed;
  126. /** Array of extension methods */
  127. apr_array_header_t *allowed_xmethods;
  128. /** List of allowed methods */
  129. ap_method_list_t *allowed_methods;
  130.  
  131. /** byte count in stream is for body */
  132. apr_off_t sent_bodyct;
  133. /** body byte count, for easy access */
  134. apr_off_t bytes_sent;
  135. /** Last modified time of the requested resource */
  136. apr_time_t mtime;
  137.  
  138. /* HTTP/1.1 connection-level features */
  139.  
  140. /** The Range: header */
  141. const char *range;
  142. /** The "real" content length */
  143. apr_off_t clength;
  144. /** sending chunked transfer-coding */
  145. int chunked;
  146.  
  147. /** Method for reading the request body
  148. * (eg. REQUEST_CHUNKED_ERROR, REQUEST_NO_BODY,
  149. * REQUEST_CHUNKED_DECHUNK, etc...) */
  150. int read_body;
  151. /** reading chunked transfer-coding */
  152. int read_chunked;
  153. /** is client waiting for a 100 response? */
  154. unsigned expecting_100;
  155. /** The optional kept body of the request. */
  156. apr_bucket_brigade *kept_body;
  157. /** For ap_body_to_table(): parsed body */
  158. /* XXX: ap_body_to_table has been removed. Remove body_table too or
  159. * XXX: keep it to reintroduce ap_body_to_table without major bump? */
  160. apr_table_t *body_table;
  161. /** Remaining bytes left to read from the request body */
  162. apr_off_t remaining;
  163. /** Number of bytes that have been read from the request body */
  164. apr_off_t read_length;
  165.  
  166. /* MIME header environments, in and out. Also, an array containing
  167. * environment variables to be passed to subprocesses, so people can
  168. * write modules to add to that environment.
  169. *
  170. * The difference between headers_out and err_headers_out is that the
  171. * latter are printed even on error, and persist across internal redirects
  172. * (so the headers printed for ErrorDocument handlers will have them).
  173. *
  174. * The 'notes' apr_table_t is for notes from one module to another, with no
  175. * other set purpose in mind...
  176. */
  177.  
  178. /** MIME header environment from the request */
  179. apr_table_t *headers_in;
  180. /** MIME header environment for the response */
  181. apr_table_t *headers_out;
  182. /** MIME header environment for the response, printed even on errors and
  183. * persist across internal redirects */
  184. apr_table_t *err_headers_out;
  185. /** Array of environment variables to be used for sub processes */
  186. apr_table_t *subprocess_env;
  187. /** Notes from one module to another */
  188. apr_table_t *notes;
  189.  
  190. /* content_type, handler, content_encoding, and all content_languages
  191. * MUST be lowercased strings. They may be pointers to static strings;
  192. * they should not be modified in place.
  193. */
  194. /** The content-type for the current request */
  195. const char *content_type; /* Break these out --- we dispatch on 'em */
  196. /** The handler string that we use to call a handler function */
  197. const char *handler; /* What we *really* dispatch on */
  198.  
  199. /** How to encode the data */
  200. const char *content_encoding;
  201. /** Array of strings representing the content languages */
  202. apr_array_header_t *content_languages;
  203.  
  204. /** variant list validator (if negotiated) */
  205. char *vlist_validator;
  206.  
  207. /** If an authentication check was made, this gets set to the user name. */
  208. char *user;
  209. /** If an authentication check was made, this gets set to the auth type. */
  210. char *ap_auth_type;
  211.  
  212. /* What object is being requested (either directly, or via include
  213. * or content-negotiation mapping).
  214. */
  215.  
  216. /** The URI without any parsing performed */
  217. char *unparsed_uri;
  218. /** The path portion of the URI, or "/" if no path provided */
  219. char *uri;
  220. /** The filename on disk corresponding to this response */
  221. char *filename;
  222. /* XXX: What does this mean? Please define "canonicalize" -aaron */
  223. /** The true filename, we canonicalize r->filename if these don't match */
  224. char *canonical_filename;
  225. /** The PATH_INFO extracted from this request */
  226. char *path_info;
  227. /** The QUERY_ARGS extracted from this request */
  228. char *args;
  229.  
  230. /**
  231. * Flag for the handler to accept or reject path_info on
  232. * the current request. All modules should respect the
  233. * AP_REQ_ACCEPT_PATH_INFO and AP_REQ_REJECT_PATH_INFO
  234. * values, while AP_REQ_DEFAULT_PATH_INFO indicates they
  235. * may follow existing conventions. This is set to the
  236. * user's preference upon HOOK_VERY_FIRST of the fixups.
  237. */
  238. int used_path_info;
  239.  
  240. /** A flag to determine if the eos bucket has been sent yet */
  241. int eos_sent;
  242.  
  243. /* Various other config info which may change with .htaccess files
  244. * These are config vectors, with one void* pointer for each module
  245. * (the thing pointed to being the module's business).
  246. */
  247.  
  248. /** Options set in config files, etc. */
  249. struct ap_conf_vector_t *per_dir_config;
  250. /** Notes on *this* request */
  251. struct ap_conf_vector_t *request_config;
  252.  
  253. /** Optional request log level configuration. Will usually point
  254. * to a server or per_dir config, i.e. must be copied before
  255. * modifying */
  256. const struct ap_logconf *log;
  257.  
  258. /** Id to identify request in access and error log. Set when the first
  259. * error log entry for this request is generated.
  260. */
  261. const char *log_id;
  262.  
  263. /**
  264. * A linked list of the .htaccess configuration directives
  265. * accessed by this request.
  266. * N.B. always add to the head of the list, _never_ to the end.
  267. * that way, a sub request's list can (temporarily) point to a parent's list
  268. */
  269. const struct htaccess_result *htaccess;
  270.  
  271. /** A list of output filters to be used for this request */
  272. struct ap_filter_t *output_filters;
  273. /** A list of input filters to be used for this request */
  274. struct ap_filter_t *input_filters;
  275.  
  276. /** A list of protocol level output filters to be used for this
  277. * request */
  278. struct ap_filter_t *proto_output_filters;
  279. /** A list of protocol level input filters to be used for this
  280. * request */
  281. struct ap_filter_t *proto_input_filters;
  282.  
  283. /** This response can not be cached */
  284. int no_cache;
  285. /** There is no local copy of this response */
  286. int no_local_copy;
  287.  
  288. /** Mutex protect callbacks registered with ap_mpm_register_timed_callback
  289. * from being run before the original handler finishes running
  290. */
  291. apr_thread_mutex_t *invoke_mtx;
  292.  
  293. /** A struct containing the components of URI */
  294. apr_uri_t parsed_uri;
  295. /** finfo.protection (st_mode) set to zero if no such file */
  296. apr_finfo_t finfo;
  297.  
  298. /** remote address information from conn_rec, can be overridden if
  299. * necessary by a module.
  300. * This is the address that originated the request.
  301. */
  302. apr_sockaddr_t *useragent_addr;
  303. char *useragent_ip;
  304. };

Relevant Link:

  1. http://www.cnblogs.com/QRcode/p/3193397.html
  2. http://blog.csdn.net/hxsstar/article/details/19820029
  3. https://publib.boulder.ibm.com/iseries/v5r1/ic2924/info/rzaie/APR/structrequest__rec.html
  4. http://docstore.mik.ua/orelly/apache_mod/128.htm
  5. http://blog.csdn.net/wind_cludy/article/details/6557776
  6. http://docstore.mik.ua/orelly/apache_mod/128.htm#listing10_1

2. mod进阶: 接收客户端数据的 echo 模块

如果Apache模块只能产生内容,那么使用普通的HTML文件(即使用httpd默认的内容生成器)也可以完成。模块存在的意义在于,它可以轻松地处理客户端传递的数据,并将这些数据加工,然后响应客户端请求

  1. /*
  2. ** mod_helloworld.c -- Apache sample helloworld module
  3. ** [Autogenerated via ``apxs -n helloworld -g'']
  4. **
  5. ** To play with this sample module first compile it into a
  6. ** DSO file and install it into Apache's modules directory
  7. ** by running:
  8. **
  9. ** $ apxs -c -i mod_helloworld.c
  10. **
  11. ** Then activate it in Apache's httpd.conf file for instance
  12. ** for the URL /helloworld in as follows:
  13. **
  14. ** # httpd.conf
  15. ** LoadModule helloworld_module modules/mod_helloworld.so
  16. ** <Location /helloworld>
  17. ** SetHandler helloworld
  18. ** </Location>
  19. **
  20. ** Then after restarting Apache via
  21. **
  22. ** $ apachectl restart
  23. **
  24. ** you immediately can request the URL /helloworld and watch for the
  25. ** output of this module. This can be achieved for instance via:
  26. **
  27. ** $ lynx -mime_header http://localhost/helloworld
  28. **
  29. ** The output should be similar to the following one:
  30. **
  31. ** HTTP/1.1 200 OK
  32. ** Date: Tue, 31 Mar 1998 14:42:22 GMT
  33. ** Server: Apache/1.3.4 (Unix)
  34. ** Connection: close
  35. ** Content-Type: text/html
  36. **
  37. ** The sample page from mod_helloworld.c
  38. */
  39.  
  40. #include "httpd.h"
  41. #include "http_config.h"
  42. #include "http_protocol.h"
  43. #include "ap_config.h"
  44.  
  45. #define DFT_BUF_SIZE 1024
  46.  
  47. /**
  48. * @brief read_post_data 从 request 中获取 POST 数据到缓冲区
  49. *
  50. * @param req apache request_rec 对象
  51. * @param post 接收缓冲区
  52. * @param post_size 接收缓冲区长度
  53. *
  54. * @return
  55. */
  56. static int read_post_data(request_rec *req, char **post, size_t *post_size)
  57. {
  58. char buffer[DFT_BUF_SIZE] = {};
  59. size_t bytes, count, offset;
  60.  
  61. bytes = count = offset = ;
  62.  
  63. if(ap_setup_client_block(req, REQUEST_CHUNKED_DECHUNK) != OK)
  64. {
  65. return HTTP_BAD_REQUEST;
  66. }
  67.  
  68. if(ap_should_client_block(req))
  69. {
  70. //通过 Apache 提供的 API:ap_get_client_block 将请求中 POST 的数据读入到缓冲区
  71. for(bytes = ap_get_client_block(req, buffer, DFT_BUF_SIZE); bytes > ; bytes = ap_get_client_block(req, buffer, DFT_BUF_SIZE))
  72. {
  73. //如果预分配的缓冲区不够,则重新分配内存存放,并同时修改缓冲区的实际长度
  74. count += bytes;
  75. if(count > *post_size)
  76. {
  77. *post = (char *)realloc(*post, count);
  78. if(*post == NULL)
  79. {
  80. return HTTP_INTERNAL_SERVER_ERROR;
  81. }
  82. }
  83. *post_size = count;
  84. offset = count - bytes;
  85. memcpy((char *)*post+offset, buffer, bytes);
  86. }
  87. }
  88. else
  89. {
  90. *post_size = ;
  91. return OK;
  92. }
  93.  
  94. return OK;
  95. }
  96.  
  97. /*
  98. The sample content handler
  99. 首先需要一个实际处理客户端请求的函数 (handler),命名方式一般为”模块名 _handler”,接收一个 request_rec 类型的指针,并返回一个 int 类型的状态值
  100. request_rec 指针中包括所有的客户端连接信息及 Apache 内部的指针,如连接信息表,内存池等,这个结构类似于 J2EE 开发中 servlet 的 HttpRequest 对象及 HttpResponse 对象。通过 request_rec,我们可以读取客户端请求数据 / 写入响应数据,获取请求中的信息 ( 如客户端浏览器类型,编码方式等 )
  101. */
  102. static int helloworld_handler(request_rec *r)
  103. {
  104. int ret;
  105. char *post = NULL;
  106. size_t post_size = ;
  107.  
  108. if (strcmp(r->handler, "helloworld"))
  109. {
  110. return DECLINED;
  111. }
  112. //只接收GET、POST请求
  113. if((r->method_number != M_GET) && (r->method_number != M_POST))
  114. {
  115. return HTTP_METHOD_NOT_ALLOWED;
  116. }
  117.  
  118. post = (char *)malloc(sizeof(char) * DFT_BUF_SIZE);
  119. post_size = DFT_BUF_SIZE;
  120. if(post == NULL)
  121. {
  122. return HTTP_INTERNAL_SERVER_ERROR;
  123. }
  124. memset(post, '\0', post_size);
  125.  
  126. //读取POST数据
  127. ret = read_post_data(r, &post, &post_size);
  128. if(ret != OK)
  129. {
  130. free(post);
  131. post = NULL;
  132. post_size = ;
  133. return ret;
  134. }
  135.  
  136. ap_set_content_type(r, "text/html;charset=utf-8");
  137. ap_set_content_length(r, post_size);
  138.  
  139. if(post_size == )
  140. {
  141. ap_rputs("no post data found", r);
  142. return OK;
  143. }
  144.  
  145. ap_rputs(post, r);
  146.  
  147. free(post);
  148. post = NULL;
  149. post_size = ;
  150.  
  151. return OK;
  152. }
  153.  
  154. //注册函数,一般命名为”模块名 _register_hooks”,传入参数为 Apache 的内存池指针。这个函数用于通知 Apache 在何时,以何种方式注册响应函数 (handler)
  155. static void helloworld_register_hooks(apr_pool_t *p)
  156. {
  157. ap_hook_handler(helloworld_handler, NULL, NULL, APR_HOOK_MIDDLE);
  158. }
  159.  
  160. /*
  161. Dispatch list for API hooks
  162. 模块的定义,Apache 模块加载器通过这个结构体中的定义来在适当的时刻调用适当的函数以处理响应。应该注意的是,第一个成员默认填写为 STANDARD20_MODULE_STUFF,最后一个成员为注册函数
  163. */
  164. module AP_MODULE_DECLARE_DATA helloworld_module = {
  165. STANDARD20_MODULE_STUFF,
  166. NULL, /* create per-dir config structures */
  167. NULL, /* merge per-dir config structures */
  168. NULL, /* create per-server config structures */
  169. NULL, /* merge per-server config structures */
  170. NULL, /* table of config file commands */
  171. helloworld_register_hooks /* register hooks */
  172. };

Relevant Link:

  1. http://www.ibm.com/developerworks/cn/opensource/os-cn-apachehttpd/

3. mod进阶: 可配置的 echo 模块

我们继续扩展上例中的 echo_post 模块,我们将 echo_post 扩展为可配置的模块,通过修改配置文件 httpd.conf 中设置 ConvertType 的值,可以使得模块在运行时的行为发生变化

0x1: 配置信息读取

  1. typedef struct{
  2. int convert_type; // 转换类型
  3. }cust_config_t;

这个结构体仅有一个成员,convert_type, 表示转换类型,如果在配置文件中该值被设置为 0,则将客户端 POST 的数据转换为大写,如果为 1,则转换为小写。这样即可通过配置信息修改模块运行时的行为

  1. //create_config 函数用以创建一个用户自定义的结构体
  2. static void *create_config(apr_pool_t *pool, server_rec *server);
  3.  
  4. //set_mod_config 函数用以设置配置结构体中的成员,这个函数注册在 command_rec 数组中
  5. static const char *set_mod_config(cmd_parms *params, void *config, const char *arg);
  6.  
  7. //而 command_rec 数组则保存在模块声明结构体中: 定义一个 command_rec 结构体类型的数组
  8. static const command_rec cust_echo_cmds[] =
  9. {
  10. AP_INIT_TAKE1("ConvertType",
  11. set_mod_config,
  12. NULL,
  13. RSRC_CONF,
  14. "convert type of post data"),
  15. {}
  16. };
  17.  
  18. //注册模块回调函数
  19. /* Dispatch list for API hooks */
  20. module AP_MODULE_DECLARE_DATA cust_echo_post_module = {
  21. STANDARD20_MODULE_STUFF,
  22. NULL, /* create per-dir config structures */
  23. NULL, /* merge per-dir config structures */
  24. create_config, /* create per-server config structures */
  25. NULL, /* merge per-server config structures */
  26. cust_echo_cmds, /* table of config file commands */
  27. cust_echo_post_register_hooks /* register hooks */
  28. };

0x2: Code

  1. /*
  2. ** mod_cust_echo_post.c -- Apache sample cust_echo_post module
  3. ** [Autogenerated via ``apxs -n cust_echo_post -g'']
  4. **
  5. ** To play with this sample module first compile it into a
  6. ** DSO file and install it into Apache's modules directory
  7. ** by running:
  8. **
  9. ** $ apxs -c -i mod_cust_echo_post.c
  10. **
  11. ** Then activate it in Apache's httpd.conf file for instance
  12. ** for the URL /cust_echo_post in as follows:
  13. **
  14. ** # httpd.conf
  15. ** LoadModule cust_echo_post_module modules/mod_cust_echo_post.so
  16. ** <Location /cust_echo_post>
  17. ** SetHandler cust_echo_post
  18. ** </Location>
  19. **
  20. ** Then after restarting Apache via
  21. **
  22. ** $ apachectl restart
  23. **
  24. ** you immediately can request the URL /cust_echo_post and watch for the
  25. ** output of this module. This can be achieved for instance via:
  26. **
  27. ** $ lynx -mime_header http://localhost/cust_echo_post
  28. **
  29. ** The output should be similar to the following one:
  30. **
  31. ** HTTP/1.1 200 OK
  32. ** Date: Tue, 31 Mar 1998 14:42:22 GMT
  33. ** Server: Apache/1.3.4 (Unix)
  34. ** Connection: close
  35. ** Content-Type: text/html
  36. **
  37. ** The sample page from mod_cust_echo_post.c
  38. */
  39.  
  40. #include "httpd.h"
  41. #include "http_config.h"
  42. #include "http_protocol.h"
  43. #include "ap_config.h"
  44.  
  45. #define DFT_BUF_SIZE 4096
  46.  
  47. module AP_MODULE_DECLARE_DATA cust_echo_post_module;
  48.  
  49. static void *create_config(apr_pool_t *pool, server_rec *server);
  50. static const char *set_mod_config(cmd_parms *params, void *config, const char *arg);
  51.  
  52. typedef struct
  53. {
  54. int convert_type; //转换类型
  55. }cust_config_t;
  56.  
  57. static const command_rec cust_echo_cmds[] =
  58. {
  59. AP_INIT_TAKE1("ConvertType", set_mod_config, NULL, RSRC_CONF, "convert type of post data"), {}
  60. };
  61.  
  62. static void *create_config(apr_pool_t *pool, server_rec *server)
  63. {
  64. cust_config_t *config;
  65. config = (cust_config_t *)apr_pcalloc(pool, sizeof(cust_config_t));
  66. return (void *)config;
  67. }
  68.  
  69. static const char *set_mod_config(cmd_parms *params, void *conf, const char *arg)
  70. {
  71. cust_config_t *config = ap_get_module_config(params->server->module_config, &cust_echo_post_module);
  72.  
  73. if(strcmp(params->cmd->name, "ConvertType") == )
  74. {
  75. config->convert_type = atoi((char *)arg);
  76. }
  77.  
  78. return NULL;
  79. }
  80.  
  81. /**
  82. * @brief read_post_data 从request中获取POST数据到缓冲区
  83. *
  84. * @param req apache request_rec对象
  85. * @param post 接收缓冲区
  86. * @param post_size 接收缓冲区长度
  87. *
  88. * @return
  89. */
  90. static int read_post_data(request_rec *req, char **post, size_t *post_size){
  91. char buffer[DFT_BUF_SIZE] = {};
  92. size_t bytes, count, offset;
  93.  
  94. bytes = count = offset = ;
  95.  
  96. if(ap_setup_client_block(req, REQUEST_CHUNKED_DECHUNK) != OK){
  97. return HTTP_BAD_REQUEST;
  98. }
  99.  
  100. if(ap_should_client_block(req)){
  101. for(bytes = ap_get_client_block(req, buffer, DFT_BUF_SIZE);
  102. bytes > ;
  103. bytes = ap_get_client_block(req, buffer, DFT_BUF_SIZE)){
  104. count += bytes;
  105. if(count > *post_size){
  106. *post = (char *)realloc(*post, count);
  107. if(*post == NULL){
  108. return HTTP_INTERNAL_SERVER_ERROR;
  109. }
  110. }
  111. *post_size = count;
  112. offset = count - bytes;
  113. memcpy((char *)*post+offset, buffer, bytes);
  114. }
  115. }else{
  116. *post_size = ;
  117. return OK;
  118. }
  119.  
  120. return OK;
  121. }
  122.  
  123. /* The sample content handler */
  124. static int cust_echo_post_handler(request_rec *req)
  125. {
  126. if (strcmp(req->handler, "cust_echo_post"))
  127. {
  128. return DECLINED;
  129. }
  130.  
  131. if((req->method_number != M_GET) && (req->method_number != M_POST))
  132. {
  133. return HTTP_METHOD_NOT_ALLOWED;
  134. }
  135.  
  136. char *post = (char *)malloc(sizeof(char)*DFT_BUF_SIZE);
  137. size_t post_size = DFT_BUF_SIZE;
  138.  
  139. if(post == NULL)
  140. {
  141. return HTTP_INTERNAL_SERVER_ERROR;
  142. }
  143.  
  144. memset(post, '\0', post_size);
  145.  
  146. int ret = read_post_data(req, &post, &post_size);
  147. if(ret != OK)
  148. {
  149. free(post);
  150. post = NULL;
  151. post_size = ;
  152. return ret;
  153. }
  154.  
  155. ap_set_content_type(req, "text/html;charset=utf-8");
  156. ap_set_content_length(req, post_size);
  157.  
  158. if(post_size == )
  159. {
  160. ap_rputs("no post data found", req);
  161. return OK;
  162. }
  163.  
  164. cust_config_t *config = ap_get_module_config(req->server->module_config, &cust_echo_post_module);
  165. if(config == NULL)
  166. {
  167. return HTTP_INTERNAL_SERVER_ERROR;
  168. }
  169.  
  170. //make a copy of user post data
  171. char *converted = strdup(post);
  172.  
  173. int i = ;
  174. //convert it according to convert_type
  175. switch(config->convert_type)
  176. {
  177. case :
  178. for(i = ; i < post_size; i++)
  179. {
  180. converted[i] = toupper(((char *)post[i]));
  181. }
  182. break;
  183. case :
  184. for(i = ; i < post_size; i++)
  185. {
  186. converted[i] = tolower(((char *)post[i]));
  187. }
  188. break;
  189. default:
  190. break;
  191. }
  192.  
  193. ap_rputs(converted, req);
  194.  
  195. free(converted);
  196. converted = NULL;
  197.  
  198. free(post);
  199. post = NULL;
  200. post_size = ;
  201.  
  202. return OK;
  203. }
  204.  
  205. static void cust_echo_post_register_hooks(apr_pool_t *p)
  206. {
  207. ap_hook_handler(cust_echo_post_handler, NULL, NULL, APR_HOOK_MIDDLE);
  208. }
  209.  
  210. /* Dispatch list for API hooks */
  211. module AP_MODULE_DECLARE_DATA cust_echo_post_module = {
  212. STANDARD20_MODULE_STUFF,
  213. NULL, /* create per-dir config structures */
  214. NULL, /* merge per-dir config structures */
  215. create_config, /* create per-server config structures */
  216. NULL, /* merge per-server config structures */
  217. cust_echo_cmds, /* table of config file commands */
  218. cust_echo_post_register_hooks /* register hooks */
  219. };

运行可配置 echo 模块

  1. LoadModule cust_echo_post_module "D:/wamp/bin/apache/APACHE~1.9/modules/mod_cust_echo_post.so"
  2. <Location /cust_echo_post>
  3. SetHandler cust_echo_post
  4. </Location>
  5.  
  6. #configure for cust_echo_post
  7. ConvertType

  1. LoadModule cust_echo_post_module "D:/wamp/bin/apache/APACHE~1.9/modules/mod_cust_echo_post.so"
  2. <Location /cust_echo_post>
  3. SetHandler cust_echo_post
  4. </Location>
  5.  
  6. #configure for cust_echo_post
  7. ConvertType

Relevant Link:

  1. http://www.ibm.com/developerworks/cn/opensource/os-cn-apachehttpd/

4. mod进阶: 过滤器

过滤器事实上是另一种形式的模块,Apache对通用的数据结构都做过一些封装,并以库的方式提供(即APR(Apache Portable Runtime))。在过滤器中,有两个比较重要的数据结构

  1. . apr_bucket
  2. . apr_bucket_brigade: apr_bucket_birgade 相当于一个环状队列,而 apr_bucket 是队列中的元素

所有的过滤器形成一个长链,数据从上一个过滤器流入,进行过滤,然后将加工过的数据流入下一个过滤器,处理一个 HTTP 事务期间可能会多次调用某个过滤器,就象不同的块通过“桶队列”。对于所有最普通的过滤器来说,这意味着过滤器必须能够在两次调用之间保存某种上下文
我们的过滤器非常简单,从上一个过滤器中读到数据,将数据中的字符串转换为大写,然后将桶 (apr_bucket) 传递给下一个过滤器。Apache 提供了丰富的 API 来完成这一系列的操作

0x1: 大小写转换过滤器

  1. static apr_status_t case_filter(ap_filter_t *filter, apr_bucket_brigade *bbin)
  2. {
  3. request_rec *req = filter->r;
  4. conn_rec *con = req->connection;
  5.  
  6. apr_bucket *bucket;
  7. apr_bucket_brigade *bbout;
  8.  
  9. //create brigade
  10. bbout = apr_brigade_create(req->pool, con->bucket_alloc);
  11.  
  12. //iterate the full brigade
  13. APR_BRIGADE_FOREACH(bucket, bbin)
  14. {
  15. if(APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket))
  16. {
  17. APR_BUCKET_REMOVE(bucket);
  18. APR_BRIGADE_INSERT_TAIL(bbout, bucket);
  19. return ap_pass_brigade(filter->next, bbout);
  20. }
  21. char *data, *buffer;
  22. apr_size_t data_len;
  23.  
  24. //read content of current bucket in brigade
  25. apr_bucket_read(bucket, &data, &data_len, APR_NONBLOCK_READ);
  26. buffer = apr_bucket_alloc(data_len, con->bucket_alloc);
  27. int i;
  28. for(i = ; i < data_len; i++)
  29. {
  30. //convert
  31. buffer[i] = apr_toupper(data[i]);
  32. }
  33.  
  34. apr_bucket *temp_bucket;
  35. temp_bucket = apr_bucket_heap_create(buffer, data_len, apr_bucket_free, con->bucket_alloc);
  36.  
  37. APR_BRIGADE_INSERT_TAIL(bbout, temp_bucket);
  38. }
  39.  
  40. return APR_SUCCESS;
  41. }

0x2: 注册过滤器

  1. static void filter_echo_post_register_hooks(apr_pool_t *p)
  2. {
  3. ap_register_output_filter(filter_name, case_filter, NULL, AP_FTYPE_RESOURCE);
  4. }

0x3: 运行过滤器模块

对过滤器的配置要稍微复杂一些,在 httpd.conf 中,不但要使用 LoadModule 指令加载过滤器模块,还要使用 SetOutputFilter 指令来指定过滤器的应用场景

  1. LoadModule filter_echo_post_module modules/mod_filter_echo_post.so
  2. AddOutputFilter CaseFilter .cf
  3. //指令中指定,CaseFilter 这个过滤器仅对扩展名为 .cf 的 URL 请求做过滤,其他请求则不过滤

0x4: Code Example

  1. /*
  2. ** mod_filter_echo_post.c -- Apache sample filter_echo_post module
  3. ** [Autogenerated via ``apxs -n filter_echo_post -g'']
  4. **
  5. ** To play with this sample module first compile it into a
  6. ** DSO file and install it into Apache's modules directory
  7. ** by running:
  8. **
  9. ** $ apxs -c -i mod_filter_echo_post.c
  10. **
  11. ** Then activate it in Apache's httpd.conf file for instance
  12. ** for the URL /filter_echo_post in as follows:
  13. **
  14. ** # httpd.conf
  15. ** LoadModule filter_echo_post_module modules/mod_filter_echo_post.so
  16. ** <Location /filter_echo_post>
  17. ** SetHandler filter_echo_post
  18. ** </Location>
  19. **
  20. ** Then after restarting Apache via
  21. **
  22. ** $ apachectl restart
  23. **
  24. ** you immediately can request the URL /filter_echo_post and watch for the
  25. ** output of this module. This can be achieved for instance via:
  26. **
  27. ** $ lynx -mime_header http://localhost/filter_echo_post
  28. **
  29. ** The output should be similar to the following one:
  30. **
  31. ** HTTP/1.1 200 OK
  32. ** Date: Tue, 31 Mar 1998 14:42:22 GMT
  33. ** Server: Apache/1.3.4 (Unix)
  34. ** Connection: close
  35. ** Content-Type: text/html
  36. **
  37. ** The sample page from mod_filter_echo_post.c
  38. */
  39.  
  40. #include "httpd.h"
  41. #include "http_config.h"
  42. #include "http_request.h"
  43. #include "http_protocol.h"
  44. #include "ap_config.h"
  45.  
  46. #include "apr_general.h"
  47. #include "apr_buckets.h"
  48. #include "apr_lib.h"
  49.  
  50. #include "util_filter.h"
  51.  
  52. static const char *filter_name = "CaseFilter";
  53.  
  54. static apr_status_t case_filter(ap_filter_t *filter,
  55. apr_bucket_brigade *bbin){
  56. request_rec *req = filter->r;
  57. conn_rec *con = req->connection;
  58.  
  59. apr_bucket *bucket;
  60. apr_bucket_brigade *bbout;
  61.  
  62. bbout = apr_brigade_create(req->pool, con->bucket_alloc);
  63.  
  64. APR_BRIGADE_FOREACH(bucket, bbin){
  65. if(APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)){
  66. APR_BUCKET_REMOVE(bucket);
  67. APR_BRIGADE_INSERT_TAIL(bbout, bucket);
  68. return ap_pass_brigade(filter->next, bbout);
  69. }
  70. char *data, *buffer;
  71. apr_size_t data_len;
  72.  
  73. apr_bucket_read(bucket, &data, &data_len, APR_NONBLOCK_READ);
  74. buffer = apr_bucket_alloc(data_len, con->bucket_alloc);
  75. int i;
  76. for(i = ; i < data_len; i++){
  77. buffer[i] = apr_toupper(data[i]);
  78. }
  79.  
  80. apr_bucket *temp_bucket;
  81. temp_bucket = apr_bucket_heap_create(
  82. buffer, data_len, apr_bucket_free, con->bucket_alloc);
  83.  
  84. APR_BRIGADE_INSERT_TAIL(bbout, temp_bucket);
  85. }
  86.  
  87. return APR_SUCCESS;
  88. }
  89.  
  90. /*
  91. static apr_status_t case_filter(ap_filter_t *filter, apr_bucket_brigade *bbin){
  92. request_rec *req = filter->r;
  93. conn_rec *con = req->connection;
  94.  
  95. apr_bucket *bucket; //the bucket of data
  96. apr_bucket_brigade *bbout;
  97.  
  98. bbout = apr_brigade_create(req->pool, con->bucket_alloc);
  99.  
  100. for(bucket = APR_BRIGADE_FIRST(bbin);
  101. bucket != APR_BRIGADE_SENTINEL(bbin);
  102. bucket = APR_BUCKET_NEXT(bucket)){
  103. char *data, *buffer;
  104. apr_size_t data_len;
  105.  
  106. apr_bucket *temp_bucket;
  107.  
  108. if(APR_BUCKET_IS_EOS(bucket)){
  109. apr_bucket *eos = apr_bucket_eos_create(con->bucket_alloc);
  110. APR_BRIGADE_INSERT_TAIL(bbout, eos);
  111. continue;
  112. }
  113.  
  114. apr_bucket_read(bucket, &data, &data_len, APR_BLOCK_READ);
  115. buffer = apr_bucket_alloc(data_len, con->bucket_alloc);
  116. int i;
  117. for(i = 0; i < data_len; i++){
  118. buffer[i] = apr_toupper(data[i]);
  119. }
  120.  
  121. temp_bucket = apr_bucket_heap_create(
  122. buffer, data_len, apr_bucket_free, con->bucket_alloc);
  123. APR_BRIGADE_INSERT_TAIL(bbout, temp_bucket);
  124. }
  125.  
  126. return ap_pass_brigade(filter->next, bbout);
  127. }
  128. */
  129.  
  130. static void filter_echo_post_register_hooks(apr_pool_t *p)
  131. {
  132. ap_register_output_filter(filter_name, case_filter, NULL, AP_FTYPE_RESOURCE);
  133. }
  134.  
  135. /* Dispatch list for API hooks */
  136. module AP_MODULE_DECLARE_DATA filter_echo_post_module = {
  137. STANDARD20_MODULE_STUFF,
  138. NULL, /* create per-dir config structures */
  139. NULL, /* merge per-dir config structures */
  140. NULL, /* create per-server config structures */
  141. NULL, /* merge per-server config structures */
  142. NULL, /* table of config file commands */
  143. filter_echo_post_register_hooks /* register hooks */
  144. };

aaarticlea/png;base64,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" alt="" />

过滤器将该文件中的字符串转换为大写字母输出

Relevant Link:

  1. http://www.ibm.com/developerworks/cn/opensource/os-cn-apachehttpd/
  2. http://www.ibm.com/developerworks/cn/linux/middleware/l-apache/

Copyright (c) 2015 LittleHann All rights reserved

Apache Mod/Filter Development的更多相关文章

  1. paip.输出内容替换在Apache 过滤器filter的设置

    paip.输出内容替换在Apache 过滤器filter的设置 作者Attilax  艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog ...

  2. C语言-apache mod(模块开发)-采用apxs开发实战(centos7.2 linux篇)

    C语言-apache mod(模块开发)-采用apxs开发实战(centos7.2 linux篇) 名词解释:apxs apxs is a tool for building and installi ...

  3. C语言-apache mod(模块开发)-采用VS2017开发实战(windows篇)

    C语言-apache mod(模块开发)-采用VS2017开发实战(windows篇) 名词解释:apxs apxs is a tool for building and installing ext ...

  4. Establish the LAMP (Linux+Apache+MySQL+PHP) Development Environment on Ubuntu 14.04 Operating System

    ######################################################## Step One: Update the software package in yo ...

  5. Apache Mina Filter

    Mina中的过滤器处于IoService与IoHandler之间,用于过滤每一个I/O事件.本文分析Mina中的过滤器是怎么串起来的? 前面提到了IoFilter,FilterChain等接口和类,在 ...

  6. Windows环境下使用Apache+mod

    1.安装Python和Apache. 2.安装mod_wsgi后获得wsgi.so,并将wsgi.so放到Apache的modules文件夹下. 3.安装webpy. 4.打开httpd.conf(在 ...

  7. Apache MiNa 实现多人聊天室

    Apache MiNa 实现多人聊天室 开发环境: System:Windows JavaSDK:1.6 IDE:eclipse.MyEclipse 6.6 开发依赖库: Jdk1.4+.mina-c ...

  8. Apache Mina(一)

    原文链接:http://www.cnblogs.com/xuekyo/archive/2013/03/06/2945826.html Apache Mina是一个能够帮助用户开发高性能和高伸缩性网络应 ...

  9. 转:LAV Filter 源代码分析

    1: 总体结构 LAV Filter 是一款视频分离和解码软件,他的分离器封装了FFMPEG中的libavformat,解码器则封装了FFMPEG中的libavcodec.它支持十分广泛的视音频格式. ...

随机推荐

  1. codevs2010 求后序遍历

    难度等级:白银 2010 求后序遍历 题目描述 Description 输入一棵二叉树的先序和中序遍历序列,输出其后序遍历序列. 输入描述 Input Description 共两行,第一行一个字符串 ...

  2. 利用writing-mode实现元素的垂直居中

    writing-mode是CSS3的新特性之一,使用场景不是很多.这个属性主要是改变文档流的显示方式.具体的介绍参考这篇文章:http://www.zhangxinxu.com/wordpress/2 ...

  3. centos6-honeyd安装&配置

    安装 需要装 libpcap libevent libdnet 等(!) 有些用的yum,有些下载的安装包手动安装 (wget tar configure make install 非常linux) ...

  4. 高性能JavaScript DOM编程

    我们知道,DOM是用于操作XML和HTML文档的应用程序接口,用脚本进行DOM操作的代价很昂贵.有个贴切的比喻,把DOM和JavaScript(这里指ECMScript)各自想象为一个岛屿,它们之间用 ...

  5. 页面跳转Transfer与Redirect的区别你知道吗?

    一 前言 关于页面跳转的方式常用的应该就是,链接跳转,js跳转,Server.Tranfser和Response.Redirect 这几种,可是在Tranfser与Redirect之间用哪种更好(本文 ...

  6. JavaScript学习笔记- 省市级联效果

    <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...

  7. Sqlsever

    Sqlsever: 获取主键当前最大值: select ident_current('tablename');

  8. chgrp 简明笔记

    改变与文件相关联的组 chgrp [options] group file-list 参数 group 为新组的名称或者数值ID,file-list 为要改变其相关联组的文件路径名列表 选项 -c   ...

  9. Nginx之负载均衡服务器揭秘

    Nginx代理服务器, 一次性代理多台后端机器, 利用负载算法, 决定将当前请求传递给某台服务器执行. 有哪些后台服务器?例如微软的IIS,Apache,Nginx 负载算法是什么? 加权轮询. ng ...

  10. Android下常见的四种对话框

    摘要:在实际开发过程有时为了能够和用户进行很好的交互,需要使用到对话框,在Android中常用的对话框有四种:普通对话框.单选对话框.多选对话框.进度对话框. 一.普度对话框 public void ...