1 2 /* 3 * Copyright (C) Igor Sysoev 4 * Copyright (C) Nginx, Inc. 5 */ 6 7 8 #include <ngx_config.h> 9 #include <ngx_core.h> 10 #include <ngx_http.h> 11 12 13 static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf); 14 15 16 static ngx_http_module_t ngx_http_write_filter_module_ctx = { 17 NULL, /* preconfiguration */ 18 ngx_http_write_filter_init, /* postconfiguration */ 19 20 NULL, /* create main configuration */ 21 NULL, /* init main configuration */ 22 23 NULL, /* create server configuration */ 24 NULL, /* merge server configuration */ 25 26 NULL, /* create location configuration */ 27 NULL, /* merge location configuration */ 28 }; 29 30 31 ngx_module_t ngx_http_write_filter_module = { 32 NGX_MODULE_V1, 33 &ngx_http_write_filter_module_ctx, /* module context */ 34 NULL, /* module directives */ 35 NGX_HTTP_MODULE, /* module type */ 36 NULL, /* init master */ 37 NULL, /* init module */ 38 NULL, /* init process */ 39 NULL, /* init thread */ 40 NULL, /* exit thread */ 41 NULL, /* exit process */ 42 NULL, /* exit master */ 43 NGX_MODULE_V1_PADDING 44 }; 45 46 47 ngx_int_t 48 ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) 49 { 50 off_t size, sent, nsent, limit; 51 ngx_uint_t last, flush, sync; 52 ngx_msec_t delay; 53 ngx_chain_t *cl, *ln, **ll, *chain; 54 ngx_connection_t *c; 55 ngx_http_core_loc_conf_t *clcf; 56 57 c = r->connection; 58 59 if (c->error) { 60 return NGX_ERROR; 61 } 62 63 size = 0; 64 flush = 0; 65 sync = 0; 66 last = 0; 67 ll = &r->out; 68 69 /* find the size, the flush point and the last link of the saved chain */ 70 71 for (cl = r->out; cl; cl = cl->next) { 72 ll = &cl->next; 73 74 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, 75 "write old buf t:%d f:%d %p, pos %p, size: %z " 76 "file: %O, size: %O", 77 cl->buf->temporary, cl->buf->in_file, 78 cl->buf->start, cl->buf->pos, 79 cl->buf->last - cl->buf->pos, 80 cl->buf->file_pos, 81 cl->buf->file_last - cl->buf->file_pos); 82 83 if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { 84 ngx_log_error(NGX_LOG_ALERT, c->log, 0, 85 "zero size buf in writer " 86 "t:%d r:%d f:%d %p %p-%p %p %O-%O", 87 cl->buf->temporary, 88 cl->buf->recycled, 89 cl->buf->in_file, 90 cl->buf->start, 91 cl->buf->pos, 92 cl->buf->last, 93 cl->buf->file, 94 cl->buf->file_pos, 95 cl->buf->file_last); 96 97 ngx_debug_point(); 98 return NGX_ERROR; 99 } 100 101 if (ngx_buf_size(cl->buf) < 0) { 102 ngx_log_error(NGX_LOG_ALERT, c->log, 0, 103 "negative size buf in writer " 104 "t:%d r:%d f:%d %p %p-%p %p %O-%O", 105 cl->buf->temporary, 106 cl->buf->recycled, 107 cl->buf->in_file, 108 cl->buf->start, 109 cl->buf->pos, 110 cl->buf->last, 111 cl->buf->file, 112 cl->buf->file_pos, 113 cl->buf->file_last); 114 115 ngx_debug_point(); 116 return NGX_ERROR; 117 } 118 119 size += ngx_buf_size(cl->buf); 120 121 if (cl->buf->flush || cl->buf->recycled) { 122 flush = 1; 123 } 124 125 if (cl->buf->sync) { 126 sync = 1; 127 } 128 129 if (cl->buf->last_buf) { 130 last = 1; 131 } 132 } 133 134 /* add the new chain to the existent one */ 135 136 for (ln = in; ln; ln = ln->next) { 137 cl = ngx_alloc_chain_link(r->pool); 138 if (cl == NULL) { 139 return NGX_ERROR; 140 } 141 142 cl->buf = ln->buf; 143 *ll = cl; 144 ll = &cl->next; 145 146 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, 147 "write new buf t:%d f:%d %p, pos %p, size: %z " 148 "file: %O, size: %O", 149 cl->buf->temporary, cl->buf->in_file, 150 cl->buf->start, cl->buf->pos, 151 cl->buf->last - cl->buf->pos, 152 cl->buf->file_pos, 153 cl->buf->file_last - cl->buf->file_pos); 154 155 if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { 156 ngx_log_error(NGX_LOG_ALERT, c->log, 0, 157 "zero size buf in writer " 158 "t:%d r:%d f:%d %p %p-%p %p %O-%O", 159 cl->buf->temporary, 160 cl->buf->recycled, 161 cl->buf->in_file, 162 cl->buf->start, 163 cl->buf->pos, 164 cl->buf->last, 165 cl->buf->file, 166 cl->buf->file_pos, 167 cl->buf->file_last); 168 169 ngx_debug_point(); 170 return NGX_ERROR; 171 } 172 173 if (ngx_buf_size(cl->buf) < 0) { 174 ngx_log_error(NGX_LOG_ALERT, c->log, 0, 175 "negative size buf in writer " 176 "t:%d r:%d f:%d %p %p-%p %p %O-%O", 177 cl->buf->temporary, 178 cl->buf->recycled, 179 cl->buf->in_file, 180 cl->buf->start, 181 cl->buf->pos, 182 cl->buf->last, 183 cl->buf->file, 184 cl->buf->file_pos, 185 cl->buf->file_last); 186 187 ngx_debug_point(); 188 return NGX_ERROR; 189 } 190 191 size += ngx_buf_size(cl->buf); 192 193 if (cl->buf->flush || cl->buf->recycled) { 194 flush = 1; 195 } 196 197 if (cl->buf->sync) { 198 sync = 1; 199 } 200 201 if (cl->buf->last_buf) { 202 last = 1; 203 } 204 } 205 206 *ll = NULL; 207 208 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, 209 "http write filter: l:%ui f:%ui s:%O", last, flush, size); 210 211 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 212 213 /* 214 * avoid the output if there are no last buf, no flush point, 215 * there are the incoming bufs and the size of all bufs 216 * is smaller than "postpone_output" directive 217 */ 218 219 if (!last && !flush && in && size < (off_t) clcf->postpone_output) { 220 return NGX_OK; 221 } 222 223 if (c->write->delayed) { 224 c->buffered |= NGX_HTTP_WRITE_BUFFERED; 225 return NGX_AGAIN; 226 } 227 228 if (size == 0 229 && !(c->buffered & NGX_LOWLEVEL_BUFFERED) 230 && !(last && c->need_last_buf)) 231 { 232 if (last || flush || sync) { 233 for (cl = r->out; cl; /* void */) { 234 ln = cl; 235 cl = cl->next; 236 ngx_free_chain(r->pool, ln); 237 } 238 239 r->out = NULL; 240 c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; 241 242 return NGX_OK; 243 } 244 245 ngx_log_error(NGX_LOG_ALERT, c->log, 0, 246 "the http output chain is empty"); 247 248 ngx_debug_point(); 249 250 return NGX_ERROR; 251 } 252 253 if (!r->limit_rate_set) { 254 r->limit_rate = ngx_http_complex_value_size(r, clcf->limit_rate, 0); 255 r->limit_rate_set = 1; 256 } 257 258 if (r->limit_rate) { 259 260 if (!r->limit_rate_after_set) { 261 r->limit_rate_after = ngx_http_complex_value_size(r, 262 clcf->limit_rate_after, 0); 263 r->limit_rate_after_set = 1; 264 } 265 266 limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1) 267 - (c->sent - r->limit_rate_after); 268 269 if (limit <= 0) { 270 c->write->delayed = 1; 271 delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1); 272 ngx_add_timer(c->write, delay); 273 274 c->buffered |= NGX_HTTP_WRITE_BUFFERED; 275 276 return NGX_AGAIN; 277 } 278 279 if (clcf->sendfile_max_chunk 280 && (off_t) clcf->sendfile_max_chunk < limit) 281 { 282 limit = clcf->sendfile_max_chunk; 283 } 284 285 } else { 286 limit = clcf->sendfile_max_chunk; 287 } 288 289 sent = c->sent; 290 291 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 292 "http write filter limit %O", limit); 293 294 chain = c->send_chain(c, r->out, limit); 295 296 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 297 "http write filter %p", chain); 298 299 if (chain == NGX_CHAIN_ERROR) { 300 c->error = 1; 301 return NGX_ERROR; 302 } 303 304 if (r->limit_rate) { 305 306 nsent = c->sent; 307 308 if (r->limit_rate_after) { 309 310 sent -= r->limit_rate_after; 311 if (sent < 0) { 312 sent = 0; 313 } 314 315 nsent -= r->limit_rate_after; 316 if (nsent < 0) { 317 nsent = 0; 318 } 319 } 320 321 delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate); 322 323 if (delay > 0) { 324 limit = 0; 325 c->write->delayed = 1; 326 ngx_add_timer(c->write, delay); 327 } 328 } 329 330 if (limit 331 && c->write->ready 332 && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize)) 333 { 334 c->write->delayed = 1; 335 ngx_add_timer(c->write, 1); 336 } 337 338 for (cl = r->out; cl && cl != chain; /* void */) { 339 ln = cl; 340 cl = cl->next; 341 ngx_free_chain(r->pool, ln); 342 } 343 344 r->out = chain; 345 346 if (chain) { 347 c->buffered |= NGX_HTTP_WRITE_BUFFERED; 348 return NGX_AGAIN; 349 } 350 351 c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; 352 353 if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) { 354 return NGX_AGAIN; 355 } 356 357 return NGX_OK; 358 } 359 360 361 static ngx_int_t 362 ngx_http_write_filter_init(ngx_conf_t *cf) 363 { 364 ngx_http_top_body_filter = ngx_http_write_filter; 365 366 return NGX_OK; 367 } 368