Exemplo n.º 1
0
static ngx_int_t
ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body)
{
		syslog(LOG_INFO, "[%s:%s:%d]", __FILE__, __func__, __LINE__);
    ssize_t                    n;
    ngx_temp_file_t           *tf;
    ngx_http_request_body_t   *rb;
    ngx_http_core_loc_conf_t  *clcf;

    rb = r->request_body;

    if (rb->temp_file == NULL) {
        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
        if (tf == NULL) {
            return NGX_ERROR;
        }

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        tf->file.fd = NGX_INVALID_FILE;
        tf->file.log = r->connection->log;
        tf->path = clcf->client_body_temp_path;
        tf->pool = r->pool;
        tf->warn = "a client request body is buffered to a temporary file";
        tf->log_level = r->request_body_file_log_level;
        tf->persistent = r->request_body_in_persistent_file;
        tf->clean = r->request_body_in_clean_file;

        if (r->request_body_file_group_access) {
            tf->access = 0660;
        }

        rb->temp_file = tf;

        if (body == NULL) {
            /* empty body with r->request_body_in_file_only */

            if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                     tf->persistent, tf->clean, tf->access)
                != NGX_OK)
            {
                return NGX_ERROR;
            }

            return NGX_OK;
        }
    }

    n = ngx_write_chain_to_temp_file(rb->temp_file, body);

    /* TODO: n == 0 or not complete and level event */

    if (n == NGX_ERROR) {
        return NGX_ERROR;
    }

    rb->temp_file->offset += n;

    return NGX_OK;
}
Exemplo n.º 2
0
ssize_t
ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain)
{
    ngx_int_t  rc;

    if (tf->file.fd == NGX_INVALID_FILE) {
        rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                  tf->persistent, tf->clean, tf->access);

        if (rc != NGX_OK) {
            return rc;
        }

        if (tf->log_level) {
            ngx_log_error(tf->log_level, tf->file.log, 0, "%s %V",
                          tf->warn, &tf->file.name);
        }
    }

#if (NGX_THREADS && NGX_HAVE_PWRITEV)

    if (tf->thread_write) {
        return ngx_thread_write_chain_to_file(&tf->file, chain, tf->offset,
                                              tf->pool);
    }

