├── .gitignore ├── README ├── README.md ├── config ├── config_without_ssl ├── doc ├── README.html ├── README.txt └── README.wiki ├── modules ├── ngx_tcp_generic_proxy_module.c ├── ngx_tcp_ssl_module.c ├── ngx_tcp_ssl_module.h ├── ngx_tcp_upstream_busyness_module.c ├── ngx_tcp_upstream_ip_hash_module.c └── ngx_tcp_websocket_proxy_module.c ├── ngx_tcp.c ├── ngx_tcp.h ├── ngx_tcp_access.c ├── ngx_tcp_core_module.c ├── ngx_tcp_log.c ├── ngx_tcp_session.c ├── ngx_tcp_session.h ├── ngx_tcp_upstream.c ├── ngx_tcp_upstream.h ├── ngx_tcp_upstream_check.c ├── ngx_tcp_upstream_check.h ├── ngx_tcp_upstream_round_robin.c ├── ngx_tcp_upstream_round_robin.h ├── parsers ├── gen.shell ├── http_request_parser.c ├── http_request_parser.h ├── http_request_parser.rl ├── http_response_parser.c ├── http_response_parser.h ├── http_response_parser.rl ├── parser.h ├── smtp_response_parser.c ├── smtp_response_parser.h └── smtp_response_parser.rl ├── tcp.patch ├── tcp_1_8.patch ├── test ├── README ├── inc │ ├── Module │ │ ├── AutoInstall.pm │ │ ├── Install.pm │ │ └── Install │ │ │ ├── AutoInstall.pm │ │ │ ├── Base.pm │ │ │ ├── Can.pm │ │ │ ├── Fetch.pm │ │ │ ├── Include.pm │ │ │ ├── Makefile.pm │ │ │ ├── Metadata.pm │ │ │ ├── TestBase.pm │ │ │ ├── Win32.pm │ │ │ └── WriteAll.pm │ ├── Spiffy.pm │ └── Test │ │ ├── Base.pm │ │ ├── Base │ │ └── Filter.pm │ │ ├── Builder.pm │ │ ├── Builder │ │ └── Module.pm │ │ └── More.pm ├── lib │ └── Test │ │ ├── Nginx.pm │ │ └── Nginx │ │ ├── LWP.pm │ │ ├── Socket.pm │ │ └── Util.pm ├── ragel │ ├── Makefile │ ├── http11.c │ ├── http11_parser.c │ ├── http11_parser.h │ ├── http11_parser.rl │ ├── http11_parser_common.rl │ ├── http11_response.c │ ├── http11_response.h │ ├── http11_response.rl │ ├── http11_response_common.rl │ ├── ragel_http_client.c │ └── ragel_http_server.c ├── t │ ├── acl.t │ ├── http_check.t │ ├── imap_check.t │ ├── mysql_check.t │ ├── pop3_check.t │ ├── smtp_check.t │ ├── ssl.t │ ├── ssl_hello_check.t │ ├── tcp_check.t │ ├── upstream_busyness.t │ └── upstream_ip_hash.t └── websocket │ └── server.rb └── util ├── update-readme.sh └── wiki2pod.pl /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.sh 3 | *.[oa] 4 | Makefile 5 | cscope* 6 | objs 7 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_feature="nginx_tcp_module" 2 | ngx_feature_name= 3 | ngx_feature_run=no 4 | ngx_feature_incs= 5 | ngx_feature_path="$ngx_addon_dir/modules $ngx_addon_dir/parsers $ngx_addon_dir" 6 | ngx_feature_deps="$ngx_addon_dir/ngx_tcp.h $ngx_addon_dir/ngx_tcp_session.h $ngx_addon_dir/ngx_tcp_upstream.h $ngx_addon_dir/ngx_tcp_upstream_check.h $ngx_addon_dir/ngx_tcp_upstream_round_robin.h" 7 | ngx_tcp_src="$ngx_addon_dir/ngx_tcp.c $ngx_addon_dir/ngx_tcp_core_module.c $ngx_addon_dir/ngx_tcp_session.c $ngx_addon_dir/ngx_tcp_access.c $ngx_addon_dir/ngx_tcp_log.c $ngx_addon_dir/ngx_tcp_upstream.c $ngx_addon_dir/ngx_tcp_upstream_round_robin.c $ngx_addon_dir/modules/ngx_tcp_generic_proxy_module.c $ngx_addon_dir/modules/ngx_tcp_websocket_proxy_module.c $ngx_addon_dir/modules/ngx_tcp_upstream_ip_hash_module.c $ngx_addon_dir/modules/ngx_tcp_upstream_busyness_module.c $ngx_addon_dir/ngx_tcp_upstream_check.c " 8 | ngx_tcp_ssl_deps="$ngx_addon_dir/modules/ngx_tcp_ssl_module.h" 9 | ngx_tcp_ssl_src="$ngx_addon_dir/modules/ngx_tcp_ssl_module.c" 10 | ngx_tcp_parser_deps="$ngx_addon_dir/parsers/parser.h $ngx_addon_dir/parsers/http_request_parser.h $ngx_addon_dir/parsers/http_response_parser.h $ngx_addon_dir/parsers/smtp_response_parser.h" 11 | ngx_tcp_parser_src="$ngx_addon_dir/parsers/http_request_parser.c $ngx_addon_dir/parsers/http_response_parser.c $ngx_addon_dir/parsers/smtp_response_parser.c" 12 | ngx_feature_test="int a;" 13 | . auto/feature 14 | 15 | if [ $ngx_found = yes ]; then 16 | CORE_INCS="$CORE_INCS $ngx_feature_path" 17 | ngx_addon_name=ngx_tcp_module 18 | 19 | TCP_CORE_MODULES="ngx_tcp_module ngx_tcp_core_module ngx_tcp_upstream_module" 20 | TCP_MODULES="ngx_tcp_proxy_module ngx_tcp_websocket_module ngx_tcp_upstream_ip_hash_module ngx_tcp_upstream_busyness_module" 21 | 22 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_feature_deps $ngx_tcp_parser_deps" 23 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_tcp_src $ngx_tcp_parser_src" 24 | 25 | have=NGX_TCP_SSL . auto/have 26 | USE_OPENSSL=YES 27 | TCP_MODULES="$TCP_MODULES ngx_tcp_ssl_module" 28 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_tcp_ssl_deps" 29 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_tcp_ssl_src" 30 | 31 | EVENT_MODULES="$EVENT_MODULES $TCP_CORE_MODULES $TCP_MODULES" 32 | HTTP_MODULES="$HTTP_MODULES ngx_tcp_upstream_check_status_module" 33 | else 34 | cat << END 35 | $0: error: the ngx_tcp_module addon error. 36 | END 37 | exit 1 38 | fi 39 | -------------------------------------------------------------------------------- /config_without_ssl: -------------------------------------------------------------------------------- 1 | ngx_feature="nginx_tcp_module" 2 | ngx_feature_name= 3 | ngx_feature_run=no 4 | ngx_feature_incs= 5 | ngx_feature_path="$ngx_addon_dir/modules $ngx_addon_dir/parsers $ngx_addon_dir" 6 | ngx_feature_deps="$ngx_addon_dir/ngx_tcp.h $ngx_addon_dir/ngx_tcp_session.h $ngx_addon_dir/ngx_tcp_upstream.h $ngx_addon_dir/ngx_tcp_upstream_check.h $ngx_addon_dir/ngx_tcp_upstream_round_robin.h" 7 | ngx_tcp_src="$ngx_addon_dir/ngx_tcp.c $ngx_addon_dir/ngx_tcp_core_module.c $ngx_addon_dir/ngx_tcp_session.c $ngx_addon_dir/ngx_tcp_access.c $ngx_addon_dir/ngx_tcp_log.c $ngx_addon_dir/ngx_tcp_upstream.c $ngx_addon_dir/ngx_tcp_upstream_round_robin.c $ngx_addon_dir/modules/ngx_tcp_generic_proxy_module.c $ngx_addon_dir/modules/ngx_tcp_websocket_proxy_module.c $ngx_addon_dir/modules/ngx_tcp_upstream_ip_hash_module.c $ngx_addon_dir/modules/ngx_tcp_upstream_busyness_module.c $ngx_addon_dir/ngx_tcp_upstream_check.c " 8 | ngx_tcp_ssl_deps="$ngx_addon_dir/modules/ngx_tcp_ssl_module.h" 9 | ngx_tcp_ssl_src="$ngx_addon_dir/modules/ngx_tcp_ssl_module.c" 10 | ngx_tcp_parser_deps="$ngx_addon_dir/parsers/parser.h $ngx_addon_dir/parsers/http_request_parser.h $ngx_addon_dir/parsers/http_response_parser.h $ngx_addon_dir/parsers/smtp_response_parser.h" 11 | ngx_tcp_parser_src="$ngx_addon_dir/parsers/http_request_parser.c $ngx_addon_dir/parsers/http_response_parser.c $ngx_addon_dir/parsers/smtp_response_parser.c" 12 | ngx_feature_test="int a;" 13 | . auto/feature 14 | 15 | if [ $ngx_found = yes ]; then 16 | CORE_INCS="$CORE_INCS $ngx_feature_path" 17 | ngx_addon_name=ngx_tcp_module 18 | 19 | TCP_CORE_MODULES="ngx_tcp_module ngx_tcp_core_module ngx_tcp_upstream_module" 20 | TCP_MODULES="ngx_tcp_proxy_module ngx_tcp_websocket_module ngx_tcp_upstream_ip_hash_module ngx_tcp_upstream_busyness_module" 21 | 22 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_feature_deps $ngx_tcp_parser_deps" 23 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_tcp_src $ngx_tcp_parser_src" 24 | 25 | #have=NGX_TCP_SSL . auto/have 26 | #USE_OPENSSL=YES 27 | #TCP_MODULES="$TCP_MODULES ngx_tcp_ssl_module" 28 | #NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_tcp_ssl_deps" 29 | #NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_tcp_ssl_src" 30 | 31 | EVENT_MODULES="$EVENT_MODULES $TCP_CORE_MODULES $TCP_MODULES" 32 | HTTP_MODULES="$HTTP_MODULES ngx_tcp_upstream_check_status_module" 33 | else 34 | cat << END 35 | $0: error: the ngx_tcp_module addon error. 36 | END 37 | exit 1 38 | fi 39 | -------------------------------------------------------------------------------- /modules/ngx_tcp_ssl_module.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NGX_TCP_SSL_H_INCLUDED_ 3 | #define _NGX_TCP_SSL_H_INCLUDED_ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | typedef struct { 12 | ngx_flag_t enable; 13 | 14 | ngx_ssl_t ssl; 15 | 16 | ngx_flag_t prefer_server_ciphers; 17 | 18 | ngx_uint_t protocols; 19 | 20 | ngx_uint_t verify; 21 | ngx_uint_t verify_depth; 22 | 23 | ssize_t builtin_session_cache; 24 | 25 | time_t session_timeout; 26 | 27 | ngx_str_t certificate; 28 | ngx_str_t certificate_key; 29 | ngx_str_t dhparam; 30 | #if defined(nginx_version) && nginx_version >= 1000006 31 | ngx_str_t ecdh_curve; 32 | #endif 33 | ngx_str_t client_certificate; 34 | ngx_str_t crl; 35 | 36 | ngx_str_t ciphers; 37 | 38 | #if defined(nginx_version) && nginx_version >= 1007003 39 | ngx_array_t *passwords; 40 | #endif 41 | 42 | ngx_shm_zone_t *shm_zone; 43 | 44 | u_char *file; 45 | ngx_uint_t line; 46 | } ngx_tcp_ssl_srv_conf_t; 47 | 48 | 49 | extern ngx_module_t ngx_tcp_ssl_module; 50 | 51 | 52 | #endif /* _NGX_TCP_SSL_H_INCLUDED_ */ 53 | -------------------------------------------------------------------------------- /modules/ngx_tcp_upstream_busyness_module.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | typedef struct { 8 | /* the round robin data must be first */ 9 | ngx_tcp_upstream_rr_peer_data_t rrp; 10 | 11 | u_char tries; 12 | 13 | ngx_event_get_peer_pt get_rr_peer; 14 | } ngx_tcp_upstream_busyness_peer_data_t; 15 | 16 | 17 | static ngx_int_t ngx_tcp_upstream_init_busyness_peer(ngx_tcp_session_t *s, 18 | ngx_tcp_upstream_srv_conf_t *us); 19 | static ngx_int_t ngx_tcp_upstream_get_busyness_peer(ngx_peer_connection_t *pc, 20 | void *data); 21 | static char *ngx_tcp_upstream_busyness(ngx_conf_t *cf, ngx_command_t *cmd, 22 | void *conf); 23 | 24 | 25 | static ngx_command_t ngx_tcp_upstream_busyness_commands[] = { 26 | 27 | { ngx_string("busyness"), 28 | NGX_TCP_UPS_CONF|NGX_CONF_NOARGS, 29 | ngx_tcp_upstream_busyness, 30 | 0, 31 | 0, 32 | NULL }, 33 | 34 | ngx_null_command 35 | }; 36 | 37 | 38 | static ngx_tcp_module_t ngx_tcp_upstream_busyness_module_ctx = { 39 | NULL, 40 | 41 | NULL, /* create main configuration */ 42 | NULL, /* init main configuration */ 43 | 44 | NULL, /* create server configuration */ 45 | NULL, /* merge server configuration */ 46 | }; 47 | 48 | 49 | ngx_module_t ngx_tcp_upstream_busyness_module = { 50 | NGX_MODULE_V1, 51 | &ngx_tcp_upstream_busyness_module_ctx, /* module context */ 52 | ngx_tcp_upstream_busyness_commands, /* module directives */ 53 | NGX_TCP_MODULE, /* module type */ 54 | NULL, /* init master */ 55 | NULL, /* init module */ 56 | NULL, /* init process */ 57 | NULL, /* init thread */ 58 | NULL, /* exit thread */ 59 | NULL, /* exit process */ 60 | NULL, /* exit master */ 61 | NGX_MODULE_V1_PADDING 62 | }; 63 | 64 | 65 | ngx_int_t 66 | ngx_tcp_upstream_init_busyness(ngx_conf_t *cf, ngx_tcp_upstream_srv_conf_t *us) 67 | { 68 | if (ngx_tcp_upstream_init_round_robin(cf, us) != NGX_OK) { 69 | return NGX_ERROR; 70 | } 71 | 72 | us->peer.init = ngx_tcp_upstream_init_busyness_peer; 73 | 74 | return NGX_OK; 75 | } 76 | 77 | 78 | static ngx_int_t 79 | ngx_tcp_upstream_init_busyness_peer(ngx_tcp_session_t *s, 80 | ngx_tcp_upstream_srv_conf_t *us) 81 | { 82 | ngx_tcp_upstream_busyness_peer_data_t *bp; 83 | 84 | bp = ngx_palloc(s->pool, sizeof(ngx_tcp_upstream_busyness_peer_data_t)); 85 | if (bp == NULL) { 86 | return NGX_ERROR; 87 | } 88 | 89 | s->upstream->peer.data = &bp->rrp; 90 | 91 | if (ngx_tcp_upstream_init_round_robin_peer(s, us) != NGX_OK) { 92 | return NGX_ERROR; 93 | } 94 | 95 | s->upstream->peer.get = ngx_tcp_upstream_get_busyness_peer; 96 | 97 | bp->tries = 0; 98 | bp->get_rr_peer = ngx_tcp_upstream_get_round_robin_peer; 99 | 100 | return NGX_OK; 101 | } 102 | 103 | 104 | static ngx_uint_t 105 | ngx_tcp_upstream_get_least_busy_index(ngx_tcp_upstream_rr_peers_t *rrps) 106 | { 107 | ngx_uint_t i, j, peer_index, check_index, busyness, min_busyness, start; 108 | 109 | min_busyness = (ngx_uint_t) (-1); 110 | 111 | peer_index = start = ngx_random() % rrps->number; 112 | 113 | for (i = 0; i < rrps->number; i++, start++) { 114 | 115 | j = start % rrps->number; 116 | check_index = rrps->peer[j].check_index; 117 | 118 | busyness = ngx_tcp_check_get_peer_busyness(check_index); 119 | if (busyness < min_busyness && !ngx_tcp_check_peer_down(check_index)) { 120 | min_busyness = busyness; 121 | peer_index = j; 122 | } 123 | } 124 | 125 | return peer_index; 126 | } 127 | 128 | 129 | static ngx_int_t 130 | ngx_tcp_upstream_get_busyness_peer(ngx_peer_connection_t *pc, void *data) 131 | { 132 | ngx_tcp_upstream_busyness_peer_data_t *bp = data; 133 | 134 | time_t now; 135 | uintptr_t m; 136 | ngx_uint_t n, p; 137 | ngx_tcp_upstream_rr_peer_t *peer; 138 | 139 | ngx_log_debug1(NGX_LOG_DEBUG_TCP, pc->log, 0, 140 | "get busyness peer, try: %ui", pc->tries); 141 | 142 | /* TODO: cached */ 143 | 144 | if (bp->tries > 20 || bp->rrp.peers->single || 145 | bp->rrp.peers->peer[0].check_index 146 | == (ngx_uint_t) NGX_INVALID_CHECK_INDEX) { 147 | 148 | ngx_log_debug1(NGX_LOG_DEBUG_TCP, pc->log, 0, 149 | "get busyness peer0, bp->tries: %ui", bp->tries); 150 | 151 | return bp->get_rr_peer(pc, &bp->rrp); 152 | } 153 | 154 | now = ngx_time(); 155 | 156 | pc->cached = 0; 157 | pc->connection = NULL; 158 | 159 | for ( ;; ) { 160 | p = ngx_tcp_upstream_get_least_busy_index(bp->rrp.peers); 161 | 162 | n = p / (8 * sizeof(uintptr_t)); 163 | m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); 164 | 165 | if (!(bp->rrp.tried[n] & m)) { 166 | 167 | peer = &bp->rrp.peers->peer[p]; 168 | 169 | ngx_log_debug4(NGX_LOG_DEBUG_TCP, pc->log, 0, 170 | "get busyness peer, check_index: %ui, %ui, " 171 | "%04XA, num: %d", 172 | peer->check_index, p, m, bp->rrp.peers->number); 173 | 174 | /* ngx_lock_mutex(bp->rrp.peers->mutex); */ 175 | 176 | if (!peer->down) { 177 | if (peer->max_fails == 0 || peer->fails < peer->max_fails) { 178 | break; 179 | } 180 | 181 | if (now - peer->accessed > peer->fail_timeout) { 182 | peer->fails = 0; 183 | break; 184 | } 185 | } 186 | 187 | bp->rrp.tried[n] |= m; 188 | 189 | /* ngx_unlock_mutex(bp->rrp.peers->mutex); */ 190 | 191 | pc->tries--; 192 | 193 | } 194 | 195 | ngx_log_debug2(NGX_LOG_DEBUG_TCP, pc->log, 0, 196 | "get busyness peer, bp->tries: %ui, p: %ui", 197 | bp->tries, p); 198 | 199 | if (++bp->tries >= 20) { 200 | return bp->get_rr_peer(pc, &bp->rrp); 201 | } 202 | } 203 | 204 | bp->rrp.current = p; 205 | 206 | pc->sockaddr = peer->sockaddr; 207 | pc->socklen = peer->socklen; 208 | pc->name = &peer->name; 209 | pc->check_index = peer->check_index; 210 | 211 | /* ngx_unlock_mutex(bp->rrp.peers->mutex); */ 212 | 213 | bp->rrp.tried[n] |= m; 214 | 215 | return NGX_OK; 216 | } 217 | 218 | 219 | static char * 220 | ngx_tcp_upstream_busyness(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 221 | { 222 | ngx_tcp_upstream_srv_conf_t *uscf; 223 | 224 | uscf = ngx_tcp_conf_get_module_srv_conf(cf, ngx_tcp_upstream_module); 225 | 226 | uscf->peer.init_upstream = ngx_tcp_upstream_init_busyness; 227 | 228 | uscf->flags = NGX_TCP_UPSTREAM_CREATE 229 | |NGX_TCP_UPSTREAM_MAX_FAILS 230 | |NGX_TCP_UPSTREAM_FAIL_TIMEOUT 231 | |NGX_TCP_UPSTREAM_MAX_BUSY 232 | |NGX_TCP_UPSTREAM_DOWN; 233 | 234 | return NGX_CONF_OK; 235 | } 236 | -------------------------------------------------------------------------------- /modules/ngx_tcp_upstream_ip_hash_module.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | typedef struct { 8 | /* the round robin data must be first */ 9 | ngx_tcp_upstream_rr_peer_data_t rrp; 10 | 11 | ngx_uint_t hash; 12 | 13 | u_char addr[3]; 14 | 15 | u_char tries; 16 | 17 | ngx_event_get_peer_pt get_rr_peer; 18 | } ngx_tcp_upstream_ip_hash_peer_data_t; 19 | 20 | 21 | static ngx_int_t ngx_tcp_upstream_init_ip_hash_peer(ngx_tcp_session_t *s, 22 | ngx_tcp_upstream_srv_conf_t *us); 23 | static ngx_int_t ngx_tcp_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, 24 | void *data); 25 | static char *ngx_tcp_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, 26 | void *conf); 27 | 28 | 29 | static ngx_command_t ngx_tcp_upstream_ip_hash_commands[] = { 30 | 31 | { ngx_string("ip_hash"), 32 | NGX_TCP_UPS_CONF|NGX_CONF_NOARGS, 33 | ngx_tcp_upstream_ip_hash, 34 | 0, 35 | 0, 36 | NULL }, 37 | 38 | ngx_null_command 39 | }; 40 | 41 | 42 | static ngx_tcp_module_t ngx_tcp_upstream_ip_hash_module_ctx = { 43 | NULL, 44 | 45 | NULL, /* create main configuration */ 46 | NULL, /* init main configuration */ 47 | 48 | NULL, /* create server configuration */ 49 | NULL, /* merge server configuration */ 50 | }; 51 | 52 | 53 | ngx_module_t ngx_tcp_upstream_ip_hash_module = { 54 | NGX_MODULE_V1, 55 | &ngx_tcp_upstream_ip_hash_module_ctx, /* module context */ 56 | ngx_tcp_upstream_ip_hash_commands, /* module directives */ 57 | NGX_TCP_MODULE, /* module type */ 58 | NULL, /* init master */ 59 | NULL, /* init module */ 60 | NULL, /* init process */ 61 | NULL, /* init thread */ 62 | NULL, /* exit thread */ 63 | NULL, /* exit process */ 64 | NULL, /* exit master */ 65 | NGX_MODULE_V1_PADDING 66 | }; 67 | 68 | 69 | ngx_int_t 70 | ngx_tcp_upstream_init_ip_hash(ngx_conf_t *cf, ngx_tcp_upstream_srv_conf_t *us) 71 | { 72 | if (ngx_tcp_upstream_init_round_robin(cf, us) != NGX_OK) { 73 | return NGX_ERROR; 74 | } 75 | 76 | us->peer.init = ngx_tcp_upstream_init_ip_hash_peer; 77 | 78 | return NGX_OK; 79 | } 80 | 81 | 82 | static ngx_int_t 83 | ngx_tcp_upstream_init_ip_hash_peer(ngx_tcp_session_t *s, 84 | ngx_tcp_upstream_srv_conf_t *us) 85 | { 86 | u_char *p; 87 | struct sockaddr_in *sin; 88 | ngx_tcp_upstream_ip_hash_peer_data_t *iphp; 89 | 90 | iphp = ngx_palloc(s->pool, sizeof(ngx_tcp_upstream_ip_hash_peer_data_t)); 91 | if (iphp == NULL) { 92 | return NGX_ERROR; 93 | } 94 | 95 | s->upstream->peer.data = &iphp->rrp; 96 | 97 | if (ngx_tcp_upstream_init_round_robin_peer(s, us) != NGX_OK) { 98 | return NGX_ERROR; 99 | } 100 | 101 | s->upstream->peer.get = ngx_tcp_upstream_get_ip_hash_peer; 102 | 103 | /* AF_INET only */ 104 | 105 | if (s->connection->sockaddr->sa_family == AF_INET) { 106 | 107 | sin = (struct sockaddr_in *) s->connection->sockaddr; 108 | p = (u_char *) &sin->sin_addr.s_addr; 109 | iphp->addr[0] = p[0]; 110 | iphp->addr[1] = p[1]; 111 | iphp->addr[2] = p[2]; 112 | 113 | } else { 114 | iphp->addr[0] = 0; 115 | iphp->addr[1] = 0; 116 | iphp->addr[2] = 0; 117 | } 118 | 119 | iphp->hash = 89; 120 | iphp->tries = 0; 121 | iphp->get_rr_peer = ngx_tcp_upstream_get_round_robin_peer; 122 | 123 | return NGX_OK; 124 | } 125 | 126 | 127 | static ngx_int_t 128 | ngx_tcp_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data) 129 | { 130 | ngx_tcp_upstream_ip_hash_peer_data_t *iphp = data; 131 | 132 | time_t now; 133 | uintptr_t m; 134 | ngx_uint_t i, n, p, hash; 135 | ngx_tcp_upstream_rr_peer_t *peer; 136 | 137 | ngx_log_debug1(NGX_LOG_DEBUG_TCP, pc->log, 0, 138 | "get ip hash peer, try: %ui", pc->tries); 139 | 140 | /* TODO: cached */ 141 | 142 | if (iphp->tries > 20 || iphp->rrp.peers->single) { 143 | return iphp->get_rr_peer(pc, &iphp->rrp); 144 | } 145 | 146 | now = ngx_time(); 147 | 148 | pc->cached = 0; 149 | pc->connection = NULL; 150 | 151 | hash = iphp->hash; 152 | 153 | for ( ;; ) { 154 | 155 | for (i = 0; i < 3; i++) { 156 | hash = (hash * 113 + iphp->addr[i]) % 6271; 157 | } 158 | 159 | p = hash % iphp->rrp.peers->number; 160 | 161 | n = p / (8 * sizeof(uintptr_t)); 162 | m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); 163 | 164 | if (!(iphp->rrp.tried[n] & m)) { 165 | 166 | ngx_log_debug4(NGX_LOG_DEBUG_TCP, pc->log, 0, 167 | "get ip hash peer, hash: %d, %ui, %04XA, num: %d", 168 | hash, p, m, iphp->rrp.peers->number); 169 | 170 | peer = &iphp->rrp.peers->peer[p]; 171 | 172 | /* ngx_lock_mutex(iphp->rrp.peers->mutex); */ 173 | 174 | if (!peer->down) { 175 | if (!ngx_tcp_check_peer_down(peer->check_index)) { 176 | 177 | if (peer->max_fails == 0 || peer->fails < peer->max_fails) { 178 | break; 179 | } 180 | 181 | if (now - peer->accessed > peer->fail_timeout) { 182 | peer->fails = 0; 183 | break; 184 | } 185 | } 186 | } 187 | 188 | iphp->rrp.tried[n] |= m; 189 | 190 | /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */ 191 | 192 | pc->tries--; 193 | } 194 | 195 | if (++iphp->tries >= 20) { 196 | return iphp->get_rr_peer(pc, &iphp->rrp); 197 | } 198 | } 199 | 200 | iphp->rrp.current = p; 201 | 202 | pc->sockaddr = peer->sockaddr; 203 | pc->socklen = peer->socklen; 204 | pc->name = &peer->name; 205 | pc->check_index = peer->check_index; 206 | 207 | /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */ 208 | 209 | iphp->rrp.tried[n] |= m; 210 | iphp->hash = hash; 211 | 212 | return NGX_OK; 213 | } 214 | 215 | 216 | static char * 217 | ngx_tcp_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 218 | { 219 | ngx_tcp_upstream_srv_conf_t *uscf; 220 | 221 | uscf = ngx_tcp_conf_get_module_srv_conf(cf, ngx_tcp_upstream_module); 222 | 223 | uscf->peer.init_upstream = ngx_tcp_upstream_init_ip_hash; 224 | 225 | uscf->flags = NGX_TCP_UPSTREAM_CREATE 226 | |NGX_TCP_UPSTREAM_MAX_FAILS 227 | |NGX_TCP_UPSTREAM_FAIL_TIMEOUT 228 | |NGX_TCP_UPSTREAM_MAX_BUSY 229 | |NGX_TCP_UPSTREAM_DOWN; 230 | 231 | return NGX_CONF_OK; 232 | } 233 | -------------------------------------------------------------------------------- /ngx_tcp.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NGX_TCP_H_INCLUDED_ 3 | #define _NGX_TCP_H_INCLUDED_ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | typedef struct ngx_tcp_protocol_s ngx_tcp_protocol_t; 14 | typedef struct ngx_tcp_upstream_s ngx_tcp_upstream_t; 15 | typedef struct ngx_tcp_cleanup_s ngx_tcp_cleanup_t; 16 | 17 | typedef struct ngx_tcp_core_srv_conf_s ngx_tcp_core_srv_conf_t; 18 | 19 | typedef struct ngx_tcp_upstream_srv_conf_s ngx_tcp_upstream_srv_conf_t; 20 | typedef struct ngx_tcp_upstream_resolved_s ngx_tcp_upstream_resolved_t; 21 | 22 | typedef struct ngx_tcp_check_peer_conf_s ngx_tcp_check_peer_conf_t; 23 | typedef struct ngx_tcp_check_peers_conf_s ngx_tcp_check_peers_conf_t; 24 | typedef struct check_conf_s check_conf_t; 25 | 26 | /* make nginx-0.8.22+ happy */ 27 | #if defined(nginx_version) && nginx_version >= 8022 28 | typedef ngx_addr_t ngx_peer_addr_t; 29 | #endif 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #if (NGX_TCP_SSL) 37 | #include 38 | #endif 39 | 40 | 41 | typedef struct { 42 | void **main_conf; 43 | void **srv_conf; 44 | } ngx_tcp_conf_ctx_t; 45 | 46 | 47 | typedef struct { 48 | u_char sockaddr[NGX_SOCKADDRLEN]; 49 | socklen_t socklen; 50 | 51 | /* server ctx */ 52 | ngx_tcp_conf_ctx_t *ctx; 53 | 54 | unsigned default_port:1; 55 | unsigned bind:1; 56 | unsigned wildcard:1; 57 | #if (NGX_TCP_SSL) 58 | unsigned ssl:1; 59 | #endif 60 | #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) 61 | unsigned ipv6only:2; 62 | #endif 63 | ngx_tcp_core_srv_conf_t *conf; 64 | } ngx_tcp_listen_t; 65 | 66 | 67 | typedef struct { 68 | ngx_str_t name; 69 | } ngx_tcp_server_name_t; 70 | 71 | 72 | typedef struct { 73 | ngx_uint_t hash; 74 | ngx_str_t name; 75 | ngx_tcp_listen_t *listen; 76 | ngx_tcp_conf_ctx_t *ctx; 77 | } ngx_tcp_virtual_server_t; 78 | 79 | 80 | typedef struct { 81 | ngx_str_t name; 82 | } ngx_tcp_core_loc_t; 83 | 84 | 85 | typedef struct { 86 | ngx_tcp_conf_ctx_t *ctx; 87 | ngx_tcp_conf_ctx_t *default_ctx; 88 | ngx_str_t addr_text; 89 | #if (NGX_TCP_SSL) 90 | ngx_uint_t ssl; /* unsigned ssl:1; */ 91 | #endif 92 | } ngx_tcp_addr_conf_t; 93 | 94 | typedef struct { 95 | in_addr_t addr; 96 | ngx_tcp_addr_conf_t conf; 97 | } ngx_tcp_in_addr_t; 98 | 99 | 100 | #if (NGX_HAVE_INET6) 101 | 102 | typedef struct { 103 | struct in6_addr addr6; 104 | ngx_tcp_addr_conf_t conf; 105 | } ngx_tcp_in6_addr_t; 106 | 107 | #endif 108 | 109 | 110 | typedef struct { 111 | /* ngx_tcp_in_addr_t or ngx_tcp_in6_addr_t */ 112 | void *addrs; 113 | ngx_uint_t naddrs; 114 | } ngx_tcp_port_t; 115 | 116 | 117 | typedef struct { 118 | int family; 119 | in_port_t port; 120 | ngx_array_t addrs; /* array of ngx_tcp_conf_addr_t */ 121 | } ngx_tcp_conf_port_t; 122 | 123 | 124 | typedef struct { 125 | struct sockaddr *sockaddr; 126 | socklen_t socklen; 127 | 128 | ngx_tcp_conf_ctx_t *ctx; 129 | ngx_tcp_conf_ctx_t *default_ctx; 130 | 131 | unsigned bind:1; 132 | unsigned wildcard:1; 133 | #if (NGX_TCP_SSL) 134 | unsigned ssl:1; 135 | #endif 136 | #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) 137 | unsigned ipv6only:2; 138 | #endif 139 | } ngx_tcp_conf_addr_t; 140 | 141 | typedef struct { 142 | in_addr_t mask; 143 | in_addr_t addr; 144 | ngx_uint_t deny; /* unsigned deny:1; */ 145 | } ngx_tcp_access_rule_t; 146 | 147 | typedef struct { 148 | ngx_array_t servers; /* ngx_tcp_core_srv_conf_t */ 149 | ngx_array_t listen; /* ngx_tcp_listen_t */ 150 | ngx_array_t virtual_servers; /* ngx_tcp_virtual_server_t */ 151 | } ngx_tcp_core_main_conf_t; 152 | 153 | typedef struct { 154 | ngx_open_file_t *file; 155 | time_t disk_full_time; 156 | time_t error_log_time; 157 | } ngx_tcp_log_t; 158 | 159 | typedef struct { 160 | u_char *start; 161 | u_char *pos; 162 | u_char *last; 163 | } ngx_tcp_log_buf_t; 164 | 165 | typedef struct { 166 | ngx_array_t *logs; /* array of ngx_tcp_log_t */ 167 | 168 | ngx_open_file_cache_t *open_file_cache; 169 | time_t open_file_cache_valid; 170 | ngx_uint_t open_file_cache_min_uses; 171 | 172 | ngx_uint_t off; /* unsigned off:1 */ 173 | } ngx_tcp_log_srv_conf_t; 174 | 175 | 176 | #define NGX_TCP_GENERIC_PROTOCOL 0 177 | #define NGX_TCP_WEBSOCKET_PROTOCOL 1 178 | 179 | 180 | struct ngx_tcp_core_srv_conf_s { 181 | /* array of the ngx_tcp_server_name_t, "server_name" directive */ 182 | ngx_array_t server_names; 183 | 184 | /* array of the ngx_tcp_core_loc_t, "location" directive */ 185 | ngx_array_t locations; 186 | 187 | ngx_tcp_protocol_t *protocol; 188 | 189 | ngx_msec_t timeout; 190 | ngx_msec_t resolver_timeout; 191 | 192 | ngx_flag_t so_keepalive; 193 | ngx_flag_t tcp_nodelay; 194 | 195 | ngx_str_t server_name; 196 | 197 | u_char *file_name; 198 | ngx_int_t line; 199 | 200 | ngx_resolver_t *resolver; 201 | 202 | /*ACL rules*/ 203 | ngx_array_t *rules; 204 | 205 | ngx_tcp_log_srv_conf_t *access_log; 206 | 207 | /* server ctx */ 208 | ngx_tcp_conf_ctx_t *ctx; 209 | }; 210 | 211 | 212 | typedef struct { 213 | ngx_str_t *client; 214 | ngx_tcp_session_t *session; 215 | } ngx_tcp_log_ctx_t; 216 | 217 | 218 | typedef void (*ngx_tcp_init_session_pt)(ngx_tcp_session_t *s); 219 | typedef void (*ngx_tcp_init_protocol_pt)(ngx_event_t *rev); 220 | typedef void (*ngx_tcp_parse_protocol_pt)(ngx_event_t *rev); 221 | 222 | 223 | struct ngx_tcp_protocol_s { 224 | ngx_str_t name; 225 | in_port_t port[4]; 226 | ngx_uint_t type; 227 | 228 | ngx_tcp_init_session_pt init_session; 229 | ngx_tcp_init_protocol_pt init_protocol; 230 | ngx_tcp_parse_protocol_pt parse_protocol; 231 | 232 | ngx_str_t internal_server_error; 233 | }; 234 | 235 | 236 | typedef struct { 237 | ngx_tcp_protocol_t *protocol; 238 | 239 | void *(*create_main_conf)(ngx_conf_t *cf); 240 | char *(*init_main_conf)(ngx_conf_t *cf, void *conf); 241 | 242 | void *(*create_srv_conf)(ngx_conf_t *cf); 243 | char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, 244 | void *conf); 245 | } ngx_tcp_module_t; 246 | 247 | 248 | #define NGX_TCP_MODULE 0x00504354 /* "TCP" */ 249 | 250 | #define NGX_TCP_MAIN_CONF 0x02000000 251 | #define NGX_TCP_SRV_CONF 0x04000000 252 | #define NGX_TCP_LOC_CONF 0x08000000 253 | #define NGX_TCP_UPS_CONF 0x10000000 254 | 255 | 256 | #define NGX_TCP_MAIN_CONF_OFFSET offsetof(ngx_tcp_conf_ctx_t, main_conf) 257 | #define NGX_TCP_SRV_CONF_OFFSET offsetof(ngx_tcp_conf_ctx_t, srv_conf) 258 | 259 | 260 | #define ngx_tcp_get_module_ctx(s, module) (s)->ctx[module.ctx_index] 261 | #define ngx_tcp_set_ctx(s, c, module) s->ctx[module.ctx_index] = c; 262 | #define ngx_tcp_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL; 263 | 264 | 265 | #define ngx_tcp_get_module_main_conf(s, module) \ 266 | (s)->main_conf[module.ctx_index] 267 | #define ngx_tcp_get_module_srv_conf(s, module) (s)->srv_conf[module.ctx_index] 268 | 269 | #define ngx_tcp_conf_get_module_main_conf(cf, module) \ 270 | ((ngx_tcp_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] 271 | #define ngx_tcp_conf_get_module_srv_conf(cf, module) \ 272 | ((ngx_tcp_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] 273 | 274 | #define ngx_tcp_cycle_get_module_main_conf(cycle, module) \ 275 | (cycle->conf_ctx[ngx_tcp_module.index] ? \ 276 | ((ngx_tcp_conf_ctx_t *) cycle->conf_ctx[ngx_tcp_module.index]) \ 277 | ->main_conf[module.ctx_index]: \ 278 | NULL) 279 | 280 | extern ngx_uint_t ngx_tcp_max_module; 281 | extern ngx_module_t ngx_tcp_core_module; 282 | extern ngx_module_t ngx_tcp_module; 283 | 284 | #endif /* _NGX_TCP_H_INCLUDED_ */ 285 | -------------------------------------------------------------------------------- /ngx_tcp_access.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | ngx_int_t 8 | ngx_tcp_access_handler(ngx_tcp_session_t *s) 9 | { 10 | ngx_uint_t i; 11 | struct sockaddr_in *sin; 12 | ngx_tcp_access_rule_t *rule; 13 | ngx_tcp_core_srv_conf_t *cscf; 14 | 15 | cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); 16 | 17 | if (cscf->rules == NULL) { 18 | return NGX_DECLINED; 19 | } 20 | 21 | /* AF_INET only */ 22 | 23 | if (s->connection->sockaddr->sa_family != AF_INET) { 24 | return NGX_DECLINED; 25 | } 26 | 27 | sin = (struct sockaddr_in *) s->connection->sockaddr; 28 | 29 | rule = cscf->rules->elts; 30 | for (i = 0; i < cscf->rules->nelts; i++) { 31 | 32 | ngx_log_debug3(NGX_LOG_DEBUG_TCP, s->connection->log, 0, 33 | "access: %08XD %08XD %08XD", 34 | sin->sin_addr.s_addr, rule[i].mask, rule[i].addr); 35 | 36 | if ((sin->sin_addr.s_addr & rule[i].mask) == rule[i].addr) { 37 | if (rule[i].deny) { 38 | ngx_log_error(NGX_LOG_NOTICE, s->connection->log, 0, 39 | "access forbidden by rule"); 40 | 41 | return NGX_ERROR; 42 | } 43 | 44 | return NGX_OK; 45 | } 46 | } 47 | 48 | return NGX_DECLINED; 49 | } 50 | -------------------------------------------------------------------------------- /ngx_tcp_log.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | static u_char * ngx_tcp_time(u_char *buf, time_t t); 9 | static u_char *ngx_tcp_log_fill(ngx_tcp_session_t *s, u_char *buf); 10 | static void ngx_tcp_log_write(ngx_tcp_session_t *s, ngx_tcp_log_t *log, 11 | u_char *buf, size_t len); 12 | 13 | 14 | ngx_int_t 15 | ngx_tcp_log_handler(ngx_tcp_session_t *s) 16 | { 17 | u_char *line, *p; 18 | size_t len; 19 | ngx_uint_t l; 20 | ngx_connection_t *c; 21 | ngx_tcp_log_t *log; 22 | ngx_open_file_t *file; 23 | #if (nginx_version) >= 1003010 || (nginx_version) >= 1002007 && (nginx_version) < 1003000 24 | ngx_tcp_log_buf_t *buffer; 25 | #endif 26 | ngx_tcp_log_srv_conf_t *lscf; 27 | ngx_tcp_core_srv_conf_t *cscf; 28 | 29 | ngx_log_debug0(NGX_LOG_DEBUG_TCP, s->connection->log, 0, 30 | "tcp access log handler"); 31 | 32 | cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); 33 | lscf = cscf->access_log; 34 | 35 | if (lscf->off) { 36 | return NGX_OK; 37 | } 38 | 39 | c = s->connection; 40 | log = lscf->logs->elts; 41 | for (l = 0; l < lscf->logs->nelts; l++) { 42 | 43 | if (ngx_time() == log[l].disk_full_time) { 44 | 45 | /* 46 | * on FreeBSD writing to a full filesystem with enabled softupdates 47 | * may block process for much longer time than writing to non-full 48 | * filesystem, so we skip writing to a log for one second 49 | */ 50 | 51 | continue; 52 | } 53 | 54 | len = 0; 55 | 56 | /* Calculate the length */ 57 | len += sizeof("1970/09/28 12:00:00"); /* log time */ 58 | len += NGX_INT64_LEN + 2; /* [ngx_pid] */ 59 | len += c->addr_text.len + 1; /* client address */ 60 | len += s->addr_text->len + 1; /* this session address */ 61 | len += sizeof("1970/09/28 12:00:00"); /* accept time */ 62 | len += sizeof("255.255.255.255:65536"); /* upstream address */ 63 | len += NGX_OFF_T_LEN + 1; /* read bytes from client */ 64 | len += NGX_OFF_T_LEN + 1; /* write bytes to client */ 65 | len += NGX_LINEFEED_SIZE; 66 | 67 | file = log[l].file; 68 | 69 | #if (nginx_version) >= 1003010 || (nginx_version) >= 1002007 && (nginx_version) < 1003000 70 | if (file && file->data) { 71 | 72 | buffer = file->data; 73 | 74 | if (len > (size_t) (buffer->last - buffer->pos)) { 75 | 76 | ngx_tcp_log_write(s, &log[l], buffer->start, 77 | buffer->pos - buffer->start); 78 | 79 | buffer->pos = buffer->start; 80 | } 81 | 82 | if (len <= (size_t) (buffer->last - buffer->pos)) { 83 | 84 | p = buffer->pos; 85 | 86 | p = ngx_tcp_log_fill(s, p); 87 | 88 | buffer->pos = p; 89 | 90 | continue; 91 | } 92 | } 93 | #else 94 | if (file && file->buffer) { 95 | 96 | if (len > (size_t) (file->last - file->pos)) { 97 | 98 | ngx_tcp_log_write(s, &log[l], file->buffer, 99 | file->pos - file->buffer); 100 | 101 | file->pos = file->buffer; 102 | } 103 | 104 | if (len <= (size_t) (file->last - file->pos)) { 105 | 106 | p = file->pos; 107 | 108 | p = ngx_tcp_log_fill(s, p); 109 | 110 | file->pos = p; 111 | 112 | continue; 113 | } 114 | } 115 | #endif 116 | 117 | line = ngx_pnalloc(s->pool, len); 118 | if (line == NULL) { 119 | return NGX_ERROR; 120 | } 121 | 122 | p = line; 123 | 124 | p = ngx_tcp_log_fill(s, p); 125 | 126 | ngx_tcp_log_write(s, &log[l], line, p - line); 127 | } 128 | 129 | return NGX_OK; 130 | } 131 | 132 | 133 | static u_char * 134 | ngx_tcp_time(u_char *buf, time_t t) 135 | { 136 | ngx_tm_t tm; 137 | 138 | ngx_localtime(t, &tm); 139 | 140 | return ngx_sprintf(buf, "%4d/%02d/%02d %02d:%02d:%02d", 141 | tm.ngx_tm_year, tm.ngx_tm_mon, 142 | tm.ngx_tm_mday, tm.ngx_tm_hour, 143 | tm.ngx_tm_min, tm.ngx_tm_sec); 144 | } 145 | 146 | 147 | static u_char * 148 | ngx_tcp_log_fill(ngx_tcp_session_t *s, u_char *buf) 149 | { 150 | u_char *last; 151 | ngx_str_t *name; 152 | ngx_connection_t *c; 153 | ngx_tcp_upstream_t *u; 154 | 155 | c = s->connection; 156 | 157 | last = ngx_cpymem(buf, ngx_cached_err_log_time.data, 158 | ngx_cached_err_log_time.len); 159 | 160 | last = ngx_sprintf(last, " [%P]", ngx_pid); 161 | last = ngx_sprintf(last, " %V", &c->addr_text); 162 | last = ngx_sprintf(last, " %V ", s->addr_text); 163 | last = ngx_tcp_time(last, s->start_sec); 164 | 165 | name = NULL; 166 | if (s->upstream) { 167 | u = s->upstream; 168 | if (u->peer.connection) { 169 | name = u->peer.name; 170 | } 171 | } 172 | 173 | if (name) { 174 | last = ngx_sprintf(last, " %V", name); 175 | } 176 | else { 177 | last = ngx_sprintf(last, " -"); 178 | } 179 | 180 | last = ngx_sprintf(last, " %O", s->bytes_read); 181 | last = ngx_sprintf(last, " %O", s->bytes_write); 182 | 183 | ngx_linefeed(last); 184 | 185 | return last; 186 | } 187 | 188 | 189 | static void 190 | ngx_tcp_log_write(ngx_tcp_session_t *s, ngx_tcp_log_t *log, u_char *buf, 191 | size_t len) 192 | { 193 | u_char *name; 194 | time_t now; 195 | ssize_t n; 196 | ngx_err_t err; 197 | 198 | if(len == 0) return; 199 | 200 | name = log->file->name.data; 201 | n = ngx_write_fd(log->file->fd, buf, len); 202 | 203 | if (n == (ssize_t) len) { 204 | return; 205 | } 206 | 207 | now = ngx_time(); 208 | 209 | if (n == -1) { 210 | err = ngx_errno; 211 | 212 | if (err == NGX_ENOSPC) { 213 | log->disk_full_time = now; 214 | } 215 | 216 | if (now - log->error_log_time > 59) { 217 | ngx_log_error(NGX_LOG_ALERT, s->connection->log, err, 218 | ngx_write_fd_n " to \"%s\" failed", name); 219 | 220 | log->error_log_time = now; 221 | } 222 | 223 | return; 224 | } 225 | 226 | if (now - log->error_log_time > 59) { 227 | ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0, 228 | ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", 229 | name, n, len); 230 | 231 | log->error_log_time = now; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /ngx_tcp_session.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | static void ngx_tcp_init_session(ngx_connection_t *c); 9 | static void ngx_tcp_set_session_socket(ngx_tcp_session_t *s); 10 | static void ngx_tcp_process_session(ngx_connection_t *c); 11 | 12 | #if (NGX_TCP_SSL) 13 | static void ngx_tcp_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); 14 | static void ngx_tcp_ssl_handshake_handler(ngx_connection_t *c); 15 | #endif 16 | 17 | 18 | void 19 | ngx_tcp_init_connection(ngx_connection_t *c) 20 | { 21 | ngx_uint_t i; 22 | ngx_tcp_port_t *port; 23 | struct sockaddr *sa; 24 | struct sockaddr_in *sin; 25 | ngx_tcp_log_ctx_t *ctx; 26 | ngx_tcp_in_addr_t *addr; 27 | ngx_tcp_session_t *s; 28 | ngx_tcp_addr_conf_t *addr_conf; 29 | #if (NGX_HAVE_INET6) 30 | struct sockaddr_in6 *sin6; 31 | ngx_tcp_in6_addr_t *addr6; 32 | #endif 33 | 34 | 35 | /* find the server configuration for the address:port */ 36 | 37 | /* AF_INET only */ 38 | 39 | port = c->listening->servers; 40 | 41 | if (port->naddrs > 1) { 42 | 43 | /* 44 | * There are several addresses on this port and one of them 45 | * is the "*:port" wildcard so getsockname() is needed to determine 46 | * the server address. 47 | * 48 | * AcceptEx() already gave this address. 49 | */ 50 | 51 | if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { 52 | ngx_tcp_close_connection(c); 53 | return; 54 | } 55 | 56 | sa = c->local_sockaddr; 57 | 58 | switch (sa->sa_family) { 59 | 60 | #if (NGX_HAVE_INET6) 61 | case AF_INET6: 62 | sin6 = (struct sockaddr_in6 *) sa; 63 | 64 | addr6 = port->addrs; 65 | 66 | /* the last address is "*" */ 67 | 68 | for (i = 0; i < port->naddrs - 1; i++) { 69 | if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { 70 | break; 71 | } 72 | } 73 | 74 | addr_conf = &addr6[i].conf; 75 | 76 | break; 77 | #endif 78 | 79 | default: /* AF_INET */ 80 | sin = (struct sockaddr_in *) sa; 81 | 82 | addr = port->addrs; 83 | 84 | /* the last address is "*" */ 85 | 86 | for (i = 0; i < port->naddrs - 1; i++) { 87 | if (addr[i].addr == sin->sin_addr.s_addr) { 88 | break; 89 | } 90 | } 91 | 92 | addr_conf = &addr[i].conf; 93 | 94 | break; 95 | } 96 | 97 | } else { 98 | switch (c->local_sockaddr->sa_family) { 99 | 100 | #if (NGX_HAVE_INET6) 101 | case AF_INET6: 102 | addr6 = port->addrs; 103 | addr_conf = &addr6[0].conf; 104 | break; 105 | #endif 106 | 107 | default: /* AF_INET */ 108 | addr = port->addrs; 109 | addr_conf = &addr[0].conf; 110 | break; 111 | } 112 | } 113 | 114 | s = ngx_pcalloc(c->pool, sizeof(ngx_tcp_session_t)); 115 | if (s == NULL) { 116 | ngx_tcp_close_connection(c); 117 | return; 118 | } 119 | 120 | if (addr_conf->default_ctx) { 121 | s->main_conf = addr_conf->default_ctx->main_conf; 122 | s->srv_conf = addr_conf->default_ctx->srv_conf; 123 | } 124 | else { 125 | s->main_conf = addr_conf->ctx->main_conf; 126 | s->srv_conf = addr_conf->ctx->srv_conf; 127 | } 128 | 129 | s->addr_text = &addr_conf->addr_text; 130 | 131 | c->data = s; 132 | s->connection = c; 133 | 134 | ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V", 135 | c->number, &c->addr_text, s->addr_text); 136 | 137 | ctx = ngx_palloc(c->pool, sizeof(ngx_tcp_log_ctx_t)); 138 | if (ctx == NULL) { 139 | ngx_tcp_close_connection(c); 140 | return; 141 | } 142 | 143 | ctx->client = &c->addr_text; 144 | ctx->session = s; 145 | 146 | c->log->connection = c->number; 147 | c->log->handler = ngx_tcp_log_error; 148 | c->log->data = ctx; 149 | c->log->action = "nginx tcp module init connection"; 150 | 151 | c->log_error = NGX_ERROR_INFO; 152 | 153 | #if (NGX_TCP_SSL) 154 | 155 | { 156 | ngx_tcp_ssl_srv_conf_t *sscf; 157 | 158 | sscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_ssl_module); 159 | if (sscf->enable || addr_conf->ssl) { 160 | 161 | if (c->ssl == NULL) { 162 | 163 | c->log->action = "SSL handshaking"; 164 | 165 | if (addr_conf->ssl && sscf->ssl.ctx == NULL) { 166 | ngx_log_error(NGX_LOG_ERR, c->log, 0, 167 | "no \"ssl_certificate\" is defined " 168 | "in server listening on SSL port"); 169 | ngx_tcp_close_connection(c); 170 | return; 171 | } 172 | 173 | ngx_tcp_ssl_init_connection(&sscf->ssl, c); 174 | return; 175 | } 176 | } 177 | } 178 | 179 | #endif 180 | 181 | ngx_tcp_init_session(c); 182 | } 183 | 184 | 185 | #if (NGX_TCP_SSL) 186 | 187 | static void 188 | ngx_tcp_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) 189 | { 190 | ngx_tcp_session_t *s; 191 | ngx_tcp_core_srv_conf_t *cscf; 192 | 193 | if (ngx_ssl_create_connection(ssl, c, NGX_SSL_BUFFER) == NGX_ERROR) { 194 | ngx_tcp_close_connection(c); 195 | return; 196 | } 197 | 198 | if (ngx_ssl_handshake(c) == NGX_AGAIN) { 199 | 200 | s = c->data; 201 | 202 | cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); 203 | 204 | ngx_add_timer(c->read, cscf->timeout); 205 | 206 | c->ssl->handler = ngx_tcp_ssl_handshake_handler; 207 | 208 | return; 209 | } 210 | 211 | ngx_tcp_ssl_handshake_handler(c); 212 | } 213 | 214 | 215 | static void 216 | ngx_tcp_ssl_handshake_handler(ngx_connection_t *c) 217 | { 218 | if (c->ssl->handshaked) { 219 | 220 | c->read->ready = 0; 221 | 222 | ngx_tcp_init_session(c); 223 | return; 224 | } 225 | 226 | ngx_tcp_close_connection(c); 227 | } 228 | 229 | #endif 230 | 231 | 232 | static void 233 | ngx_tcp_init_session(ngx_connection_t *c) 234 | { 235 | ngx_time_t *tp; 236 | ngx_tcp_session_t *s; 237 | ngx_tcp_core_srv_conf_t *cscf; 238 | 239 | s = c->data; 240 | 241 | s->signature = NGX_TCP_MODULE; 242 | s->pool = c->pool; 243 | 244 | cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); 245 | if (cscf == NULL) { 246 | ngx_tcp_finalize_session(s); 247 | return; 248 | } 249 | 250 | s->ctx = ngx_pcalloc(s->pool, sizeof(void *) * ngx_tcp_max_module); 251 | if (s->ctx == NULL) { 252 | ngx_tcp_finalize_session(s); 253 | return; 254 | } 255 | 256 | tp = ngx_timeofday(); 257 | s->start_sec = tp->sec; 258 | s->start_msec = tp->msec; 259 | 260 | s->bytes_read = 0; 261 | s->bytes_write = 0; 262 | 263 | ngx_tcp_set_session_socket(s); 264 | 265 | ngx_tcp_process_session(c); 266 | } 267 | 268 | 269 | static void 270 | ngx_tcp_set_session_socket(ngx_tcp_session_t *s) 271 | { 272 | int keepalive; 273 | int tcp_nodelay; 274 | ngx_tcp_core_srv_conf_t *cscf; 275 | 276 | cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); 277 | 278 | if (cscf->so_keepalive) { 279 | keepalive = 1; 280 | 281 | if (setsockopt(s->connection->fd, SOL_SOCKET, SO_KEEPALIVE, 282 | (const void *) &keepalive, sizeof(int)) == -1) 283 | { 284 | ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno, 285 | "setsockopt(SO_KEEPALIVE) failed"); 286 | } 287 | } 288 | 289 | if (cscf->tcp_nodelay) { 290 | tcp_nodelay = 1; 291 | if (setsockopt(s->connection->fd, IPPROTO_TCP, TCP_NODELAY, 292 | (const void *) &tcp_nodelay, sizeof(int)) 293 | == -1) 294 | { 295 | ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno, 296 | "setsockopt(TCP_NODELAY) failed"); 297 | } 298 | 299 | s->connection->tcp_nodelay = NGX_TCP_NODELAY_SET; 300 | } 301 | } 302 | 303 | 304 | static void 305 | ngx_tcp_process_session(ngx_connection_t *c) 306 | { 307 | ngx_tcp_session_t *s; 308 | ngx_tcp_core_srv_conf_t *cscf; 309 | 310 | s = c->data; 311 | 312 | cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); 313 | 314 | /* process the ACL */ 315 | if (ngx_tcp_access_handler(s) == NGX_ERROR) { 316 | ngx_tcp_finalize_session(s); 317 | return; 318 | } 319 | 320 | cscf->protocol->init_session(s); 321 | } 322 | 323 | 324 | void 325 | ngx_tcp_send(ngx_event_t *wev) 326 | { 327 | ngx_int_t n; 328 | ngx_connection_t *c; 329 | ngx_tcp_session_t *s; 330 | ngx_tcp_core_srv_conf_t *cscf; 331 | 332 | c = wev->data; 333 | s = c->data; 334 | 335 | if (wev->timedout) { 336 | ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); 337 | c->timedout = 1; 338 | ngx_tcp_close_connection(c); 339 | return; 340 | } 341 | 342 | if (s->out.len == 0) { 343 | if (ngx_handle_write_event(c->write, 0) != NGX_OK) { 344 | ngx_tcp_close_connection(c); 345 | } 346 | 347 | return; 348 | } 349 | 350 | n = c->send(c, s->out.data, s->out.len); 351 | ngx_log_debug1(NGX_LOG_DEBUG_TCP, wev->log, 0, "nginx tcp send:%d", n); 352 | 353 | if (n > 0) { 354 | s->out.len -= n; 355 | 356 | if (wev->timer_set) { 357 | ngx_del_timer(wev); 358 | } 359 | 360 | if (s->quit) { 361 | ngx_tcp_close_connection(c); 362 | return; 363 | } 364 | 365 | return; 366 | } 367 | 368 | if (n == NGX_ERROR) { 369 | ngx_tcp_close_connection(c); 370 | return; 371 | } 372 | 373 | /* n == NGX_AGAIN */ 374 | 375 | cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); 376 | 377 | ngx_add_timer(c->write, cscf->timeout); 378 | 379 | if (ngx_handle_write_event(c->write, 0) != NGX_OK) { 380 | ngx_tcp_close_connection(c); 381 | return; 382 | } 383 | } 384 | 385 | 386 | void 387 | ngx_tcp_session_internal_server_error(ngx_tcp_session_t *s) 388 | { 389 | ngx_tcp_core_srv_conf_t *cscf; 390 | 391 | cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module); 392 | 393 | s->out = cscf->protocol->internal_server_error; 394 | s->quit = 1; 395 | 396 | ngx_tcp_send(s->connection->write); 397 | } 398 | 399 | 400 | void 401 | ngx_tcp_finalize_session(ngx_tcp_session_t *s) 402 | { 403 | ngx_connection_t *c; 404 | ngx_tcp_cleanup_t *cln; 405 | 406 | c = s->connection; 407 | 408 | ngx_tcp_log_handler(s); 409 | 410 | ngx_log_debug1(NGX_LOG_DEBUG_TCP, c->log, 0, 411 | "close tcp session: %d", c->fd); 412 | 413 | for (cln = s->cleanup; cln; cln = cln->next) { 414 | if (cln->handler) { 415 | cln->handler(cln->data); 416 | cln->handler = NULL; 417 | } 418 | } 419 | 420 | ngx_tcp_close_connection(c); 421 | 422 | return; 423 | } 424 | 425 | 426 | void 427 | ngx_tcp_close_connection(ngx_connection_t *c) 428 | { 429 | ngx_pool_t *pool; 430 | 431 | ngx_log_debug1(NGX_LOG_DEBUG_TCP, c->log, 0, 432 | "close tcp connection: %d", c->fd); 433 | 434 | #if (NGX_TCP_SSL) 435 | 436 | if (c->ssl) { 437 | if (ngx_ssl_shutdown(c) == NGX_AGAIN) { 438 | c->ssl->handler = ngx_tcp_close_connection; 439 | return; 440 | } 441 | } 442 | 443 | #endif 444 | 445 | #if (NGX_STAT_STUB) 446 | (void) ngx_atomic_fetch_add(ngx_stat_active, -1); 447 | #endif 448 | 449 | c->destroyed = 1; 450 | 451 | pool = c->pool; 452 | 453 | ngx_close_connection(c); 454 | 455 | ngx_destroy_pool(pool); 456 | } 457 | 458 | 459 | u_char * 460 | ngx_tcp_log_error(ngx_log_t *log, u_char *buf, size_t len) 461 | { 462 | u_char *p; 463 | ngx_tcp_session_t *s; 464 | ngx_tcp_log_ctx_t *ctx; 465 | 466 | p = buf; 467 | 468 | if (log->action) { 469 | p = ngx_snprintf(p, len + (buf - p), " while %s", log->action); 470 | } 471 | 472 | ctx = log->data; 473 | 474 | p = ngx_snprintf(p, len + (buf - p), ", client: %V", ctx->client); 475 | 476 | s = ctx->session; 477 | 478 | if (s == NULL) { 479 | return p; 480 | } 481 | 482 | p = ngx_snprintf(p, len + (buf - p), ", server: %V", s->addr_text); 483 | 484 | if (s->upstream) { 485 | if (s->upstream->peer.connection) { 486 | p = ngx_snprintf(p, len + (buf - p), ", upstream: %V", s->upstream->peer.name); 487 | } 488 | } 489 | 490 | return p; 491 | } 492 | 493 | 494 | ngx_tcp_cleanup_t * 495 | ngx_tcp_cleanup_add(ngx_tcp_session_t *s, size_t size) 496 | { 497 | ngx_tcp_cleanup_t *cln; 498 | 499 | cln = ngx_palloc(s->pool, sizeof(ngx_tcp_cleanup_t)); 500 | if (cln == NULL) { 501 | return NULL; 502 | } 503 | 504 | if (size) { 505 | cln->data = ngx_palloc(s->pool, size); 506 | if (cln->data == NULL) { 507 | return NULL; 508 | } 509 | 510 | } else { 511 | cln->data = NULL; 512 | } 513 | 514 | cln->handler = NULL; 515 | cln->next = s->cleanup; 516 | 517 | s->cleanup = cln; 518 | 519 | ngx_log_debug1(NGX_LOG_DEBUG_TCP, s->connection->log, 0, 520 | "tcp cleanup add: %p", cln); 521 | 522 | return cln; 523 | } 524 | -------------------------------------------------------------------------------- /ngx_tcp_session.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NGX_TCP_SESSION_H_INCLUDED_ 3 | #define _NGX_TCP_SESSION_H_INCLUDED_ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | typedef struct ngx_tcp_session_s { 14 | uint32_t signature; /* "TCP" */ 15 | 16 | ngx_pool_t *pool; 17 | 18 | ngx_connection_t *connection; 19 | ngx_tcp_upstream_t *upstream; 20 | 21 | ngx_str_t out; 22 | ngx_buf_t *buffer; 23 | 24 | void **ctx; 25 | void **main_conf; 26 | void **srv_conf; 27 | 28 | ngx_resolver_ctx_t *resolver_ctx; 29 | 30 | ngx_tcp_cleanup_t *cleanup; 31 | 32 | time_t start_sec; 33 | ngx_msec_t start_msec; 34 | 35 | off_t bytes_read; 36 | off_t bytes_write; 37 | 38 | unsigned quit:1; 39 | ngx_str_t *addr_text; 40 | ngx_str_t host; 41 | 42 | } ngx_tcp_session_t; 43 | 44 | 45 | typedef void (*ngx_tcp_cleanup_pt)(void *data); 46 | 47 | 48 | struct ngx_tcp_cleanup_s { 49 | ngx_tcp_cleanup_pt handler; 50 | void *data; 51 | ngx_tcp_cleanup_t *next; 52 | }; 53 | 54 | void ngx_tcp_init_connection(ngx_connection_t *c); 55 | 56 | void ngx_tcp_send(ngx_event_t *wev); 57 | ngx_int_t ngx_tcp_read_command(ngx_tcp_session_t *s, ngx_connection_t *c); 58 | void ngx_tcp_auth(ngx_tcp_session_t *s, ngx_connection_t *c); 59 | void ngx_tcp_close_connection(ngx_connection_t *c); 60 | void ngx_tcp_session_internal_server_error(ngx_tcp_session_t *s); 61 | 62 | u_char *ngx_tcp_log_error(ngx_log_t *log, u_char *buf, size_t len); 63 | 64 | void ngx_tcp_finalize_session(ngx_tcp_session_t *s); 65 | 66 | ngx_tcp_cleanup_t * ngx_tcp_cleanup_add(ngx_tcp_session_t *s, size_t size); 67 | 68 | ngx_int_t ngx_tcp_access_handler(ngx_tcp_session_t *s); 69 | ngx_int_t ngx_tcp_log_handler(ngx_tcp_session_t *s); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /ngx_tcp_upstream.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NGX_TCP_UPSTREAM_H_INCLUDED_ 3 | #define _NGX_TCP_UPSTREAM_H_INCLUDED_ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | #define NGX_TCP_UPSTREAM_FT_ERROR 0x00000002 15 | #define NGX_TCP_UPSTREAM_FT_TIMEOUT 0x00000004 16 | #define NGX_TCP_UPSTREAM_FT_INVALID_HEADER 0x00000008 17 | #define NGX_TCP_UPSTREAM_FT_TCP_500 0x00000010 18 | #define NGX_TCP_UPSTREAM_FT_TCP_502 0x00000020 19 | #define NGX_TCP_UPSTREAM_FT_TCP_503 0x00000040 20 | #define NGX_TCP_UPSTREAM_FT_TCP_504 0x00000080 21 | #define NGX_TCP_UPSTREAM_FT_TCP_404 0x00000100 22 | #define NGX_TCP_UPSTREAM_FT_UPDATING 0x00000200 23 | #define NGX_TCP_UPSTREAM_FT_BUSY_LOCK 0x00000400 24 | #define NGX_TCP_UPSTREAM_FT_MAX_WAITING 0x00000800 25 | #define NGX_TCP_UPSTREAM_FT_NOLIVE 0x40000000 26 | #define NGX_TCP_UPSTREAM_FT_OFF 0x80000000 27 | 28 | #define NGX_TCP_UPSTREAM_FT_STATUS (NGX_TCP_UPSTREAM_FT_TCP_500 \ 29 | |NGX_TCP_UPSTREAM_FT_TCP_502 \ 30 | |NGX_TCP_UPSTREAM_FT_TCP_503 \ 31 | |NGX_TCP_UPSTREAM_FT_TCP_504 \ 32 | |NGX_TCP_UPSTREAM_FT_TCP_404) 33 | 34 | #define NGX_TCP_UPSTREAM_INVALID_HEADER 40 35 | 36 | 37 | #define NGX_TCP_UPSTREAM_IGN_XA_REDIRECT 0x00000002 38 | #define NGX_TCP_UPSTREAM_IGN_XA_EXPIRES 0x00000004 39 | #define NGX_TCP_UPSTREAM_IGN_EXPIRES 0x00000008 40 | #define NGX_TCP_UPSTREAM_IGN_CACHE_CONTROL 0x00000010 41 | 42 | typedef struct { 43 | ngx_msec_t bl_time; 44 | ngx_uint_t bl_state; 45 | 46 | ngx_uint_t status; 47 | time_t response_sec; 48 | ngx_uint_t response_msec; 49 | 50 | ngx_str_t *peer; 51 | } ngx_tcp_upstream_state_t; 52 | 53 | 54 | typedef struct { 55 | ngx_uint_t check_shm_size; 56 | ngx_tcp_check_peers_conf_t *peers_conf; 57 | ngx_array_t upstreams; /* ngx_tcp_upstream_srv_conf_t */ 58 | } ngx_tcp_upstream_main_conf_t; 59 | 60 | 61 | typedef ngx_int_t (*ngx_tcp_upstream_init_pt)(ngx_conf_t *cf, 62 | ngx_tcp_upstream_srv_conf_t *us); 63 | typedef ngx_int_t (*ngx_tcp_upstream_init_peer_pt)(ngx_tcp_session_t *s, 64 | ngx_tcp_upstream_srv_conf_t *us); 65 | 66 | typedef struct { 67 | ngx_tcp_upstream_init_pt init_upstream; 68 | ngx_tcp_upstream_init_peer_pt init; 69 | void *data; 70 | } ngx_tcp_upstream_peer_t; 71 | 72 | typedef struct { 73 | ngx_peer_addr_t *addrs; 74 | ngx_uint_t naddrs; 75 | ngx_uint_t weight; 76 | ngx_uint_t max_fails; 77 | time_t fail_timeout; 78 | ngx_uint_t max_busy; 79 | 80 | unsigned down:1; 81 | unsigned backup:1; 82 | } ngx_tcp_upstream_server_t; 83 | 84 | 85 | #define NGX_TCP_UPSTREAM_CREATE 0x0001 86 | #define NGX_TCP_UPSTREAM_WEIGHT 0x0002 87 | #define NGX_TCP_UPSTREAM_MAX_FAILS 0x0004 88 | #define NGX_TCP_UPSTREAM_FAIL_TIMEOUT 0x0008 89 | #define NGX_TCP_UPSTREAM_DOWN 0x0010 90 | #define NGX_TCP_UPSTREAM_BACKUP 0x0020 91 | #define NGX_TCP_UPSTREAM_SRUN_ID 0x0040 92 | #define NGX_TCP_UPSTREAM_MAX_BUSY 0x0080 93 | 94 | struct ngx_tcp_upstream_srv_conf_s { 95 | 96 | ngx_tcp_upstream_peer_t peer; 97 | void **srv_conf; 98 | 99 | ngx_array_t *servers; /* ngx_tcp_upstream_server_t */ 100 | 101 | ngx_uint_t flags; 102 | ngx_str_t host; 103 | u_char *file_name; 104 | ngx_uint_t line; 105 | in_port_t port; 106 | in_port_t default_port; 107 | #if (nginx_version) >= 1003011 108 | ngx_uint_t no_port; /* unsigned no_port:1 */ 109 | #endif 110 | 111 | ngx_uint_t fall_count; 112 | ngx_uint_t rise_count; 113 | ngx_msec_t check_interval; 114 | ngx_msec_t check_timeout; 115 | 116 | check_conf_t *check_type_conf; 117 | ngx_str_t send; 118 | 119 | union { 120 | ngx_uint_t return_code; 121 | ngx_uint_t status_alive; 122 | } code; 123 | }; 124 | 125 | 126 | typedef struct { 127 | ngx_tcp_upstream_srv_conf_t *upstream; 128 | 129 | ngx_msec_t connect_timeout; 130 | ngx_msec_t send_timeout; 131 | ngx_msec_t read_timeout; 132 | ngx_msec_t timeout; 133 | } ngx_tcp_upstream_conf_t; 134 | 135 | 136 | struct ngx_tcp_upstream_resolved_s { 137 | ngx_str_t host; 138 | in_port_t port; 139 | ngx_uint_t no_port; /* unsigned no_port:1 */ 140 | 141 | ngx_uint_t naddrs; 142 | #if (nginx_version) >= 1005008 143 | #if (nginx_version) >= 1009001 144 | ngx_resolver_addr_t *addrs; 145 | #else 146 | ngx_addr_t *addrs; 147 | #endif 148 | #else 149 | in_addr_t *addrs; 150 | #endif 151 | 152 | struct sockaddr *sockaddr; 153 | socklen_t socklen; 154 | 155 | ngx_resolver_ctx_t *ctx; 156 | }; 157 | 158 | 159 | typedef void (*ngx_tcp_upstream_handler_pt)(ngx_tcp_session_t *s, 160 | ngx_tcp_upstream_t *u); 161 | 162 | struct ngx_tcp_upstream_s { 163 | ngx_tcp_upstream_handler_pt read_event_handler; 164 | ngx_tcp_upstream_handler_pt write_event_handler; 165 | 166 | ngx_peer_connection_t peer; 167 | ngx_tcp_upstream_conf_t *conf; 168 | ngx_tcp_upstream_resolved_t *resolved; 169 | ngx_tcp_upstream_state_t *state; 170 | ngx_tcp_cleanup_pt *cleanup; 171 | }; 172 | 173 | 174 | typedef struct { 175 | ngx_uint_t status; 176 | ngx_uint_t mask; 177 | } ngx_tcp_upstream_next_t; 178 | 179 | 180 | ngx_int_t ngx_tcp_upstream_create(ngx_tcp_session_t *s); 181 | void ngx_tcp_upstream_init(ngx_tcp_session_t *s); 182 | ngx_tcp_upstream_srv_conf_t *ngx_tcp_upstream_add(ngx_conf_t *cf, 183 | ngx_url_t *u, ngx_uint_t flags); 184 | 185 | ngx_int_t ngx_tcp_upstream_check_broken_connection(ngx_tcp_session_t *s); 186 | void ngx_tcp_upstream_next(ngx_tcp_session_t *s, ngx_tcp_upstream_t *u, 187 | ngx_uint_t ft_type); 188 | 189 | #define ngx_tcp_conf_upstream_srv_conf(uscf, module) \ 190 | uscf->srv_conf[module.ctx_index] 191 | 192 | 193 | extern ngx_module_t ngx_tcp_upstream_module; 194 | 195 | 196 | #endif /* _NGX_TCP_UPSTREAM_H_INCLUDED_ */ 197 | -------------------------------------------------------------------------------- /ngx_tcp_upstream_check.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_TCP_UPSTREAM_CHECK_H_INCLUDED_ 2 | #define _NGX_TCP_UPSTREAM_CHECK_H_INCLUDED_ 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | typedef struct { 18 | u_char major; 19 | u_char minor; 20 | } ssl_protocol_version_t; 21 | 22 | typedef struct { 23 | u_char msg_type; 24 | ssl_protocol_version_t version; 25 | uint16_t length; 26 | 27 | u_char handshake_type; 28 | u_char handshake_length[3]; 29 | ssl_protocol_version_t hello_version; 30 | 31 | time_t time; 32 | u_char random[28]; 33 | 34 | u_char others[0]; 35 | } __attribute__((packed)) server_ssl_hello_t; 36 | 37 | typedef struct { 38 | u_char packet_length[3]; 39 | u_char packet_number; 40 | 41 | u_char protocol_version; 42 | u_char others[0]; 43 | } __attribute__((packed)) mysql_handshake_init_t; 44 | 45 | typedef struct { 46 | ngx_buf_t send; 47 | ngx_buf_t recv; 48 | 49 | void *parser; 50 | } ngx_tcp_check_ctx; 51 | 52 | /*state*/ 53 | #define NGX_TCP_CHECK_CONNECT_DONE 0x0001 54 | #define NGX_TCP_CHECK_SEND_DONE 0x0002 55 | #define NGX_TCP_CHECK_RECV_DONE 0x0004 56 | #define NGX_TCP_CHECK_ALL_DONE 0x0008 57 | 58 | typedef struct { 59 | ngx_pid_t owner; 60 | 61 | ngx_msec_t access_time; 62 | 63 | ngx_uint_t fall_count; 64 | ngx_uint_t rise_count; 65 | 66 | ngx_atomic_t lock; 67 | ngx_atomic_t busyness; 68 | ngx_atomic_t down; 69 | 70 | ngx_uint_t access_count; 71 | } ngx_tcp_check_peer_shm_t; 72 | 73 | typedef struct { 74 | ngx_uint_t generation; 75 | 76 | ngx_uint_t state; 77 | ngx_atomic_t lock; 78 | 79 | /*store the ngx_tcp_check_status_peer_t*/ 80 | ngx_tcp_check_peer_shm_t peers[1]; 81 | } ngx_tcp_check_peers_shm_t; 82 | 83 | typedef ngx_int_t (*ngx_tcp_check_packet_init_pt) 84 | (ngx_tcp_check_peer_conf_t *peer_conf); 85 | typedef ngx_int_t (*ngx_tcp_check_packet_parse_pt) 86 | (ngx_tcp_check_peer_conf_t *peer_conf); 87 | typedef void (*ngx_tcp_check_packet_clean_pt) 88 | (ngx_tcp_check_peer_conf_t *peer_conf); 89 | 90 | #define NGX_TCP_CHECK_TCP 0x0001 91 | #define NGX_TCP_CHECK_HTTP 0x0002 92 | #define NGX_TCP_CHECK_SSL_HELLO 0x0004 93 | #define NGX_TCP_CHECK_SMTP 0x0008 94 | #define NGX_TCP_CHECK_MYSQL 0x0010 95 | #define NGX_TCP_CHECK_POP3 0x0020 96 | #define NGX_TCP_CHECK_IMAP 0x0040 97 | 98 | #define NGX_CHECK_HTTP_2XX 0x0002 99 | #define NGX_CHECK_HTTP_3XX 0x0004 100 | #define NGX_CHECK_HTTP_4XX 0x0008 101 | #define NGX_CHECK_HTTP_5XX 0x0010 102 | #define NGX_CHECK_HTTP_6XX 0x0020 103 | #define NGX_CHECK_HTTP_ERR 0x8000 104 | 105 | #define NGX_CHECK_SMTP_2XX 0x0002 106 | #define NGX_CHECK_SMTP_3XX 0x0004 107 | #define NGX_CHECK_SMTP_4XX 0x0008 108 | #define NGX_CHECK_SMTP_5XX 0x0010 109 | #define NGX_CHECK_SMTP_6XX 0x0020 110 | #define NGX_CHECK_SMTP_ERR 0x8000 111 | 112 | 113 | struct check_conf_s { 114 | ngx_uint_t type; 115 | 116 | char *name; 117 | 118 | ngx_str_t default_send; 119 | 120 | /* HTTP */ 121 | ngx_uint_t default_status_alive; 122 | 123 | ngx_event_handler_pt send_handler; 124 | ngx_event_handler_pt recv_handler; 125 | 126 | ngx_tcp_check_packet_init_pt init; 127 | ngx_tcp_check_packet_parse_pt parse; 128 | ngx_tcp_check_packet_clean_pt reinit; 129 | 130 | unsigned need_pool; 131 | }; 132 | 133 | struct ngx_tcp_check_peer_conf_s { 134 | 135 | ngx_flag_t state; 136 | ngx_pool_t *pool; 137 | ngx_uint_t index; 138 | ngx_uint_t max_busy; 139 | ngx_tcp_upstream_srv_conf_t *conf; 140 | ngx_peer_addr_t *peer; 141 | ngx_event_t check_ev; 142 | ngx_event_t check_timeout_ev; 143 | ngx_peer_connection_t pc; 144 | 145 | void * check_data; 146 | ngx_event_handler_pt send_handler; 147 | ngx_event_handler_pt recv_handler; 148 | 149 | ngx_tcp_check_packet_init_pt init; 150 | ngx_tcp_check_packet_parse_pt parse; 151 | ngx_tcp_check_packet_clean_pt reinit; 152 | 153 | ngx_tcp_check_peer_shm_t *shm; 154 | }; 155 | 156 | struct ngx_tcp_check_peers_conf_s { 157 | ngx_str_t check_shm_name; 158 | ngx_array_t peers; 159 | 160 | ngx_tcp_check_peers_shm_t *peers_shm; 161 | }; 162 | 163 | 164 | ngx_int_t ngx_tcp_upstream_init_main_check_conf(ngx_conf_t *cf, void*conf); 165 | 166 | ngx_uint_t ngx_tcp_check_add_peer(ngx_conf_t *cf, 167 | ngx_tcp_upstream_srv_conf_t *uscf, 168 | ngx_peer_addr_t *peer, ngx_uint_t max_busy); 169 | 170 | ngx_uint_t ngx_tcp_check_peer_down(ngx_uint_t index); 171 | 172 | ngx_uint_t ngx_tcp_check_get_peer_busyness(ngx_uint_t index); 173 | 174 | void ngx_tcp_check_get_peer(ngx_uint_t index); 175 | void ngx_tcp_check_free_peer(ngx_uint_t index); 176 | 177 | check_conf_t *ngx_tcp_get_check_type_conf(ngx_str_t *str); 178 | 179 | #endif //_NGX_TCP_UPSTREAM_CHECK_H_INCLUDED_ 180 | 181 | -------------------------------------------------------------------------------- /ngx_tcp_upstream_round_robin.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NGX_TCP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ 3 | #define _NGX_TCP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | typedef struct { 12 | struct sockaddr *sockaddr; 13 | socklen_t socklen; 14 | ngx_str_t name; 15 | 16 | ngx_int_t current_weight; 17 | ngx_int_t weight; 18 | 19 | ngx_uint_t fails; 20 | time_t accessed; 21 | 22 | ngx_uint_t max_fails; 23 | time_t fail_timeout; 24 | 25 | ngx_uint_t check_index; 26 | 27 | ngx_uint_t down; /* unsigned down:1; */ 28 | 29 | #if (NGX_TCP_SSL) 30 | ngx_ssl_session_t *ssl_session; /* local to a process */ 31 | #endif 32 | } ngx_tcp_upstream_rr_peer_t; 33 | 34 | 35 | typedef struct ngx_tcp_upstream_rr_peers_s ngx_tcp_upstream_rr_peers_t; 36 | 37 | struct ngx_tcp_upstream_rr_peers_s { 38 | ngx_uint_t single; /* unsigned single:1; */ 39 | ngx_uint_t number; 40 | ngx_uint_t last_cached; 41 | 42 | /* ngx_mutex_t *mutex; */ 43 | ngx_connection_t **cached; 44 | 45 | ngx_str_t *name; 46 | 47 | ngx_tcp_upstream_rr_peers_t *next; 48 | 49 | ngx_tcp_upstream_rr_peer_t peer[1]; 50 | }; 51 | 52 | 53 | typedef struct { 54 | ngx_tcp_upstream_rr_peers_t *peers; 55 | ngx_uint_t current; 56 | uintptr_t *tried; 57 | uintptr_t data; 58 | } ngx_tcp_upstream_rr_peer_data_t; 59 | 60 | 61 | ngx_int_t ngx_tcp_upstream_init_round_robin(ngx_conf_t *cf, 62 | ngx_tcp_upstream_srv_conf_t *us); 63 | ngx_int_t ngx_tcp_upstream_init_round_robin_peer(ngx_tcp_session_t *s, 64 | ngx_tcp_upstream_srv_conf_t *us); 65 | ngx_int_t ngx_tcp_upstream_create_round_robin_peer(ngx_tcp_session_t *s, 66 | ngx_tcp_upstream_resolved_t *ur); 67 | ngx_int_t ngx_tcp_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, 68 | void *data); 69 | void ngx_tcp_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, 70 | void *data, ngx_uint_t state); 71 | 72 | #if (NGX_TCP_SSL) 73 | ngx_int_t ngx_tcp_upstream_set_round_robin_peer_session( 74 | ngx_peer_connection_t *pc, void *data); 75 | void ngx_tcp_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc, 76 | void *data); 77 | #endif 78 | 79 | 80 | #endif /* _NGX_TCP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */ 81 | -------------------------------------------------------------------------------- /parsers/gen.shell: -------------------------------------------------------------------------------- 1 | 2 | ragel -G2 http_request_parser.rl 3 | ragel -G2 http_response_parser.rl 4 | ragel -G2 smtp_response_parser.rl 5 | -------------------------------------------------------------------------------- /parsers/http_request_parser.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NGX_TCP_HTTP_REQUEST_PARSER_H_INCLUDED_ 3 | #define _NGX_TCP_HTTP_REQUEST_PARSER_H_INCLUDED_ 4 | 5 | #include 6 | 7 | 8 | typedef struct http_request_parser { 9 | int cs; 10 | size_t body_start; 11 | int content_len; 12 | size_t nread; 13 | size_t mark; 14 | size_t field_start; 15 | size_t field_len; 16 | size_t query_start; 17 | 18 | void *data; 19 | 20 | field_cb http_field; 21 | element_cb request_method; 22 | element_cb request_uri; 23 | element_cb fragment; 24 | element_cb request_path; 25 | element_cb query_string; 26 | element_cb http_version; 27 | element_cb header_done; 28 | 29 | } http_request_parser; 30 | 31 | int http_request_parser_init(http_request_parser *parser); 32 | int http_request_parser_finish(http_request_parser *parser); 33 | size_t http_request_parser_execute(http_request_parser *parser, 34 | const signed char *data, size_t len, size_t off); 35 | int http_request_parser_has_error(http_request_parser *parser); 36 | int http_request_parser_is_finished(http_request_parser *parser); 37 | 38 | #define http_request_parser_nread(parser) (parser)->nread 39 | 40 | 41 | #endif //_NGX_TCP_HTTP_REQUEST_PARSER_H_INCLUDED_ 42 | -------------------------------------------------------------------------------- /parsers/http_request_parser.rl: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Copyright (c) 2005 Zed A. Shaw 4 | * You can redistribute it and/or modify it under the same terms as Ruby. 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | #define LEN(AT, FPC) (FPC - buffer - parser->AT) 17 | #define MARK(M,FPC) (parser->M = (FPC) - buffer) 18 | #define PTR_TO(F) (buffer + parser->F) 19 | 20 | /** Machine **/ 21 | 22 | %%{ 23 | 24 | machine http_request_parser; 25 | 26 | action mark {MARK(mark, fpc); } 27 | 28 | 29 | action start_field { MARK(field_start, fpc); } 30 | action write_field { 31 | parser->field_len = LEN(field_start, fpc); 32 | } 33 | 34 | action start_value { MARK(mark, fpc); } 35 | action write_value { 36 | if(parser->http_field != NULL) { 37 | parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc)); 38 | } 39 | } 40 | action request_method { 41 | if(parser->request_method != NULL) 42 | parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc)); 43 | } 44 | action request_uri { 45 | if(parser->request_uri != NULL) 46 | parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc)); 47 | } 48 | action fragment { 49 | if(parser->fragment != NULL) 50 | parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc)); 51 | } 52 | 53 | action start_query {MARK(query_start, fpc); } 54 | action query_string { 55 | if(parser->query_string != NULL) 56 | parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc)); 57 | } 58 | 59 | action http_version { 60 | if(parser->http_version != NULL) 61 | parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc)); 62 | } 63 | 64 | action request_path { 65 | if(parser->request_path != NULL) 66 | parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc)); 67 | } 68 | 69 | action done { 70 | parser->body_start = fpc - buffer + 1; 71 | if(parser->header_done != NULL) 72 | parser->header_done(parser->data, fpc + 1, pe - fpc - 1); 73 | fbreak; 74 | } 75 | 76 | #### HTTP PROTOCOL GRAMMAR 77 | # line endings 78 | CRLF = "\r\n"; 79 | 80 | # character types 81 | CTL = (cntrl | 127); 82 | safe = ("$" | "-" | "_" | "."); 83 | extra = ("!" | "*" | "'" | "(" | ")" | ","); 84 | reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+"); 85 | unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">"); 86 | national = any -- (alpha | digit | reserved | extra | safe | unsafe); 87 | unreserved = (alpha | digit | safe | extra | national); 88 | escape = ("%" xdigit xdigit); 89 | uchar = (unreserved | escape); 90 | pchar = (uchar | ":" | "@" | "&" | "=" | "+"); 91 | tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t"); 92 | 93 | # elements 94 | token = (ascii -- (CTL | tspecials)); 95 | 96 | # URI schemes and absolute paths 97 | scheme = ( alpha | digit | "+" | "-" | "." )* ; 98 | absolute_uri = (scheme ":" (uchar | reserved )*); 99 | 100 | path = ( pchar+ ( "/" pchar* )* ) ; 101 | query = ( uchar | reserved )* %query_string ; 102 | param = ( pchar | "/" )* ; 103 | params = ( param ( ";" param )* ) ; 104 | rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?; 105 | absolute_path = ( "/"+ rel_path ); 106 | 107 | Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri; 108 | Fragment = ( uchar | reserved )* >mark %fragment; 109 | Method = ( upper | digit | safe ){1,20} >mark %request_method; 110 | 111 | http_number = ( digit+ "." digit+ ) ; 112 | HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ; 113 | Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ; 114 | 115 | #field_name = ( token -- ":" )+ >start_field $snake_upcase_field %write_field; 116 | field_name = ( token -- ":" )+ >start_field %write_field; 117 | 118 | field_value = any* >start_value %write_value; 119 | 120 | message_header = field_name ":" " "* field_value :> CRLF; 121 | 122 | Request = Request_Line ( message_header )* ( CRLF @done ); 123 | 124 | main := Request; 125 | 126 | }%% 127 | 128 | /** Data **/ 129 | %% write data; 130 | 131 | int http_request_parser_init(http_request_parser *parser) { 132 | int cs = 0; 133 | %% write init; 134 | parser->cs = cs; 135 | parser->body_start = 0; 136 | parser->content_len = 0; 137 | parser->mark = 0; 138 | parser->nread = 0; 139 | parser->field_len = 0; 140 | parser->field_start = 0; 141 | 142 | return(1); 143 | } 144 | 145 | 146 | /** exec **/ 147 | size_t http_request_parser_execute(http_request_parser *parser, const signed char *buffer, size_t len, size_t off) { 148 | const signed char *p, *pe; 149 | int cs = parser->cs; 150 | 151 | assert(off <= len && "offset past end of buffer"); 152 | 153 | p = buffer + off; 154 | pe = buffer + len; 155 | 156 | %% write exec; 157 | 158 | if (!http_request_parser_has_error(parser)) 159 | parser->cs = cs; 160 | parser->nread += p - (buffer + off); 161 | 162 | assert(p <= pe && "buffer overflow after parsing execute"); 163 | assert(parser->nread <= len && "nread longer than length"); 164 | assert(parser->body_start <= len && "body starts after buffer end"); 165 | assert(parser->mark < len && "mark is after buffer end"); 166 | assert(parser->field_len <= len && "field has length longer than whole buffer"); 167 | assert(parser->field_start < len && "field starts after buffer end"); 168 | 169 | return(parser->nread); 170 | } 171 | 172 | int http_request_parser_finish(http_request_parser *parser) 173 | { 174 | if (http_request_parser_has_error(parser) ) { 175 | return -1; 176 | } else if (http_request_parser_is_finished(parser) ) { 177 | return 1; 178 | } else { 179 | return 0; 180 | } 181 | } 182 | 183 | int http_request_parser_has_error(http_request_parser *parser) { 184 | return parser->cs == http_request_parser_error; 185 | } 186 | 187 | int http_request_parser_is_finished(http_request_parser *parser) { 188 | return parser->cs >= http_request_parser_first_final; 189 | } 190 | -------------------------------------------------------------------------------- /parsers/http_response_parser.c: -------------------------------------------------------------------------------- 1 | 2 | #line 1 "http_response_parser.rl" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | #define LEN(AT, FPC) (FPC - buffer - parser->AT) 14 | #define MARK(M,FPC) (parser->M = (FPC) - buffer) 15 | #define PTR_TO(F) (buffer + parser->F) 16 | 17 | /** Machine **/ 18 | 19 | 20 | #line 88 "http_response_parser.rl" 21 | 22 | 23 | /** Data **/ 24 | 25 | #line 26 "http_response_parser.c" 26 | static const int http_response_parser_start = 1; 27 | static const int http_response_parser_first_final = 20; 28 | static const int http_response_parser_error = 0; 29 | 30 | static const int http_response_parser_en_main = 1; 31 | 32 | 33 | #line 92 "http_response_parser.rl" 34 | 35 | int http_response_parser_init(http_response_parser *parser) { 36 | int cs = 0; 37 | 38 | #line 39 "http_response_parser.c" 39 | { 40 | cs = http_response_parser_start; 41 | } 42 | 43 | #line 96 "http_response_parser.rl" 44 | parser->cs = cs; 45 | parser->body_start = 0; 46 | parser->content_len = 0; 47 | parser->mark = 0; 48 | parser->nread = 0; 49 | parser->field_len = 0; 50 | parser->field_start = 0; 51 | 52 | return(1); 53 | } 54 | 55 | 56 | /** exec **/ 57 | size_t http_response_parser_execute(http_response_parser *parser, const signed char *buffer, size_t len, size_t off) { 58 | const signed char *p, *pe; 59 | int cs = parser->cs; 60 | 61 | assert(off <= len && "offset past end of buffer"); 62 | 63 | p = buffer + off; 64 | pe = buffer + len; 65 | 66 | 67 | #line 68 "http_response_parser.c" 68 | { 69 | if ( p == pe ) 70 | goto _test_eof; 71 | switch ( cs ) 72 | { 73 | case 1: 74 | if ( (*p) == 72 ) 75 | goto tr0; 76 | goto st0; 77 | st0: 78 | cs = 0; 79 | goto _out; 80 | tr0: 81 | #line 21 "http_response_parser.rl" 82 | {MARK(mark, p); } 83 | goto st2; 84 | st2: 85 | if ( ++p == pe ) 86 | goto _test_eof2; 87 | case 2: 88 | #line 89 "http_response_parser.c" 89 | if ( (*p) == 84 ) 90 | goto st3; 91 | goto st0; 92 | st3: 93 | if ( ++p == pe ) 94 | goto _test_eof3; 95 | case 3: 96 | if ( (*p) == 84 ) 97 | goto st4; 98 | goto st0; 99 | st4: 100 | if ( ++p == pe ) 101 | goto _test_eof4; 102 | case 4: 103 | if ( (*p) == 80 ) 104 | goto st5; 105 | goto st0; 106 | st5: 107 | if ( ++p == pe ) 108 | goto _test_eof5; 109 | case 5: 110 | if ( (*p) == 47 ) 111 | goto st6; 112 | goto st0; 113 | st6: 114 | if ( ++p == pe ) 115 | goto _test_eof6; 116 | case 6: 117 | if ( 48 <= (*p) && (*p) <= 57 ) 118 | goto st7; 119 | goto st0; 120 | st7: 121 | if ( ++p == pe ) 122 | goto _test_eof7; 123 | case 7: 124 | if ( (*p) == 46 ) 125 | goto st8; 126 | if ( 48 <= (*p) && (*p) <= 57 ) 127 | goto st7; 128 | goto st0; 129 | st8: 130 | if ( ++p == pe ) 131 | goto _test_eof8; 132 | case 8: 133 | if ( 48 <= (*p) && (*p) <= 57 ) 134 | goto st9; 135 | goto st0; 136 | st9: 137 | if ( ++p == pe ) 138 | goto _test_eof9; 139 | case 9: 140 | if ( (*p) == 32 ) 141 | goto tr9; 142 | if ( 48 <= (*p) && (*p) <= 57 ) 143 | goto st9; 144 | goto st0; 145 | tr9: 146 | #line 36 "http_response_parser.rl" 147 | { 148 | if(parser->http_version != NULL) 149 | parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p)); 150 | } 151 | goto st10; 152 | st10: 153 | if ( ++p == pe ) 154 | goto _test_eof10; 155 | case 10: 156 | #line 157 "http_response_parser.c" 157 | if ( 48 <= (*p) && (*p) <= 57 ) 158 | goto tr10; 159 | goto st0; 160 | tr10: 161 | #line 21 "http_response_parser.rl" 162 | {MARK(mark, p); } 163 | goto st11; 164 | st11: 165 | if ( ++p == pe ) 166 | goto _test_eof11; 167 | case 11: 168 | #line 169 "http_response_parser.c" 169 | if ( (*p) == 32 ) 170 | goto tr11; 171 | if ( 48 <= (*p) && (*p) <= 57 ) 172 | goto st11; 173 | goto st0; 174 | tr11: 175 | #line 41 "http_response_parser.rl" 176 | { 177 | if(parser->status_code != NULL) 178 | parser->status_code(parser->data, PTR_TO(mark), LEN(mark,p)); 179 | } 180 | goto st12; 181 | st12: 182 | if ( ++p == pe ) 183 | goto _test_eof12; 184 | case 12: 185 | #line 186 "http_response_parser.c" 186 | if ( (*p) < 11 ) { 187 | if ( 0 <= (*p) && (*p) <= 9 ) 188 | goto tr13; 189 | } else if ( (*p) > 12 ) { 190 | if ( 14 <= (*p) ) 191 | goto tr13; 192 | } else 193 | goto tr13; 194 | goto st0; 195 | tr13: 196 | #line 21 "http_response_parser.rl" 197 | {MARK(mark, p); } 198 | goto st13; 199 | st13: 200 | if ( ++p == pe ) 201 | goto _test_eof13; 202 | case 13: 203 | #line 204 "http_response_parser.c" 204 | if ( (*p) == 13 ) 205 | goto tr15; 206 | if ( (*p) > 9 ) { 207 | if ( 11 <= (*p) ) 208 | goto st13; 209 | } else if ( (*p) >= 0 ) 210 | goto st13; 211 | goto st0; 212 | tr15: 213 | #line 46 "http_response_parser.rl" 214 | { 215 | if(parser->reason_phrase != NULL) 216 | parser->reason_phrase(parser->data, PTR_TO(mark), LEN(mark,p)); 217 | } 218 | goto st14; 219 | tr23: 220 | #line 28 "http_response_parser.rl" 221 | { MARK(mark, p); } 222 | #line 30 "http_response_parser.rl" 223 | { 224 | if(parser->http_field != NULL) { 225 | parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p)); 226 | } 227 | } 228 | goto st14; 229 | tr26: 230 | #line 30 "http_response_parser.rl" 231 | { 232 | if(parser->http_field != NULL) { 233 | parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p)); 234 | } 235 | } 236 | goto st14; 237 | st14: 238 | if ( ++p == pe ) 239 | goto _test_eof14; 240 | case 14: 241 | #line 242 "http_response_parser.c" 242 | if ( (*p) == 10 ) 243 | goto st15; 244 | goto st0; 245 | st15: 246 | if ( ++p == pe ) 247 | goto _test_eof15; 248 | case 15: 249 | switch( (*p) ) { 250 | case 13: goto st16; 251 | case 33: goto tr18; 252 | case 124: goto tr18; 253 | case 126: goto tr18; 254 | } 255 | if ( (*p) < 45 ) { 256 | if ( (*p) > 39 ) { 257 | if ( 42 <= (*p) && (*p) <= 43 ) 258 | goto tr18; 259 | } else if ( (*p) >= 35 ) 260 | goto tr18; 261 | } else if ( (*p) > 46 ) { 262 | if ( (*p) < 65 ) { 263 | if ( 48 <= (*p) && (*p) <= 57 ) 264 | goto tr18; 265 | } else if ( (*p) > 90 ) { 266 | if ( 94 <= (*p) && (*p) <= 122 ) 267 | goto tr18; 268 | } else 269 | goto tr18; 270 | } else 271 | goto tr18; 272 | goto st0; 273 | st16: 274 | if ( ++p == pe ) 275 | goto _test_eof16; 276 | case 16: 277 | if ( (*p) == 10 ) 278 | goto tr19; 279 | goto st0; 280 | tr19: 281 | #line 51 "http_response_parser.rl" 282 | { 283 | parser->body_start = p - buffer + 1; 284 | if(parser->header_done != NULL) 285 | parser->header_done(parser->data, p + 1, pe - p - 1); 286 | {p++; cs = 20; goto _out;} 287 | } 288 | goto st20; 289 | st20: 290 | if ( ++p == pe ) 291 | goto _test_eof20; 292 | case 20: 293 | #line 294 "http_response_parser.c" 294 | goto st0; 295 | tr18: 296 | #line 23 "http_response_parser.rl" 297 | { MARK(field_start, p); } 298 | goto st17; 299 | st17: 300 | if ( ++p == pe ) 301 | goto _test_eof17; 302 | case 17: 303 | #line 304 "http_response_parser.c" 304 | switch( (*p) ) { 305 | case 33: goto st17; 306 | case 58: goto tr21; 307 | case 124: goto st17; 308 | case 126: goto st17; 309 | } 310 | if ( (*p) < 45 ) { 311 | if ( (*p) > 39 ) { 312 | if ( 42 <= (*p) && (*p) <= 43 ) 313 | goto st17; 314 | } else if ( (*p) >= 35 ) 315 | goto st17; 316 | } else if ( (*p) > 46 ) { 317 | if ( (*p) < 65 ) { 318 | if ( 48 <= (*p) && (*p) <= 57 ) 319 | goto st17; 320 | } else if ( (*p) > 90 ) { 321 | if ( 94 <= (*p) && (*p) <= 122 ) 322 | goto st17; 323 | } else 324 | goto st17; 325 | } else 326 | goto st17; 327 | goto st0; 328 | tr21: 329 | #line 24 "http_response_parser.rl" 330 | { 331 | parser->field_len = LEN(field_start, p); 332 | } 333 | goto st18; 334 | tr24: 335 | #line 28 "http_response_parser.rl" 336 | { MARK(mark, p); } 337 | goto st18; 338 | st18: 339 | if ( ++p == pe ) 340 | goto _test_eof18; 341 | case 18: 342 | #line 343 "http_response_parser.c" 343 | switch( (*p) ) { 344 | case 13: goto tr23; 345 | case 32: goto tr24; 346 | } 347 | goto tr22; 348 | tr22: 349 | #line 28 "http_response_parser.rl" 350 | { MARK(mark, p); } 351 | goto st19; 352 | st19: 353 | if ( ++p == pe ) 354 | goto _test_eof19; 355 | case 19: 356 | #line 357 "http_response_parser.c" 357 | if ( (*p) == 13 ) 358 | goto tr26; 359 | goto st19; 360 | } 361 | _test_eof2: cs = 2; goto _test_eof; 362 | _test_eof3: cs = 3; goto _test_eof; 363 | _test_eof4: cs = 4; goto _test_eof; 364 | _test_eof5: cs = 5; goto _test_eof; 365 | _test_eof6: cs = 6; goto _test_eof; 366 | _test_eof7: cs = 7; goto _test_eof; 367 | _test_eof8: cs = 8; goto _test_eof; 368 | _test_eof9: cs = 9; goto _test_eof; 369 | _test_eof10: cs = 10; goto _test_eof; 370 | _test_eof11: cs = 11; goto _test_eof; 371 | _test_eof12: cs = 12; goto _test_eof; 372 | _test_eof13: cs = 13; goto _test_eof; 373 | _test_eof14: cs = 14; goto _test_eof; 374 | _test_eof15: cs = 15; goto _test_eof; 375 | _test_eof16: cs = 16; goto _test_eof; 376 | _test_eof20: cs = 20; goto _test_eof; 377 | _test_eof17: cs = 17; goto _test_eof; 378 | _test_eof18: cs = 18; goto _test_eof; 379 | _test_eof19: cs = 19; goto _test_eof; 380 | 381 | _test_eof: {} 382 | _out: {} 383 | } 384 | 385 | #line 119 "http_response_parser.rl" 386 | 387 | if (!http_response_parser_has_error(parser)) 388 | parser->cs = cs; 389 | parser->nread += p - (buffer + off); 390 | 391 | assert(p <= pe && "buffer overflow after parsing execute"); 392 | assert(parser->nread <= len && "nread longer than length"); 393 | assert(parser->body_start <= len && "body starts after buffer end"); 394 | assert(parser->mark < len && "mark is after buffer end"); 395 | assert(parser->field_len <= len && "field has length longer than whole buffer"); 396 | assert(parser->field_start < len && "field starts after buffer end"); 397 | 398 | return(parser->nread); 399 | } 400 | 401 | int http_response_parser_finish(http_response_parser *parser) 402 | { 403 | if (http_response_parser_has_error(parser) ) { 404 | return -1; 405 | } else if (http_response_parser_is_finished(parser) ) { 406 | return 1; 407 | } else { 408 | return 0; 409 | } 410 | } 411 | 412 | int http_response_parser_has_error(http_response_parser *parser) { 413 | return parser->cs == http_response_parser_error; 414 | } 415 | 416 | int http_response_parser_is_finished(http_response_parser *parser) { 417 | return parser->cs >= http_response_parser_first_final; 418 | } 419 | -------------------------------------------------------------------------------- /parsers/http_response_parser.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NGX_TCP_HTTP_RESPONSE_PARSER_H_INCLUDED_ 3 | #define _NGX_TCP_HTTP_RESPONSE_PARSER_H_INCLUDED_ 4 | 5 | #include 6 | 7 | typedef struct http_response_parser { 8 | int cs; 9 | size_t body_start; 10 | int content_len; 11 | int status_code_n; 12 | size_t nread; 13 | size_t mark; 14 | size_t field_start; 15 | size_t field_len; 16 | 17 | void *data; 18 | 19 | field_cb http_field; 20 | 21 | element_cb http_version; 22 | element_cb status_code; 23 | element_cb reason_phrase; 24 | element_cb header_done; 25 | 26 | } http_response_parser; 27 | 28 | 29 | int http_response_parser_init(http_response_parser *parser); 30 | int http_response_parser_finish(http_response_parser *parser); 31 | size_t http_response_parser_execute(http_response_parser *parser, 32 | const signed char *data, size_t len, size_t off); 33 | int http_response_parser_has_error(http_response_parser *parser); 34 | int http_response_parser_is_finished(http_response_parser *parser); 35 | 36 | #define http_response_parser_nread(parser) (parser)->nread 37 | 38 | 39 | #endif //_NGX_TCP_HTTP_RESPONSE_PARSER_H_INCLUDED_ 40 | -------------------------------------------------------------------------------- /parsers/http_response_parser.rl: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #define LEN(AT, FPC) (FPC - buffer - parser->AT) 12 | #define MARK(M,FPC) (parser->M = (FPC) - buffer) 13 | #define PTR_TO(F) (buffer + parser->F) 14 | 15 | /** Machine **/ 16 | 17 | %%{ 18 | 19 | machine http_response_parser; 20 | 21 | action mark {MARK(mark, fpc); } 22 | 23 | action start_field { MARK(field_start, fpc); } 24 | action write_field { 25 | parser->field_len = LEN(field_start, fpc); 26 | } 27 | 28 | action start_value { MARK(mark, fpc); } 29 | 30 | action write_value { 31 | if(parser->http_field != NULL) { 32 | parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc)); 33 | } 34 | } 35 | 36 | action http_version { 37 | if(parser->http_version != NULL) 38 | parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc)); 39 | } 40 | 41 | action status_code { 42 | if(parser->status_code != NULL) 43 | parser->status_code(parser->data, PTR_TO(mark), LEN(mark,fpc)); 44 | } 45 | 46 | action reason_phrase { 47 | if(parser->reason_phrase != NULL) 48 | parser->reason_phrase(parser->data, PTR_TO(mark), LEN(mark,fpc)); 49 | } 50 | 51 | action done { 52 | parser->body_start = fpc - buffer + 1; 53 | if(parser->header_done != NULL) 54 | parser->header_done(parser->data, fpc + 1, pe - fpc - 1); 55 | fbreak; 56 | } 57 | 58 | #### HTTP PROTOCOL GRAMMAR 59 | # line endings 60 | CRLF = "\r\n"; 61 | 62 | # character types 63 | CTL = (cntrl | 127); 64 | tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t"); 65 | 66 | # elements 67 | token = (ascii -- (CTL | tspecials)); 68 | 69 | Reason_Phrase = ( ascii -- ("\r" | "\n") )+ >mark %reason_phrase; 70 | 71 | Status_Code = ( digit+ ) >mark %status_code ; 72 | 73 | http_number = ( digit+ "." digit+ ) ; 74 | HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ; 75 | 76 | Response_Line = ( HTTP_Version " " Status_Code " " Reason_Phrase CRLF ) ; 77 | 78 | field_name = ( token -- ":" )+ >start_field %write_field; 79 | 80 | field_value = any* >start_value %write_value; 81 | 82 | message_header = field_name ":" " "* field_value :> CRLF; 83 | 84 | Response = Response_Line ( message_header )* ( CRLF @done ); 85 | 86 | main := Response; 87 | 88 | }%% 89 | 90 | /** Data **/ 91 | %% write data; 92 | 93 | int http_response_parser_init(http_response_parser *parser) { 94 | int cs = 0; 95 | %% write init; 96 | parser->cs = cs; 97 | parser->body_start = 0; 98 | parser->content_len = 0; 99 | parser->mark = 0; 100 | parser->nread = 0; 101 | parser->field_len = 0; 102 | parser->field_start = 0; 103 | 104 | return(1); 105 | } 106 | 107 | 108 | /** exec **/ 109 | size_t http_response_parser_execute(http_response_parser *parser, const signed char *buffer, size_t len, size_t off) { 110 | const signed char *p, *pe; 111 | int cs = parser->cs; 112 | 113 | assert(off <= len && "offset past end of buffer"); 114 | 115 | p = buffer + off; 116 | pe = buffer + len; 117 | 118 | %% write exec; 119 | 120 | if (!http_response_parser_has_error(parser)) 121 | parser->cs = cs; 122 | parser->nread += p - (buffer + off); 123 | 124 | assert(p <= pe && "buffer overflow after parsing execute"); 125 | assert(parser->nread <= len && "nread longer than length"); 126 | assert(parser->body_start <= len && "body starts after buffer end"); 127 | assert(parser->mark < len && "mark is after buffer end"); 128 | assert(parser->field_len <= len && "field has length longer than whole buffer"); 129 | assert(parser->field_start < len && "field starts after buffer end"); 130 | 131 | return(parser->nread); 132 | } 133 | 134 | int http_response_parser_finish(http_response_parser *parser) 135 | { 136 | if (http_response_parser_has_error(parser) ) { 137 | return -1; 138 | } else if (http_response_parser_is_finished(parser) ) { 139 | return 1; 140 | } else { 141 | return 0; 142 | } 143 | } 144 | 145 | int http_response_parser_has_error(http_response_parser *parser) { 146 | return parser->cs == http_response_parser_error; 147 | } 148 | 149 | int http_response_parser_is_finished(http_response_parser *parser) { 150 | return parser->cs >= http_response_parser_first_final; 151 | } 152 | -------------------------------------------------------------------------------- /parsers/parser.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NGX_TCP_PARSER_H_INCLUDED_ 3 | #define _NGX_TCP_PARSER_H_INCLUDED_ 4 | 5 | #include 6 | 7 | /*HTTP parser*/ 8 | typedef void (*element_cb)(void *data, const signed char *at, size_t length); 9 | typedef void (*field_cb)(void *data, const signed char *field, 10 | size_t flen, const signed char *value, size_t vlen); 11 | 12 | 13 | #endif //_NGX_TCP_PARSER_H_INCLUDED_ 14 | -------------------------------------------------------------------------------- /parsers/smtp_response_parser.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NGX_TCP_SMTP_RESPONSE_PARSER_H_INCLUDED_ 3 | #define _NGX_TCP_SMTP_RESPONSE_PARSER_H_INCLUDED_ 4 | 5 | #include 6 | 7 | typedef struct smtp_parser { 8 | 9 | int cs; 10 | size_t nread; 11 | size_t mark; 12 | 13 | int hello_reply_code; 14 | 15 | void *data; 16 | 17 | element_cb domain; 18 | element_cb greeting_text; 19 | element_cb reply_code; 20 | element_cb reply_text; 21 | element_cb smtp_done; 22 | 23 | } smtp_parser; 24 | 25 | int smtp_parser_init(smtp_parser *parser); 26 | int smtp_parser_finish(smtp_parser *parser); 27 | size_t smtp_parser_execute(smtp_parser *parser, const signed char *data, size_t len, size_t off); 28 | int smtp_parser_has_error(smtp_parser *parser); 29 | int smtp_parser_is_finished(smtp_parser *parser); 30 | 31 | #endif //_NGX_TCP_SMTP_RESPONSE_PARSER_H_INCLUDED_ 32 | -------------------------------------------------------------------------------- /parsers/smtp_response_parser.rl: -------------------------------------------------------------------------------- 1 | 2 | #include "../ngx_tcp_upstream_check.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define LEN(AT, FPC) (FPC - buffer - parser->AT) 11 | #define MARK(M,FPC) (parser->M = (FPC) - buffer) 12 | #define PTR_TO(F) (buffer + parser->F) 13 | 14 | /** Machine **/ 15 | 16 | %%{ 17 | 18 | machine smtp_parser; 19 | 20 | action mark {MARK(mark, fpc);} 21 | 22 | action domain { 23 | if(parser->domain != NULL) { 24 | parser->domain(parser->data, PTR_TO(mark), LEN(mark, fpc)); 25 | } 26 | } 27 | 28 | action greeting_text { 29 | if(parser->greeting_text != NULL) 30 | parser->greeting_text(parser->data, PTR_TO(mark), LEN(mark, fpc)); 31 | } 32 | 33 | action reply_code { 34 | if(parser->reply_code != NULL) 35 | parser->reply_code(parser->data, PTR_TO(mark), LEN(mark,fpc)); 36 | } 37 | 38 | action reply_text { 39 | if(parser->reply_text != NULL) 40 | parser->reply_text(parser->data, PTR_TO(mark), LEN(mark,fpc)); 41 | } 42 | 43 | action done { 44 | if(parser->smtp_done != NULL) 45 | parser->smtp_done(parser->data, fpc + 1, pe - fpc - 1); 46 | fbreak; 47 | } 48 | 49 | #### SMTP PROTOCOL GRAMMAR 50 | CRLF = "\r\n"; 51 | SP = " "; 52 | 53 | Let_dig = alnum; 54 | Ldh_str = ( alnum | "-" )* alnum; 55 | Snum = digit{1,3}; 56 | #Standardized_tag = Ldh_str; 57 | #Not supported yet 58 | #General_address_literal = Standardized_tag ":" content{1,d}; 59 | 60 | IPv4_address_literal = Snum ("." Snum){3}; 61 | 62 | IPv6_hex = xdigit{1,4}; 63 | IPv6_full = IPv6_hex ( ":" IPv6_hex ){7}; 64 | IPv6_comp = (IPv6_hex (":" IPv6_hex){0,5})? "::" (IPv6_hex (":" IPv6_hex){0,5})?; 65 | IPv6v4_full = IPv6_hex (":" IPv6_hex){5} ":" IPv4_address_literal; 66 | IPv6v4_comp = (IPv6_hex (":" IPv6_hex){0,3})? "::" (IPv6_hex (":" IPv6_hex){0,3} ":")? IPv4_address_literal; 67 | 68 | IPv6_addr = ( IPv6_full | IPv6_comp | IPv6v4_full | IPv6v4_comp ); 69 | 70 | IPv6_address_literal = "IPv6:" IPv6_addr; 71 | 72 | Sub_domain = Let_dig Ldh_str?; 73 | #Address_literal = "[" ( Pv4_address_literal | IPv6_address_literal | General_address_literal ) "]"; 74 | Address_literal = "[" ( IPv4_address_literal | IPv6_address_literal ) "]"; 75 | 76 | #It should be '+', but smtp.163.com is sucks. 77 | #Domain = (( Sub_domain ( '.' Sub_domain )+ ) | Address_literal ) >mark %domain; 78 | Domain = (( Sub_domain ( '.' Sub_domain )? ) | Address_literal ) >mark %domain; 79 | 80 | Greeting_text = ( ascii -- ("\r" | "\n") )+ >mark %greeting_text; 81 | 82 | Greeting_line = "220 " Domain ( SP Greeting_text )? CRLF; 83 | 84 | 85 | 86 | Reply_code = ( digit+ ) >mark %reply_code; 87 | 88 | Ehlo_keyword = Let_dig ( Let_dig | "-" )*; 89 | Ehlo_param = ( ascii -- ( cntrl | SP ) )+; 90 | 91 | #the "=" is not in the RFC, the reason see also: http://www.linuxquestions.org/questions/linux-networking-3/qmail-auth-login-auth%3Dlogin-arghhhhhhhh-226524/ 92 | Ehlo_line = ( Ehlo_keyword ( ( SP | "=" ) Ehlo_param )* ) >mark %reply_text; 93 | 94 | Ehlo_reply_ok = ( ( "250" Domain ( SP Greeting_text )? CRLF ) 95 | | ("250-" Domain ( SP Greeting_text)? CRLF ( "250-" Ehlo_line CRLF )* Reply_code SP Ehlo_line CRLF) ); 96 | 97 | Reply_text = ( ascii -- ("\r" | "\n") )+ >mark %reply_text; 98 | 99 | General_reply_line = Reply_code ( SP Reply_text )? CRLF; 100 | 101 | Reply_line = ( General_reply_line | Ehlo_reply_ok ); 102 | 103 | 104 | Response = Greeting_line Reply_line @done; 105 | 106 | main := Response; 107 | 108 | }%% 109 | 110 | /** Data **/ 111 | %% write data; 112 | 113 | int smtp_parser_init(smtp_parser *parser) { 114 | 115 | int cs = 0; 116 | %% write init; 117 | parser->cs = cs; 118 | parser->mark = 0; 119 | parser->nread = 0; 120 | 121 | return(1); 122 | } 123 | 124 | 125 | /** exec **/ 126 | size_t smtp_parser_execute(smtp_parser *parser, const signed char *buffer, size_t len, size_t off) { 127 | 128 | const signed char *p, *pe; 129 | int cs = parser->cs; 130 | 131 | assert(off <= len && "offset past end of buffer"); 132 | 133 | p = buffer + off; 134 | pe = buffer + len; 135 | 136 | %% write exec; 137 | 138 | if (!smtp_parser_has_error(parser)) 139 | parser->cs = cs; 140 | parser->nread += p - (buffer + off); 141 | 142 | return(parser->nread); 143 | } 144 | 145 | int smtp_parser_finish(smtp_parser *parser) 146 | { 147 | if (smtp_parser_has_error(parser) ) { 148 | return -1; 149 | } else if (smtp_parser_is_finished(parser) ) { 150 | return 1; 151 | } else { 152 | return 0; 153 | } 154 | } 155 | 156 | int smtp_parser_has_error(smtp_parser *parser) { 157 | return parser->cs == smtp_parser_error; 158 | } 159 | 160 | int smtp_parser_is_finished(smtp_parser *parser) { 161 | return parser->cs >= smtp_parser_first_final; 162 | } 163 | -------------------------------------------------------------------------------- /tcp.patch: -------------------------------------------------------------------------------- 1 | From c30ac038b33e16890291f8dd5b7c8b2cc87b42a3 Mon Sep 17 00:00:00 2001 2 | From: medzeus2 3 | Date: Fri, 19 Aug 2016 18:06:53 +0800 4 | Subject: [PATCH] create patch fro nginx_tcp_proxy_module 5 | 6 | --- 7 | src/core/ngx_log.c | 2 +- 8 | src/core/ngx_log.h | 3 ++- 9 | src/event/ngx_event_connect.h | 2 ++ 10 | 3 files changed, 5 insertions(+), 2 deletions(-) 11 | 12 | diff --git a/src/core/ngx_log.c b/src/core/ngx_log.c 13 | index 8e9408d..b511284 100644 14 | --- a/src/core/ngx_log.c 15 | +++ b/src/core/ngx_log.c 16 | @@ -86,7 +86,7 @@ static ngx_str_t err_levels[] = { 17 | 18 | static const char *debug_levels[] = { 19 | "debug_core", "debug_alloc", "debug_mutex", "debug_event", 20 | - "debug_http", "debug_mail", "debug_stream" 21 | + "debug_http", "debug_mail", "debug_stream", "debug_tcp" 22 | }; 23 | 24 | 25 | diff --git a/src/core/ngx_log.h b/src/core/ngx_log.h 26 | index afb73bf..28b1aff 100644 27 | --- a/src/core/ngx_log.h 28 | +++ b/src/core/ngx_log.h 29 | @@ -30,6 +30,7 @@ 30 | #define NGX_LOG_DEBUG_HTTP 0x100 31 | #define NGX_LOG_DEBUG_MAIL 0x200 32 | #define NGX_LOG_DEBUG_STREAM 0x400 33 | +#define NGX_LOG_DEBUG_TCP 0x800 34 | 35 | /* 36 | * do not forget to update debug_levels[] in src/core/ngx_log.c 37 | @@ -37,7 +38,7 @@ 38 | */ 39 | 40 | #define NGX_LOG_DEBUG_FIRST NGX_LOG_DEBUG_CORE 41 | -#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_STREAM 42 | +#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_TCP 43 | #define NGX_LOG_DEBUG_CONNECTION 0x80000000 44 | #define NGX_LOG_DEBUG_ALL 0x7ffffff0 45 | 46 | diff --git a/src/event/ngx_event_connect.h b/src/event/ngx_event_connect.h 47 | index 10b72a1..384586b 100644 48 | --- a/src/event/ngx_event_connect.h 49 | +++ b/src/event/ngx_event_connect.h 50 | @@ -33,6 +33,7 @@ typedef void (*ngx_event_save_peer_session_pt)(ngx_peer_connection_t *pc, 51 | void *data); 52 | #endif 53 | 54 | +#define NGX_INVALID_CHECK_INDEX (ngx_uint_t)(-1) 55 | 56 | struct ngx_peer_connection_s { 57 | ngx_connection_t *connection; 58 | @@ -42,6 +43,7 @@ struct ngx_peer_connection_s { 59 | ngx_str_t *name; 60 | 61 | ngx_uint_t tries; 62 | + ngx_uint_t check_index; 63 | ngx_msec_t start_time; 64 | 65 | ngx_event_get_peer_pt get; 66 | -- 67 | 1.7.1 68 | 69 | -------------------------------------------------------------------------------- /tcp_1_8.patch: -------------------------------------------------------------------------------- 1 | From c30ac038b33e16890291f8dd5b7c8b2cc87b42a3 Mon Sep 17 00:00:00 2001 2 | From: medzeus2 3 | Date: Fri, 19 Aug 2016 18:06:53 +0800 4 | Subject: [PATCH] create patch fro nginx_tcp_proxy_module 5 | 6 | --- 7 | src/core/ngx_log.c | 2 +- 8 | src/core/ngx_log.h | 3 ++- 9 | src/event/ngx_event_connect.h | 2 ++ 10 | 3 files changed, 5 insertions(+), 2 deletions(-) 11 | 12 | diff --git a/src/core/ngx_log.c b/src/core/ngx_log.c 13 | index 8e9408d..b511284 100644 14 | --- a/src/core/ngx_log.c 15 | +++ b/src/core/ngx_log.c 16 | @@ -86,7 +86,7 @@ static ngx_str_t err_levels[] = { 17 | 18 | static const char *debug_levels[] = { 19 | "debug_core", "debug_alloc", "debug_mutex", "debug_event", 20 | - "debug_http", "debug_mail", "debug_stream" 21 | + "debug_http", "debug_mail", "debug_stream", "debug_tcp" 22 | }; 23 | 24 | 25 | diff --git a/src/core/ngx_log.h b/src/core/ngx_log.h 26 | index afb73bf..28b1aff 100644 27 | --- a/src/core/ngx_log.h 28 | +++ b/src/core/ngx_log.h 29 | @@ -30,6 +30,7 @@ 30 | #define NGX_LOG_DEBUG_HTTP 0x100 31 | #define NGX_LOG_DEBUG_MAIL 0x200 32 | #define NGX_LOG_DEBUG_STREAM 0x400 33 | +#define NGX_LOG_DEBUG_TCP 0x800 34 | 35 | /* 36 | * do not forget to update debug_levels[] in src/core/ngx_log.c 37 | @@ -37,7 +38,7 @@ 38 | */ 39 | 40 | #define NGX_LOG_DEBUG_FIRST NGX_LOG_DEBUG_CORE 41 | -#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_STREAM 42 | +#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_TCP 43 | #define NGX_LOG_DEBUG_CONNECTION 0x80000000 44 | #define NGX_LOG_DEBUG_ALL 0x7ffffff0 45 | 46 | diff --git a/src/event/ngx_event_connect.h b/src/event/ngx_event_connect.h 47 | index 10b72a1..384586b 100644 48 | --- a/src/event/ngx_event_connect.h 49 | +++ b/src/event/ngx_event_connect.h 50 | @@ -33,6 +33,7 @@ typedef void (*ngx_event_save_peer_session_pt)(ngx_peer_connection_t *pc, 51 | void *data); 52 | #endif 53 | 54 | +#define NGX_INVALID_CHECK_INDEX (ngx_uint_t)(-1) 55 | 56 | struct ngx_peer_connection_s { 57 | ngx_connection_t *connection; 58 | @@ -42,6 +43,7 @@ struct ngx_peer_connection_s { 59 | ngx_str_t *name; 60 | 61 | ngx_uint_t tries; 62 | + ngx_uint_t check_index; 63 | ngx_msec_t start_time; 64 | 65 | ngx_event_get_peer_pt get; 66 | -- 67 | 1.7.1 68 | 69 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | NAME 2 | Test::Nginx - Testing modules for Nginx C module development 3 | 4 | DESCRIPTION 5 | This distribution provides two testing modules for Nginx C module 6 | development: 7 | 8 | * Test::Nginx::LWP 9 | 10 | * Test::Nginx::Socket 11 | 12 | All of them are based on Test::Base. 13 | 14 | SOURCE REPOSITORY 15 | This module has a Git repository on Github, which has access for all. 16 | 17 | http://github.com/agentzh/test-nginx 18 | 19 | If you want a commit bit, feel free to drop me a line. 20 | 21 | AUTHOR 22 | agentzh (章亦春) "" 23 | 24 | COPYRIGHT & LICENSE 25 | Copyright (c) 2009, Taobao Inc., Alibaba Group 26 | (). 27 | 28 | Copyright (c) 2009, agentzh "". 29 | 30 | This module is licensed under the terms of the BSD license. 31 | 32 | Redistribution and use in source and binary forms, with or without 33 | modification, are permitted provided that the following conditions are 34 | met: 35 | 36 | * Redistributions of source code must retain the above copyright 37 | notice, this list of conditions and the following disclaimer. 38 | 39 | * Redistributions in binary form must reproduce the above copyright 40 | notice, this list of conditions and the following disclaimer in the 41 | documentation and/or other materials provided with the distribution. 42 | 43 | * Neither the name of the Taobao Inc. nor the names of its 44 | contributors may be used to endorse or promote products derived from 45 | this software without specific prior written permission. 46 | 47 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 48 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 49 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 50 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 51 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 52 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 53 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 54 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 55 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 56 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 57 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 58 | 59 | SEE ALSO 60 | Test::Nginx::LWP, Test::Nginx::Socket, Test::Base. 61 | 62 | -------------------------------------------------------------------------------- /test/inc/Module/Install.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install; 3 | 4 | # For any maintainers: 5 | # The load order for Module::Install is a bit magic. 6 | # It goes something like this... 7 | # 8 | # IF ( host has Module::Install installed, creating author mode ) { 9 | # 1. Makefile.PL calls "use inc::Module::Install" 10 | # 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install 11 | # 3. The installed version of inc::Module::Install loads 12 | # 4. inc::Module::Install calls "require Module::Install" 13 | # 5. The ./inc/ version of Module::Install loads 14 | # } ELSE { 15 | # 1. Makefile.PL calls "use inc::Module::Install" 16 | # 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install 17 | # 3. The ./inc/ version of Module::Install loads 18 | # } 19 | 20 | use 5.005; 21 | use strict 'vars'; 22 | 23 | use vars qw{$VERSION $MAIN}; 24 | BEGIN { 25 | # All Module::Install core packages now require synchronised versions. 26 | # This will be used to ensure we don't accidentally load old or 27 | # different versions of modules. 28 | # This is not enforced yet, but will be some time in the next few 29 | # releases once we can make sure it won't clash with custom 30 | # Module::Install extensions. 31 | $VERSION = '0.91'; 32 | 33 | # Storage for the pseudo-singleton 34 | $MAIN = undef; 35 | 36 | *inc::Module::Install::VERSION = *VERSION; 37 | @inc::Module::Install::ISA = __PACKAGE__; 38 | 39 | } 40 | 41 | 42 | 43 | 44 | 45 | # Whether or not inc::Module::Install is actually loaded, the 46 | # $INC{inc/Module/Install.pm} is what will still get set as long as 47 | # the caller loaded module this in the documented manner. 48 | # If not set, the caller may NOT have loaded the bundled version, and thus 49 | # they may not have a MI version that works with the Makefile.PL. This would 50 | # result in false errors or unexpected behaviour. And we don't want that. 51 | my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; 52 | unless ( $INC{$file} ) { die <<"END_DIE" } 53 | 54 | Please invoke ${\__PACKAGE__} with: 55 | 56 | use inc::${\__PACKAGE__}; 57 | 58 | not: 59 | 60 | use ${\__PACKAGE__}; 61 | 62 | END_DIE 63 | 64 | 65 | 66 | 67 | 68 | # If the script that is loading Module::Install is from the future, 69 | # then make will detect this and cause it to re-run over and over 70 | # again. This is bad. Rather than taking action to touch it (which 71 | # is unreliable on some platforms and requires write permissions) 72 | # for now we should catch this and refuse to run. 73 | if ( -f $0 ) { 74 | my $s = (stat($0))[9]; 75 | 76 | # If the modification time is only slightly in the future, 77 | # sleep briefly to remove the problem. 78 | my $a = $s - time; 79 | if ( $a > 0 and $a < 5 ) { sleep 5 } 80 | 81 | # Too far in the future, throw an error. 82 | my $t = time; 83 | if ( $s > $t ) { die <<"END_DIE" } 84 | 85 | Your installer $0 has a modification time in the future ($s > $t). 86 | 87 | This is known to create infinite loops in make. 88 | 89 | Please correct this, then run $0 again. 90 | 91 | END_DIE 92 | } 93 | 94 | 95 | 96 | 97 | 98 | # Build.PL was formerly supported, but no longer is due to excessive 99 | # difficulty in implementing every single feature twice. 100 | if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } 101 | 102 | Module::Install no longer supports Build.PL. 103 | 104 | It was impossible to maintain duel backends, and has been deprecated. 105 | 106 | Please remove all Build.PL files and only use the Makefile.PL installer. 107 | 108 | END_DIE 109 | 110 | 111 | 112 | 113 | 114 | # To save some more typing in Module::Install installers, every... 115 | # use inc::Module::Install 116 | # ...also acts as an implicit use strict. 117 | $^H |= strict::bits(qw(refs subs vars)); 118 | 119 | 120 | 121 | 122 | 123 | use Cwd (); 124 | use File::Find (); 125 | use File::Path (); 126 | use FindBin; 127 | 128 | sub autoload { 129 | my $self = shift; 130 | my $who = $self->_caller; 131 | my $cwd = Cwd::cwd(); 132 | my $sym = "${who}::AUTOLOAD"; 133 | $sym->{$cwd} = sub { 134 | my $pwd = Cwd::cwd(); 135 | if ( my $code = $sym->{$pwd} ) { 136 | # Delegate back to parent dirs 137 | goto &$code unless $cwd eq $pwd; 138 | } 139 | $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym"; 140 | my $method = $1; 141 | if ( uc($method) eq $method ) { 142 | # Do nothing 143 | return; 144 | } elsif ( $method =~ /^_/ and $self->can($method) ) { 145 | # Dispatch to the root M:I class 146 | return $self->$method(@_); 147 | } 148 | 149 | # Dispatch to the appropriate plugin 150 | unshift @_, ( $self, $1 ); 151 | goto &{$self->can('call')}; 152 | }; 153 | } 154 | 155 | sub import { 156 | my $class = shift; 157 | my $self = $class->new(@_); 158 | my $who = $self->_caller; 159 | 160 | unless ( -f $self->{file} ) { 161 | require "$self->{path}/$self->{dispatch}.pm"; 162 | File::Path::mkpath("$self->{prefix}/$self->{author}"); 163 | $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); 164 | $self->{admin}->init; 165 | @_ = ($class, _self => $self); 166 | goto &{"$self->{name}::import"}; 167 | } 168 | 169 | *{"${who}::AUTOLOAD"} = $self->autoload; 170 | $self->preload; 171 | 172 | # Unregister loader and worker packages so subdirs can use them again 173 | delete $INC{"$self->{file}"}; 174 | delete $INC{"$self->{path}.pm"}; 175 | 176 | # Save to the singleton 177 | $MAIN = $self; 178 | 179 | return 1; 180 | } 181 | 182 | sub preload { 183 | my $self = shift; 184 | unless ( $self->{extensions} ) { 185 | $self->load_extensions( 186 | "$self->{prefix}/$self->{path}", $self 187 | ); 188 | } 189 | 190 | my @exts = @{$self->{extensions}}; 191 | unless ( @exts ) { 192 | @exts = $self->{admin}->load_all_extensions; 193 | } 194 | 195 | my %seen; 196 | foreach my $obj ( @exts ) { 197 | while (my ($method, $glob) = each %{ref($obj) . '::'}) { 198 | next unless $obj->can($method); 199 | next if $method =~ /^_/; 200 | next if $method eq uc($method); 201 | $seen{$method}++; 202 | } 203 | } 204 | 205 | my $who = $self->_caller; 206 | foreach my $name ( sort keys %seen ) { 207 | *{"${who}::$name"} = sub { 208 | ${"${who}::AUTOLOAD"} = "${who}::$name"; 209 | goto &{"${who}::AUTOLOAD"}; 210 | }; 211 | } 212 | } 213 | 214 | sub new { 215 | my ($class, %args) = @_; 216 | 217 | # ignore the prefix on extension modules built from top level. 218 | my $base_path = Cwd::abs_path($FindBin::Bin); 219 | unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { 220 | delete $args{prefix}; 221 | } 222 | 223 | return $args{_self} if $args{_self}; 224 | 225 | $args{dispatch} ||= 'Admin'; 226 | $args{prefix} ||= 'inc'; 227 | $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); 228 | $args{bundle} ||= 'inc/BUNDLES'; 229 | $args{base} ||= $base_path; 230 | $class =~ s/^\Q$args{prefix}\E:://; 231 | $args{name} ||= $class; 232 | $args{version} ||= $class->VERSION; 233 | unless ( $args{path} ) { 234 | $args{path} = $args{name}; 235 | $args{path} =~ s!::!/!g; 236 | } 237 | $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; 238 | $args{wrote} = 0; 239 | 240 | bless( \%args, $class ); 241 | } 242 | 243 | sub call { 244 | my ($self, $method) = @_; 245 | my $obj = $self->load($method) or return; 246 | splice(@_, 0, 2, $obj); 247 | goto &{$obj->can($method)}; 248 | } 249 | 250 | sub load { 251 | my ($self, $method) = @_; 252 | 253 | $self->load_extensions( 254 | "$self->{prefix}/$self->{path}", $self 255 | ) unless $self->{extensions}; 256 | 257 | foreach my $obj (@{$self->{extensions}}) { 258 | return $obj if $obj->can($method); 259 | } 260 | 261 | my $admin = $self->{admin} or die <<"END_DIE"; 262 | The '$method' method does not exist in the '$self->{prefix}' path! 263 | Please remove the '$self->{prefix}' directory and run $0 again to load it. 264 | END_DIE 265 | 266 | my $obj = $admin->load($method, 1); 267 | push @{$self->{extensions}}, $obj; 268 | 269 | $obj; 270 | } 271 | 272 | sub load_extensions { 273 | my ($self, $path, $top) = @_; 274 | 275 | unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { 276 | unshift @INC, $self->{prefix}; 277 | } 278 | 279 | foreach my $rv ( $self->find_extensions($path) ) { 280 | my ($file, $pkg) = @{$rv}; 281 | next if $self->{pathnames}{$pkg}; 282 | 283 | local $@; 284 | my $new = eval { require $file; $pkg->can('new') }; 285 | unless ( $new ) { 286 | warn $@ if $@; 287 | next; 288 | } 289 | $self->{pathnames}{$pkg} = delete $INC{$file}; 290 | push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); 291 | } 292 | 293 | $self->{extensions} ||= []; 294 | } 295 | 296 | sub find_extensions { 297 | my ($self, $path) = @_; 298 | 299 | my @found; 300 | File::Find::find( sub { 301 | my $file = $File::Find::name; 302 | return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; 303 | my $subpath = $1; 304 | return if lc($subpath) eq lc($self->{dispatch}); 305 | 306 | $file = "$self->{path}/$subpath.pm"; 307 | my $pkg = "$self->{name}::$subpath"; 308 | $pkg =~ s!/!::!g; 309 | 310 | # If we have a mixed-case package name, assume case has been preserved 311 | # correctly. Otherwise, root through the file to locate the case-preserved 312 | # version of the package name. 313 | if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { 314 | my $content = Module::Install::_read($subpath . '.pm'); 315 | my $in_pod = 0; 316 | foreach ( split //, $content ) { 317 | $in_pod = 1 if /^=\w/; 318 | $in_pod = 0 if /^=cut/; 319 | next if ($in_pod || /^=cut/); # skip pod text 320 | next if /^\s*#/; # and comments 321 | if ( m/^\s*package\s+($pkg)\s*;/i ) { 322 | $pkg = $1; 323 | last; 324 | } 325 | } 326 | } 327 | 328 | push @found, [ $file, $pkg ]; 329 | }, $path ) if -d $path; 330 | 331 | @found; 332 | } 333 | 334 | 335 | 336 | 337 | 338 | ##################################################################### 339 | # Common Utility Functions 340 | 341 | sub _caller { 342 | my $depth = 0; 343 | my $call = caller($depth); 344 | while ( $call eq __PACKAGE__ ) { 345 | $depth++; 346 | $call = caller($depth); 347 | } 348 | return $call; 349 | } 350 | 351 | sub _read { 352 | local *FH; 353 | if ( $] >= 5.006 ) { 354 | open( FH, '<', $_[0] ) or die "open($_[0]): $!"; 355 | } else { 356 | open( FH, "< $_[0]" ) or die "open($_[0]): $!"; 357 | } 358 | my $string = do { local $/; }; 359 | close FH or die "close($_[0]): $!"; 360 | return $string; 361 | } 362 | 363 | sub _readperl { 364 | my $string = Module::Install::_read($_[0]); 365 | $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; 366 | $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; 367 | $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; 368 | return $string; 369 | } 370 | 371 | sub _readpod { 372 | my $string = Module::Install::_read($_[0]); 373 | $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; 374 | return $string if $_[0] =~ /\.pod\z/; 375 | $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; 376 | $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; 377 | $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; 378 | $string =~ s/^\n+//s; 379 | return $string; 380 | } 381 | 382 | sub _write { 383 | local *FH; 384 | if ( $] >= 5.006 ) { 385 | open( FH, '>', $_[0] ) or die "open($_[0]): $!"; 386 | } else { 387 | open( FH, "> $_[0]" ) or die "open($_[0]): $!"; 388 | } 389 | foreach ( 1 .. $#_ ) { 390 | print FH $_[$_] or die "print($_[0]): $!"; 391 | } 392 | close FH or die "close($_[0]): $!"; 393 | } 394 | 395 | # _version is for processing module versions (eg, 1.03_05) not 396 | # Perl versions (eg, 5.8.1). 397 | sub _version ($) { 398 | my $s = shift || 0; 399 | my $d =()= $s =~ /(\.)/g; 400 | if ( $d >= 2 ) { 401 | # Normalise multipart versions 402 | $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; 403 | } 404 | $s =~ s/^(\d+)\.?//; 405 | my $l = $1 || 0; 406 | my @v = map { 407 | $_ . '0' x (3 - length $_) 408 | } $s =~ /(\d{1,3})\D?/g; 409 | $l = $l . '.' . join '', @v if @v; 410 | return $l + 0; 411 | } 412 | 413 | sub _cmp ($$) { 414 | _version($_[0]) <=> _version($_[1]); 415 | } 416 | 417 | # Cloned from Params::Util::_CLASS 418 | sub _CLASS ($) { 419 | ( 420 | defined $_[0] 421 | and 422 | ! ref $_[0] 423 | and 424 | $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s 425 | ) ? $_[0] : undef; 426 | } 427 | 428 | 1; 429 | 430 | # Copyright 2008 - 2009 Adam Kennedy. 431 | -------------------------------------------------------------------------------- /test/inc/Module/Install/AutoInstall.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install::AutoInstall; 3 | 4 | use strict; 5 | use Module::Install::Base (); 6 | 7 | use vars qw{$VERSION @ISA $ISCORE}; 8 | BEGIN { 9 | $VERSION = '0.91'; 10 | @ISA = 'Module::Install::Base'; 11 | $ISCORE = 1; 12 | } 13 | 14 | sub AutoInstall { $_[0] } 15 | 16 | sub run { 17 | my $self = shift; 18 | $self->auto_install_now(@_); 19 | } 20 | 21 | sub write { 22 | my $self = shift; 23 | $self->auto_install(@_); 24 | } 25 | 26 | sub auto_install { 27 | my $self = shift; 28 | return if $self->{done}++; 29 | 30 | # Flatten array of arrays into a single array 31 | my @core = map @$_, map @$_, grep ref, 32 | $self->build_requires, $self->requires; 33 | 34 | my @config = @_; 35 | 36 | # We'll need Module::AutoInstall 37 | $self->include('Module::AutoInstall'); 38 | require Module::AutoInstall; 39 | 40 | Module::AutoInstall->import( 41 | (@config ? (-config => \@config) : ()), 42 | (@core ? (-core => \@core) : ()), 43 | $self->features, 44 | ); 45 | 46 | $self->makemaker_args( Module::AutoInstall::_make_args() ); 47 | 48 | my $class = ref($self); 49 | $self->postamble( 50 | "# --- $class section:\n" . 51 | Module::AutoInstall::postamble() 52 | ); 53 | } 54 | 55 | sub auto_install_now { 56 | my $self = shift; 57 | $self->auto_install(@_); 58 | Module::AutoInstall::do_install(); 59 | } 60 | 61 | 1; 62 | -------------------------------------------------------------------------------- /test/inc/Module/Install/Base.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install::Base; 3 | 4 | use strict 'vars'; 5 | use vars qw{$VERSION}; 6 | BEGIN { 7 | $VERSION = '0.91'; 8 | } 9 | 10 | # Suspend handler for "redefined" warnings 11 | BEGIN { 12 | my $w = $SIG{__WARN__}; 13 | $SIG{__WARN__} = sub { $w }; 14 | } 15 | 16 | #line 42 17 | 18 | sub new { 19 | my $class = shift; 20 | unless ( defined &{"${class}::call"} ) { 21 | *{"${class}::call"} = sub { shift->_top->call(@_) }; 22 | } 23 | unless ( defined &{"${class}::load"} ) { 24 | *{"${class}::load"} = sub { shift->_top->load(@_) }; 25 | } 26 | bless { @_ }, $class; 27 | } 28 | 29 | #line 61 30 | 31 | sub AUTOLOAD { 32 | local $@; 33 | my $func = eval { shift->_top->autoload } or return; 34 | goto &$func; 35 | } 36 | 37 | #line 75 38 | 39 | sub _top { 40 | $_[0]->{_top}; 41 | } 42 | 43 | #line 90 44 | 45 | sub admin { 46 | $_[0]->_top->{admin} 47 | or 48 | Module::Install::Base::FakeAdmin->new; 49 | } 50 | 51 | #line 106 52 | 53 | sub is_admin { 54 | $_[0]->admin->VERSION; 55 | } 56 | 57 | sub DESTROY {} 58 | 59 | package Module::Install::Base::FakeAdmin; 60 | 61 | my $fake; 62 | 63 | sub new { 64 | $fake ||= bless(\@_, $_[0]); 65 | } 66 | 67 | sub AUTOLOAD {} 68 | 69 | sub DESTROY {} 70 | 71 | # Restore warning handler 72 | BEGIN { 73 | $SIG{__WARN__} = $SIG{__WARN__}->(); 74 | } 75 | 76 | 1; 77 | 78 | #line 154 79 | -------------------------------------------------------------------------------- /test/inc/Module/Install/Can.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install::Can; 3 | 4 | use strict; 5 | use Config (); 6 | use File::Spec (); 7 | use ExtUtils::MakeMaker (); 8 | use Module::Install::Base (); 9 | 10 | use vars qw{$VERSION @ISA $ISCORE}; 11 | BEGIN { 12 | $VERSION = '0.91'; 13 | @ISA = 'Module::Install::Base'; 14 | $ISCORE = 1; 15 | } 16 | 17 | # check if we can load some module 18 | ### Upgrade this to not have to load the module if possible 19 | sub can_use { 20 | my ($self, $mod, $ver) = @_; 21 | $mod =~ s{::|\\}{/}g; 22 | $mod .= '.pm' unless $mod =~ /\.pm$/i; 23 | 24 | my $pkg = $mod; 25 | $pkg =~ s{/}{::}g; 26 | $pkg =~ s{\.pm$}{}i; 27 | 28 | local $@; 29 | eval { require $mod; $pkg->VERSION($ver || 0); 1 }; 30 | } 31 | 32 | # check if we can run some command 33 | sub can_run { 34 | my ($self, $cmd) = @_; 35 | 36 | my $_cmd = $cmd; 37 | return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); 38 | 39 | for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { 40 | next if $dir eq ''; 41 | my $abs = File::Spec->catfile($dir, $_[1]); 42 | return $abs if (-x $abs or $abs = MM->maybe_command($abs)); 43 | } 44 | 45 | return; 46 | } 47 | 48 | # can we locate a (the) C compiler 49 | sub can_cc { 50 | my $self = shift; 51 | my @chunks = split(/ /, $Config::Config{cc}) or return; 52 | 53 | # $Config{cc} may contain args; try to find out the program part 54 | while (@chunks) { 55 | return $self->can_run("@chunks") || (pop(@chunks), next); 56 | } 57 | 58 | return; 59 | } 60 | 61 | # Fix Cygwin bug on maybe_command(); 62 | if ( $^O eq 'cygwin' ) { 63 | require ExtUtils::MM_Cygwin; 64 | require ExtUtils::MM_Win32; 65 | if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) { 66 | *ExtUtils::MM_Cygwin::maybe_command = sub { 67 | my ($self, $file) = @_; 68 | if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) { 69 | ExtUtils::MM_Win32->maybe_command($file); 70 | } else { 71 | ExtUtils::MM_Unix->maybe_command($file); 72 | } 73 | } 74 | } 75 | } 76 | 77 | 1; 78 | 79 | __END__ 80 | 81 | #line 156 82 | -------------------------------------------------------------------------------- /test/inc/Module/Install/Fetch.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install::Fetch; 3 | 4 | use strict; 5 | use Module::Install::Base (); 6 | 7 | use vars qw{$VERSION @ISA $ISCORE}; 8 | BEGIN { 9 | $VERSION = '0.91'; 10 | @ISA = 'Module::Install::Base'; 11 | $ISCORE = 1; 12 | } 13 | 14 | sub get_file { 15 | my ($self, %args) = @_; 16 | my ($scheme, $host, $path, $file) = 17 | $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; 18 | 19 | if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) { 20 | $args{url} = $args{ftp_url} 21 | or (warn("LWP support unavailable!\n"), return); 22 | ($scheme, $host, $path, $file) = 23 | $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; 24 | } 25 | 26 | $|++; 27 | print "Fetching '$file' from $host... "; 28 | 29 | unless (eval { require Socket; Socket::inet_aton($host) }) { 30 | warn "'$host' resolve failed!\n"; 31 | return; 32 | } 33 | 34 | return unless $scheme eq 'ftp' or $scheme eq 'http'; 35 | 36 | require Cwd; 37 | my $dir = Cwd::getcwd(); 38 | chdir $args{local_dir} or return if exists $args{local_dir}; 39 | 40 | if (eval { require LWP::Simple; 1 }) { 41 | LWP::Simple::mirror($args{url}, $file); 42 | } 43 | elsif (eval { require Net::FTP; 1 }) { eval { 44 | # use Net::FTP to get past firewall 45 | my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); 46 | $ftp->login("anonymous", 'anonymous@example.com'); 47 | $ftp->cwd($path); 48 | $ftp->binary; 49 | $ftp->get($file) or (warn("$!\n"), return); 50 | $ftp->quit; 51 | } } 52 | elsif (my $ftp = $self->can_run('ftp')) { eval { 53 | # no Net::FTP, fallback to ftp.exe 54 | require FileHandle; 55 | my $fh = FileHandle->new; 56 | 57 | local $SIG{CHLD} = 'IGNORE'; 58 | unless ($fh->open("|$ftp -n")) { 59 | warn "Couldn't open ftp: $!\n"; 60 | chdir $dir; return; 61 | } 62 | 63 | my @dialog = split(/\n/, <<"END_FTP"); 64 | open $host 65 | user anonymous anonymous\@example.com 66 | cd $path 67 | binary 68 | get $file $file 69 | quit 70 | END_FTP 71 | foreach (@dialog) { $fh->print("$_\n") } 72 | $fh->close; 73 | } } 74 | else { 75 | warn "No working 'ftp' program available!\n"; 76 | chdir $dir; return; 77 | } 78 | 79 | unless (-f $file) { 80 | warn "Fetching failed: $@\n"; 81 | chdir $dir; return; 82 | } 83 | 84 | return if exists $args{size} and -s $file != $args{size}; 85 | system($args{run}) if exists $args{run}; 86 | unlink($file) if $args{remove}; 87 | 88 | print(((!exists $args{check_for} or -e $args{check_for}) 89 | ? "done!" : "failed! ($!)"), "\n"); 90 | chdir $dir; return !$?; 91 | } 92 | 93 | 1; 94 | -------------------------------------------------------------------------------- /test/inc/Module/Install/Include.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install::Include; 3 | 4 | use strict; 5 | use Module::Install::Base (); 6 | 7 | use vars qw{$VERSION @ISA $ISCORE}; 8 | BEGIN { 9 | $VERSION = '0.91'; 10 | @ISA = 'Module::Install::Base'; 11 | $ISCORE = 1; 12 | } 13 | 14 | sub include { 15 | shift()->admin->include(@_); 16 | } 17 | 18 | sub include_deps { 19 | shift()->admin->include_deps(@_); 20 | } 21 | 22 | sub auto_include { 23 | shift()->admin->auto_include(@_); 24 | } 25 | 26 | sub auto_include_deps { 27 | shift()->admin->auto_include_deps(@_); 28 | } 29 | 30 | sub auto_include_dependent_dists { 31 | shift()->admin->auto_include_dependent_dists(@_); 32 | } 33 | 34 | 1; 35 | -------------------------------------------------------------------------------- /test/inc/Module/Install/Makefile.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install::Makefile; 3 | 4 | use strict 'vars'; 5 | use ExtUtils::MakeMaker (); 6 | use Module::Install::Base (); 7 | 8 | use vars qw{$VERSION @ISA $ISCORE}; 9 | BEGIN { 10 | $VERSION = '0.91'; 11 | @ISA = 'Module::Install::Base'; 12 | $ISCORE = 1; 13 | } 14 | 15 | sub Makefile { $_[0] } 16 | 17 | my %seen = (); 18 | 19 | sub prompt { 20 | shift; 21 | 22 | # Infinite loop protection 23 | my @c = caller(); 24 | if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) { 25 | die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])"; 26 | } 27 | 28 | # In automated testing, always use defaults 29 | if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) { 30 | local $ENV{PERL_MM_USE_DEFAULT} = 1; 31 | goto &ExtUtils::MakeMaker::prompt; 32 | } else { 33 | goto &ExtUtils::MakeMaker::prompt; 34 | } 35 | } 36 | 37 | sub makemaker_args { 38 | my $self = shift; 39 | my $args = ( $self->{makemaker_args} ||= {} ); 40 | %$args = ( %$args, @_ ); 41 | return $args; 42 | } 43 | 44 | # For mm args that take multiple space-separated args, 45 | # append an argument to the current list. 46 | sub makemaker_append { 47 | my $self = sShift; 48 | my $name = shift; 49 | my $args = $self->makemaker_args; 50 | $args->{name} = defined $args->{$name} 51 | ? join( ' ', $args->{name}, @_ ) 52 | : join( ' ', @_ ); 53 | } 54 | 55 | sub build_subdirs { 56 | my $self = shift; 57 | my $subdirs = $self->makemaker_args->{DIR} ||= []; 58 | for my $subdir (@_) { 59 | push @$subdirs, $subdir; 60 | } 61 | } 62 | 63 | sub clean_files { 64 | my $self = shift; 65 | my $clean = $self->makemaker_args->{clean} ||= {}; 66 | %$clean = ( 67 | %$clean, 68 | FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_), 69 | ); 70 | } 71 | 72 | sub realclean_files { 73 | my $self = shift; 74 | my $realclean = $self->makemaker_args->{realclean} ||= {}; 75 | %$realclean = ( 76 | %$realclean, 77 | FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_), 78 | ); 79 | } 80 | 81 | sub libs { 82 | my $self = shift; 83 | my $libs = ref $_[0] ? shift : [ shift ]; 84 | $self->makemaker_args( LIBS => $libs ); 85 | } 86 | 87 | sub inc { 88 | my $self = shift; 89 | $self->makemaker_args( INC => shift ); 90 | } 91 | 92 | my %test_dir = (); 93 | 94 | sub _wanted_t { 95 | /\.t$/ and -f $_ and $test_dir{$File::Find::dir} = 1; 96 | } 97 | 98 | sub tests_recursive { 99 | my $self = shift; 100 | if ( $self->tests ) { 101 | die "tests_recursive will not work if tests are already defined"; 102 | } 103 | my $dir = shift || 't'; 104 | unless ( -d $dir ) { 105 | die "tests_recursive dir '$dir' does not exist"; 106 | } 107 | %test_dir = (); 108 | require File::Find; 109 | File::Find::find( \&_wanted_t, $dir ); 110 | $self->tests( join ' ', map { "$_/*.t" } sort keys %test_dir ); 111 | } 112 | 113 | sub write { 114 | my $self = shift; 115 | die "&Makefile->write() takes no arguments\n" if @_; 116 | 117 | # Check the current Perl version 118 | my $perl_version = $self->perl_version; 119 | if ( $perl_version ) { 120 | eval "use $perl_version; 1" 121 | or die "ERROR: perl: Version $] is installed, " 122 | . "but we need version >= $perl_version"; 123 | } 124 | 125 | # Make sure we have a new enough MakeMaker 126 | require ExtUtils::MakeMaker; 127 | 128 | if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) { 129 | # MakeMaker can complain about module versions that include 130 | # an underscore, even though its own version may contain one! 131 | # Hence the funny regexp to get rid of it. See RT #35800 132 | # for details. 133 | $self->build_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/ ); 134 | $self->configure_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/ ); 135 | } else { 136 | # Allow legacy-compatibility with 5.005 by depending on the 137 | # most recent EU:MM that supported 5.005. 138 | $self->build_requires( 'ExtUtils::MakeMaker' => 6.42 ); 139 | $self->configure_requires( 'ExtUtils::MakeMaker' => 6.42 ); 140 | } 141 | 142 | # Generate the MakeMaker params 143 | my $args = $self->makemaker_args; 144 | $args->{DISTNAME} = $self->name; 145 | $args->{NAME} = $self->module_name || $self->name; 146 | $args->{VERSION} = $self->version; 147 | $args->{NAME} =~ s/-/::/g; 148 | if ( $self->tests ) { 149 | $args->{test} = { TESTS => $self->tests }; 150 | } 151 | if ( $] >= 5.005 ) { 152 | $args->{ABSTRACT} = $self->abstract; 153 | $args->{AUTHOR} = $self->author; 154 | } 155 | if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) { 156 | $args->{NO_META} = 1; 157 | } 158 | if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) { 159 | $args->{SIGN} = 1; 160 | } 161 | unless ( $self->is_admin ) { 162 | delete $args->{SIGN}; 163 | } 164 | 165 | # Merge both kinds of requires into prereq_pm 166 | my $prereq = ($args->{PREREQ_PM} ||= {}); 167 | %$prereq = ( %$prereq, 168 | map { @$_ } 169 | map { @$_ } 170 | grep $_, 171 | ($self->configure_requires, $self->build_requires, $self->requires) 172 | ); 173 | 174 | # Remove any reference to perl, PREREQ_PM doesn't support it 175 | delete $args->{PREREQ_PM}->{perl}; 176 | 177 | # merge both kinds of requires into prereq_pm 178 | my $subdirs = ($args->{DIR} ||= []); 179 | if ($self->bundles) { 180 | foreach my $bundle (@{ $self->bundles }) { 181 | my ($file, $dir) = @$bundle; 182 | push @$subdirs, $dir if -d $dir; 183 | delete $prereq->{$file}; 184 | } 185 | } 186 | 187 | if ( my $perl_version = $self->perl_version ) { 188 | eval "use $perl_version; 1" 189 | or die "ERROR: perl: Version $] is installed, " 190 | . "but we need version >= $perl_version"; 191 | } 192 | 193 | $args->{INSTALLDIRS} = $self->installdirs; 194 | 195 | my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args; 196 | 197 | my $user_preop = delete $args{dist}->{PREOP}; 198 | if (my $preop = $self->admin->preop($user_preop)) { 199 | foreach my $key ( keys %$preop ) { 200 | $args{dist}->{$key} = $preop->{$key}; 201 | } 202 | } 203 | 204 | my $mm = ExtUtils::MakeMaker::WriteMakefile(%args); 205 | $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile'); 206 | } 207 | 208 | sub fix_up_makefile { 209 | my $self = shift; 210 | my $makefile_name = shift; 211 | my $top_class = ref($self->_top) || ''; 212 | my $top_version = $self->_top->VERSION || ''; 213 | 214 | my $preamble = $self->preamble 215 | ? "# Preamble by $top_class $top_version\n" 216 | . $self->preamble 217 | : ''; 218 | my $postamble = "# Postamble by $top_class $top_version\n" 219 | . ($self->postamble || ''); 220 | 221 | local *MAKEFILE; 222 | open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; 223 | my $makefile = do { local $/; }; 224 | close MAKEFILE or die $!; 225 | 226 | $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; 227 | $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; 228 | $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; 229 | $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m; 230 | $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m; 231 | 232 | # Module::Install will never be used to build the Core Perl 233 | # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks 234 | # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist 235 | $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m; 236 | #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m; 237 | 238 | # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well. 239 | $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g; 240 | 241 | # XXX - This is currently unused; not sure if it breaks other MM-users 242 | # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg; 243 | 244 | open MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; 245 | print MAKEFILE "$preamble$makefile$postamble" or die $!; 246 | close MAKEFILE or die $!; 247 | 248 | 1; 249 | } 250 | 251 | sub preamble { 252 | my ($self, $text) = @_; 253 | $self->{preamble} = $text . $self->{preamble} if defined $text; 254 | $self->{preamble}; 255 | } 256 | 257 | sub postamble { 258 | my ($self, $text) = @_; 259 | $self->{postamble} ||= $self->admin->postamble; 260 | $self->{postamble} .= $text if defined $text; 261 | $self->{postamble} 262 | } 263 | 264 | 1; 265 | 266 | __END__ 267 | 268 | #line 394 269 | -------------------------------------------------------------------------------- /test/inc/Module/Install/TestBase.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install::TestBase; 3 | use strict; 4 | use warnings; 5 | 6 | use Module::Install::Base; 7 | 8 | use vars qw($VERSION @ISA); 9 | BEGIN { 10 | $VERSION = '0.11'; 11 | @ISA = 'Module::Install::Base'; 12 | } 13 | 14 | sub use_test_base { 15 | my $self = shift; 16 | $self->include('Test::Base'); 17 | $self->include('Test::Base::Filter'); 18 | $self->include('Spiffy'); 19 | $self->include('Test::More'); 20 | $self->include('Test::Builder'); 21 | $self->include('Test::Builder::Module'); 22 | $self->requires('Filter::Util::Call'); 23 | } 24 | 25 | 1; 26 | 27 | =encoding utf8 28 | 29 | #line 70 30 | -------------------------------------------------------------------------------- /test/inc/Module/Install/Win32.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install::Win32; 3 | 4 | use strict; 5 | use Module::Install::Base (); 6 | 7 | use vars qw{$VERSION @ISA $ISCORE}; 8 | BEGIN { 9 | $VERSION = '0.91'; 10 | @ISA = 'Module::Install::Base'; 11 | $ISCORE = 1; 12 | } 13 | 14 | # determine if the user needs nmake, and download it if needed 15 | sub check_nmake { 16 | my $self = shift; 17 | $self->load('can_run'); 18 | $self->load('get_file'); 19 | 20 | require Config; 21 | return unless ( 22 | $^O eq 'MSWin32' and 23 | $Config::Config{make} and 24 | $Config::Config{make} =~ /^nmake\b/i and 25 | ! $self->can_run('nmake') 26 | ); 27 | 28 | print "The required 'nmake' executable not found, fetching it...\n"; 29 | 30 | require File::Basename; 31 | my $rv = $self->get_file( 32 | url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', 33 | ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', 34 | local_dir => File::Basename::dirname($^X), 35 | size => 51928, 36 | run => 'Nmake15.exe /o > nul', 37 | check_for => 'Nmake.exe', 38 | remove => 1, 39 | ); 40 | 41 | die <<'END_MESSAGE' unless $rv; 42 | 43 | ------------------------------------------------------------------------------- 44 | 45 | Since you are using Microsoft Windows, you will need the 'nmake' utility 46 | before installation. It's available at: 47 | 48 | http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe 49 | or 50 | ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe 51 | 52 | Please download the file manually, save it to a directory in %PATH% (e.g. 53 | C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to 54 | that directory, and run "Nmake15.exe" from there; that will create the 55 | 'nmake.exe' file needed by this module. 56 | 57 | You may then resume the installation process described in README. 58 | 59 | ------------------------------------------------------------------------------- 60 | END_MESSAGE 61 | 62 | } 63 | 64 | 1; 65 | -------------------------------------------------------------------------------- /test/inc/Module/Install/WriteAll.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Module::Install::WriteAll; 3 | 4 | use strict; 5 | use Module::Install::Base (); 6 | 7 | use vars qw{$VERSION @ISA $ISCORE}; 8 | BEGIN { 9 | $VERSION = '0.91';; 10 | @ISA = qw{Module::Install::Base}; 11 | $ISCORE = 1; 12 | } 13 | 14 | sub WriteAll { 15 | my $self = shift; 16 | my %args = ( 17 | meta => 1, 18 | sign => 0, 19 | inline => 0, 20 | check_nmake => 1, 21 | @_, 22 | ); 23 | 24 | $self->sign(1) if $args{sign}; 25 | $self->admin->WriteAll(%args) if $self->is_admin; 26 | 27 | $self->check_nmake if $args{check_nmake}; 28 | unless ( $self->makemaker_args->{PL_FILES} ) { 29 | $self->makemaker_args( PL_FILES => {} ); 30 | } 31 | 32 | # Until ExtUtils::MakeMaker support MYMETA.yml, make sure 33 | # we clean it up properly ourself. 34 | $self->realclean_files('MYMETA.yml'); 35 | 36 | if ( $args{inline} ) { 37 | $self->Inline->write; 38 | } else { 39 | $self->Makefile->write; 40 | } 41 | 42 | # The Makefile write process adds a couple of dependencies, 43 | # so write the META.yml files after the Makefile. 44 | if ( $args{meta} ) { 45 | $self->Meta->write; 46 | } 47 | 48 | # Experimental support for MYMETA 49 | if ( $ENV{X_MYMETA} ) { 50 | if ( $ENV{X_MYMETA} eq 'JSON' ) { 51 | $self->Meta->write_mymeta_json; 52 | } else { 53 | $self->Meta->write_mymeta_yaml; 54 | } 55 | } 56 | 57 | return 1; 58 | } 59 | 60 | 1; 61 | -------------------------------------------------------------------------------- /test/inc/Test/Base/Filter.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | #. TODO: 3 | #. 4 | 5 | #=============================================================================== 6 | # This is the default class for handling Test::Base data filtering. 7 | #=============================================================================== 8 | package Test::Base::Filter; 9 | use Spiffy -Base; 10 | use Spiffy ':XXX'; 11 | 12 | field 'current_block'; 13 | 14 | our $arguments; 15 | sub current_arguments { 16 | return undef unless defined $arguments; 17 | my $args = $arguments; 18 | $args =~ s/(\\s)/ /g; 19 | $args =~ s/(\\[a-z])/'"' . $1 . '"'/gee; 20 | return $args; 21 | } 22 | 23 | sub assert_scalar { 24 | return if @_ == 1; 25 | require Carp; 26 | my $filter = (caller(1))[3]; 27 | $filter =~ s/.*:://; 28 | Carp::croak "Input to the '$filter' filter must be a scalar, not a list"; 29 | } 30 | 31 | sub _apply_deepest { 32 | my $method = shift; 33 | return () unless @_; 34 | if (ref $_[0] eq 'ARRAY') { 35 | for my $aref (@_) { 36 | @$aref = $self->_apply_deepest($method, @$aref); 37 | } 38 | return @_; 39 | } 40 | $self->$method(@_); 41 | } 42 | 43 | sub _split_array { 44 | map { 45 | [$self->split($_)]; 46 | } @_; 47 | } 48 | 49 | sub _peel_deepest { 50 | return () unless @_; 51 | if (ref $_[0] eq 'ARRAY') { 52 | if (ref $_[0]->[0] eq 'ARRAY') { 53 | for my $aref (@_) { 54 | @$aref = $self->_peel_deepest(@$aref); 55 | } 56 | return @_; 57 | } 58 | return map { $_->[0] } @_; 59 | } 60 | return @_; 61 | } 62 | 63 | #=============================================================================== 64 | # these filters work on the leaves of nested arrays 65 | #=============================================================================== 66 | sub Join { $self->_peel_deepest($self->_apply_deepest(join => @_)) } 67 | sub Reverse { $self->_apply_deepest(reverse => @_) } 68 | sub Split { $self->_apply_deepest(_split_array => @_) } 69 | sub Sort { $self->_apply_deepest(sort => @_) } 70 | 71 | 72 | sub append { 73 | my $suffix = $self->current_arguments; 74 | map { $_ . $suffix } @_; 75 | } 76 | 77 | sub array { 78 | return [@_]; 79 | } 80 | 81 | sub base64_decode { 82 | $self->assert_scalar(@_); 83 | require MIME::Base64; 84 | MIME::Base64::decode_base64(shift); 85 | } 86 | 87 | sub base64_encode { 88 | $self->assert_scalar(@_); 89 | require MIME::Base64; 90 | MIME::Base64::encode_base64(shift); 91 | } 92 | 93 | sub chomp { 94 | map { CORE::chomp; $_ } @_; 95 | } 96 | 97 | sub chop { 98 | map { CORE::chop; $_ } @_; 99 | } 100 | 101 | sub dumper { 102 | no warnings 'once'; 103 | require Data::Dumper; 104 | local $Data::Dumper::Sortkeys = 1; 105 | local $Data::Dumper::Indent = 1; 106 | local $Data::Dumper::Terse = 1; 107 | Data::Dumper::Dumper(@_); 108 | } 109 | 110 | sub escape { 111 | $self->assert_scalar(@_); 112 | my $text = shift; 113 | $text =~ s/(\\.)/eval "qq{$1}"/ge; 114 | return $text; 115 | } 116 | 117 | sub eval { 118 | $self->assert_scalar(@_); 119 | my @return = CORE::eval(shift); 120 | return $@ if $@; 121 | return @return; 122 | } 123 | 124 | sub eval_all { 125 | $self->assert_scalar(@_); 126 | my $out = ''; 127 | my $err = ''; 128 | Test::Base::tie_output(*STDOUT, $out); 129 | Test::Base::tie_output(*STDERR, $err); 130 | my $return = CORE::eval(shift); 131 | no warnings; 132 | untie *STDOUT; 133 | untie *STDERR; 134 | return $return, $@, $out, $err; 135 | } 136 | 137 | sub eval_stderr { 138 | $self->assert_scalar(@_); 139 | my $output = ''; 140 | Test::Base::tie_output(*STDERR, $output); 141 | CORE::eval(shift); 142 | no warnings; 143 | untie *STDERR; 144 | return $output; 145 | } 146 | 147 | sub eval_stdout { 148 | $self->assert_scalar(@_); 149 | my $output = ''; 150 | Test::Base::tie_output(*STDOUT, $output); 151 | CORE::eval(shift); 152 | no warnings; 153 | untie *STDOUT; 154 | return $output; 155 | } 156 | 157 | sub exec_perl_stdout { 158 | my $tmpfile = "/tmp/test-blocks-$$"; 159 | $self->_write_to($tmpfile, @_); 160 | open my $execution, "$^X $tmpfile 2>&1 |" 161 | or die "Couldn't open subprocess: $!\n"; 162 | local $/; 163 | my $output = <$execution>; 164 | close $execution; 165 | unlink($tmpfile) 166 | or die "Couldn't unlink $tmpfile: $!\n"; 167 | return $output; 168 | } 169 | 170 | sub flatten { 171 | $self->assert_scalar(@_); 172 | my $ref = shift; 173 | if (ref($ref) eq 'HASH') { 174 | return map { 175 | ($_, $ref->{$_}); 176 | } sort keys %$ref; 177 | } 178 | if (ref($ref) eq 'ARRAY') { 179 | return @$ref; 180 | } 181 | die "Can only flatten a hash or array ref"; 182 | } 183 | 184 | sub get_url { 185 | $self->assert_scalar(@_); 186 | my $url = shift; 187 | CORE::chomp($url); 188 | require LWP::Simple; 189 | LWP::Simple::get($url); 190 | } 191 | 192 | sub hash { 193 | return +{ @_ }; 194 | } 195 | 196 | sub head { 197 | my $size = $self->current_arguments || 1; 198 | return splice(@_, 0, $size); 199 | } 200 | 201 | sub join { 202 | my $string = $self->current_arguments; 203 | $string = '' unless defined $string; 204 | CORE::join $string, @_; 205 | } 206 | 207 | sub lines { 208 | $self->assert_scalar(@_); 209 | my $text = shift; 210 | return () unless length $text; 211 | my @lines = ($text =~ /^(.*\n?)/gm); 212 | return @lines; 213 | } 214 | 215 | sub norm { 216 | $self->assert_scalar(@_); 217 | my $text = shift; 218 | $text = '' unless defined $text; 219 | $text =~ s/\015\012/\n/g; 220 | $text =~ s/\r/\n/g; 221 | return $text; 222 | } 223 | 224 | sub prepend { 225 | my $prefix = $self->current_arguments; 226 | map { $prefix . $_ } @_; 227 | } 228 | 229 | sub read_file { 230 | $self->assert_scalar(@_); 231 | my $file = shift; 232 | CORE::chomp $file; 233 | open my $fh, $file 234 | or die "Can't open '$file' for input:\n$!"; 235 | CORE::join '', <$fh>; 236 | } 237 | 238 | sub regexp { 239 | $self->assert_scalar(@_); 240 | my $text = shift; 241 | my $flags = $self->current_arguments; 242 | if ($text =~ /\n.*?\n/s) { 243 | $flags = 'xism' 244 | unless defined $flags; 245 | } 246 | else { 247 | CORE::chomp($text); 248 | } 249 | $flags ||= ''; 250 | my $regexp = eval "qr{$text}$flags"; 251 | die $@ if $@; 252 | return $regexp; 253 | } 254 | 255 | sub reverse { 256 | CORE::reverse(@_); 257 | } 258 | 259 | sub slice { 260 | die "Invalid args for slice" 261 | unless $self->current_arguments =~ /^(\d+)(?:,(\d))?$/; 262 | my ($x, $y) = ($1, $2); 263 | $y = $x if not defined $y; 264 | die "Invalid args for slice" 265 | if $x > $y; 266 | return splice(@_, $x, 1 + $y - $x); 267 | } 268 | 269 | sub sort { 270 | CORE::sort(@_); 271 | } 272 | 273 | sub split { 274 | $self->assert_scalar(@_); 275 | my $separator = $self->current_arguments; 276 | if (defined $separator and $separator =~ s{^/(.*)/$}{$1}) { 277 | my $regexp = $1; 278 | $separator = qr{$regexp}; 279 | } 280 | $separator = qr/\s+/ unless $separator; 281 | CORE::split $separator, shift; 282 | } 283 | 284 | sub strict { 285 | $self->assert_scalar(@_); 286 | <<'...' . shift; 287 | use strict; 288 | use warnings; 289 | ... 290 | } 291 | 292 | sub tail { 293 | my $size = $self->current_arguments || 1; 294 | return splice(@_, @_ - $size, $size); 295 | } 296 | 297 | sub trim { 298 | map { 299 | s/\A([ \t]*\n)+//; 300 | s/(?<=\n)\s*\z//g; 301 | $_; 302 | } @_; 303 | } 304 | 305 | sub unchomp { 306 | map { $_ . "\n" } @_; 307 | } 308 | 309 | sub write_file { 310 | my $file = $self->current_arguments 311 | or die "No file specified for write_file filter"; 312 | if ($file =~ /(.*)[\\\/]/) { 313 | my $dir = $1; 314 | if (not -e $dir) { 315 | require File::Path; 316 | File::Path::mkpath($dir) 317 | or die "Can't create $dir"; 318 | } 319 | } 320 | open my $fh, ">$file" 321 | or die "Can't open '$file' for output\n:$!"; 322 | print $fh @_; 323 | close $fh; 324 | return $file; 325 | } 326 | 327 | sub yaml { 328 | $self->assert_scalar(@_); 329 | require YAML; 330 | return YAML::Load(shift); 331 | } 332 | 333 | sub _write_to { 334 | my $filename = shift; 335 | open my $script, ">$filename" 336 | or die "Couldn't open $filename: $!\n"; 337 | print $script @_; 338 | close $script 339 | or die "Couldn't close $filename: $!\n"; 340 | } 341 | 342 | __DATA__ 343 | 344 | #line 639 345 | -------------------------------------------------------------------------------- /test/inc/Test/Builder/Module.pm: -------------------------------------------------------------------------------- 1 | #line 1 2 | package Test::Builder::Module; 3 | 4 | use strict; 5 | 6 | use Test::Builder; 7 | 8 | require Exporter; 9 | our @ISA = qw(Exporter); 10 | 11 | our $VERSION = '0.94'; 12 | $VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) 13 | 14 | 15 | #line 74 16 | 17 | sub import { 18 | my($class) = shift; 19 | 20 | # Don't run all this when loading ourself. 21 | return 1 if $class eq 'Test::Builder::Module'; 22 | 23 | my $test = $class->builder; 24 | 25 | my $caller = caller; 26 | 27 | $test->exported_to($caller); 28 | 29 | $class->import_extra( \@_ ); 30 | my(@imports) = $class->_strip_imports( \@_ ); 31 | 32 | $test->plan(@_); 33 | 34 | $class->export_to_level( 1, $class, @imports ); 35 | } 36 | 37 | sub _strip_imports { 38 | my $class = shift; 39 | my $list = shift; 40 | 41 | my @imports = (); 42 | my @other = (); 43 | my $idx = 0; 44 | while( $idx <= $#{$list} ) { 45 | my $item = $list->[$idx]; 46 | 47 | if( defined $item and $item eq 'import' ) { 48 | push @imports, @{ $list->[ $idx + 1 ] }; 49 | $idx++; 50 | } 51 | else { 52 | push @other, $item; 53 | } 54 | 55 | $idx++; 56 | } 57 | 58 | @$list = @other; 59 | 60 | return @imports; 61 | } 62 | 63 | #line 137 64 | 65 | sub import_extra { } 66 | 67 | #line 167 68 | 69 | sub builder { 70 | return Test::Builder->new; 71 | } 72 | 73 | 1; 74 | -------------------------------------------------------------------------------- /test/lib/Test/Nginx.pm: -------------------------------------------------------------------------------- 1 | package Test::Nginx; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | our $VERSION = '0.08'; 7 | 8 | __END__ 9 | 10 | =encoding utf-8 11 | 12 | =head1 NAME 13 | 14 | Test::Nginx - Testing modules for Nginx C module development 15 | 16 | =head1 DESCRIPTION 17 | 18 | This distribution provides two testing modules for Nginx C module development: 19 | 20 | =over 21 | 22 | =item * 23 | 24 | L 25 | 26 | =item * 27 | 28 | L 29 | 30 | =back 31 | 32 | All of them are based on L. 33 | 34 | =head1 SOURCE REPOSITORY 35 | 36 | This module has a Git repository on Github, which has access for all. 37 | 38 | http://github.com/agentzh/test-nginx 39 | 40 | If you want a commit bit, feel free to drop me a line. 41 | 42 | =head1 AUTHOR 43 | 44 | agentzh (章亦春) C<< >> 45 | 46 | =head1 COPYRIGHT & LICENSE 47 | 48 | Copyright (c) 2009, Taobao Inc., Alibaba Group (L). 49 | 50 | Copyright (c) 2009, agentzh C<< >>. 51 | 52 | This module is licensed under the terms of the BSD license. 53 | 54 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 55 | 56 | =over 57 | 58 | =item * 59 | 60 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 61 | 62 | =item * 63 | 64 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 65 | 66 | =item * 67 | 68 | Neither the name of the Taobao Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 69 | 70 | =back 71 | 72 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 73 | 74 | =head1 SEE ALSO 75 | 76 | L, L, L. 77 | 78 | -------------------------------------------------------------------------------- /test/lib/Test/Nginx/LWP.pm: -------------------------------------------------------------------------------- 1 | package Test::Nginx::LWP; 2 | 3 | use lib 'lib'; 4 | use lib 'inc'; 5 | use Test::Base -Base; 6 | 7 | our $VERSION = '0.08'; 8 | 9 | our $NoLongString; 10 | 11 | use LWP::UserAgent; 12 | use Time::HiRes qw(sleep); 13 | use Test::LongString; 14 | use Test::Nginx::Util qw( 15 | setup_server_root 16 | write_config_file 17 | get_canon_version 18 | get_nginx_version 19 | trim 20 | show_all_chars 21 | parse_headers 22 | run_tests 23 | $ServerPortForClient 24 | $PidFile 25 | $ServRoot 26 | $ConfFile 27 | $ServerPort 28 | $RunTestHelper 29 | $NoNginxManager 30 | $RepeatEach 31 | worker_connections 32 | master_process_enabled 33 | config_preamble 34 | repeat_each 35 | ); 36 | 37 | our $UserAgent = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 }); 38 | $UserAgent->agent(__PACKAGE__); 39 | #$UserAgent->default_headers(HTTP::Headers->new); 40 | 41 | #use Smart::Comments::JSON '##'; 42 | 43 | our @EXPORT = qw( plan run_tests run_test 44 | repeat_each config_preamble worker_connections 45 | master_process_enabled 46 | no_long_string); 47 | 48 | sub no_long_string () { 49 | $NoLongString = 1; 50 | } 51 | 52 | sub run_test_helper ($); 53 | 54 | $RunTestHelper = \&run_test_helper; 55 | 56 | sub parse_request ($$) { 57 | my ($name, $rrequest) = @_; 58 | open my $in, '<', $rrequest; 59 | my $first = <$in>; 60 | if (!$first) { 61 | Test::More::BAIL_OUT("$name - Request line should be non-empty"); 62 | die; 63 | } 64 | $first =~ s/^\s+|\s+$//g; 65 | my ($meth, $rel_url) = split /\s+/, $first, 2; 66 | my $url = "http://localhost:$ServerPortForClient" . $rel_url; 67 | 68 | my $content = do { local $/; <$in> }; 69 | if ($content) { 70 | $content =~ s/^\s+|\s+$//s; 71 | } 72 | 73 | close $in; 74 | 75 | return { 76 | method => $meth, 77 | url => $url, 78 | content => $content, 79 | }; 80 | } 81 | 82 | sub parse_request_https ($$) { 83 | my ($name, $rrequest) = @_; 84 | open my $in, '<', $rrequest; 85 | my $first = <$in>; 86 | if (!$first) { 87 | Test::More::BAIL_OUT("$name - Request line should be non-empty"); 88 | die; 89 | } 90 | $first =~ s/^\s+|\s+$//g; 91 | my ($meth, $rel_url) = split /\s+/, $first, 2; 92 | my $url = "https://localhost:$ServerPortForClient" . $rel_url; 93 | 94 | my $content = do { local $/; <$in> }; 95 | if ($content) { 96 | $content =~ s/^\s+|\s+$//s; 97 | } 98 | 99 | close $in; 100 | 101 | return { 102 | method => $meth, 103 | url => $url, 104 | content => $content, 105 | }; 106 | } 107 | 108 | sub chunk_it ($$$) { 109 | my ($chunks, $start_delay, $middle_delay) = @_; 110 | my $i = 0; 111 | return sub { 112 | if ($i == 0) { 113 | if ($start_delay) { 114 | sleep($start_delay); 115 | } 116 | } elsif ($middle_delay) { 117 | sleep($middle_delay); 118 | } 119 | return $chunks->[$i++]; 120 | } 121 | } 122 | 123 | sub run_test_helper ($) { 124 | my ($block) = @_; 125 | 126 | my $request = $block->request; 127 | my $request_https = $block->request_https; 128 | 129 | my $name = $block->name; 130 | #if (defined $TODO) { 131 | #$name .= "# $TODO"; 132 | #} 133 | 134 | my $req_spec; 135 | 136 | if ($request) { 137 | $req_spec = parse_request($name, \$request); 138 | } 139 | else { 140 | $req_spec = parse_request_https($name, \$request_https); 141 | } 142 | 143 | ## $req_spec 144 | my $method = $req_spec->{method}; 145 | my $req = HTTP::Request->new($method); 146 | my $content = $req_spec->{content}; 147 | 148 | if (defined ($block->request_headers)) { 149 | my $headers = parse_headers($block->request_headers); 150 | while (my ($key, $val) = each %$headers) { 151 | $req->header($key => $val); 152 | } 153 | } 154 | 155 | #$req->header('Accept', '*/*'); 156 | $req->url($req_spec->{url}); 157 | if ($content) { 158 | if ($method eq 'GET' or $method eq 'HEAD') { 159 | croak "HTTP 1.0/1.1 $method request should not have content: $content"; 160 | } 161 | $req->content($content); 162 | } elsif ($method eq 'POST' or $method eq 'PUT') { 163 | my $chunks = $block->chunked_body; 164 | if (defined $chunks) { 165 | if (!ref $chunks or ref $chunks ne 'ARRAY') { 166 | 167 | Test::More::BAIL_OUT("$name - --- chunked_body should takes a Perl array ref as its value"); 168 | } 169 | 170 | my $start_delay = $block->start_chunk_delay || 0; 171 | my $middle_delay = $block->middle_chunk_delay || 0; 172 | $req->content(chunk_it($chunks, $start_delay, $middle_delay)); 173 | if (!defined $req->header('Content-Type')) { 174 | $req->header('Content-Type' => 'text/plain'); 175 | } 176 | } else { 177 | if (!defined $req->header('Content-Type')) { 178 | $req->header('Content-Type' => 'text/plain'); 179 | } 180 | 181 | $req->header('Content-Length' => 0); 182 | } 183 | } 184 | 185 | if ($block->more_headers) { 186 | my @headers = split /\n+/, $block->more_headers; 187 | for my $header (@headers) { 188 | next if $header =~ /^\s*\#/; 189 | my ($key, $val) = split /:\s*/, $header, 2; 190 | #warn "[$key, $val]\n"; 191 | $req->header($key => $val); 192 | } 193 | } 194 | 195 | #warn "req: ", $req->as_string, "\n"; 196 | #warn "DONE!!!!!!!!!!!!!!!!!!!!"; 197 | 198 | my $res = $UserAgent->request($req); 199 | 200 | #warn "res returned!!!"; 201 | 202 | if (defined $block->error_code) { 203 | is($res->code, $block->error_code, "$name - status code ok"); 204 | } else { 205 | is($res->code, 200, "$name - status code ok"); 206 | } 207 | 208 | if (defined $block->response_headers) { 209 | my $headers = parse_headers($block->response_headers); 210 | while (my ($key, $val) = each %$headers) { 211 | my $expected_val = $res->header($key); 212 | if (!defined $expected_val) { 213 | $expected_val = ''; 214 | } 215 | is $expected_val, $val, 216 | "$name - header $key ok"; 217 | } 218 | } elsif (defined $block->response_headers_like) { 219 | my $headers = parse_headers($block->response_headers_like); 220 | while (my ($key, $val) = each %$headers) { 221 | my $expected_val = $res->header($key); 222 | if (!defined $expected_val) { 223 | $expected_val = ''; 224 | } 225 | like $expected_val, qr/^$val$/, 226 | "$name - header $key like ok"; 227 | } 228 | } 229 | 230 | if (defined $block->response_body) { 231 | my $content = $res->content; 232 | if (defined $content) { 233 | $content =~ s/^TE: deflate,gzip;q=0\.3\r\n//gms; 234 | } 235 | 236 | $content =~ s/^Connection: TE, close\r\n//gms; 237 | my $expected = $block->response_body; 238 | $expected =~ s/\$ServerPort\b/$ServerPort/g; 239 | $expected =~ s/\$ServerPortForClient\b/$ServerPortForClient/g; 240 | #warn show_all_chars($content); 241 | 242 | if ($NoLongString) { 243 | is($content, $expected, "$name - response_body - response is expected"); 244 | } else { 245 | is_string($content, $expected, "$name - response_body - response is expected"); 246 | } 247 | #is($content, $expected, "$name - response_body - response is expected"); 248 | 249 | } elsif (defined $block->response_body_like) { 250 | my $content = $res->content; 251 | if (defined $content) { 252 | $content =~ s/^TE: deflate,gzip;q=0\.3\r\n//gms; 253 | } 254 | $content =~ s/^Connection: TE, close\r\n//gms; 255 | my $expected_pat = $block->response_body_like; 256 | $expected_pat =~ s/\$ServerPort\b/$ServerPort/g; 257 | $expected_pat =~ s/\$ServerPortForClient\b/$ServerPortForClient/g; 258 | my $summary = trim($content); 259 | like($content, qr/$expected_pat/s, "$name - response_body_like - response is expected ($summary)"); 260 | } 261 | } 262 | 263 | 1; 264 | __END__ 265 | 266 | =encoding utf-8 267 | 268 | =head1 NAME 269 | 270 | Test::Nginx::LWP - LWP-backed test scaffold for the Nginx C modules 271 | 272 | =head1 SYNOPSIS 273 | 274 | use Test::Nginx::LWP; 275 | 276 | plan tests => $Test::Nginx::LWP::RepeatEach * 2 * blocks(); 277 | 278 | run_tests(); 279 | 280 | __DATA__ 281 | 282 | === TEST 1: sanity 283 | --- config 284 | location /echo { 285 | echo_before_body hello; 286 | echo world; 287 | } 288 | --- request 289 | GET /echo 290 | --- response_body 291 | hello 292 | world 293 | --- error_code: 200 294 | 295 | 296 | === TEST 2: set Server 297 | --- config 298 | location /foo { 299 | echo hi; 300 | more_set_headers 'Server: Foo'; 301 | } 302 | --- request 303 | GET /foo 304 | --- response_headers 305 | Server: Foo 306 | --- response_body 307 | hi 308 | 309 | 310 | === TEST 3: clear Server 311 | --- config 312 | location /foo { 313 | echo hi; 314 | more_clear_headers 'Server: '; 315 | } 316 | --- request 317 | GET /foo 318 | --- response_headers_like 319 | Server: nginx.* 320 | --- response_body 321 | hi 322 | 323 | 324 | === TEST 4: set request header at client side and rewrite it 325 | --- config 326 | location /foo { 327 | more_set_input_headers 'X-Foo: howdy'; 328 | echo $http_x_foo; 329 | } 330 | --- request 331 | GET /foo 332 | --- request_headers 333 | X-Foo: blah 334 | --- response_headers 335 | X-Foo: 336 | --- response_body 337 | howdy 338 | 339 | 340 | === TEST 3: rewrite content length 341 | --- config 342 | location /bar { 343 | more_set_input_headers 'Content-Length: 2048'; 344 | echo_read_request_body; 345 | echo_request_body; 346 | } 347 | --- request eval 348 | "POST /bar\n" . 349 | "a" x 4096 350 | --- response_body eval 351 | "a" x 2048 352 | 353 | 354 | === TEST 4: timer without explicit reset 355 | --- config 356 | location /timer { 357 | echo_sleep 0.03; 358 | echo "elapsed $echo_timer_elapsed sec."; 359 | } 360 | --- request 361 | GET /timer 362 | --- response_body_like 363 | ^elapsed 0\.0(2[6-9]|3[0-6]) sec\.$ 364 | 365 | 366 | === TEST 5: small buf (using 2-byte buf) 367 | --- config 368 | chunkin on; 369 | location /main { 370 | client_body_buffer_size 2; 371 | echo "body:"; 372 | echo $echo_request_body; 373 | echo_request_body; 374 | } 375 | --- request 376 | POST /main 377 | --- start_chunk_delay: 0.01 378 | --- middle_chunk_delay: 0.01 379 | --- chunked_body eval 380 | ["hello", "world"] 381 | --- error_code: 200 382 | --- response_body eval 383 | "body: 384 | 385 | helloworld" 386 | 387 | =head1 DESCRIPTION 388 | 389 | This module provides a test scaffold based on L for automated testing in Nginx C module development. 390 | 391 | This class inherits from L, thus bringing all its 392 | declarative power to the Nginx C module testing practices. 393 | 394 | You need to terminate or kill any Nginx processes before running the test suite if you have changed the Nginx server binary. Normally it's as simple as 395 | 396 | killall nginx 397 | PATH=/path/to/your/nginx-with-memc-module:$PATH prove -r t 398 | 399 | This module will create a temporary server root under t/servroot/ of the current working directory and starts and uses the nginx executable in the PATH environment. 400 | 401 | You will often want to look into F 402 | when things go wrong ;) 403 | 404 | =head1 Sections supported 405 | 406 | The following sections are supported: 407 | 408 | =over 409 | 410 | =item config 411 | 412 | =item http_config 413 | 414 | =item request 415 | 416 | =item request_headers 417 | 418 | =item more_headers 419 | 420 | =item response_body 421 | 422 | =item response_body_like 423 | 424 | =item response_headers 425 | 426 | =item response_headers_like 427 | 428 | =item error_code 429 | 430 | =item chunked_body 431 | 432 | =item middle_chunk_delay 433 | 434 | =item start_chunk_delay 435 | 436 | =back 437 | 438 | =head1 Samples 439 | 440 | You'll find live samples in the following Nginx 3rd-party modules: 441 | 442 | =over 443 | 444 | =item ngx_echo 445 | 446 | L 447 | 448 | =item ngx_headers_more 449 | 450 | L 451 | 452 | =item ngx_chunkin 453 | 454 | L 455 | 456 | =item ngx_memc 457 | 458 | L 459 | 460 | =back 461 | 462 | =head1 SOURCE REPOSITORY 463 | 464 | This module has a Git repository on Github, which has access for all. 465 | 466 | http://github.com/agentzh/test-nginx 467 | 468 | If you want a commit bit, feel free to drop me a line. 469 | 470 | =head1 AUTHOR 471 | 472 | agentzh (章亦春) C<< >> 473 | 474 | =head1 COPYRIGHT & LICENSE 475 | 476 | Copyright (c) 2009, Taobao Inc., Alibaba Group (L). 477 | 478 | Copyright (c) 2009, agentzh C<< >>. 479 | 480 | This module is licensed under the terms of the BSD license. 481 | 482 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 483 | 484 | =over 485 | 486 | =item * 487 | 488 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 489 | 490 | =item * 491 | 492 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 493 | 494 | =item * 495 | 496 | Neither the name of the Taobao Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 497 | 498 | =back 499 | 500 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 501 | 502 | =head1 SEE ALSO 503 | 504 | L, L. 505 | 506 | -------------------------------------------------------------------------------- /test/ragel/Makefile: -------------------------------------------------------------------------------- 1 | default: server client 2 | 3 | server: http11_parser.rl ragel_http_server.c 4 | ragel -G2 http11_parser.rl 5 | gcc -g -Wall ragel_http_server.c http11_parser.c -o server 6 | 7 | 8 | client: http11_response.rl ragel_http_client.c 9 | ragel -G2 http11_response.rl 10 | gcc -g -Wall ragel_http_client.c http11_response.c -o client 11 | 12 | clean: 13 | rm client server 14 | -------------------------------------------------------------------------------- /test/ragel/http11_parser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2005 Zed A. Shaw 3 | * You can redistribute it and/or modify it under the same terms as Ruby. 4 | */ 5 | 6 | #ifndef http11_parser_h 7 | #define http11_parser_h 8 | 9 | #include 10 | 11 | #if defined(_WIN32) 12 | #include 13 | #endif 14 | 15 | typedef void (*element_cb)(void *data, const char *at, size_t length); 16 | typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen); 17 | 18 | typedef struct http_parser { 19 | int cs; 20 | size_t body_start; 21 | int content_len; 22 | size_t nread; 23 | size_t mark; 24 | size_t field_start; 25 | size_t field_len; 26 | size_t query_start; 27 | 28 | void *data; 29 | 30 | field_cb http_field; 31 | element_cb request_method; 32 | element_cb request_uri; 33 | element_cb fragment; 34 | element_cb request_path; 35 | element_cb query_string; 36 | element_cb http_version; 37 | element_cb header_done; 38 | 39 | } http_parser; 40 | 41 | int http_parser_init(http_parser *parser); 42 | int http_parser_finish(http_parser *parser); 43 | size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off); 44 | int http_parser_has_error(http_parser *parser); 45 | int http_parser_is_finished(http_parser *parser); 46 | 47 | #define http_parser_nread(parser) (parser)->nread 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /test/ragel/http11_parser.rl: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2005 Zed A. Shaw 3 | * You can redistribute it and/or modify it under the same terms as Ruby. 4 | */ 5 | #include "http11_parser.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | #define LEN(AT, FPC) (FPC - buffer - parser->AT) 14 | #define MARK(M,FPC) (parser->M = (FPC) - buffer) 15 | #define PTR_TO(F) (buffer + parser->F) 16 | 17 | /** Machine **/ 18 | 19 | %%{ 20 | 21 | machine http_parser; 22 | 23 | action mark {MARK(mark, fpc); } 24 | 25 | 26 | action start_field { MARK(field_start, fpc); } 27 | action write_field { 28 | parser->field_len = LEN(field_start, fpc); 29 | } 30 | 31 | action start_value { MARK(mark, fpc); } 32 | action write_value { 33 | if(parser->http_field != NULL) { 34 | parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc)); 35 | } 36 | } 37 | action request_method { 38 | if(parser->request_method != NULL) 39 | parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc)); 40 | } 41 | action request_uri { 42 | if(parser->request_uri != NULL) 43 | parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc)); 44 | } 45 | action fragment { 46 | if(parser->fragment != NULL) 47 | parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc)); 48 | } 49 | 50 | action start_query {MARK(query_start, fpc); } 51 | action query_string { 52 | if(parser->query_string != NULL) 53 | parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc)); 54 | } 55 | 56 | action http_version { 57 | if(parser->http_version != NULL) 58 | parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc)); 59 | } 60 | 61 | action request_path { 62 | if(parser->request_path != NULL) 63 | parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc)); 64 | } 65 | 66 | action done { 67 | parser->body_start = fpc - buffer + 1; 68 | if(parser->header_done != NULL) 69 | parser->header_done(parser->data, fpc + 1, pe - fpc - 1); 70 | fbreak; 71 | } 72 | 73 | include http_parser_common "http11_parser_common.rl"; 74 | 75 | }%% 76 | 77 | /** Data **/ 78 | %% write data; 79 | 80 | int http_parser_init(http_parser *parser) { 81 | int cs = 0; 82 | %% write init; 83 | parser->cs = cs; 84 | parser->body_start = 0; 85 | parser->content_len = 0; 86 | parser->mark = 0; 87 | parser->nread = 0; 88 | parser->field_len = 0; 89 | parser->field_start = 0; 90 | 91 | return(1); 92 | } 93 | 94 | 95 | /** exec **/ 96 | size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) { 97 | const char *p, *pe; 98 | int cs = parser->cs; 99 | 100 | assert(off <= len && "offset past end of buffer"); 101 | 102 | p = buffer+off; 103 | pe = buffer+len; 104 | 105 | /* assert(*pe == '\0' && "pointer does not end on NUL"); */ 106 | assert(pe - p == len - off && "pointers aren't same distance"); 107 | 108 | %% write exec; 109 | 110 | if (!http_parser_has_error(parser)) 111 | parser->cs = cs; 112 | parser->nread += p - (buffer + off); 113 | 114 | assert(p <= pe && "buffer overflow after parsing execute"); 115 | assert(parser->nread <= len && "nread longer than length"); 116 | assert(parser->body_start <= len && "body starts after buffer end"); 117 | assert(parser->mark < len && "mark is after buffer end"); 118 | assert(parser->field_len <= len && "field has length longer than whole buffer"); 119 | assert(parser->field_start < len && "field starts after buffer end"); 120 | 121 | return(parser->nread); 122 | } 123 | 124 | int http_parser_finish(http_parser *parser) 125 | { 126 | if (http_parser_has_error(parser) ) { 127 | return -1; 128 | } else if (http_parser_is_finished(parser) ) { 129 | return 1; 130 | } else { 131 | return 0; 132 | } 133 | } 134 | 135 | int http_parser_has_error(http_parser *parser) { 136 | return parser->cs == http_parser_error; 137 | } 138 | 139 | int http_parser_is_finished(http_parser *parser) { 140 | return parser->cs >= http_parser_first_final; 141 | } 142 | -------------------------------------------------------------------------------- /test/ragel/http11_parser_common.rl: -------------------------------------------------------------------------------- 1 | %%{ 2 | 3 | machine http_parser_common; 4 | 5 | #### HTTP PROTOCOL GRAMMAR 6 | # line endings 7 | CRLF = "\r\n"; 8 | 9 | # character types 10 | CTL = (cntrl | 127); 11 | safe = ("$" | "-" | "_" | "."); 12 | extra = ("!" | "*" | "'" | "(" | ")" | ","); 13 | reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+"); 14 | unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">"); 15 | national = any -- (alpha | digit | reserved | extra | safe | unsafe); 16 | unreserved = (alpha | digit | safe | extra | national); 17 | escape = ("%" xdigit xdigit); 18 | uchar = (unreserved | escape); 19 | pchar = (uchar | ":" | "@" | "&" | "=" | "+"); 20 | tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t"); 21 | 22 | # elements 23 | token = (ascii -- (CTL | tspecials)); 24 | 25 | # URI schemes and absolute paths 26 | scheme = ( alpha | digit | "+" | "-" | "." )* ; 27 | absolute_uri = (scheme ":" (uchar | reserved )*); 28 | 29 | path = ( pchar+ ( "/" pchar* )* ) ; 30 | query = ( uchar | reserved )* %query_string ; 31 | param = ( pchar | "/" )* ; 32 | params = ( param ( ";" param )* ) ; 33 | rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?; 34 | absolute_path = ( "/"+ rel_path ); 35 | 36 | Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri; 37 | Fragment = ( uchar | reserved )* >mark %fragment; 38 | Method = ( upper | digit | safe ){1,20} >mark %request_method; 39 | 40 | http_number = ( digit+ "." digit+ ) ; 41 | HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ; 42 | Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ; 43 | 44 | #field_name = ( token -- ":" )+ >start_field $snake_upcase_field %write_field; 45 | field_name = ( token -- ":" )+ >start_field %write_field; 46 | 47 | field_value = any* >start_value %write_value; 48 | 49 | message_header = field_name ":" " "* field_value :> CRLF; 50 | 51 | Request = Request_Line ( message_header )* ( CRLF @done ); 52 | 53 | main := Request; 54 | 55 | }%% 56 | -------------------------------------------------------------------------------- /test/ragel/http11_response.c: -------------------------------------------------------------------------------- 1 | 2 | #line 1 "http11_response.rl" 3 | 4 | #include "http11_response.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | #define LEN(AT, FPC) (FPC - buffer - parser->AT) 13 | #define MARK(M,FPC) (parser->M = (FPC) - buffer) 14 | #define PTR_TO(F) (buffer + parser->F) 15 | 16 | /** Machine **/ 17 | 18 | 19 | #line 59 "http11_response.rl" 20 | 21 | 22 | /** Data **/ 23 | 24 | #line 25 "http11_response.c" 25 | static const int http_parser_start = 1; 26 | static const int http_parser_first_final = 20; 27 | static const int http_parser_error = 0; 28 | 29 | static const int http_parser_en_main = 1; 30 | 31 | 32 | #line 63 "http11_response.rl" 33 | 34 | int http_parser_init(http_parser *parser) { 35 | int cs = 0; 36 | 37 | #line 38 "http11_response.c" 38 | { 39 | cs = http_parser_start; 40 | } 41 | 42 | #line 67 "http11_response.rl" 43 | parser->cs = cs; 44 | parser->body_start = 0; 45 | parser->content_len = 0; 46 | parser->mark = 0; 47 | parser->nread = 0; 48 | parser->field_len = 0; 49 | parser->field_start = 0; 50 | 51 | return(1); 52 | } 53 | 54 | 55 | /** exec **/ 56 | size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) { 57 | const char *p, *pe; 58 | int cs = parser->cs; 59 | 60 | assert(off <= len && "offset past end of buffer"); 61 | 62 | p = buffer+off; 63 | pe = buffer+len; 64 | 65 | assert(pe - p == len - off && "pointers aren't same distance"); 66 | 67 | 68 | #line 69 "http11_response.c" 69 | { 70 | if ( p == pe ) 71 | goto _test_eof; 72 | switch ( cs ) 73 | { 74 | case 1: 75 | if ( (*p) == 72 ) 76 | goto tr0; 77 | goto st0; 78 | st0: 79 | cs = 0; 80 | goto _out; 81 | tr0: 82 | #line 20 "http11_response.rl" 83 | {MARK(mark, p); } 84 | goto st2; 85 | st2: 86 | if ( ++p == pe ) 87 | goto _test_eof2; 88 | case 2: 89 | #line 90 "http11_response.c" 90 | if ( (*p) == 84 ) 91 | goto st3; 92 | goto st0; 93 | st3: 94 | if ( ++p == pe ) 95 | goto _test_eof3; 96 | case 3: 97 | if ( (*p) == 84 ) 98 | goto st4; 99 | goto st0; 100 | st4: 101 | if ( ++p == pe ) 102 | goto _test_eof4; 103 | case 4: 104 | if ( (*p) == 80 ) 105 | goto st5; 106 | goto st0; 107 | st5: 108 | if ( ++p == pe ) 109 | goto _test_eof5; 110 | case 5: 111 | if ( (*p) == 47 ) 112 | goto st6; 113 | goto st0; 114 | st6: 115 | if ( ++p == pe ) 116 | goto _test_eof6; 117 | case 6: 118 | if ( 48 <= (*p) && (*p) <= 57 ) 119 | goto st7; 120 | goto st0; 121 | st7: 122 | if ( ++p == pe ) 123 | goto _test_eof7; 124 | case 7: 125 | if ( (*p) == 46 ) 126 | goto st8; 127 | if ( 48 <= (*p) && (*p) <= 57 ) 128 | goto st7; 129 | goto st0; 130 | st8: 131 | if ( ++p == pe ) 132 | goto _test_eof8; 133 | case 8: 134 | if ( 48 <= (*p) && (*p) <= 57 ) 135 | goto st9; 136 | goto st0; 137 | st9: 138 | if ( ++p == pe ) 139 | goto _test_eof9; 140 | case 9: 141 | if ( (*p) == 32 ) 142 | goto tr9; 143 | if ( 48 <= (*p) && (*p) <= 57 ) 144 | goto st9; 145 | goto st0; 146 | tr9: 147 | #line 35 "http11_response.rl" 148 | { 149 | if(parser->http_version != NULL) 150 | parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p)); 151 | } 152 | goto st10; 153 | st10: 154 | if ( ++p == pe ) 155 | goto _test_eof10; 156 | case 10: 157 | #line 158 "http11_response.c" 158 | if ( 48 <= (*p) && (*p) <= 57 ) 159 | goto tr10; 160 | goto st0; 161 | tr10: 162 | #line 20 "http11_response.rl" 163 | {MARK(mark, p); } 164 | goto st11; 165 | st11: 166 | if ( ++p == pe ) 167 | goto _test_eof11; 168 | case 11: 169 | #line 170 "http11_response.c" 170 | if ( (*p) == 32 ) 171 | goto tr11; 172 | if ( 48 <= (*p) && (*p) <= 57 ) 173 | goto st11; 174 | goto st0; 175 | tr11: 176 | #line 40 "http11_response.rl" 177 | { 178 | if(parser->status_code != NULL) 179 | parser->status_code(parser->data, PTR_TO(mark), LEN(mark,p)); 180 | } 181 | goto st12; 182 | st12: 183 | if ( ++p == pe ) 184 | goto _test_eof12; 185 | case 12: 186 | #line 187 "http11_response.c" 187 | if ( (*p) < 11 ) { 188 | if ( 0 <= (*p) && (*p) <= 9 ) 189 | goto tr13; 190 | } else if ( (*p) > 12 ) { 191 | if ( 14 <= (*p) ) 192 | goto tr13; 193 | } else 194 | goto tr13; 195 | goto st0; 196 | tr13: 197 | #line 20 "http11_response.rl" 198 | {MARK(mark, p); } 199 | goto st13; 200 | st13: 201 | if ( ++p == pe ) 202 | goto _test_eof13; 203 | case 13: 204 | #line 205 "http11_response.c" 205 | if ( (*p) == 13 ) 206 | goto tr15; 207 | if ( (*p) > 9 ) { 208 | if ( 11 <= (*p) ) 209 | goto st13; 210 | } else if ( (*p) >= 0 ) 211 | goto st13; 212 | goto st0; 213 | tr15: 214 | #line 45 "http11_response.rl" 215 | { 216 | if(parser->reason_phrase != NULL) 217 | parser->reason_phrase(parser->data, PTR_TO(mark), LEN(mark,p)); 218 | } 219 | goto st14; 220 | tr23: 221 | #line 27 "http11_response.rl" 222 | { MARK(mark, p); } 223 | #line 29 "http11_response.rl" 224 | { 225 | if(parser->http_field != NULL) { 226 | parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p)); 227 | } 228 | } 229 | goto st14; 230 | tr26: 231 | #line 29 "http11_response.rl" 232 | { 233 | if(parser->http_field != NULL) { 234 | parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p)); 235 | } 236 | } 237 | goto st14; 238 | st14: 239 | if ( ++p == pe ) 240 | goto _test_eof14; 241 | case 14: 242 | #line 243 "http11_response.c" 243 | if ( (*p) == 10 ) 244 | goto st15; 245 | goto st0; 246 | st15: 247 | if ( ++p == pe ) 248 | goto _test_eof15; 249 | case 15: 250 | switch( (*p) ) { 251 | case 13: goto st16; 252 | case 33: goto tr18; 253 | case 124: goto tr18; 254 | case 126: goto tr18; 255 | } 256 | if ( (*p) < 45 ) { 257 | if ( (*p) > 39 ) { 258 | if ( 42 <= (*p) && (*p) <= 43 ) 259 | goto tr18; 260 | } else if ( (*p) >= 35 ) 261 | goto tr18; 262 | } else if ( (*p) > 46 ) { 263 | if ( (*p) < 65 ) { 264 | if ( 48 <= (*p) && (*p) <= 57 ) 265 | goto tr18; 266 | } else if ( (*p) > 90 ) { 267 | if ( 94 <= (*p) && (*p) <= 122 ) 268 | goto tr18; 269 | } else 270 | goto tr18; 271 | } else 272 | goto tr18; 273 | goto st0; 274 | st16: 275 | if ( ++p == pe ) 276 | goto _test_eof16; 277 | case 16: 278 | if ( (*p) == 10 ) 279 | goto tr19; 280 | goto st0; 281 | tr19: 282 | #line 50 "http11_response.rl" 283 | { 284 | parser->body_start = p - buffer + 1; 285 | if(parser->header_done != NULL) 286 | parser->header_done(parser->data, p + 1, pe - p - 1); 287 | {p++; cs = 20; goto _out;} 288 | } 289 | goto st20; 290 | st20: 291 | if ( ++p == pe ) 292 | goto _test_eof20; 293 | case 20: 294 | #line 295 "http11_response.c" 295 | goto st0; 296 | tr18: 297 | #line 22 "http11_response.rl" 298 | { MARK(field_start, p); } 299 | goto st17; 300 | st17: 301 | if ( ++p == pe ) 302 | goto _test_eof17; 303 | case 17: 304 | #line 305 "http11_response.c" 305 | switch( (*p) ) { 306 | case 33: goto st17; 307 | case 58: goto tr21; 308 | case 124: goto st17; 309 | case 126: goto st17; 310 | } 311 | if ( (*p) < 45 ) { 312 | if ( (*p) > 39 ) { 313 | if ( 42 <= (*p) && (*p) <= 43 ) 314 | goto st17; 315 | } else if ( (*p) >= 35 ) 316 | goto st17; 317 | } else if ( (*p) > 46 ) { 318 | if ( (*p) < 65 ) { 319 | if ( 48 <= (*p) && (*p) <= 57 ) 320 | goto st17; 321 | } else if ( (*p) > 90 ) { 322 | if ( 94 <= (*p) && (*p) <= 122 ) 323 | goto st17; 324 | } else 325 | goto st17; 326 | } else 327 | goto st17; 328 | goto st0; 329 | tr21: 330 | #line 23 "http11_response.rl" 331 | { 332 | parser->field_len = LEN(field_start, p); 333 | } 334 | goto st18; 335 | tr24: 336 | #line 27 "http11_response.rl" 337 | { MARK(mark, p); } 338 | goto st18; 339 | st18: 340 | if ( ++p == pe ) 341 | goto _test_eof18; 342 | case 18: 343 | #line 344 "http11_response.c" 344 | switch( (*p) ) { 345 | case 13: goto tr23; 346 | case 32: goto tr24; 347 | } 348 | goto tr22; 349 | tr22: 350 | #line 27 "http11_response.rl" 351 | { MARK(mark, p); } 352 | goto st19; 353 | st19: 354 | if ( ++p == pe ) 355 | goto _test_eof19; 356 | case 19: 357 | #line 358 "http11_response.c" 358 | if ( (*p) == 13 ) 359 | goto tr26; 360 | goto st19; 361 | } 362 | _test_eof2: cs = 2; goto _test_eof; 363 | _test_eof3: cs = 3; goto _test_eof; 364 | _test_eof4: cs = 4; goto _test_eof; 365 | _test_eof5: cs = 5; goto _test_eof; 366 | _test_eof6: cs = 6; goto _test_eof; 367 | _test_eof7: cs = 7; goto _test_eof; 368 | _test_eof8: cs = 8; goto _test_eof; 369 | _test_eof9: cs = 9; goto _test_eof; 370 | _test_eof10: cs = 10; goto _test_eof; 371 | _test_eof11: cs = 11; goto _test_eof; 372 | _test_eof12: cs = 12; goto _test_eof; 373 | _test_eof13: cs = 13; goto _test_eof; 374 | _test_eof14: cs = 14; goto _test_eof; 375 | _test_eof15: cs = 15; goto _test_eof; 376 | _test_eof16: cs = 16; goto _test_eof; 377 | _test_eof20: cs = 20; goto _test_eof; 378 | _test_eof17: cs = 17; goto _test_eof; 379 | _test_eof18: cs = 18; goto _test_eof; 380 | _test_eof19: cs = 19; goto _test_eof; 381 | 382 | _test_eof: {} 383 | _out: {} 384 | } 385 | 386 | #line 92 "http11_response.rl" 387 | 388 | if (!http_parser_has_error(parser)) 389 | parser->cs = cs; 390 | parser->nread += p - (buffer + off); 391 | 392 | assert(p <= pe && "buffer overflow after parsing execute"); 393 | assert(parser->nread <= len && "nread longer than length"); 394 | assert(parser->body_start <= len && "body starts after buffer end"); 395 | assert(parser->mark < len && "mark is after buffer end"); 396 | assert(parser->field_len <= len && "field has length longer than whole buffer"); 397 | assert(parser->field_start < len && "field starts after buffer end"); 398 | 399 | return(parser->nread); 400 | } 401 | 402 | int http_parser_finish(http_parser *parser) 403 | { 404 | if (http_parser_has_error(parser) ) { 405 | return -1; 406 | } else if (http_parser_is_finished(parser) ) { 407 | return 1; 408 | } else { 409 | return 0; 410 | } 411 | } 412 | 413 | int http_parser_has_error(http_parser *parser) { 414 | return parser->cs == http_parser_error; 415 | } 416 | 417 | int http_parser_is_finished(http_parser *parser) { 418 | return parser->cs >= http_parser_first_final; 419 | } 420 | -------------------------------------------------------------------------------- /test/ragel/http11_response.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef http11_parser_h 3 | #define http11_parser_h 4 | 5 | #include 6 | 7 | #if defined(_WIN32) 8 | #include 9 | #endif 10 | 11 | typedef void (*element_cb)(void *data, const char *at, size_t length); 12 | typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen); 13 | 14 | typedef struct http_parser { 15 | int cs; 16 | size_t body_start; 17 | int content_len; 18 | size_t nread; 19 | size_t mark; 20 | size_t field_start; 21 | size_t field_len; 22 | size_t query_start; 23 | 24 | void *data; 25 | 26 | field_cb http_field; 27 | 28 | element_cb http_version; 29 | element_cb status_code; 30 | element_cb reason_phrase; 31 | element_cb header_done; 32 | 33 | } http_parser; 34 | 35 | int http_parser_init(http_parser *parser); 36 | int http_parser_finish(http_parser *parser); 37 | size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off); 38 | int http_parser_has_error(http_parser *parser); 39 | int http_parser_is_finished(http_parser *parser); 40 | 41 | #define http_parser_nread(parser) (parser)->nread 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /test/ragel/http11_response.rl: -------------------------------------------------------------------------------- 1 | 2 | #include "http11_response.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #define LEN(AT, FPC) (FPC - buffer - parser->AT) 11 | #define MARK(M,FPC) (parser->M = (FPC) - buffer) 12 | #define PTR_TO(F) (buffer + parser->F) 13 | 14 | /** Machine **/ 15 | 16 | %%{ 17 | 18 | machine http_parser; 19 | 20 | action mark {MARK(mark, fpc); } 21 | 22 | action start_field { MARK(field_start, fpc); } 23 | action write_field { 24 | parser->field_len = LEN(field_start, fpc); 25 | } 26 | 27 | action start_value { MARK(mark, fpc); } 28 | 29 | action write_value { 30 | if(parser->http_field != NULL) { 31 | parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc)); 32 | } 33 | } 34 | 35 | action http_version { 36 | if(parser->http_version != NULL) 37 | parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc)); 38 | } 39 | 40 | action status_code { 41 | if(parser->status_code != NULL) 42 | parser->status_code(parser->data, PTR_TO(mark), LEN(mark,fpc)); 43 | } 44 | 45 | action reason_phrase { 46 | if(parser->reason_phrase != NULL) 47 | parser->reason_phrase(parser->data, PTR_TO(mark), LEN(mark,fpc)); 48 | } 49 | 50 | action done { 51 | parser->body_start = fpc - buffer + 1; 52 | if(parser->header_done != NULL) 53 | parser->header_done(parser->data, fpc + 1, pe - fpc - 1); 54 | fbreak; 55 | } 56 | 57 | include http_response_common "http11_response_common.rl"; 58 | 59 | }%% 60 | 61 | /** Data **/ 62 | %% write data; 63 | 64 | int http_parser_init(http_parser *parser) { 65 | int cs = 0; 66 | %% write init; 67 | parser->cs = cs; 68 | parser->body_start = 0; 69 | parser->content_len = 0; 70 | parser->mark = 0; 71 | parser->nread = 0; 72 | parser->field_len = 0; 73 | parser->field_start = 0; 74 | 75 | return(1); 76 | } 77 | 78 | 79 | /** exec **/ 80 | size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) { 81 | const char *p, *pe; 82 | int cs = parser->cs; 83 | 84 | assert(off <= len && "offset past end of buffer"); 85 | 86 | p = buffer+off; 87 | pe = buffer+len; 88 | 89 | assert(pe - p == len - off && "pointers aren't same distance"); 90 | 91 | %% write exec; 92 | 93 | if (!http_parser_has_error(parser)) 94 | parser->cs = cs; 95 | parser->nread += p - (buffer + off); 96 | 97 | assert(p <= pe && "buffer overflow after parsing execute"); 98 | assert(parser->nread <= len && "nread longer than length"); 99 | assert(parser->body_start <= len && "body starts after buffer end"); 100 | assert(parser->mark < len && "mark is after buffer end"); 101 | assert(parser->field_len <= len && "field has length longer than whole buffer"); 102 | assert(parser->field_start < len && "field starts after buffer end"); 103 | 104 | return(parser->nread); 105 | } 106 | 107 | int http_parser_finish(http_parser *parser) 108 | { 109 | if (http_parser_has_error(parser) ) { 110 | return -1; 111 | } else if (http_parser_is_finished(parser) ) { 112 | return 1; 113 | } else { 114 | return 0; 115 | } 116 | } 117 | 118 | int http_parser_has_error(http_parser *parser) { 119 | return parser->cs == http_parser_error; 120 | } 121 | 122 | int http_parser_is_finished(http_parser *parser) { 123 | return parser->cs >= http_parser_first_final; 124 | } 125 | -------------------------------------------------------------------------------- /test/ragel/http11_response_common.rl: -------------------------------------------------------------------------------- 1 | %%{ 2 | 3 | machine http_response_common; 4 | 5 | #### HTTP PROTOCOL GRAMMAR 6 | # line endings 7 | CRLF = "\r\n"; 8 | 9 | # character types 10 | CTL = (cntrl | 127); 11 | tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t"); 12 | 13 | # elements 14 | token = (ascii -- (CTL | tspecials)); 15 | 16 | Reason_Phrase = ( ascii -- ("\r" | "\n") )+ >mark %reason_phrase; 17 | 18 | Status_Code = ( digit+ ) >mark %status_code ; 19 | 20 | http_number = ( digit+ "." digit+ ) ; 21 | HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ; 22 | 23 | Response_Line = ( HTTP_Version " " Status_Code " " Reason_Phrase CRLF ) ; 24 | 25 | field_name = ( token -- ":" )+ >start_field %write_field; 26 | 27 | field_value = any* >start_value %write_value; 28 | 29 | message_header = field_name ":" " "* field_value :> CRLF; 30 | 31 | Response = Response_Line ( message_header )* ( CRLF @done ); 32 | 33 | main := Response; 34 | 35 | }%% 36 | -------------------------------------------------------------------------------- /test/ragel/ragel_http_client.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include "http11_response.h" 6 | #include 7 | 8 | #define BUFF_LEN 4096 9 | 10 | void http_field(void *data, const char *field, 11 | size_t flen, const char *value, size_t vlen) 12 | { 13 | char buff[BUFF_LEN] = {0}; 14 | 15 | strncpy(buff, field, flen); 16 | strcat(buff, ": "); 17 | strncat(buff, value, vlen); 18 | 19 | printf("HEADER: \"%s\"\n", buff); 20 | } 21 | 22 | void http_version(void *data, const char *at, size_t length) 23 | { 24 | printf("VERSION: \"%.*s\"\n", length, at); 25 | } 26 | 27 | void status_code(void *data, const char *at, size_t length) 28 | { 29 | printf("STATUS_CODE: \"%.*s\"\n", length, at); 30 | } 31 | 32 | void reason_phrase(void *data, const char *at, size_t length) 33 | { 34 | printf("REASON_PHRASE: \"%.*s\"\n", length, at); 35 | } 36 | 37 | void header_done(void *data, const char *at, size_t length) 38 | { 39 | printf("HEADER_DONE.\n"); 40 | } 41 | 42 | void parser_init(http_parser *hp) 43 | { 44 | hp->http_field = http_field; 45 | hp->http_version = http_version; 46 | hp->status_code = status_code; 47 | hp->reason_phrase = reason_phrase; 48 | hp->header_done = header_done; 49 | http_parser_init(hp); 50 | } 51 | 52 | int main1 () 53 | { 54 | char *data = "HTTP/1.0 200 OK\r\n" 55 | "Server: nginx\r\n" 56 | "Date: Fri, 26 Mar 2010 03:39:03 GMT\r\n" 57 | "Content-Type: text/html; charset=GBK\r\n" 58 | "Vary: Accept-Encoding\r\n" 59 | "Expires: Fri, 26 Mar 2010 03:40:23 GMT\r\n" 60 | "Cache-Control: max-age=80\r\n" 61 | "Vary: User-Agent\r\n" 62 | "Vary: Accept\r\n" 63 | "X-Cache: MISS from cache.163.com\r\n" 64 | "Connection: close\r\n" 65 | "\r\n" 66 | "I am the body" 67 | ; 68 | size_t dlen; 69 | http_parser parser, *hp; 70 | 71 | hp = &parser; 72 | dlen = strlen(data); 73 | 74 | parser_init(hp); 75 | 76 | http_parser_execute(hp, data, dlen, 0); 77 | 78 | return 0; 79 | } 80 | 81 | int main () 82 | { 83 | char *data = "HTTP/1.0 200 OK\r\n" 84 | "Server: nginx\r\n" 85 | "Date: Fri, 26 Mar 2010 03:39:03 GMT\r\n" 86 | "Content-Type: text/html; charset=GBK\r\n" 87 | "Vary: Accept-Encoding\r\n" 88 | "Expires: Fri, 26 Mar 2010 03:40:23 GMT\r\n" 89 | "Cache-Control: max-age=80\r\n" 90 | "Vary: User-Agent\r\n" 91 | "Vary: Accept\r\n" 92 | "X-Cache: MISS from cache.163.com\r\n" 93 | "Connection: close\r\n" 94 | "\r\n" 95 | "I am the body" 96 | ; 97 | 98 | 99 | size_t dlen, dlen1; 100 | http_parser parser, *hp; 101 | int i; 102 | 103 | hp = &parser; 104 | dlen = strlen(data); 105 | 106 | for (i = 1;i < dlen;i++) { 107 | printf("\n\nblock point: %d\n", i); 108 | parser_init(hp); 109 | dlen1 = http_parser_execute(hp, data, i, 0); 110 | dlen1 = http_parser_execute(hp, data, dlen, dlen1); 111 | printf("BODY: \"%s\"\n", data + hp->body_start); 112 | } 113 | 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /test/ragel/ragel_http_server.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include "http11_parser.h" 6 | #include 7 | 8 | #define BUFF_LEN 4096 9 | 10 | void http_field(void *data, const char *field, 11 | size_t flen, const char *value, size_t vlen) 12 | { 13 | char buff[BUFF_LEN] = {0}; 14 | 15 | strncpy(buff, field, flen); 16 | strcat(buff, ": "); 17 | strncat(buff, value, vlen); 18 | 19 | printf("HEADER: \"%s\"\n", buff); 20 | } 21 | 22 | void request_method(void *data, const char *at, size_t length) 23 | { 24 | char buff[BUFF_LEN] = {0}; 25 | 26 | strncpy(buff, at, length); 27 | 28 | printf("METHOD: \"%s\"\n", buff); 29 | } 30 | 31 | void request_uri(void *data, const char *at, size_t length) 32 | { 33 | char buff[BUFF_LEN] = {0}; 34 | 35 | strncpy(buff, at, length); 36 | 37 | printf("URI: \"%s\"\n", buff); 38 | } 39 | 40 | void fragment(void *data, const char *at, size_t length) 41 | { 42 | char buff[BUFF_LEN] = {0}; 43 | 44 | strncpy(buff, at, length); 45 | 46 | printf("FRAGMENT: \"%s\"\n", buff); 47 | } 48 | 49 | void request_path(void *data, const char *at, size_t length) 50 | { 51 | char buff[BUFF_LEN] = {0}; 52 | 53 | strncpy(buff, at, length); 54 | 55 | printf("PATH: \"%s\"\n", buff); 56 | } 57 | 58 | void query_string(void *data, const char *at, size_t length) 59 | { 60 | char buff[BUFF_LEN] = {0}; 61 | 62 | strncpy(buff, at, length); 63 | 64 | printf("QUERY: \"%s\"\n", buff); 65 | } 66 | 67 | void http_version(void *data, const char *at, size_t length) 68 | { 69 | char buff[BUFF_LEN] = {0}; 70 | 71 | strncpy(buff, at, length); 72 | 73 | printf("VERSION: \"%s\"\n", buff); 74 | } 75 | 76 | void header_done(void *data, const char *at, size_t length) 77 | { 78 | printf("done.\n"); 79 | } 80 | 81 | void parser_init(http_parser *hp) 82 | { 83 | hp->http_field = http_field; 84 | hp->request_method = request_method; 85 | hp->request_uri = request_uri; 86 | hp->fragment = fragment; 87 | hp->request_path = request_path; 88 | hp->query_string = query_string; 89 | hp->http_version = http_version; 90 | hp->header_done = header_done; 91 | http_parser_init(hp); 92 | } 93 | 94 | int main1 () 95 | { 96 | char *data = "GET / HTTP/1.0\r\n" 97 | "User-Agent: Wget/1.11.4\r\n" 98 | "Accept: */*\r\n" 99 | "Host: www.163.com\r\n" 100 | "Connection: Keep-Alive\r\n" 101 | "\r\n"; 102 | size_t dlen; 103 | http_parser parser, *hp; 104 | 105 | hp = &parser; 106 | dlen = strlen(data); 107 | 108 | parser_init(hp); 109 | 110 | http_parser_execute(hp, data, dlen, 0); 111 | 112 | return 0; 113 | } 114 | 115 | int main () 116 | { 117 | char *data = "GET / HTTP/1.0\r\n" 118 | "User-Agent: Wget/1.11.4\r\n" 119 | "Accept: */*\r\n" 120 | "Host: www.163.com\r\n" 121 | "Connection: Keep-Alive\r\n" 122 | "\r\n"; 123 | 124 | 125 | size_t dlen, dlen1; 126 | http_parser parser, *hp; 127 | int i; 128 | 129 | hp = &parser; 130 | dlen = strlen(data); 131 | 132 | for (i = 1;i < dlen;i++) { 133 | parser_init(hp); 134 | dlen1 = http_parser_execute(hp, data, i, 0); 135 | dlen1 = http_parser_execute(hp, data, dlen, dlen1); 136 | } 137 | 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /test/t/acl.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: acl.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: test ACL 33 | --- config 34 | upstream test{ 35 | server www.taobao.com; 36 | } 37 | 38 | server { 39 | deny 127.0.0.1; 40 | 41 | listen 1984; 42 | 43 | proxy_pass test; 44 | } 45 | --- request 46 | GET / 47 | --- request_headers 48 | Host: www.taobao.com 49 | --- error_code: 500 50 | --- response_body_like: ^.*$ 51 | 52 | === TEST 2: test ACL without anything 53 | --- config 54 | upstream test{ 55 | server www.taobao.com; 56 | } 57 | 58 | server { 59 | listen 1984; 60 | 61 | proxy_pass test; 62 | } 63 | --- request 64 | GET / 65 | --- request_headers 66 | Host: www.taobao.com 67 | --- response_body_like: ^.*$ 68 | 69 | === TEST 3: test ACL witht other ip 70 | --- config 71 | upstream test{ 72 | server www.taobao.com; 73 | } 74 | 75 | server { 76 | deny 10.231.143.122; 77 | listen 1984; 78 | 79 | server_name _; 80 | 81 | tcp_nodelay on; 82 | so_keepalive on; 83 | 84 | proxy_pass test; 85 | } 86 | --- request 87 | GET / 88 | --- request_headers 89 | Host: www.taobao.com 90 | --- response_body_like: ^.*$ 91 | -------------------------------------------------------------------------------- /test/t/http_check.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: http_check.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the http_check test 33 | --- config 34 | upstream test{ 35 | server www.taobao.com; 36 | 37 | check interval=3000 rise=1 fall=5 timeout=1000 type=http; 38 | check_http_send "GET /test HTTP/1.0\r\n\r\n"; 39 | check_http_expect_alive http_2xx http_3xx http_4xx; 40 | } 41 | 42 | server { 43 | listen 1984; 44 | 45 | proxy_pass test; 46 | } 47 | --- request 48 | GET / 49 | --- request_headers 50 | Host: www.taobao.com 51 | --- response_body_like: ^<(.*)> 52 | 53 | === TEST 2: the http_check test without check 54 | --- config 55 | upstream test{ 56 | server www.taobao.com; 57 | } 58 | 59 | server { 60 | listen 1984; 61 | 62 | proxy_pass test; 63 | } 64 | --- request 65 | GET / 66 | --- request_headers 67 | Host: www.taobao.com 68 | --- response_body_like: ^<(.*)> 69 | 70 | === TEST 3: the http_check which use the raw name 71 | --- config 72 | upstream test{ 73 | server www.taobao.com; 74 | 75 | check interval=3000 rise=1 fall=5 timeout=1000 type=http; 76 | check_http_send "GET /test HTTP/1.0\r\n\r\n"; 77 | check_http_expect_alive http_2xx http_3xx http_4xx; 78 | } 79 | 80 | server { 81 | listen 1984; 82 | 83 | proxy_pass www.taobao.com; 84 | } 85 | --- request 86 | GET / 87 | --- request_headers 88 | Host: www.taobao.com 89 | --- response_body_like: ^<(.*)> 90 | -------------------------------------------------------------------------------- /test/t/imap_check.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: imap_check.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the imap_check 33 | --- config 34 | upstream test{ 35 | server imap.163.com:143; 36 | 37 | check interval=3000 rise=1 fall=5 timeout=1000 type=imap; 38 | } 39 | 40 | server { 41 | listen 1984; 42 | 43 | proxy_pass test; 44 | } 45 | --- request 46 | GET / 47 | --- response_body_like: ^(.*)$ 48 | -------------------------------------------------------------------------------- /test/t/mysql_check.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: mysql_check.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the mysql_check 33 | --- config 34 | upstream test{ 35 | server 127.0.0.1:3306; 36 | 37 | #ip_hash; 38 | check interval=3000 rise=1 fall=5 timeout=1000 type=mysql; 39 | } 40 | 41 | server { 42 | listen 1984; 43 | 44 | proxy_pass test; 45 | } 46 | --- request 47 | GET / 48 | --- response_body_like: ^(.*)$ 49 | -------------------------------------------------------------------------------- /test/t/pop3_check.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: pop3_check.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the pop3_check 33 | --- config 34 | upstream test{ 35 | server pop3.163.com:110; 36 | 37 | #ip_hash; 38 | check interval=3000 rise=1 fall=5 timeout=1000 type=pop3; 39 | } 40 | 41 | server { 42 | listen 1984; 43 | 44 | proxy_pass test; 45 | } 46 | --- request 47 | GET / 48 | --- response_body_like: ^(.*)$ 49 | -------------------------------------------------------------------------------- /test/t/smtp_check.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: smtp_check.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the smtp_check 33 | --- config 34 | upstream test{ 35 | #server 127.0.0.1:25; 36 | server smtp.163.com:25; 37 | 38 | #ip_hash; 39 | check interval=3000 rise=1 fall=5 timeout=1000 type=smtp; 40 | check_smtp_send "HELO localhost\r\n"; 41 | } 42 | 43 | server { 44 | listen 1984; 45 | 46 | proxy_pass test; 47 | } 48 | --- request 49 | GET / 50 | --- response_body_like: ^2(.*)$ 51 | -------------------------------------------------------------------------------- /test/t/ssl.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: sample.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the ssl command 33 | --- config 34 | upstream test{ 35 | server www.taobao.com; 36 | ip_hash; 37 | } 38 | 39 | server { 40 | listen 1984 ssl; 41 | 42 | proxy_pass test; 43 | } 44 | --- request_https 45 | GET / 46 | --- request_headers 47 | Host: www.taobao.com 48 | --- response_body_like: ^<(.*)> 49 | 50 | === TEST 2: the ssl command with websocket 51 | --- config 52 | upstream test{ 53 | server www.taobao.com; 54 | } 55 | 56 | server { 57 | listen 1984 ssl; 58 | 59 | websocket_pass test; 60 | } 61 | --- request_https 62 | GET / 63 | --- request_headers 64 | Host: www.taobao.com 65 | --- response_body_like: ^<(.*)> 66 | 67 | === TEST 3: the ssl command with websocket 68 | 69 | --- config 70 | upstream test{ 71 | server www.taobao.com; 72 | } 73 | 74 | server { 75 | listen 1984 ssl; 76 | 77 | ssl_session_cache builtin:1000 shared:SSL:5m; 78 | 79 | websocket_pass test; 80 | } 81 | --- request_https 82 | GET / 83 | --- request_headers 84 | Host: www.taobao.com 85 | --- response_body_like: ^<(.*)> 86 | 87 | === TEST 4: the ssl command with ssl on 88 | 89 | --- config 90 | upstream test{ 91 | server www.taobao.com; 92 | } 93 | 94 | server { 95 | listen 1984; 96 | 97 | ssl on; 98 | ssl_session_cache builtin:1000 shared:SSL:5m; 99 | 100 | websocket_pass test; 101 | } 102 | --- request_https 103 | GET / 104 | --- request_headers 105 | Host: www.taobao.com 106 | --- response_body_like: ^<(.*)> 107 | 108 | === TEST 5: the ssl command with ssl on and listen ssl 109 | 110 | --- config 111 | upstream test{ 112 | server www.taobao.com; 113 | } 114 | 115 | server { 116 | listen 1984 ssl; 117 | 118 | ssl on; 119 | ssl_session_cache builtin:1000 shared:SSL:5m; 120 | 121 | websocket_pass test; 122 | } 123 | --- request_https 124 | GET / 125 | --- request_headers 126 | Host: www.taobao.com 127 | --- response_body_like: ^<(.*)> 128 | -------------------------------------------------------------------------------- /test/t/ssl_hello_check.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: ssl_hello_check.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the ssl_hello_check test 33 | --- config 34 | upstream test{ 35 | server www.alipay.com:443; 36 | 37 | #ip_hash; 38 | check interval=3000 rise=1 fall=5 timeout=1000 type=ssl_hello; 39 | } 40 | 41 | server { 42 | listen 1984; 43 | 44 | proxy_pass test; 45 | } 46 | --- request_https 47 | GET / 48 | --- request_headers 49 | Host: www.alipay.com 50 | --- response_body_like: ^.*$ 51 | -------------------------------------------------------------------------------- /test/t/tcp_check.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: tcp_check.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the tcp_check test 33 | --- config 34 | upstream test{ 35 | server www.taobao.com; 36 | #ip_hash; 37 | check interval=3000 rise=1 fall=5 timeout=1000; 38 | } 39 | 40 | server { 41 | listen 1984; 42 | 43 | protocol tcp_generic; 44 | proxy_pass test; 45 | } 46 | --- request 47 | GET / 48 | --- request_headers 49 | Host: www.taobao.com 50 | --- response_body_like: ^<(.*)> 51 | 52 | === TEST 2: the round robin test without check 53 | --- config 54 | upstream test{ 55 | server www.taobao.com; 56 | } 57 | 58 | server { 59 | listen 1984; 60 | #server_names a.b.c d.e.f; 61 | 62 | proxy_pass test; 63 | } 64 | --- request 65 | GET / 66 | --- request_headers 67 | Host: www.taobao.com 68 | --- response_body_like: ^<(.*)> 69 | 70 | === TEST 3: the ip_hash test without check 71 | --- config 72 | upstream test{ 73 | server www.taobao.com; 74 | ip_hash; 75 | } 76 | 77 | server { 78 | listen 1984; 79 | 80 | proxy_pass test; 81 | } 82 | --- request 83 | GET / 84 | --- request_headers 85 | Host: www.taobao.com 86 | --- response_body_like: ^<(.*)> 87 | -------------------------------------------------------------------------------- /test/t/upstream_busyness.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: sample.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the upstream_busyness command 33 | --- config 34 | upstream test{ 35 | server www.taobao.com; 36 | busyness; 37 | } 38 | 39 | server { 40 | listen 1984; 41 | 42 | proxy_pass test; 43 | } 44 | --- request 45 | GET / 46 | --- request_headers 47 | Host: www.taobao.com 48 | --- response_body_like: ^<(.*)> 49 | 50 | === TEST 2: the upstream_busyness command with check 51 | --- config 52 | upstream test{ 53 | server www.taobao.com; 54 | 55 | check interval=3000 rise=1 fall=5 timeout=1000; 56 | busyness; 57 | } 58 | 59 | server { 60 | listen 1984; 61 | 62 | proxy_pass test; 63 | } 64 | --- request 65 | GET / 66 | --- request_headers 67 | Host: www.taobao.com 68 | --- response_body_like: ^<(.*)> 69 | -------------------------------------------------------------------------------- /test/t/upstream_ip_hash.t: -------------------------------------------------------------------------------- 1 | # 2 | #=============================================================================== 3 | # 4 | # FILE: sample.t 5 | # 6 | # DESCRIPTION: test 7 | # 8 | # FILES: --- 9 | # BUGS: --- 10 | # NOTES: --- 11 | # AUTHOR: Weibin Yao (http://yaoweibin.cn/), yaoweibin@gmail.com 12 | # COMPANY: 13 | # VERSION: 1.0 14 | # CREATED: 03/02/2010 03:18:28 PM 15 | # REVISION: --- 16 | #=============================================================================== 17 | 18 | 19 | # vi:filetype=perl 20 | 21 | use lib 'lib'; 22 | use Test::Nginx::LWP; 23 | 24 | plan tests => repeat_each() * 2 * blocks(); 25 | 26 | #no_diff; 27 | 28 | run_tests(); 29 | 30 | __DATA__ 31 | 32 | === TEST 1: the upstream_ip_hash command 33 | --- config 34 | upstream test{ 35 | server www.taobao.com; 36 | ip_hash; 37 | } 38 | 39 | server { 40 | listen 1984; 41 | 42 | proxy_pass test; 43 | } 44 | --- request 45 | GET / 46 | --- request_headers 47 | Host: www.taobao.com 48 | --- request_headers 49 | Host: www.taobao.com 50 | --- response_body_like: ^<(.*)> 51 | 52 | === TEST 2: the upstream_ip_hash command with check 53 | --- config 54 | upstream test{ 55 | server www.taobao.com; 56 | 57 | check interval=2000; 58 | ip_hash; 59 | } 60 | 61 | server { 62 | listen 1984; 63 | 64 | proxy_pass test; 65 | } 66 | --- request 67 | GET / 68 | --- request_headers 69 | Host: www.taobao.com 70 | --- request_headers 71 | Host: www.taobao.com 72 | --- response_body_like: ^<(.*)> 73 | -------------------------------------------------------------------------------- /test/websocket/server.rb: -------------------------------------------------------------------------------- 1 | require 'em-websocket' 2 | 3 | EventMachine.run { 4 | 5 | EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 2000) do |ws| 6 | ws.onopen { 7 | puts "WebSocket connection open" 8 | 9 | # publish message to the client 10 | ws.send "Hello Client" 11 | } 12 | 13 | ws.onclose { puts "Connection closed" } 14 | ws.onmessage { |msg| 15 | puts "Received message: #{msg}" 16 | ws.send "Pong: #{msg}" 17 | } 18 | end 19 | 20 | } 21 | -------------------------------------------------------------------------------- /util/update-readme.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | perl util/wiki2pod.pl doc/README.wiki > /tmp/a.pod && pod2text /tmp/a.pod > doc/README.txt 4 | 5 | perl util/wiki2pod.pl doc/README.wiki > /tmp/a.pod && pod2html /tmp/a.pod > doc/README.html 6 | 7 | cp doc/README.txt README 8 | -------------------------------------------------------------------------------- /util/wiki2pod.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use bytes; 6 | 7 | my @nl_counts; 8 | my $last_nl_count_level; 9 | 10 | my @bl_counts; 11 | my $last_bl_count_level; 12 | 13 | sub fmt_pos ($) { 14 | (my $s = $_[0]) =~ s{\#(.*)}{/"$1"}; 15 | $s; 16 | } 17 | 18 | sub fmt_mark ($$) { 19 | my ($tag, $s) = @_; 20 | my $max_level = 0; 21 | while ($s =~ /([<>])\1*/g) { 22 | my $level = length $&; 23 | if ($level > $max_level) { 24 | $max_level = $level; 25 | } 26 | } 27 | 28 | my $times = $max_level + 1; 29 | if ($times > 1) { 30 | $s = " $s "; 31 | } 32 | return $tag . ('<' x $times) . $s . ('>' x $times); 33 | } 34 | 35 | print "=encoding utf-8\n\n"; 36 | 37 | while (<>) { 38 | if ($. == 1) { 39 | # strip the leading U+FEFF byte in MS-DOS text files 40 | my $first = ord(substr($_, 0, 1)); 41 | #printf STDERR "0x%x", $first; 42 | #my $second = ord(substr($_, 2, 1)); 43 | #printf STDERR "0x%x", $second; 44 | if ($first == 0xEF) { 45 | substr($_, 0, 1, ''); 46 | #warn "Hit!"; 47 | } 48 | } 49 | s{\[(http[^ \]]+) ([^\]]*)\]}{$2 (L<$1>)}gi; 50 | s{ \[\[ ( [^\]\|]+ ) \| ([^\]]*) \]\] }{"L<$2|" . fmt_pos($1) . ">"}gixe; 51 | s{(.*?)}{fmt_mark('C', $1)}gie; 52 | s{'''(.*?)'''}{fmt_mark('B', $1)}ge; 53 | s{''(.*?)''}{fmt_mark('I', $1)}ge; 54 | if (s{^\s*<[^>]+>\s*$}{}) { 55 | next; 56 | } 57 | 58 | if (/^\s*$/) { 59 | print "\n"; 60 | next; 61 | } 62 | 63 | =begin cmt 64 | 65 | if ($. == 1) { 66 | warn $_; 67 | for my $i (0..length($_) - 1) { 68 | my $chr = substr($_, $i, 1); 69 | warn "chr ord($i): ".ord($chr)." \"$chr\"\n"; 70 | } 71 | } 72 | 73 | =end cmt 74 | =cut 75 | 76 | if (/(=+) (.*) \1$/) { 77 | #warn "HERE! $_" if $. == 1; 78 | my ($level, $title) = (length $1, $2); 79 | collapse_lists(); 80 | 81 | print "\n=head$level $title\n\n"; 82 | } elsif (/^(\#+) (.*)/) { 83 | my ($level, $txt) = (length($1) - 1, $2); 84 | if (defined $last_nl_count_level && $level != $last_nl_count_level) { 85 | print "\n=back\n\n"; 86 | } 87 | $last_nl_count_level = $level; 88 | $nl_counts[$level] ||= 0; 89 | if ($nl_counts[$level] == 0) { 90 | print "\n=over\n\n"; 91 | } 92 | $nl_counts[$level]++; 93 | print "\n=item $nl_counts[$level].\n\n"; 94 | print "$txt\n"; 95 | } elsif (/^(\*+) (.*)/) { 96 | my ($level, $txt) = (length($1) - 1, $2); 97 | if (defined $last_bl_count_level && $level != $last_bl_count_level) { 98 | print "\n=back\n\n"; 99 | } 100 | $last_bl_count_level = $level; 101 | $bl_counts[$level] ||= 0; 102 | if ($bl_counts[$level] == 0) { 103 | print "\n=over\n\n"; 104 | } 105 | $bl_counts[$level]++; 106 | print "\n=item *\n\n"; 107 | print "$txt\n"; 108 | } else { 109 | collapse_lists(); 110 | print; 111 | } 112 | } 113 | 114 | collapse_lists(); 115 | 116 | sub collapse_lists { 117 | while (defined $last_nl_count_level && $last_nl_count_level >= 0) { 118 | print "\n=back\n\n"; 119 | $last_nl_count_level--; 120 | } 121 | undef $last_nl_count_level; 122 | undef @nl_counts; 123 | 124 | while (defined $last_bl_count_level && $last_bl_count_level >= 0) { 125 | print "\n=back\n\n"; 126 | $last_bl_count_level--; 127 | } 128 | undef $last_bl_count_level; 129 | undef @bl_counts; 130 | } 131 | 132 | --------------------------------------------------------------------------------