PageRenderTime 29ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/http_copy/ngx_http_copy_module.c

https://bitbucket.org/chobits/ngx_module_practice
C | 1813 lines | 1265 code | 426 blank | 122 comment | 306 complexity | 6f0a641234d91e335f3680c3a3eadac2 MD5 | raw file
  1. /*
  2. * Copyright (C) Xiaochen Wang (xiaochen.wxc@alibaba-inc.com)
  3. * Copyright (C) Taobao, Inc.
  4. * Copyright (C) Alibaba, Inc.
  5. */
  6. #include <ngx_config.h>
  7. #include <ngx_core.h>
  8. #include <ngx_http.h>
  9. typedef struct {
  10. ngx_addr_t *addrs;
  11. ngx_uint_t naddrs;
  12. ngx_uint_t multiple;
  13. ngx_queue_t cache_connections; /* cached connection list*/
  14. ngx_int_t cached;
  15. ngx_int_t max_cached;
  16. ngx_msec_t cached_timeout;
  17. ngx_int_t max_connection;
  18. ngx_int_t connection;
  19. ngx_flag_t on;
  20. ngx_flag_t keepalive;
  21. ngx_flag_t force_keepalive;
  22. ngx_flag_t serial;
  23. } ngx_http_copy_loc_conf_t;
  24. typedef struct {
  25. ngx_uint_t state;
  26. off_t size;
  27. } ngx_http_copy_chunk_t;
  28. typedef struct ngx_http_copy_request_s ngx_http_copy_request_t;
  29. struct ngx_http_copy_request_s {
  30. ngx_http_request_t *r; /* incoming request */
  31. ngx_pool_t *pool;
  32. ngx_peer_connection_t peer;
  33. ngx_chain_t *request_bufs;
  34. ngx_buf_t *buffer;
  35. ngx_output_chain_ctx_t output;
  36. ngx_chain_writer_ctx_t writer;
  37. ngx_http_request_t response; /* used by http response parser */
  38. ngx_http_status_t status; /* used by http response parser */
  39. off_t length; /* response body length or chunk body size */
  40. ngx_http_copy_chunk_t *chunk;
  41. ngx_http_copy_loc_conf_t *cplcf; /* used when response is sent back */
  42. ngx_int_t (*process_header)(ngx_http_copy_request_t *cpr);
  43. ngx_queue_t queue; /* in ngx_http_copy_ctx_t::copy_request */
  44. /* serial copy */
  45. ngx_chain_t *serial_request_bufs;
  46. ngx_uint_t serial_sent;
  47. unsigned discard_body:2;
  48. unsigned request_sent:1;
  49. unsigned keepalive_connect:1;
  50. unsigned connect:1;
  51. unsigned serial:1;
  52. };
  53. typedef struct {
  54. ngx_queue_t copy_request;
  55. } ngx_http_copy_ctx_t;
  56. typedef struct {
  57. /* long time */
  58. ngx_atomic_t request_count;
  59. ngx_atomic_t response_count;
  60. ngx_atomic_t response_ok_count;
  61. ngx_atomic_t response_err_count;
  62. ngx_atomic_t connect_count;
  63. ngx_atomic_t connect_keepalive_count;
  64. ngx_atomic_t read_bytes;
  65. ngx_atomic_t read_chunk_bytes;
  66. ngx_atomic_t write_bytes;
  67. /* real time */
  68. ngx_atomic_t active_connect;
  69. ngx_atomic_t active_connect_keepalive;
  70. } ngx_http_copy_status_shm_t;
  71. static ngx_int_t ngx_http_copy_test_connect(ngx_connection_t *c);
  72. static ngx_int_t ngx_http_copy_init(ngx_conf_t *conf);
  73. static void *ngx_http_copy_create_loc_conf(ngx_conf_t *conf);
  74. static ngx_int_t ngx_http_copy_handler(ngx_http_request_t *r);
  75. static char *ngx_http_copy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  76. static ngx_int_t ngx_http_copy_send_request(ngx_http_copy_request_t *cpr);
  77. static char *ngx_http_copy_status(ngx_conf_t *cf, ngx_command_t *cmd,
  78. void *conf);
  79. static void ngx_http_copy_dummy_handler(ngx_event_t *ev);
  80. static ngx_int_t ngx_http_copy_try_keepalive_connection(ngx_http_copy_request_t *cpr);
  81. static char * ngx_http_copy_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
  82. void *conf);
  83. static char * ngx_http_copy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
  84. static char * ngx_http_copy_init_shm(ngx_conf_t *cf);
  85. static ngx_int_t ngx_chain_buf_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
  86. ngx_chain_t *in);
  87. static ngx_int_t ngx_http_copy_parse_status_line(ngx_http_copy_request_t *cpr);
  88. #define NGX_HTTP_COPY_DISCARD_BODY 0x01 /* 0b01 */
  89. #define NGX_HTTP_COPY_DISCARD_CHUNK_BODY 0x02 /* 0b10 */
  90. /* define directive in nginx.conf */
  91. static ngx_command_t ngx_http_copy_commands[] = {
  92. { ngx_string("http_copy"),
  93. NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  94. ngx_http_copy,
  95. NGX_HTTP_LOC_CONF_OFFSET,
  96. 0,
  97. NULL },
  98. { ngx_string("http_copy_keepalive"),
  99. NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  100. ngx_http_copy_keepalive,
  101. NGX_HTTP_LOC_CONF_OFFSET,
  102. 0,
  103. NULL },
  104. { ngx_string("http_copy_status"),
  105. NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
  106. ngx_http_copy_status,
  107. 0,
  108. 0,
  109. NULL },
  110. ngx_null_command
  111. };
  112. static ngx_http_module_t ngx_http_copy_module_ctx = {
  113. NULL, /* preconfiguration */
  114. ngx_http_copy_init, /* postconfiguration */
  115. NULL, /* create main configuration */
  116. NULL, /* init main configuration */
  117. NULL, /* create server configuration */
  118. NULL, /* merge server configuration */
  119. ngx_http_copy_create_loc_conf, /* create location configuration */
  120. ngx_http_copy_merge_loc_conf /* merge location configuration */
  121. };
  122. ngx_module_t ngx_http_copy_module = {
  123. NGX_MODULE_V1,
  124. &ngx_http_copy_module_ctx,
  125. ngx_http_copy_commands,
  126. NGX_HTTP_MODULE,
  127. NULL,
  128. NULL,
  129. NULL,
  130. NULL,
  131. NULL,
  132. NULL,
  133. NULL,
  134. NGX_MODULE_V1_PADDING
  135. };
  136. static ngx_http_input_body_filter_pt ngx_http_next_input_body_filter;
  137. static ngx_http_copy_status_shm_t *copy_status = NULL;
  138. static char ngx_http_copy_version[] = " HTTP/1.0" CRLF;
  139. static char ngx_http_copy_version_11[] = " HTTP/1.1" CRLF;
  140. static ngx_connection_t *
  141. ngx_http_copy_get_keepalive_connection(ngx_http_copy_request_t *cpr)
  142. {
  143. ngx_http_copy_loc_conf_t *cplcf = cpr->cplcf;
  144. ngx_queue_t *q;
  145. ngx_connection_t *c;
  146. if (cplcf->keepalive && ngx_queue_empty(&cplcf->cache_connections) == 0) {
  147. /* get connection from connection cache */
  148. q = ngx_queue_last(&cplcf->cache_connections);
  149. c = ngx_queue_data(q, ngx_connection_t, queue);
  150. ngx_queue_remove(&c->queue);
  151. cplcf->cached--;
  152. ngx_log_error(NGX_LOG_INFO, cpr->peer.log, 0,
  153. "[copy] keepalive: get no.%d cached connection %p",
  154. cplcf->cached + 1, c);
  155. /* reinit connection, although ngx_http_copy_connect will init it also */
  156. c->idle = 0;
  157. c->data = cpr;
  158. c->read->handler = NULL; /* use dummy_handler()? */
  159. c->write->handler = NULL;
  160. c->log = cpr->peer.log;
  161. c->read->log = cpr->peer.log;
  162. c->write->log = cpr->peer.log;
  163. c->pool = cpr->pool;
  164. if (c->read->timer_set) {
  165. ngx_del_timer(c->read);
  166. }
  167. /* assert write timer is deleted */
  168. if (c->write->timer_set) {
  169. ngx_del_timer(c->write);
  170. }
  171. (void) ngx_atomic_fetch_add(&copy_status->connect_keepalive_count, 1);
  172. cpr->keepalive_connect = 1;
  173. /* not necessary to detect whether cached connection is valid */
  174. return c;
  175. }
  176. return NULL;
  177. }
  178. static void
  179. ngx_http_copy_keepalive_close_handler(ngx_event_t *ev)
  180. {
  181. ngx_connection_t *c = ev->data;
  182. ngx_http_copy_loc_conf_t *cplcf;
  183. ngx_int_t n;
  184. char buf[1];
  185. /* keepalive timedout */
  186. if (ev->timedout) {
  187. ngx_log_error(NGX_LOG_INFO, ev->log, 0,
  188. "[copy] keepalive: cached connection is timed out");
  189. goto close;
  190. }
  191. /*
  192. * ngx_worker_process_cycle() will set it when receiving EXITING signal.
  193. * and then it calls c->read->handler(ngx_http_copy_keepalive_close_handler)
  194. *
  195. * Note although ngx_drain_connections() could set it, but this connection
  196. * has been deleted from ngx_cycle->reusable_connections_queue.
  197. * So ngx_drain_connections() cannot touch this conneciton.
  198. */
  199. if (c->close) {
  200. ngx_log_error(NGX_LOG_INFO, ev->log, 0,
  201. "[copy] keepalive: server is exiting");
  202. goto close;
  203. }
  204. /* detect whether connection is closed by peer */
  205. n = recv(c->fd, buf, 1, MSG_PEEK);
  206. if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
  207. if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  208. goto close;
  209. }
  210. return;
  211. }
  212. /* TCP RESET or TCP HALF CLOSE */
  213. ngx_log_error(NGX_LOG_INFO, ev->log, 0,
  214. "[copy] keepalive: the peer has closed connection");
  215. close:
  216. // TODO: debug cplcf
  217. cplcf = c->data;
  218. ngx_log_error(NGX_LOG_INFO, ev->log, 0,
  219. "[copy] keepalive: close no.%d cached connection %p",
  220. cplcf->cached, c);
  221. /* delete it from connection cache */
  222. ngx_queue_remove(&c->queue);
  223. cplcf->cached--;
  224. c->pool = NULL; /* pool in cpr has been destroyed */
  225. ngx_close_connection(c);
  226. cplcf->connection--;
  227. }
  228. static ngx_int_t
  229. ngx_http_copy_try_keepalive_connection(ngx_http_copy_request_t *cpr)
  230. {
  231. ngx_connection_t *c = cpr->peer.connection;
  232. ngx_http_copy_loc_conf_t *cplcf = cpr->cplcf;
  233. if (c == NULL || !cplcf->keepalive || !cpr->response.keepalive) {
  234. return 0;
  235. }
  236. if (cplcf->cached >= cplcf->max_cached) {
  237. ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
  238. "[copy] keepalive: keepalive connection cache is full");
  239. return 0;
  240. }
  241. if (c->read->eof || c->read->error || c->read->timedout
  242. || c->write->error || c->write->timedout)
  243. {
  244. return 0;
  245. }
  246. if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  247. return 0;
  248. }
  249. /* cache valid connections */
  250. ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
  251. "[copy] keepalive: save no.%d connection %p",
  252. cplcf->cached + 1, c);
  253. cpr->peer.connection = NULL; /* skip ngx_close_connection() */
  254. /* add to cache: hack c->queue */
  255. /* delete it from reusable connection list _if necessary_ */
  256. ngx_reusable_connection(c, 0);
  257. /* add it to connection cache */
  258. ngx_queue_insert_head(&cplcf->cache_connections, &c->queue);
  259. cplcf->cached++;
  260. if (c->write->timer_set) {
  261. ngx_del_timer(c->write);
  262. }
  263. ngx_add_timer(c->read, cplcf->cached_timeout);
  264. c->write->handler = ngx_http_copy_dummy_handler;
  265. c->read->handler = ngx_http_copy_keepalive_close_handler;
  266. c->data = cplcf; /* maybe modify cplcf->connection */
  267. c->idle = 1;
  268. c->log = ngx_cycle->log;
  269. c->read->log = c->log;
  270. c->write->log = c->log;
  271. /* c->pool is NULL */
  272. if (c->read->ready) {
  273. ngx_http_copy_keepalive_close_handler(c->read);
  274. }
  275. return 1;
  276. }
  277. static ngx_int_t
  278. ngx_http_copy_get_peer(ngx_peer_connection_t *pc, void *data)
  279. {
  280. ngx_http_copy_request_t *cpr = data;
  281. ngx_http_copy_loc_conf_t *cplcf = cpr->cplcf;
  282. pc->sockaddr = cplcf->addrs[0].sockaddr;
  283. pc->socklen = cplcf->addrs[0].socklen;
  284. pc->name = &cplcf->addrs[0].name;
  285. pc->connection = ngx_http_copy_get_keepalive_connection(cpr);
  286. /* no keepalive connection & connections limit */
  287. if (cplcf->connection > cplcf->max_connection && pc->connection == NULL) {
  288. ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
  289. "[copy] open too many connections");
  290. return NGX_BUSY;
  291. }
  292. return pc->connection ? NGX_DONE : NGX_OK;
  293. }
  294. static void
  295. ngx_http_copy_finalize_request(ngx_http_copy_request_t *cpr)
  296. {
  297. if (cpr->connect) {
  298. if (cpr->keepalive_connect) {
  299. (void) ngx_atomic_fetch_add(&copy_status->active_connect_keepalive,
  300. -1);
  301. }
  302. (void) ngx_atomic_fetch_add(&copy_status->active_connect, -1);
  303. }
  304. /* wont receive body in input body filter */
  305. if (cpr->r) {
  306. ngx_log_error(NGX_LOG_DEBUG, cpr->r->connection->log, 0,
  307. "[copy] cpr %p disattach request \"%V\"",
  308. cpr, &cpr->r->uri);
  309. ngx_queue_remove(&cpr->queue);
  310. }
  311. /* close connection(not cached) */
  312. if (cpr->peer.connection) {
  313. cpr->peer.connection->pool = NULL; /* equal to cpr->pool */
  314. ngx_close_connection(cpr->peer.connection);
  315. cpr->cplcf->connection--;
  316. cpr->peer.connection = NULL;
  317. }
  318. /* free copy request */
  319. ngx_destroy_pool(cpr->pool);
  320. }
  321. static ngx_inline void
  322. ngx_http_copy_next(ngx_http_copy_request_t *cpr)
  323. {
  324. /* serial send request */
  325. if (cpr->serial) {
  326. ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
  327. "[copy] serial: no.%d request has been sent",
  328. cpr->serial_sent + 1);
  329. if (++cpr->serial_sent >= cpr->cplcf->multiple) {
  330. ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
  331. "[copy] serial: total requests have been sent");
  332. goto next;
  333. }
  334. if (!cpr->response.keepalive) {
  335. ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
  336. "[copy] serial: keepalive is disabled, cant send no.%d request",
  337. cpr->serial_sent);
  338. goto next;
  339. }
  340. cpr->request_sent = 0;
  341. cpr->request_bufs = NULL;
  342. ngx_chain_buf_add_copy(cpr->pool, &cpr->request_bufs, cpr->serial_request_bufs);
  343. cpr->discard_body = 0;
  344. cpr->length = -1;
  345. cpr->process_header = ngx_http_copy_parse_status_line;
  346. ngx_memzero(&cpr->response, sizeof(ngx_http_request_t));
  347. ngx_memzero(&cpr->status, sizeof(ngx_http_status_t));
  348. if (cpr->buffer != NULL) {
  349. cpr->buffer->pos = cpr->buffer->start;
  350. cpr->buffer->last = cpr->buffer->start;
  351. }
  352. /* wevent has been deleted, revent will be readded by ngx_http_copy_send_request() */
  353. ngx_http_copy_send_request(cpr);
  354. return;
  355. }
  356. next:
  357. (void) ngx_http_copy_try_keepalive_connection(cpr);
  358. ngx_http_copy_finalize_request(cpr);
  359. }
  360. static ngx_int_t
  361. ngx_http_copy_test_connect(ngx_connection_t *c)
  362. {
  363. int err;
  364. socklen_t len;
  365. if (c->log->action == NULL) {
  366. c->log->action = "connecting to backend server";
  367. }
  368. #if (NGX_HAVE_KQUEUE)
  369. if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
  370. if (c->write->pending_eof || c->read->pending_eof) {
  371. if (c->write->pending_eof) {
  372. err = c->write->kq_errno;
  373. } else {
  374. err = c->read->kq_errno;
  375. }
  376. /* ngx_cycle->log->handler dont print 'while %s' */
  377. ngx_log_error(NGX_LOG_ERR, c->log, err,
  378. "[copy] kevent() reported that connect() failed while %s",
  379. c->log->action);
  380. return NGX_ERROR;
  381. }
  382. }/* else: drop else, let it call getsockopt after kqueue test */
  383. #endif
  384. {
  385. err = 0;
  386. len = sizeof(int);
  387. /*
  388. * BSDs and Linux return 0 and set a pending error in err
  389. * Solaris returns -1 and sets errno
  390. */
  391. if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
  392. == -1)
  393. {
  394. err = ngx_errno;
  395. }
  396. if (err) {
  397. ngx_log_error(NGX_LOG_ERR, c->log, err,
  398. "[copy] getsockopt() reported that connect() failed while %s",
  399. c->log->action);
  400. return NGX_ERROR;
  401. }
  402. }
  403. return NGX_OK;
  404. }
  405. static void
  406. ngx_http_copy_dummy_handler(ngx_event_t *ev)
  407. {
  408. /*
  409. * When added, wev is triggered at once.
  410. * So you should use DEBUG log_level to avoid noice.
  411. */
  412. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "[copy] dummy handler");
  413. }
  414. static void
  415. ngx_http_copy_send_request_handler(ngx_event_t *ev)
  416. {
  417. ngx_connection_t *c;
  418. ngx_http_copy_request_t *cpr;
  419. c = ev->data;
  420. cpr = c->data;
  421. if (c->write->timedout) {
  422. ngx_http_copy_finalize_request(cpr);
  423. return;
  424. }
  425. (void) ngx_http_copy_send_request(cpr);
  426. }
  427. static void
  428. ngx_http_copy_connected_handler(ngx_event_t *ev)
  429. {
  430. ngx_connection_t *c = ev->data;
  431. if (c->write->timedout == 0) {
  432. ngx_log_error(NGX_LOG_DEBUG, c->log, 0,
  433. "[copy] nonblocking connection is established");
  434. (void) ngx_atomic_fetch_add(&copy_status->connect_count, 1);
  435. }
  436. ngx_http_copy_send_request_handler(ev);
  437. }
  438. static void
  439. ngx_http_copy_discard_body(ngx_http_copy_request_t *cpr)
  440. {
  441. u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
  442. ngx_connection_t *c = cpr->peer.connection;
  443. ssize_t n;
  444. size_t size;
  445. /* recv and discard response body */
  446. for ( ;; ) {
  447. /*
  448. * nginx upstream doesnt do it, which maybe readahead something wrong
  449. * that client sent
  450. */
  451. if (cpr->length == 0) {
  452. break;
  453. }
  454. size = (cpr->length != -1 && cpr->length < NGX_HTTP_DISCARD_BUFFER_SIZE)
  455. ? cpr->length
  456. : NGX_HTTP_DISCARD_BUFFER_SIZE;
  457. n = c->recv(c, buffer, size);
  458. if (n == NGX_AGAIN) {
  459. break;
  460. } else if (n == NGX_ERROR) {
  461. ngx_http_copy_finalize_request(cpr);
  462. return;
  463. } else if (n == 0) {
  464. /* backend server closes connection. */
  465. ngx_http_copy_finalize_request(cpr);
  466. return;
  467. }
  468. /* n > 0 */
  469. if (cpr->length != -1) {
  470. cpr->length -= n;
  471. }
  472. (void) ngx_atomic_fetch_add(&copy_status->read_bytes, n);
  473. }
  474. /* n == NGX_AGAIN */
  475. if (cpr->length == 0) {
  476. ngx_http_copy_next(cpr); /* try keepalive */
  477. return;
  478. }
  479. // TODO: re-add timer
  480. if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  481. ngx_http_copy_finalize_request(cpr);
  482. return;
  483. }
  484. }
  485. static ngx_int_t
  486. ngx_http_copy_process_one_header(ngx_http_copy_request_t *cpr)
  487. {
  488. ngx_http_request_t *r = &cpr->response;
  489. ngx_keyval_t h;
  490. /* assert one header has been parsed */
  491. h.key.len = r->header_name_end - r->header_name_start;
  492. h.key.data = r->header_name_start;
  493. h.value.len = r->header_end - r->header_start;
  494. h.value.data = r->header_start;
  495. #define header_key_is(s) \
  496. (h.key.len == sizeof(s) - 1 \
  497. && ngx_strncmp(h.key.data, (s), sizeof(s) - 1) == 0)
  498. #define header_value_has(s) \
  499. (ngx_strlcasestrn(h.value.data, h.value.data + h.value.len, \
  500. (u_char *)(s), sizeof(s) - 1 - 1/* len - 1 */) \
  501. != NULL)
  502. if (header_key_is("Content-Length")) {
  503. r->headers_in.content_length_n = ngx_atoof(h.value.data, h.value.len);
  504. cpr->length = r->headers_in.content_length_n;
  505. } else if (header_key_is("Connection")) {
  506. if (header_value_has("close")) {
  507. r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
  508. }
  509. } else if (header_key_is("Transfer-Encoding")) {
  510. if (header_value_has("chunked")) {
  511. r->chunked = 1;
  512. }
  513. }
  514. return NGX_OK;
  515. }
  516. static ngx_int_t
  517. ngx_http_copy_parse_header(ngx_http_copy_request_t *cpr)
  518. {
  519. ngx_http_request_t *r = &cpr->response;
  520. ngx_int_t rc;
  521. /* parse header */
  522. for ( ;; ) {
  523. rc = ngx_http_parse_header_line(&cpr->response, cpr->buffer, 1);
  524. if (rc == NGX_OK) {
  525. /* process this header */
  526. (void) ngx_http_copy_process_one_header(cpr);
  527. continue;
  528. }
  529. if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
  530. /* process response args */
  531. if (r->chunked) {
  532. r->headers_in.content_length_n = -1;
  533. cpr->length = -1;
  534. }
  535. if (r->headers_out.status == NGX_HTTP_NO_CONTENT
  536. || r->headers_out.status == NGX_HTTP_NOT_MODIFIED)
  537. {
  538. cpr->length = 0;
  539. }
  540. r->keepalive = (r->headers_in.connection_type != NGX_HTTP_CONNECTION_CLOSE);
  541. return NGX_OK;
  542. }
  543. /* rc == NGX_ERROR || rc == NGX_AGAIN */
  544. return rc;
  545. }
  546. }
  547. static ngx_int_t
  548. ngx_http_copy_parse_status_line(ngx_http_copy_request_t *cpr)
  549. {
  550. ngx_int_t rc;
  551. /* process status line */
  552. rc = ngx_http_parse_status_line(&cpr->response, cpr->buffer, &cpr->status);
  553. if (rc == NGX_AGAIN) {
  554. return rc;
  555. }
  556. if (rc == NGX_ERROR) {
  557. ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  558. "[copy] HTTP response: invalid status line");
  559. return rc;
  560. }
  561. /* rc == NGX_OK */
  562. cpr->response.headers_out.status = cpr->status.code;
  563. cpr->response.headers_out.status_line.len = cpr->status.end - cpr->status.start;
  564. cpr->response.headers_out.status_line.data = cpr->status.start; /* not necessary for new buffer */
  565. if (cpr->status.http_version < NGX_HTTP_VERSION_11) {
  566. cpr->response.headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
  567. }
  568. cpr->process_header = ngx_http_copy_parse_header;
  569. return ngx_http_copy_parse_header(cpr);
  570. }
  571. static ngx_int_t
  572. ngx_http_copy_parse_chunked(ngx_http_copy_chunk_t *ctx, ngx_buf_t *buf)
  573. {
  574. u_char *pos, ch, c;
  575. ngx_int_t rc;
  576. enum {
  577. sw_chunk_start = 0,
  578. sw_chunk_size,
  579. sw_chunk_extension,
  580. sw_chunk_extension_almost_done,
  581. sw_chunk_data,
  582. sw_after_data,
  583. sw_after_data_almost_done,
  584. sw_last_chunk_extension,
  585. sw_last_chunk_extension_almost_done,
  586. sw_trailer,
  587. sw_trailer_almost_done,
  588. sw_trailer_header,
  589. sw_trailer_header_almost_done
  590. } state;
  591. state = ctx->state;
  592. if (state == sw_chunk_data && ctx->size == 0) {
  593. state = sw_after_data;
  594. }
  595. rc = NGX_AGAIN;
  596. for (pos = buf->pos; pos < buf->last; pos++) {
  597. ch = *pos;
  598. switch (state) {
  599. case sw_chunk_start:
  600. if (ch >= '0' && ch <= '9') {
  601. state = sw_chunk_size;
  602. ctx->size = ch - '0';
  603. break;
  604. }
  605. c = (u_char) (ch | 0x20);
  606. if (c >= 'a' && c <= 'f') {
  607. state = sw_chunk_size;
  608. ctx->size = c - 'a' + 10;
  609. break;
  610. }
  611. goto invalid;
  612. case sw_chunk_size:
  613. if (ch >= '0' && ch <= '9') {
  614. ctx->size = ctx->size * 16 + (ch - '0');
  615. break;
  616. }
  617. c = (u_char) (ch | 0x20);
  618. if (c >= 'a' && c <= 'f') {
  619. ctx->size = ctx->size * 16 + (c - 'a' + 10);
  620. break;
  621. }
  622. if (ctx->size == 0) {
  623. switch (ch) {
  624. case CR:
  625. state = sw_last_chunk_extension_almost_done;
  626. break;
  627. case LF:
  628. state = sw_trailer;
  629. break;
  630. case ';':
  631. case ' ':
  632. case '\t':
  633. state = sw_last_chunk_extension;
  634. break;
  635. default:
  636. goto invalid;
  637. }
  638. break;
  639. }
  640. switch (ch) {
  641. case CR:
  642. state = sw_chunk_extension_almost_done;
  643. break;
  644. case LF:
  645. state = sw_chunk_data;
  646. break;
  647. case ';':
  648. case ' ':
  649. case '\t':
  650. state = sw_chunk_extension;
  651. break;
  652. default:
  653. goto invalid;
  654. }
  655. break;
  656. case sw_chunk_extension:
  657. switch (ch) {
  658. case CR:
  659. state = sw_chunk_extension_almost_done;
  660. break;
  661. case LF:
  662. state = sw_chunk_data;
  663. }
  664. break;
  665. case sw_chunk_extension_almost_done:
  666. if (ch == LF) {
  667. state = sw_chunk_data;
  668. break;
  669. }
  670. goto invalid;
  671. case sw_chunk_data:
  672. rc = NGX_OK;
  673. goto data;
  674. case sw_after_data:
  675. switch (ch) {
  676. case CR:
  677. state = sw_after_data_almost_done;
  678. break;
  679. case LF:
  680. state = sw_chunk_start;
  681. }
  682. break;
  683. case sw_after_data_almost_done:
  684. if (ch == LF) {
  685. state = sw_chunk_start;
  686. break;
  687. }
  688. goto invalid;
  689. case sw_last_chunk_extension:
  690. switch (ch) {
  691. case CR:
  692. state = sw_last_chunk_extension_almost_done;
  693. break;
  694. case LF:
  695. state = sw_trailer;
  696. }
  697. break;
  698. case sw_last_chunk_extension_almost_done:
  699. if (ch == LF) {
  700. state = sw_trailer;
  701. break;
  702. }
  703. goto invalid;
  704. case sw_trailer:
  705. switch (ch) {
  706. case CR:
  707. state = sw_trailer_almost_done;
  708. break;
  709. case LF:
  710. goto done;
  711. default:
  712. state = sw_trailer_header;
  713. }
  714. break;
  715. case sw_trailer_almost_done:
  716. if (ch == LF) {
  717. goto done;
  718. }
  719. goto invalid;
  720. case sw_trailer_header:
  721. switch (ch) {
  722. case CR:
  723. state = sw_trailer_header_almost_done;
  724. break;
  725. case LF:
  726. state = sw_trailer;
  727. }
  728. break;
  729. case sw_trailer_header_almost_done:
  730. if (ch == LF) {
  731. state = sw_trailer;
  732. break;
  733. }
  734. goto invalid;
  735. }
  736. }
  737. data:
  738. ctx->state = state;
  739. buf->pos = pos;
  740. return rc;
  741. done:
  742. return NGX_DONE;
  743. invalid:
  744. return NGX_ERROR;
  745. }
  746. static void
  747. ngx_http_copy_discard_chunk_body(ngx_http_copy_request_t *cpr)
  748. {
  749. ngx_connection_t *c = cpr->peer.connection;
  750. ngx_buf_t *buf;
  751. ngx_http_copy_chunk_t *chunk;
  752. ngx_int_t rc;
  753. ssize_t n;
  754. buf = cpr->buffer;
  755. chunk = cpr->chunk;
  756. for ( ;; ) {
  757. /* read response */
  758. if (buf->pos >= buf->last) {
  759. buf->pos = buf->start;
  760. buf->last = buf->start;
  761. n = c->recv(c, buf->pos, buf->end - buf->start);
  762. if (n == NGX_AGAIN) {
  763. // TODO: re-add timer
  764. if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  765. ngx_http_copy_finalize_request(cpr);
  766. return;
  767. }
  768. return;
  769. } else if (n == NGX_ERROR) {
  770. ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  771. "[copy] discard chunk body: read error occured");
  772. ngx_http_copy_finalize_request(cpr);
  773. return;
  774. } else if (n == 0) {
  775. /* backend server closes connection. */
  776. ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  777. "[copy] discard chunk body: backend closed connection");
  778. ngx_http_copy_finalize_request(cpr);
  779. return;
  780. }
  781. /* n > 0 */
  782. buf->last = buf->pos + n;
  783. }
  784. /* discard chunk */
  785. if (cpr->length > 0) {
  786. if (cpr->length > buf->last - buf->pos) {
  787. cpr->length -= buf->last - buf->pos;
  788. buf->pos = buf->last;
  789. } else {
  790. buf->pos += cpr->length; /* maybe buf->pos == buf->last */
  791. cpr->length = 0;
  792. }
  793. if (cpr->length > 0 || buf->pos == buf->last) {
  794. continue;
  795. }
  796. }
  797. /* parse chunk */
  798. rc = ngx_http_copy_parse_chunked(cpr->chunk, buf);
  799. if (rc == NGX_AGAIN) {
  800. /* continue to read more data */
  801. continue;
  802. }
  803. if (rc == NGX_OK) {
  804. /* chunk is parsed, continue to discard chunk body */
  805. (void) ngx_atomic_fetch_add(&copy_status->read_bytes, chunk->size);
  806. (void) ngx_atomic_fetch_add(&copy_status->read_chunk_bytes, chunk->size);
  807. cpr->length = chunk->size; /* length: size of current chunk */
  808. chunk->size = 0; /* chunk->state will goto sw_after_data */
  809. continue;
  810. }
  811. if (rc == NGX_DONE) {
  812. /* a whole response is pared */
  813. ngx_http_copy_next(cpr); /* try keepalive */
  814. return;
  815. }
  816. /* rc == NGX_ERROR, invalid response */
  817. ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  818. "[copy] discard chunk body: backend sent invalid chunked response");
  819. ngx_http_copy_finalize_request(cpr);
  820. return;
  821. }
  822. }
  823. static void
  824. ngx_http_copy_discard_response(ngx_http_copy_request_t *cpr)
  825. {
  826. ngx_int_t n, rc;
  827. ngx_connection_t *c = cpr->peer.connection;
  828. c->log->action = "discarding response";
  829. if (!cpr->request_sent && ngx_http_copy_test_connect(c) != NGX_OK) {
  830. ngx_http_copy_finalize_request(cpr);
  831. return;
  832. }
  833. if (cpr->discard_body == NGX_HTTP_COPY_DISCARD_BODY) {
  834. ngx_http_copy_discard_body(cpr);
  835. return;
  836. } else if (cpr->discard_body == NGX_HTTP_COPY_DISCARD_CHUNK_BODY) {
  837. ngx_http_copy_discard_chunk_body(cpr);
  838. return;
  839. }
  840. /* create buffer for response, detect for reenterring */
  841. if (cpr->buffer == NULL) {
  842. cpr->buffer = ngx_create_temp_buf(cpr->pool, 4096);
  843. if (cpr->buffer == NULL) {
  844. ngx_http_copy_finalize_request(cpr);
  845. return;
  846. }
  847. }
  848. /* recv and parse response header */
  849. for ( ;; ) {
  850. /* read response */
  851. n = c->recv(c, cpr->buffer->last, cpr->buffer->end - cpr->buffer->last);
  852. if (n == NGX_AGAIN) {
  853. // TODO: re-add timer
  854. if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  855. ngx_http_copy_finalize_request(cpr);
  856. return;
  857. }
  858. return;
  859. } else if (n == NGX_ERROR) {
  860. /* if worker_connections is too small, it maybe come here */
  861. ngx_log_error(NGX_LOG_ERR, c->log, 0,
  862. "[copy] discard body: read error occured");
  863. /* TODO: status log */
  864. ngx_http_copy_finalize_request(cpr);
  865. return;
  866. } else if (n == 0) {
  867. /* backend server closes connection. */
  868. ngx_log_error(NGX_LOG_ERR, c->log, 0,
  869. "[copy] discard body: backend closed connection");
  870. /* TODO: status log */
  871. ngx_http_copy_finalize_request(cpr);
  872. return;
  873. }
  874. /* n > 0: parse response */
  875. cpr->buffer->last += n;
  876. rc = cpr->process_header(cpr);
  877. if (rc == NGX_ERROR) {
  878. ngx_http_copy_finalize_request(cpr);
  879. return;
  880. } else if (rc == NGX_OK) {
  881. /* log status info */
  882. (void) ngx_atomic_fetch_add(&copy_status->response_count, 1);
  883. if (cpr->status.code >= NGX_HTTP_OK
  884. && cpr->status.code < NGX_HTTP_BAD_REQUEST)
  885. {
  886. (void) ngx_atomic_fetch_add(&copy_status->response_ok_count, 1);
  887. } else {
  888. (void) ngx_atomic_fetch_add(&copy_status->response_err_count, 1);
  889. }
  890. /* chunked response */
  891. if (cpr->response.chunked) {
  892. cpr->chunk = ngx_pcalloc(cpr->pool, sizeof(ngx_http_copy_chunk_t));
  893. if (cpr->chunk == NULL) {
  894. ngx_http_copy_finalize_request(cpr);
  895. return;
  896. }
  897. cpr->discard_body = NGX_HTTP_COPY_DISCARD_CHUNK_BODY;
  898. ngx_http_copy_discard_chunk_body(cpr);
  899. return;
  900. }
  901. /* discard data left in buffer */
  902. n = cpr->buffer->last - cpr->buffer->pos;
  903. if (n > 0) {
  904. (void) ngx_atomic_fetch_add(&copy_status->read_bytes, n);
  905. cpr->buffer->pos = cpr->buffer->last;
  906. if (cpr->length > 0) {
  907. cpr->length -= n;
  908. }
  909. }
  910. /* discard response body(headers + body) */
  911. cpr->discard_body = NGX_HTTP_COPY_DISCARD_BODY;
  912. ngx_http_copy_discard_body(cpr);
  913. return;
  914. }
  915. /* rc == NGX_AGAIN: read again and parse */
  916. if (cpr->buffer->last == cpr->buffer->end) {
  917. ngx_log_error(NGX_LOG_ERR, c->log, 0,
  918. "[copy] response header is too big ( > 4096 bytes)");
  919. ngx_http_copy_finalize_request(cpr);
  920. return;
  921. }
  922. }
  923. }
  924. static void
  925. ngx_http_copy_recv_response_handler(ngx_event_t *ev)
  926. {
  927. ngx_connection_t *c;
  928. ngx_http_copy_request_t *cpr;
  929. c = ev->data;
  930. cpr = c->data;
  931. if (c->read->timedout) {
  932. if (cpr->discard_body == NGX_HTTP_COPY_DISCARD_BODY) {
  933. ngx_log_error(NGX_LOG_ERR, c->log, 0,
  934. "[copy] discard body: read timedout");
  935. } else if (cpr->discard_body == NGX_HTTP_COPY_DISCARD_CHUNK_BODY) {
  936. ngx_log_error(NGX_LOG_ERR, c->log, 0,
  937. "[copy] discard chunk: read timedout");
  938. } else if (cpr->buffer == NULL) {
  939. ngx_log_error(NGX_LOG_ERR, c->log, 0,
  940. "[copy] recv response: timedout. No data has been read.");
  941. } else {
  942. if (cpr->process_header == ngx_http_copy_parse_status_line) {
  943. ngx_log_error(NGX_LOG_ERR, c->log, 0,
  944. "[copy] parse status line: read timedout.");
  945. } else {
  946. ngx_log_error(NGX_LOG_ERR, c->log, 0,
  947. "[copy] parse headers: read timedout");
  948. }
  949. ngx_log_error(NGX_LOG_INFO, c->log, 0,
  950. "[copy] read %d bytes data:\"%*s\"",
  951. cpr->buffer->last - cpr->buffer->start,
  952. cpr->buffer->last - cpr->buffer->start,
  953. cpr->buffer->start);
  954. }
  955. ngx_http_copy_finalize_request(cpr);
  956. return;
  957. }
  958. ngx_http_copy_discard_response(cpr);
  959. }
  960. static ngx_int_t
  961. ngx_http_copy_send_request(ngx_http_copy_request_t *cpr)
  962. {
  963. ngx_int_t rc;
  964. ngx_connection_t *c;
  965. c = cpr->peer.connection;
  966. c->log->action = "sending request";
  967. // TODO: action for serial
  968. if (!cpr->request_sent && ngx_http_copy_test_connect(c) != NGX_OK) {
  969. ngx_http_copy_finalize_request(cpr);
  970. return NGX_ERROR;
  971. }
  972. rc = ngx_output_chain(&cpr->output, cpr->request_sent ? NULL : cpr->request_bufs);
  973. cpr->request_sent = 1;
  974. if (rc == NGX_ERROR) {
  975. ngx_log_error(NGX_LOG_ERR, c->log, 0, "[copy] cannot send request to backend");
  976. ngx_http_copy_finalize_request(cpr);
  977. return NGX_ERROR;
  978. }
  979. if (c->write->timer_set) {
  980. ngx_del_timer(c->write);
  981. }
  982. if (rc == NGX_AGAIN) {
  983. ngx_add_timer(c->write, 5000); /* TODO: configure this value */
  984. if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
  985. ngx_http_copy_finalize_request(cpr);
  986. return NGX_ERROR;
  987. }
  988. return NGX_OK;
  989. }
  990. /* rc == NGX_OK */
  991. (void) ngx_atomic_fetch_add(&copy_status->request_count, 1);
  992. ngx_add_timer(c->read, 5000); /* TODO: configure this value */
  993. #if 0
  994. /* should not handle response, which has bad effect on performance */
  995. if (c->read->ready) {
  996. /* prehandle read event */
  997. ngx_log_error(NGX_LOG_INFO, c->log, 0, "[copy] readahead response");
  998. ngx_http_copy_recv_response_handler(c->read);
  999. return; /* why return? */
  1000. }
  1001. #endif
  1002. c->write->handler = ngx_http_copy_dummy_handler;
  1003. if(ngx_handle_write_event(c->write, 0) != NGX_OK) {
  1004. ngx_http_copy_finalize_request(cpr);
  1005. return NGX_ERROR;
  1006. }
  1007. return NGX_OK;
  1008. }
  1009. static ngx_int_t
  1010. ngx_chain_buf_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)
  1011. {
  1012. ngx_chain_t *cl, **ll;
  1013. ll = chain;
  1014. for (cl = *chain; cl; cl = cl->next) {
  1015. ll = &cl->next;
  1016. }
  1017. while (in) {
  1018. cl = ngx_alloc_chain_link(pool);
  1019. if (cl == NULL) {
  1020. return NGX_ERROR;
  1021. }
  1022. cl->buf = ngx_calloc_buf(pool);
  1023. if (cl->buf == NULL) {
  1024. return NGX_ERROR;
  1025. }
  1026. *cl->buf = *in->buf;
  1027. *ll = cl;
  1028. ll = &cl->next;
  1029. in = in->next;
  1030. }
  1031. *ll = NULL;
  1032. return NGX_OK;
  1033. }
  1034. static ngx_int_t
  1035. ngx_http_copy_request(ngx_http_copy_request_t *cpr)
  1036. {
  1037. ngx_http_request_t *r = cpr->r; /* assert cpr->r != NULL*/
  1038. ngx_buf_t *b;
  1039. ngx_chain_t *cl;
  1040. ngx_list_part_t *part;
  1041. ngx_table_elt_t *header;
  1042. ngx_uint_t i, force_keepalive;
  1043. size_t len;
  1044. /* change request to HTTP/1.1 and delete "Connection: ..." header */
  1045. force_keepalive = cpr->cplcf->force_keepalive;
  1046. /* calculate request(excluding body) length */
  1047. len = r->method_name.len + 1 + r->unparsed_uri.len;
  1048. /* http version length */
  1049. if (force_keepalive || r->http_version == NGX_HTTP_VERSION_11) {
  1050. len += sizeof(ngx_http_copy_version_11) - 1;
  1051. } else {
  1052. len += sizeof(ngx_http_copy_version) - 1;
  1053. }
  1054. /* header length */
  1055. part = &r->headers_in.headers.part;
  1056. header = part->elts;
  1057. for (i = 0; /* void */; i++) {
  1058. if (i >= part->nelts) {
  1059. if (part->next == NULL) {
  1060. break;
  1061. }
  1062. part = part->next;
  1063. header = part->elts;
  1064. i = 0;
  1065. }
  1066. if (force_keepalive
  1067. && header[i].key.len == 10
  1068. && ngx_strncmp(header[i].key.data, "Connection", 10) == 0)
  1069. {
  1070. continue;
  1071. }
  1072. len += header[i].key.len + sizeof(": ") - 1
  1073. + header[i].value.len + sizeof(CRLF) - 1;
  1074. }
  1075. len += 2; /* header end "\r\n" */
  1076. /* copy request */
  1077. b = ngx_create_temp_buf(cpr->pool, len);
  1078. if (b == NULL) {
  1079. return NGX_ERROR;
  1080. }
  1081. cl = ngx_alloc_chain_link(cpr->pool);
  1082. if (cl == NULL) {
  1083. return NGX_ERROR;
  1084. }
  1085. cl->buf = b;
  1086. /* copy request line */
  1087. b->last = ngx_copy(b->last, r->method_name.data,
  1088. r->method_name.len + 1/* space char */);
  1089. len = r->method_name.len + 1;
  1090. b->last = ngx_copy(b->last, r->unparsed_uri.data,
  1091. r->unparsed_uri.len);
  1092. len += r->unparsed_uri.len;
  1093. if (force_keepalive || r->http_version == NGX_HTTP_VERSION_11) {
  1094. b->last = ngx_cpymem(b->last, ngx_http_copy_version_11,
  1095. sizeof(ngx_http_copy_version_11) - 1);
  1096. len += sizeof(ngx_http_copy_version_11) - 1;
  1097. } else {
  1098. b->last = ngx_cpymem(b->last, ngx_http_copy_version,
  1099. sizeof(ngx_http_copy_version) - 1);
  1100. len += sizeof(ngx_http_copy_version) - 1;
  1101. }
  1102. /* copy headers */
  1103. for (i = 0; /* void */; i++) {
  1104. if (i >= part->nelts) {
  1105. if (part->next == NULL) {
  1106. break;
  1107. }
  1108. part = part->next;
  1109. header = part->elts;
  1110. i = 0;
  1111. }
  1112. if (force_keepalive
  1113. && header[i].key.len == 10
  1114. && ngx_strncmp(header[i].key.data, "Connection", 10) == 0)
  1115. {
  1116. continue;
  1117. }
  1118. b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
  1119. len += header[i].key.len;
  1120. *b->last++ = ':'; *b->last++ = ' ';
  1121. len += 2;
  1122. b->last = ngx_copy(b->last, header[i].value.data,
  1123. header[i].value.len);
  1124. len += header[i].value.len;
  1125. *b->last++ = CR; *b->last++ = LF;
  1126. len += 2;
  1127. }
  1128. /* add "\r\n" at the header end */
  1129. *b->last++ = CR; *b->last++ = LF;
  1130. len += 2;
  1131. /* copy body */
  1132. cpr->request_bufs = cl; /* cl -> status_lien & headers */
  1133. b->flush = 1;
  1134. cl->next = NULL;
  1135. if (cpr->serial) {
  1136. ngx_chain_buf_add_copy(cpr->pool, &cpr->serial_request_bufs,
  1137. cpr->request_bufs);
  1138. }
  1139. return NGX_OK;
  1140. }
  1141. static ngx_int_t
  1142. ngx_http_copy_connect(ngx_http_copy_request_t *cpr)
  1143. {
  1144. ngx_http_copy_loc_conf_t *cplcf = cpr->cplcf;
  1145. ngx_peer_connection_t *pc;
  1146. ngx_connection_t *c; /* connection to backend */
  1147. ngx_int_t rc;
  1148. pc = &cpr->peer;
  1149. pc->log = ngx_cycle->log;
  1150. pc->data = cpr;
  1151. pc->get = ngx_http_copy_get_peer; /* get conn from cache if keepalive is on */
  1152. rc = ngx_event_connect_peer(pc);
  1153. if (rc == NGX_ERROR || rc == NGX_DECLINED || rc == NGX_BUSY) {
  1154. /*
  1155. * NGX_DECLINED: get new peer, but cannot connect it
  1156. * NGX_BUSY: pc->get() return this value because of max_connections limit.
  1157. * NGX_ERROR: cannot get peer or syscalls error
  1158. */
  1159. if (rc == NGX_DECLINED) {
  1160. ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  1161. "[copy] cannot connect backend: connect() returns error");
  1162. } else if (rc == NGX_BUSY) {
  1163. ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  1164. "[copy] cannot connect backend: too many worker connections");
  1165. } else {
  1166. ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  1167. "[copy] cannot connect backend");
  1168. }
  1169. return NGX_ERROR;
  1170. }
  1171. /* rc == NGX_OK || rc == NGX_DONE (keepalive) || rc == NGX_AGAIN */
  1172. cpr->connect = 1;
  1173. (void) ngx_atomic_fetch_add(&copy_status->active_connect, 1);
  1174. if (rc != NGX_DONE) {
  1175. cplcf->connection++;
  1176. } else {
  1177. (void) ngx_atomic_fetch_add(&copy_status->active_connect_keepalive, 1);
  1178. }
  1179. c = pc->connection;
  1180. c->data = cpr;
  1181. c->write->handler = ngx_http_copy_send_request_handler;
  1182. c->read->handler = ngx_http_copy_recv_response_handler;
  1183. c->sendfile &= cpr->r->connection->sendfile;
  1184. c->pool = cpr->pool;
  1185. c->log = ngx_cycle->log; /* FIXME: better to alloc a new one */
  1186. c->read->log = c->log;
  1187. c->write->log = c->log;
  1188. if (rc == NGX_AGAIN) {
  1189. /*
  1190. * ngx_event_connect_peer will add write event when rc == NGX_AGAIN
  1191. * connected handler: log connect_count and send request
  1192. */
  1193. c->write->handler = ngx_http_copy_connected_handler;
  1194. ngx_add_timer(c->write, 5000);
  1195. } else {
  1196. (void) ngx_atomic_fetch_add(&copy_status->connect_count, 1);
  1197. }
  1198. return rc;
  1199. }
  1200. static void
  1201. ngx_http_copy_cleanup(void *data)
  1202. {
  1203. ngx_queue_t *copy_request, *q;
  1204. ngx_http_copy_request_t *cpr;
  1205. copy_request = data;
  1206. while (!ngx_queue_empty(copy_request)) {
  1207. q = ngx_queue_last(copy_request);
  1208. cpr = ngx_queue_data(q, ngx_http_copy_request_t, queue);
  1209. ngx_log_error(NGX_LOG_DEBUG, cpr->r->connection->log, 0,
  1210. "[copy] cleanup: request \"%V\" disattach cpr %p",
  1211. &cpr->r->uri, cpr);
  1212. cpr->r = NULL;
  1213. ngx_queue_remove(&cpr->queue);
  1214. }
  1215. }
  1216. static ngx_int_t
  1217. ngx_http_copy_init_request(ngx_http_copy_request_t *cpr)
  1218. {
  1219. ngx_http_core_loc_conf_t *clcf;
  1220. ngx_int_t rc;
  1221. ngx_http_copy_ctx_t *ctx;
  1222. /* connect backend server */
  1223. rc = ngx_http_copy_connect(cpr);
  1224. if (rc == NGX_ERROR) {
  1225. ngx_http_copy_finalize_request(cpr);
  1226. return NGX_ERROR;
  1227. }
  1228. /* copy request */
  1229. if (ngx_http_copy_request(cpr) == NGX_ERROR) {
  1230. ngx_http_copy_finalize_request(cpr);
  1231. return NGX_ERROR;
  1232. }
  1233. /* handle incoming body */
  1234. if (1 /* TODO: detect whether there is body data */
  1235. && cpr->r
  1236. && ngx_queue_empty(&cpr->queue))
  1237. {
  1238. ctx = ngx_http_get_module_ctx(cpr->r, ngx_http_copy_module);
  1239. ngx_queue_insert_head(&ctx->copy_request, &cpr->queue);
  1240. }
  1241. /* set output chain context */
  1242. clcf = ngx_http_get_module_loc_conf(cpr->r, ngx_http_core_module);
  1243. cpr->output.alignment = clcf->directio_alignment;
  1244. cpr->output.pool = cpr->pool;
  1245. cpr->output.bufs.num = 1;
  1246. cpr->output.bufs.size = clcf->client_body_buffer_size;
  1247. cpr->output.output_filter = ngx_chain_writer;
  1248. cpr->output.filter_ctx = &cpr->writer;
  1249. /* writer.out .. *writer.last are data waiting to send */
  1250. cpr->writer.out = NULL;
  1251. cpr->writer.last = &cpr->writer.out;
  1252. cpr->writer.connection = cpr->peer.connection;
  1253. cpr->writer.limit = 0;
  1254. cpr->writer.pool = cpr->pool;
  1255. cpr->request_sent = 0;
  1256. if (rc == NGX_AGAIN) {
  1257. return NGX_OK;
  1258. }
  1259. /* send request, rc == NGX_DONE || rc == NGX_OK */
  1260. return ngx_http_copy_send_request(cpr);
  1261. }
  1262. static ngx_http_copy_request_t *
  1263. ngx_http_copy_alloc_request(ngx_http_request_t *r, ngx_http_copy_loc_conf_t *cplcf)
  1264. {
  1265. ngx_pool_t *pool;
  1266. ngx_http_copy_request_t *cpr;
  1267. /* create request (copied from @r) */
  1268. pool = ngx_create_pool(512, ngx_cycle->log);
  1269. if (pool == NULL) {
  1270. return NULL;
  1271. }
  1272. cpr = ngx_pcalloc(pool, sizeof(ngx_http_copy_request_t));
  1273. if (cpr == NULL) {
  1274. ngx_destroy_pool(pool);
  1275. return NULL;
  1276. }
  1277. cpr->r = r; /* can only read r, shouldnt modify r */
  1278. cpr->cplcf = cplcf; /* used when response is sent back */
  1279. cpr->length = -1; /* response length */
  1280. cpr->pool = pool;
  1281. cpr->process_header = ngx_http_copy_parse_status_line;
  1282. ngx_queue_init(&cpr->queue);
  1283. if (cplcf->serial && r->method == NGX_HTTP_GET) {
  1284. cpr->serial = 1;
  1285. }
  1286. return cpr;
  1287. }
  1288. static void
  1289. ngx_http_copy_init_requests(ngx_http_request_t *r)
  1290. {
  1291. ngx_http_copy_request_t *cpr; /* request copied from @r */
  1292. ngx_http_copy_loc_conf_t *cplcf;
  1293. ngx_http_copy_ctx_t *ctx;
  1294. ngx_http_cleanup_t *cln;
  1295. ngx_uint_t i;
  1296. cplcf = ngx_http_get_module_loc_conf(r, ngx_http_copy_module);
  1297. /* init copy context */
  1298. ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_copy_ctx_t));
  1299. if (ctx == NULL) {
  1300. return;
  1301. }
  1302. ngx_queue_init(&ctx->copy_request);
  1303. ngx_http_set_ctx(r, ctx, ngx_http_copy_module);
  1304. /* add cleanup handler */
  1305. cln = ngx_http_cleanup_add(r, 0 /* zero data size*/);
  1306. if (cln == NULL) {
  1307. return;
  1308. }
  1309. cln->handler = ngx_http_copy_cleanup; /* called in ngx_http_free_request() */
  1310. cln->data = &ctx->copy_request;
  1311. for (i = 0; i < cplcf->multiple; i++) {
  1312. /* create request (copied from @r) */
  1313. cpr = ngx_http_copy_alloc_request(r, cplcf);
  1314. if (cpr == NULL) {
  1315. return;
  1316. }
  1317. if (ngx_http_copy_init_request(cpr) == NGX_ERROR) {
  1318. ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1319. "[copy] should send %d requests, only complete %d requests",
  1320. cplcf->multiple, i);
  1321. break;
  1322. }
  1323. if (cpr->serial) {
  1324. ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  1325. "[copy] serial: first copied request has been prepared");
  1326. break;
  1327. }
  1328. }
  1329. }
  1330. static ngx_int_t
  1331. ngx_http_copy_handler(ngx_http_request_t *r)
  1332. {
  1333. ngx_http_copy_loc_conf_t *cplcf;
  1334. cplcf = ngx_http_get_module_loc_conf(r, ngx_http_copy_module);
  1335. if (cplcf->on) {
  1336. ngx_http_copy_init_requests(r);
  1337. }
  1338. /* let original request continue to run */
  1339. return NGX_DECLINED;
  1340. }
  1341. static char *
  1342. ngx_http_copy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  1343. {
  1344. ngx_http_copy_loc_conf_t *prev = parent;
  1345. ngx_http_copy_loc_conf_t *conf = child;
  1346. ngx_conf_merge_ptr_value(conf->addrs, prev->addrs, NULL);
  1347. ngx_conf_merge_uint_value(conf->naddrs, prev->naddrs, (ngx_uint_t)0);
  1348. ngx_conf_merge_uint_value(conf->multiple, prev->multiple, 1);
  1349. if (ngx_queue_empty(&prev->cache_connections)) {
  1350. ngx_queue_init(&conf->cache_connections);
  1351. } else {
  1352. conf->cache_connections = prev->cache_connections;
  1353. }
  1354. ngx_conf_merge_value(conf->max_cached, prev->max_cached, (ngx_int_t)65535);
  1355. ngx_conf_merge_value(conf->cached, prev->cached, (ngx_int_t)0);
  1356. ngx_conf_merge_msec_value(conf->cached_timeout,
  1357. prev->cached_timeout, 60000);
  1358. ngx_conf_merge_value(conf->max_connection, prev->max_connection, (ngx_int_t)65535);
  1359. ngx_conf_merge_value(conf->connection, prev->connection, (ngx_int_t)0);
  1360. ngx_conf_merge_value(conf->on, prev->on, 0);
  1361. ngx_conf_merge_value(conf->keepalive, prev->keepalive, 1);
  1362. ngx_conf_merge_value(conf->force_keepalive, prev->force_keepalive, 1);
  1363. ngx_conf_merge_value(conf->serial, prev->serial, 0);
  1364. return NGX_CONF_OK;
  1365. }
  1366. static void *
  1367. ngx_http_copy_create_loc_conf(ngx_conf_t *cf)
  1368. {
  1369. ngx_http_copy_loc_conf_t *cplcf;
  1370. cplcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_copy_loc_conf_t));
  1371. if (cplcf == NULL) {
  1372. return NULL;
  1373. }
  1374. /* keepalive is disabled by default */
  1375. /* NOTE: NGX_CONF_UNSET_<TYPE> used by ngx_conf_merge_<type>_value */
  1376. cplcf->addrs = NGX_CONF_UNSET_PTR;
  1377. cplcf->naddrs = NGX_CONF_UNSET_UINT;
  1378. cplcf->multiple = NGX_CONF_UNSET_UINT;
  1379. ngx_queue_init(&cplcf->cache_connections);
  1380. cplcf->max_cached = NGX_CONF_UNSET;
  1381. cplcf->cached = NGX_CONF_UNSET;
  1382. cplcf->cached_timeout = NGX_CONF_UNSET_MSEC;
  1383. cplcf->max_connection = NGX_CONF_UNSET;
  1384. cplcf->connection = NGX_CONF_UNSET;
  1385. cplcf->on = NGX_CONF_UNSET;
  1386. cplcf->keepalive = NGX_CONF_UNSET;
  1387. cpl