#endif

    return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);
}
ngx_int_t ngx_http_small_light_imlib2_init(ngx_http_request_t *r, ngx_http_small_light_ctx_t *ctx)
{
    ngx_http_small_light_imlib2_ctx_t *ictx;

    ictx            = (ngx_http_small_light_imlib2_ctx_t *)ctx->ictx;
    ictx->image     = ctx->content;
    ictx->image_len = ctx->content_length;
    ictx->type      = ngx_http_small_light_type_detect(ictx->image, ictx->image_len);
    ictx->r         = r;
    if (ictx->type == NGX_HTTP_SMALL_LIGHT_IMAGE_NONE) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to get image type %s:%d",
                      __FUNCTION__,
                      __LINE__);
        return NGX_ERROR;
    }
    ictx->tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
    if (ictx->tf == NULL) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to allocate memory from r->pool %s:%d",
                      __FUNCTION__,
                      __LINE__);
        return NGX_ERROR;
    }
    ictx->tf->file.fd  = NGX_INVALID_FILE;
    ictx->tf->file.log = r->connection->log;
    ictx->tf->path     = ctx->imlib2_temp_dir;
    ictx->tf->pool     = r->pool;

    if (ngx_create_temp_file(&ictx->tf->file, ictx->tf->path, ictx->tf->pool, 1, 0, 0600) != NGX_OK) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to create temporary file %s:%d",
                      __FUNCTION__,
                      __LINE__);
        return NGX_ERROR;
    }

    if (ngx_write_file(&ictx->tf->file, ictx->image, ictx->image_len, 0) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to save temporary file %s:%d",
                      __FUNCTION__,
                      __LINE__);
        ngx_http_small_light_imlib2_term(ctx);
        return NGX_ERROR;
    }

    return NGX_OK;
}
Exemplo n.º 4
0
int ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain)
{
  int  rc;

  if (tf->file.fd == NGX_INVALID_FILE) {
    rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                  tf->persistent);

    if (rc == NGX_ERROR || rc == NGX_AGAIN) {
      return rc;
    }

    if (!tf->persistent && tf->warn) {
      ngx_log_error(NGX_LOG_WARN, tf->file.log, 0, tf->warn);
    }
  }

  return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);
}
Exemplo n.º 5
0
ssize_t
ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain)
{
    ngx_int_t  rc;

    if (tf->file.fd == NGX_INVALID_FILE) {
        rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                  tf->persistent, tf->clean, tf->access);

        if (rc == NGX_ERROR || rc == NGX_AGAIN) {
            return rc;
        }

        if (tf->log_level) {
            ngx_log_error(tf->log_level, tf->file.log, 0, "%s %V",
                          tf->warn, &tf->file.name);
        }
    }

    return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);
}
static ngx_int_t
ngx_http_write_request_body(ngx_http_request_t *r)
{
    ssize_t                    n;
    ngx_chain_t               *cl, *ln;
    ngx_temp_file_t           *tf;
    ngx_http_request_body_t   *rb;
    ngx_http_core_loc_conf_t  *clcf;

    rb = r->request_body;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http write client request body, bufs %p", rb->bufs);

    if (rb->temp_file == NULL) {
        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
        if (tf == NULL) {
            return NGX_ERROR;
        }

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        tf->file.fd = NGX_INVALID_FILE;
        tf->file.log = r->connection->log;
        tf->path = clcf->client_body_temp_path;
        tf->pool = r->pool;
        tf->warn = "a client request body is buffered to a temporary file";
        tf->log_level = r->request_body_file_log_level;
        tf->persistent = r->request_body_in_persistent_file;
        tf->clean = r->request_body_in_clean_file;

        if (r->request_body_file_group_access) {
            tf->access = 0660;
        }

        rb->temp_file = tf;

        if (rb->bufs == NULL) {
            /* empty body with r->request_body_in_file_only */

            if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                     tf->persistent, tf->clean, tf->access)
                    != NGX_OK)
            {
                return NGX_ERROR;
            }

            return NGX_OK;
        }
    }

    if (rb->bufs == NULL) {
        return NGX_OK;
    }

    n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);

    /* TODO: n == 0 or not complete and level event */

    if (n == NGX_ERROR) {
        return NGX_ERROR;
    }

    rb->temp_file->offset += n;

    /* mark all buffers as written */

    for (cl = rb->bufs; cl; /* void */) {

        cl->buf->pos = cl->buf->last;

        ln = cl;
        cl = cl->next;
        ngx_free_chain(r->pool, ln);
    }

    rb->bufs = NULL;

    return NGX_OK;
}
Exemplo n.º 7
0
ngx_int_t
ngx_http_read_client_request_body(ngx_http_request_t *r,
    ngx_http_client_body_handler_pt post_handler)
{
    size_t                     preread;
    ssize_t                    size;
    ngx_buf_t                 *b, buf;
    ngx_int_t                  rc;
    ngx_chain_t               *cl, **next;
    ngx_temp_file_t           *tf;
    ngx_http_request_body_t   *rb;
    ngx_http_core_loc_conf_t  *clcf;

    r->main->count++;

    if (r->request_body || r->discard_body) {
        ngx_http_probe_read_body_abort(r,
                                       (r->request_body ? "body exists"
                                                        : "body discarded"));
        post_handler(r);
        return NGX_OK;
    }

    if (ngx_http_test_expect(r) != NGX_OK) {
        ngx_http_probe_read_body_abort(r, "test expect failed");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
    if (rb == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    r->request_body = rb;

    if (r->headers_in.content_length_n < 0) {
        ngx_http_probe_read_body_abort(r, "no content length");
        post_handler(r);
        return NGX_OK;
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (r->headers_in.content_length_n == 0) {

        if (r->request_body_in_file_only) {
            tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
            if (tf == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            tf->file.fd = NGX_INVALID_FILE;
            tf->file.log = r->connection->log;
            tf->path = clcf->client_body_temp_path;
            tf->pool = r->pool;
            tf->warn = "a client request body is buffered to a temporary file";
            tf->log_level = r->request_body_file_log_level;
            tf->persistent = r->request_body_in_persistent_file;
            tf->clean = r->request_body_in_clean_file;

            if (r->request_body_file_group_access) {
                tf->access = 0660;
            }

            rb->temp_file = tf;

            if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                     tf->persistent, tf->clean, tf->access)
                != NGX_OK)
            {
                ngx_http_probe_read_body_abort(r, "create temp file failed");
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
        }

        ngx_http_probe_read_body_done(r);

        post_handler(r);

        return NGX_OK;
    }

    rb->post_handler = post_handler;

    /*
     * set by ngx_pcalloc():
     *
     *     rb->bufs = NULL;
     *     rb->buf = NULL;
     *     rb->rest = 0;
     */

    preread = r->header_in->last - r->header_in->pos;

    if (preread) {

        /* there is the pre-read part of the request body */

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http client request body preread %uz", preread);

        b = ngx_calloc_buf(r->pool);
        if (b == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        b->temporary = 1;
        b->start = r->header_in->pos;
        b->pos = r->header_in->pos;
        b->last = r->header_in->last;
        b->end = r->header_in->end;

        buf.start = r->header_in->pos;
        buf.pos = r->header_in->pos;
        buf.last = (off_t) preread >= r->headers_in.content_length_n
                 ? r->header_in->pos + (size_t) r->headers_in.content_length_n
                 : r->header_in->last;
        buf.end = r->header_in->end;

        rc = ngx_http_top_input_body_filter(r, &buf);
        if (rc != NGX_OK) {
            return rc;
        }

        rb->bufs = ngx_alloc_chain_link(r->pool);
        if (rb->bufs == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        rb->bufs->buf = b;
        rb->bufs->next = NULL;

        rb->buf = b;

        if ((off_t) preread >= r->headers_in.content_length_n) {

            /* the whole request body was pre-read */

            r->header_in->pos += (size_t) r->headers_in.content_length_n;
            r->request_length += r->headers_in.content_length_n;
            b->last = r->header_in->pos;

            if (r->request_body_in_file_only) {
                if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) {
                    ngx_http_probe_read_body_abort(r, "write temp file failed");
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }
            }

            ngx_http_probe_read_body_done(r);

            post_handler(r);

            return NGX_OK;
        }

        /*
         * to not consider the body as pipelined request in
         * ngx_http_set_keepalive()
         */
        r->header_in->pos = r->header_in->last;

        r->request_length += preread;

        rb->rest = r->headers_in.content_length_n - preread;

        if (rb->rest <= (off_t) (b->end - b->last)) {

            /* the whole request body may be placed in r->header_in */

            rb->to_write = rb->bufs;

            r->read_event_handler = ngx_http_read_client_request_body_handler;

            return ngx_http_do_read_client_request_body(r);
        }

        next = &rb->bufs->next;

    } else {
        b = NULL;
        rb->rest = r->headers_in.content_length_n;
        next = &rb->bufs;
    }

    size = clcf->client_body_buffer_size;
    size += size >> 2;

    if (rb->rest < size) {
        size = (ssize_t) rb->rest;

        if (r->request_body_in_single_buf) {
            size += preread;
        }

    } else {
        size = clcf->client_body_buffer_size;

        /* disable copying buffer for r->request_body_in_single_buf */
        b = NULL;
    }

    rb->buf = ngx_create_temp_buf(r->pool, size);
    if (rb->buf == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    cl = ngx_alloc_chain_link(r->pool);
    if (cl == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    cl->buf = rb->buf;
    cl->next = NULL;

    if (b && r->request_body_in_single_buf) {
        size = b->last - b->pos;
        ngx_memcpy(rb->buf->pos, b->pos, size);
        rb->buf->last += size;

        next = &rb->bufs;
    }

    *next = cl;

    if (r->request_body_in_file_only || r->request_body_in_single_buf) {
        rb->to_write = rb->bufs;

    } else {
        rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
    }

    r->read_event_handler = ngx_http_read_client_request_body_handler;

    return ngx_http_do_read_client_request_body(r);
}
static ngx_int_t
ngx_http_filter_cache_header_filter(ngx_http_request_t *r)
{
    ngx_http_filter_cache_ctx_t *ctx = NULL;
    ngx_http_filter_cache_conf_t *conf = NULL;
    time_t  now, valid;
    ngx_temp_file_t *tf;
    ngx_chain_t   out;
    ssize_t offset;
    ngx_list_part_t *part;
    ngx_table_elt_t *h;
    ngx_uint_t i;
    u_char *p;
    size_t len;
    ngx_pool_cleanup_t *cln = NULL;

    ngx_http_filter_cache_meta_t meta;

    if(r != r->main) {
        /*just skip as we got headers in main*/
        return ngx_http_next_header_filter(r);
    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_filter_cache_module);

    switch (ngx_http_test_predicates(r, conf->upstream.no_cache)) {
    case NGX_ERROR:
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, __FILE__" ngx_http_test_predicates returned an error for no_cache");
        return NGX_ERROR;
    case NGX_DECLINED:
        goto nocache;
    default: /* NGX_OK */
        break;
    }

    ctx = r->filter_cache;

    if(!ctx || (FILTER_DONOTCACHE == ctx->cacheable)) {
        goto nocache;
    }
    /* ngx_http_filter_cache_create(r); */

    if (ctx->cache && ctx->cache->file.fd != NGX_INVALID_FILE) {
        ngx_pool_run_cleanup_file(r->pool, ctx->cache->file.fd);
        ctx->cache->file.fd = NGX_INVALID_FILE;
    }

    ctx->cache->valid_sec = 0;

    now = ngx_time();

    valid = 0;
    valid = ngx_http_filter_cache_valid(conf->upstream.cache_valid,
                                      r->headers_out.status);
    if (valid) {
        ctx->cache->valid_sec = now + valid;
    } else {
        goto nocache;
    }

    tf =  ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));

    if (tf == NULL) {
        return NGX_ERROR;
    }

    tf->file.fd = NGX_INVALID_FILE;
    tf->file.log = r->connection->log;
    tf->path = conf->upstream.temp_path;
    tf->pool = r->pool;
    tf->persistent = 1;

    if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                             tf->persistent, tf->clean, tf->access)
        != NGX_OK) {
        return NGX_ERROR;
    }
    ctx->tf = tf;

    cln = ngx_pool_cleanup_add(r->pool, 0);
    if (cln == NULL) {
        return NGX_ERROR;
    }
    cln->handler = filter_cache_cleanup;
    cln->data = ctx;

    ctx->buffer.pos = ctx->buffer.start = ngx_palloc(r->pool, conf->upstream.buffer_size);
    ctx->buffer.end = ctx->buffer.start + conf->upstream.buffer_size;
    ctx->buffer.temporary = 1;
    ctx->buffer.memory = 1;
    ctx->buffer.last_buf = 1;

    ctx->buffer.pos += ctx->cache->header_start;

    ctx->cache->last_modified = r->headers_out.last_modified_time;
    ctx->cache->date = now;

    /* Headers */

    /* fill in the metadata*/
    meta.status = r->headers_out.status;

#if (NGX_HTTP_GZIP)
    meta.gzip_vary = r->gzip_vary; /* Note: there is still some wierdness to how gzip_vary works...*/
#endif

    meta.last_modified_time = r->headers_out.last_modified_time;

    ngx_memcpy((void *)(ctx->buffer.pos), (void *)(&meta), sizeof(ngx_http_filter_cache_meta_t) );
    ctx->buffer.pos += sizeof(ngx_http_filter_cache_meta_t);

    /* Headers taht aren't in teh table for some reason */

    /*Do we need to try to set it if it's not set???*/
    /* Content Type */
    if ( r->headers_out.content_type.data ) {
        p = memchr((void *)r->headers_out.content_type.data, ';', r->headers_out.content_type.len );
        if ( p ) {
            len = p - r->headers_out.content_type.data;
            ngx_cpystrn( ctx->buffer.pos, r->headers_out.content_type.data, len + 1);
            ctx->buffer.pos += len + 1;
        }
        else {
            ngx_cpystrn( ctx->buffer.pos, r->headers_out.content_type.data, r->headers_out.content_type.len + 1 );
            ctx->buffer.pos += r->headers_out.content_type.len + 1;
        }
    }
    else {
        *ctx->buffer.pos = (u_char)'\0';
        ctx->buffer.pos++;
    }

    /* Charset */
    if ( r->headers_out.charset.data ) {
        ngx_cpystrn( ctx->buffer.pos, r->headers_out.charset.data, r->headers_out.charset.len + 1 );
        ctx->buffer.pos += r->headers_out.charset.len + 1;
    }
    else {
        *ctx->buffer.pos = (u_char)'\0';
        ctx->buffer.pos++;
    }

    /* Content Encoding */
    if ( r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) {
        ngx_cpystrn( ctx->buffer.pos, r->headers_out.content_encoding->value.data, r->headers_out.content_encoding->value.len + 1 );
        ctx->buffer.pos += r->headers_out.content_encoding->value.len + 1;
    }
    else {
        *ctx->buffer.pos = (u_char)'\0';
        ctx->buffer.pos++;
    }

    /* Last-Modified */
    if(r->headers_out.last_modified_time && r->headers_out.last_modified && r->headers_out.last_modified->value.len) {
        ngx_cpystrn( ctx->buffer.pos, r->headers_out.last_modified->value.data, r->headers_out.last_modified->value.len + 1 );
        ctx->buffer.pos += r->headers_out.last_modified->value.len + 1;
    } else {
        *ctx->buffer.pos = (u_char)'\0';
        ctx->buffer.pos++;
    }


    /* XXX: is last-modified special???*/
    /* Everything From the Table */
    part = &r->headers_out.headers.part;
    h = part->elts;
    for (i=0; /* void */; i++) {
        if ( i >= part->nelts || !part->nelts ) {
            if ( part->next == NULL ) {
                ctx->cacheable = FILTER_CACHEABLE;
                break;
            }
            part = part->next;
            h = part->elts;
            i = 0;
        }

        /*need to be really sure this header is "valid"*/
        /* if(h[i].key.len && h[i].value.len && h[i].hash && h[i].lowcase_key) {*/

            /* if(!h[i].lowcase_key) { */
            /*     if((h[i].lowcase_key = ngx_pnalloc(r->pool, h->key.len +1)) == NULL) { */
            /*         continue; */
            /*     } */
            /*     ngx_strlow(h[i].lowcase_key, h[i].key.data, h[i].key.len); */
            /* } */

            /* if(!h[i].hash) { */
            /*     h[i].hash = ngx_hash_key_lc(h[i].key.data, h[i].key.len); */
            /* } */

            /* if (ngx_hash_find(&conf->upstream.hide_headers_hash, h[i].hash, */
            /*                   h[i].lowcase_key, h[i].key.len)) */
            /* { */
            /*     continue; */
            /* } */

        if(h[i].key.len && h[i].value.len) {
            if(find_string_in_array(&(h[i].key), conf->upstream.hide_headers)){
                continue;
            }
            if ( (ngx_uint_t)(h[i].key.len + h[i].value.len + 4) > (ngx_uint_t)(ctx->buffer.last - ctx->buffer.pos) ) {
                ctx->cacheable = FILTER_DONOTCACHE;
                break;
            }

            ngx_cpystrn( ctx->buffer.pos, h[i].key.data, h[i].key.len + 1 );
            ctx->buffer.pos += h[i].key.len + 1;

            ngx_cpystrn( ctx->buffer.pos, h[i].value.data, h[i].value.len + 1 );
            ctx->buffer.pos += h[i].value.len + 1;
        }

    }

    if(FILTER_CACHEABLE != ctx->cacheable) {
        goto nocache;
    }

    ctx->buffer.last = ctx->buffer.pos;

    ctx->cache->body_start = (u_short) (ctx->buffer.pos - ctx->buffer.start);
    ngx_http_filter_cache_set_header(r, ctx->buffer.start);
    ctx->cache->date = now;

    /*write to temp file*/
    ctx->buffer.pos =  ctx->buffer.start;
    out.buf = &ctx->buffer;
    out.next = NULL;
    offset = ngx_write_chain_to_temp_file(tf, &out);
    tf->offset += offset;


    r->main_filter_need_in_memory = 1;

    return ngx_http_next_header_filter(r);

nocache:

    if(ctx) {
        ctx->cacheable = FILTER_DONOTCACHE;
        /* if(ctx->cache) { */
            /* ngx_http_filter_cache_free(ctx->cache, ctx->tf); */
        /* } */

    }
    return ngx_http_next_header_filter(r);
}
static ngx_buf_t *
ngx_http_pngquant_quantize(ngx_http_request_t *r, ngx_http_pngquant_ctx_t *ctx)
{
    u_char                    *out;
    ngx_buf_t                 *b;
    ngx_pool_cleanup_t        *cln;
    ngx_http_pngquant_conf_t  *conf;
    gdImagePtr                 img;
    int                        size;

    ngx_int_t                  rc;
    ngx_temp_file_t           *tf;
    ssize_t                    n;
    ngx_ext_rename_file_t      ext;
    ngx_str_t                  dest;
    ngx_str_t                  value;


    img = gdImageCreateFromPngPtr(ctx->length, ctx->image);

    if (img == NULL) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
            "gdImageCreateFromPngPtr() failed");

        return NULL;
    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_pngquant_module);

    /*
     * gdImageTrueColorToPaletteSetMethod(img, GD_QUANT_LIQ, conf->speed);
     * gdImageTrueColorToPalette(img, conf->dither, conf->colors);
     */

    ngx_pngquant_gd_image(img, conf->dither, conf->colors, conf->speed);

    out = gdImagePngPtr(img, &size);

    gdImageDestroy(img);

    ngx_pfree(r->pool, ctx->image);

    if (out == NULL) {

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "gdImagePngPtr() failed");

        return NULL;
    }

    if (conf->store) {

        if(ngx_http_complex_value(r, conf->store, &value) != NGX_OK) {

            goto failed;
        }

        dest.len = value.len + 1;
        dest.data = ngx_pnalloc(r->pool, dest.len);

        if (dest.data == NULL) {

            goto failed;
        }

        ngx_memzero(dest.data, dest.len);
        ngx_memcpy(dest.data, value.data, value.len);

        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "pngquant_store (%s)", dest.data);

        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));

        if (tf == NULL) {

            goto failed;
        }

        tf->file.fd = NGX_INVALID_FILE;
        tf->file.log = r->connection->log;
        tf->path = conf->temp_path;
        tf->pool = r->pool;
        tf->persistent = 1;
        rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool, tf->persistent,
                                  tf->clean, tf->access);

        if (rc != NGX_OK) {

            goto failed;
        }

        n = ngx_write_fd(tf->file.fd, out, size);

        if (n == NGX_FILE_ERROR) {

            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                          ngx_write_fd_n " \"%s\" failed", tf->file.name.data);

            goto failed;
        }

        if ((int) n != size) {

            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                          ngx_write_fd_n " has written only %z of %uz bytes",
                          n, size);

            goto failed;
        }

        ext.access = conf->store_access;
        ext.path_access = conf->store_access;
        ext.time = -1;
        ext.create_path = 1;
        ext.delete_file = 1;
        ext.log = r->connection->log;

        rc = ngx_ext_rename_file(&tf->file.name, &dest, &ext);

        if (rc != NGX_OK) {

            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "ngx_ext_rename_file() failed");

            goto failed;
        }
    }

    cln = ngx_pool_cleanup_add(r->pool, 0);

    if (cln == NULL) {

        goto failed;
    }

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));

    if (b == NULL) {

        goto failed;
    }

    cln->handler = ngx_http_pngquant_cleanup;
    cln->data = out;

    b->pos = out;
    b->last = out + size;
    b->memory = 1;
    b->last_buf = 1;

    ngx_http_pngquant_length(r, b);

