/* mod_diagnostics Copyright (C) 2003, Nick Kew This is free software. You may use and redistribute it under the terms of the Apache License at http://www.apache.org/LICENSE.txt */ /* mod_diagnostics: print diagnostic and debug information on data (and metadata) passing through an Apache Filter chain. Insert a mod_diagnostics filter anywhere you want to watch traffic. See below for registered input and output filter names. Two filters are defined for each level, so that you can insert mod_diagnostics before and after any module you are investigating or debugging. */ #include #include #include #include module AP_MODULE_DECLARE_DATA diagnostic_filter_module ; static void diagnostic_log(ap_filter_t* f, apr_bucket* b) { const char* t ; if ( APR_BUCKET_IS_METADATA(b) ) t = "(metadata)" ; else t = "(data)" ; if ( b->type == &apr_bucket_type_flush ) t = "FLUSH" ; else if ( b->type == &apr_bucket_type_eos ) t = "EOS" ; else if ( b->type == &apr_bucket_type_file ) t = "FILE" ; else if ( b->type == &apr_bucket_type_pipe ) t = "PIPE" ; else if ( b->type == &apr_bucket_type_socket ) t = "SOCKET" ; else if ( b->type == &apr_bucket_type_heap ) t = "HEAP" ; else if ( b->type == &apr_bucket_type_transient ) t = "TRANSIENT" ; else if ( b->type == &apr_bucket_type_immortal ) t = "IMMORTAL" ; else if ( b->type == &apr_bucket_type_mmap ) t = "MMAP" ; else if ( b->type == &apr_bucket_type_pool ) t = "POOL" ; /* use the connection pool, so it works with all filter types (Request may not be valid in a connection or network filter) This doesn't work with APLOG_DEBUG (looks like a bug in log.c around line 409 in 2.0.44), so we use APLOG_NOTICE. This is worth updating if httpd gets fixed. */ ap_log_perror(APLOG_MARK, APLOG_NOTICE, NULL, f->c->pool, " %s %s: %d bytes", f->frec->name, t, b->length) ; } static int diagnostic_ofilter (ap_filter_t* f, apr_bucket_brigade* bb) { apr_bucket* b ; ap_log_perror(APLOG_MARK, APLOG_NOTICE, NULL, f->c->pool, f->frec->name) ; for ( b = APR_BRIGADE_FIRST(bb) ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_NEXT(b) ) diagnostic_log(f, b) ; return ap_pass_brigade(f->next, bb) ; } static const char* getmode(ap_input_mode_t mode) { switch ( mode ) { case AP_MODE_READBYTES: return "READBYTES" ; case AP_MODE_GETLINE: return "GETLINE" ; case AP_MODE_EATCRLF: return "EATCRLF" ; case AP_MODE_SPECULATIVE: return "SPECULATIVE" ; case AP_MODE_EXHAUSTIVE: return "EXHAUSTIVE" ; case AP_MODE_INIT: return "INIT" ; } return "(unknown)" ; } #define gettype(block) ((block) == APR_BLOCK_READ) ? "blocking" : "non-blocking" static int diagnostic_ifilter (ap_filter_t* f, apr_bucket_brigade* bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { apr_bucket* b ; apr_status_t ret ; ap_log_perror(APLOG_MARK, APLOG_NOTICE, NULL, f->c->pool, "%s: mode %s; %s; %d bytes", f->frec->name, getmode(mode), gettype(block), readbytes) ; if ( ret = ap_get_brigade(f->next, bb, mode, block, readbytes) , ret == APR_SUCCESS ) for ( b = APR_BRIGADE_FIRST(bb) ; b != APR_BRIGADE_SENTINEL(bb) ; b = APR_BUCKET_NEXT(b) ) diagnostic_log(f, b) ; else ap_log_perror(APLOG_MARK, APLOG_NOTICE, NULL, f->c->pool, "%s: ap_get_brigade returned %d", f->frec->name, ret) ; return ret ; } #define ofilter_init NULL #define ifilter_init NULL static void diagnostic_hooks(apr_pool_t* p) { /* by registering twice under each phase, we can insert filters BEFORE and AFTER one we are debugging, and distinguish between them I don't think this makes much sense at the network level, but we'll do it anyway: nothing to lose! */ ap_register_output_filter("o-resource-1", diagnostic_ofilter, ofilter_init, AP_FTYPE_RESOURCE) ; ap_register_output_filter("o-resource-2", diagnostic_ofilter, ofilter_init, AP_FTYPE_RESOURCE) ; ap_register_output_filter("o-content-1", diagnostic_ofilter, ofilter_init, AP_FTYPE_CONTENT_SET) ; ap_register_output_filter("o-content-2", diagnostic_ofilter, ofilter_init, AP_FTYPE_CONTENT_SET) ; ap_register_output_filter("o-protocol-1", diagnostic_ofilter, ofilter_init, AP_FTYPE_PROTOCOL) ; ap_register_output_filter("o-protocol-2", diagnostic_ofilter, ofilter_init, AP_FTYPE_PROTOCOL) ; ap_register_output_filter("o-transcode-1", diagnostic_ofilter, ofilter_init, AP_FTYPE_TRANSCODE) ; ap_register_output_filter("o-transcode-2", diagnostic_ofilter, ofilter_init, AP_FTYPE_TRANSCODE) ; ap_register_output_filter("o-connection-1", diagnostic_ofilter, ofilter_init, AP_FTYPE_CONNECTION) ; ap_register_output_filter("o-connection-2", diagnostic_ofilter, ofilter_init, AP_FTYPE_CONNECTION) ; ap_register_output_filter("o-network-1", diagnostic_ofilter, ofilter_init, AP_FTYPE_NETWORK) ; ap_register_output_filter("o-network-2", diagnostic_ofilter, ofilter_init, AP_FTYPE_NETWORK) ; ap_register_input_filter("i-resource-1", diagnostic_ifilter, ifilter_init, AP_FTYPE_RESOURCE) ; ap_register_input_filter("i-resource-2", diagnostic_ifilter, ifilter_init, AP_FTYPE_RESOURCE) ; ap_register_input_filter("i-content-1", diagnostic_ifilter, ifilter_init, AP_FTYPE_CONTENT_SET) ; ap_register_input_filter("i-content-2", diagnostic_ifilter, ifilter_init, AP_FTYPE_CONTENT_SET) ; ap_register_input_filter("i-protocol-1", diagnostic_ifilter, ifilter_init, AP_FTYPE_PROTOCOL) ; ap_register_input_filter("i-protocol-2", diagnostic_ifilter, ifilter_init, AP_FTYPE_PROTOCOL) ; ap_register_input_filter("i-transcode-1", diagnostic_ifilter, ifilter_init, AP_FTYPE_TRANSCODE) ; ap_register_input_filter("i-transcode-2", diagnostic_ifilter, ifilter_init, AP_FTYPE_TRANSCODE) ; ap_register_input_filter("i-connection-1", diagnostic_ifilter, ifilter_init, AP_FTYPE_CONNECTION) ; ap_register_input_filter("i-connection-2", diagnostic_ifilter, ifilter_init, AP_FTYPE_CONNECTION) ; ap_register_input_filter("i-network-1", diagnostic_ifilter, ifilter_init, AP_FTYPE_NETWORK) ; ap_register_input_filter("i-network-2", diagnostic_ifilter, ifilter_init, AP_FTYPE_NETWORK) ; } module AP_MODULE_DECLARE_DATA diagnostic_filter_module = { STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, NULL, diagnostic_hooks } ;