#if defined(nginx_version) && (nginx_version >= 1007003)
    ngx_http_weak_etag(r);
#endif

    return b;

failed:

    gdFree(out);

    return NULL;
}
static void 
ngx_http_graphicsmagick_command_handler(ngx_http_request_t *r)
{
	ngx_str_t				 *source;
	ngx_str_t				 *dest;
	ngx_str_t				 *ai;
	ngx_str_t				 *cmd;
	ngx_str_t                *uri;
	ngx_array_t				 *tokens;
	ngx_int_t				  rc;
	ngx_uint_t				  i;
	ngx_log_t				 *log;
	ngx_buf_t				 *b;
	ngx_chain_t				  out;
	
	ngx_fd_t				  fd;
	ngx_open_file_info_t	  of;
	ngx_http_core_loc_conf_t *clcf;
	
	size_t					  argc;
	char					**argv;
	
	u_char					 *cp;
	u_char					 *last;
	size_t					  root;
	ngx_temp_file_t			 *tf;
	
	unsigned int		  status;
	
	
	log = r->connection->log;
	
	tokens = ngx_array_create(r->pool, 10, sizeof(ngx_str_t));
	if (tokens == NULL) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	
	ai = ngx_array_push(tokens);
	if (ai == NULL) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	ai->data = (u_char *) "convert";
	ai->len = 7;
	
	// get command from HTTP headers or queryString
	cmd = ngx_http_graphicsmagick_get_command(r);
	
	if (cmd == NULL) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	
	ngx_log_error(NGX_LOG_ERR, log, 0, "graphicsmagick convert command: \"%s\"", cmd->data);
	
	//ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
	//			   "graphicsmagick convert command: \"%s\"", cmd->data);
	
	clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
	
	if (r->method & NGX_HTTP_POST) {
		source = dest = &r->request_body->temp_file->file.name;
	} else {
		uri = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
		source = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
		
		cp = cmd->data;
		while (cp < cmd->data + cmd->len) {
			if (*cp == ' ') {
				uri->data = cmd->data;
				uri->len = cp - cmd->data;
				
				cmd->data = cp + 1;
				cmd->len = cmd->len - uri->len - 1;
				break;
			}
			cp++;
		}
		
		if (uri->len == 0) {
			ngx_http_graphicsmagick_server_error(r);
			return;
		}
		
		last = ngx_http_graphicsmagickd_map_uri_to_path(r, uri, source, &root, 0);
		
		if (last == NULL) {
			ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
			return;
		}
		source->len = last - source->data;
		
		tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
		if (tf == NULL) {
			ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
			return;
		}

		tf->file.fd = NGX_INVALID_FILE;
		tf->file.log = r->connection->log;
		tf->path = clcf->client_body_temp_path;
		tf->pool = r->pool;
		tf->log_level = r->request_body_file_log_level;
		tf->persistent = r->request_body_in_persistent_file;
		tf->clean = 1;

		if (r->request_body_file_group_access) {
			tf->access = 0660;
		}

		if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
								 tf->persistent, tf->clean, tf->access)
			!= NGX_OK)
		{
			ngx_http_graphicsmagick_server_error(r);
			return;
		}
		
		dest = &tf->file.name;
	}
	
	// push source file name into tokens
	ai = ngx_array_push(tokens);
	if (ai == NULL) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	*ai = *source;

	// tokenize command, and push them into tokens array
	rc = ngx_http_graphicsmagick_tokenize_command(r, cmd, tokens);
	if (rc == NGX_ERROR) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	
	ai = ngx_array_push_n(tokens, 2);
	if (ai == NULL) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	ai->data = (u_char *) "-compress";
	ai->len = 9;
	ai++;
	ai->data = (u_char *) "JPEG";
	ai->len = 4;
	
	// push dest filename into tokens again, to save generated thumbnail into dest file
	ai = ngx_array_push(tokens);
	if (ai == NULL) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	*ai = *dest;
	
	// OK, prepare convert args
	argc = tokens->nelts;
	
	argv = ngx_palloc(r->pool, argc * sizeof(char*));
	if (argv == NULL) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	
	ai = tokens->elts;
	for (i = 0; i < argc; i++) {
		argv[i] = (char *) ai[i].data;
		ngx_log_error(NGX_LOG_ERR, log, 0, "current[%d]: %s", i, argv[i]);
	}
	
	ngx_array_destroy(tokens);
	
	// DO graphicsmagick converting
	status = ngx_http_graphicsmagick_convert(argv, argc);
	
	if (status == 0) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	
	// Done, write response

	of.test_dir = 0;
	//of.retest = clcf->open_file_cache_retest;
	of.errors = clcf->open_file_cache_errors;
	of.events = clcf->open_file_cache_events;

	rc = ngx_open_cached_file(clcf->open_file_cache, dest, &of, r->pool);

	if (rc == NGX_ERROR) {
		ngx_log_error(NGX_LOG_ERR, log, of.err,
						  "failed to open file \"%s\"", dest->data);
		ngx_http_graphicsmagick_server_error(r);
		return;
	}

	fd = of.fd;
	
	log->action = "sending response to client";
	
	r->headers_out.status = NGX_HTTP_OK;
	r->headers_out.content_type.len = sizeof("image/jpeg") - 1;
	r->headers_out.content_type.data = (u_char *) "image/jpeg";
	
	r->headers_out.content_length_n = of.size;
	r->headers_out.last_modified_time = of.mtime;
	
	if (r != r->main && of.size == 0) {
		rc = ngx_http_send_header(r);
		ngx_http_finalize_request(r, rc);
		return;
	}
	
	b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
	if (b == NULL) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	
	b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
	if (b->file == NULL) {
		ngx_http_graphicsmagick_server_error(r);
		return;
	}
	
	rc = ngx_http_send_header(r);
	
	b->file_pos = 0;
	b->file_last = of.size;

	b->in_file = b->file_last ? 1: 0;
	b->last_buf = (r == r->main) ? 1: 0;
	b->last_in_chain = 1;

	b->file->fd = fd;
	b->file->name = *dest;
	b->file->log = log;

	out.buf = b;
	out.next = NULL;

	rc = ngx_http_output_filter(r, &out);
	ngx_http_finalize_request(r, rc);
	return;
}
ngx_int_t
ngx_http_read_client_request_body(ngx_http_request_t *r,
    ngx_http_client_body_handler_pt post_handler)
{
    size_t                     preread;
    ssize_t                    size;
    ngx_buf_t                 *b;
    ngx_chain_t               *cl, **next;
    ngx_temp_file_t           *tf;
    ngx_http_request_body_t   *rb;
    ngx_http_core_loc_conf_t  *clcf;
    
    //主请求引用+1
    r->main->count++;
    
    //请求体已经被读取或丢弃
    if (r->request_body || r->discard_body) {
        post_handler(r);
        return NGX_OK;
    }

    //检查是否有Expect: 100-continue头,该请求头表示客户端期望发送请求体,服务器回复“HTTP/1.1 100 Continue”允许客户端发送请求体
    if (ngx_http_test_expect(r) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    
    //分配ngx_http_request_body_t结构
    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
    if (rb == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    r->request_body = rb;
    
    //无请求体
    if (r->headers_in.content_length_n < 0) {
        post_handler(r);
        return NGX_OK;
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
    //请求体长度为0
    if (r->headers_in.content_length_n == 0) {

        if (r->request_body_in_file_only) {
            //创建一个空的临时文件
            tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
            if (tf == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            tf->file.fd = NGX_INVALID_FILE;
            tf->file.log = r->connection->log;
            tf->path = clcf->client_body_temp_path;
            tf->pool = r->pool;
            tf->warn = "a client request body is buffered to a temporary file";
            tf->log_level = r->request_body_file_log_level;
            tf->persistent = r->request_body_in_persistent_file;
            tf->clean = r->request_body_in_clean_file;

            if (r->request_body_file_group_access) {
                tf->access = 0660;
            }

            rb->temp_file = tf;

            if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                     tf->persistent, tf->clean, tf->access)
                != NGX_OK)
            {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
        }

        post_handler(r);

        return NGX_OK;
    }
    
    //有请求体
    rb->post_handler = post_handler;

    /*
     * set by ngx_pcalloc():
     *
     *     rb->bufs = NULL;
     *     rb->buf = NULL;
     *     rb->rest = 0;
     */

    preread = r->header_in->last - r->header_in->pos;
    
    //header_in中有未处理数据,表明预读了请求体
    if (preread) {

        /* there is the pre-read part of the request body */

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http client request body preread %uz", preread);
        
        //分配空间存储预读的请求体
        b = ngx_calloc_buf(r->pool);
        if (b == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        b->temporary = 1;
        b->start = r->header_in->pos;
        b->pos = r->header_in->pos;
        b->last = r->header_in->last;
        b->end = r->header_in->end;

        rb->bufs = ngx_alloc_chain_link(r->pool);
        if (rb->bufs == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        rb->bufs->buf = b;
        rb->bufs->next = NULL;

        rb->buf = b;
        
        //已预读了全部请求体
        if ((off_t) preread >= r->headers_in.content_length_n) {

            /* the whole request body was pre-read */

            r->header_in->pos += (size_t) r->headers_in.content_length_n;
            r->request_length += r->headers_in.content_length_n;
            b->last = r->header_in->pos;

            if (r->request_body_in_file_only) {
                if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }
            }

            post_handler(r);

            return NGX_OK;
        }

        /*
         * to not consider the body as pipelined request in
         * ngx_http_set_keepalive()
         */
        r->header_in->pos = r->header_in->last;

        r->request_length += preread;

        rb->rest = r->headers_in.content_length_n - preread;

        if (rb->rest <= (off_t) (b->end - b->last)) {

            /* the whole request body may be placed in r->header_in */
            
            rb->to_write = rb->bufs;

            r->read_event_handler = ngx_http_read_client_request_body_handler;

            return ngx_http_do_read_client_request_body(r);
        }

        next = &rb->bufs->next;

    } else {
        b = NULL;
        rb->rest = r->headers_in.content_length_n;
        next = &rb->bufs;
    }

    size = clcf->client_body_buffer_size;
    size += size >> 2;

    if (rb->rest < size) {
        size = (ssize_t) rb->rest;

        if (r->request_body_in_single_buf) {
            size += preread;
        }

    } else {
        size = clcf->client_body_buffer_size;

        /* disable copying buffer for r->request_body_in_single_buf */
        b = NULL;
    }

    rb->buf = ngx_create_temp_buf(r->pool, size);
    if (rb->buf == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    cl = ngx_alloc_chain_link(r->pool);
    if (cl == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    cl->buf = rb->buf;
    cl->next = NULL;

    if (b && r->request_body_in_single_buf) {
        size = b->last - b->pos;
        ngx_memcpy(rb->buf->pos, b->pos, size);
        rb->buf->last += size;

        next = &rb->bufs;
    }

    *next = cl;

    if (r->request_body_in_file_only || r->request_body_in_single_buf) {
        rb->to_write = rb->bufs;

    } else {
        rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
    }

    //读完请求体后,调用该回调函数
    r->read_event_handler = ngx_http_read_client_request_body_handler;

    return ngx_http_do_read_client_request_body(r);
}