static int upload_progress_handle_request(request_rec *r)
{
/**/up_log(APLOG_MARK, APLOG_DEBUG, 0, r->server, "upload_progress_handle_request()");

    DirConfig* dir = (DirConfig*)ap_get_module_config(r->per_dir_config, &upload_progress_module);
    ServerConfig *config = get_server_config(r->server);

    if (dir && dir->track_enabled > 0) {
        if (r->method_number == M_POST) {

            int param_error;
            const char* id = get_progress_id(r, &param_error);

            if (id) {
                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                             "Upload Progress: Upload id='%s' in trackable location: %s.", id, r->uri);
                CACHE_LOCK();
                clean_old_connections(r);
                upload_progress_node_t *node = find_node(r, id);
                if (node == NULL) {
                    node = insert_node(r, id);
                    if (node)
                        up_log(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                               "Upload Progress: Added upload with id='%s' to list.", id);
                } else if (node->done) {
                    fill_new_upload_node_data(node, r);
                    up_log(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                                 "Upload Progress: Reused existing node with id='%s'.", id);
                } else {
                    node = NULL;
                    ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
                                 "Upload Progress: Upload with id='%s' already exists, ignoring.", id);
                }

                if (node) {
                    upload_progress_context_t *ctx = (upload_progress_context_t*)apr_pcalloc(r->pool, sizeof(upload_progress_context_t));
                    ctx->node = node;
                    ctx->r = r;
                    apr_pool_cleanup_register(r->pool, ctx, upload_progress_cleanup, apr_pool_cleanup_null);
                    ap_add_input_filter("UPLOAD_PROGRESS", NULL, r, r->connection);
                }
                CACHE_UNLOCK();

            } else if (param_error < 0) {
                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                             "Upload Progress: Upload with invalid ID in trackable location: %s.", r->uri);
                /*
                return HTTP_BAD_REQUEST;
                return HTTP_NOT_FOUND;
                */

            } else {
                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                             "Upload Progress: Upload without ID in trackable location: %s.", r->uri);
            }
        }
    }

    return DECLINED;
}
static void clean_old_connections(request_rec *r) {
    upload_progress_node_t *prev = NULL;
    ServerConfig *config = get_server_config(r);
    CACHE_LOCK();
    upload_progress_node_t *node = fetch_first_node(config);
    while(node != NULL) {
        if(time(NULL) > node->expires && node->done == 1 && node->expires != -1) {
            /*clean*/
	    if(prev == NULL) {
		/* head */
		upload_progress_cache_t *cache = fetch_cache(config);
		cache->head = fetch_node(config, node->next);
		cache_free(config, node->key);
		cache_free(config, node);
		node = cache->head;
		continue;
	    } else {
		prev->next = node->next;
		cache_free(config, node->key);
		cache_free(config, node);
		node = prev;
		continue;
	    }
        }
	prev = node;
	node = fetch_node(config, node->next);
  }
  CACHE_UNLOCK();
}
static int upload_progress_handle_request(request_rec *r)
{
  DirConfig* dir = (DirConfig*)ap_get_module_config(r->per_dir_config, &upload_progress_module);
  ServerConfig *config = get_server_config(r);
  
  if(dir->track_enabled) {
    if(r->method_number == M_POST) {
      ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                         "Upload Progress: Upload in trackable location: %s.", r->uri);
      const char* id = get_progress_id(r);
      if(id != NULL) {
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                         "Upload Progress: Progress id found: %s.", id);
        CACHE_LOCK();
        upload_progress_node_t *node = find_node(r, id);
	CACHE_UNLOCK();
        if(node == NULL) {
          add_upload_to_track(r, id);
          ap_add_input_filter("UPLOAD_PROGRESS", NULL, r, r->connection);
	} else {
          ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                         "Upload Progress: Node with id '%s' already exists.", id);
	}
        return DECLINED;
      }
    }
  }
  
  return DECLINED;
}
static const char* upload_progress_shared_memory_size_cmd(cmd_parms *cmd,
                                                 void *dummy, const char *arg)
{
/**/up_log(APLOG_MARK, APLOG_DEBUG, 0, global_server, "upload_progress_shared_memory_size_cmd()");
    ServerConfig *config = get_server_config(cmd->server);

    long long int n = atoi(arg);

    if (n <= 0) {
        return "UploadProgressSharedMemorySize should be positive";
    }

    config->cache_bytes = (apr_size_t)n;

    return NULL;
}
int add_upload_to_track(request_rec* r, const char* key) {
  ServerConfig *config = get_server_config(r);
  upload_progress_node_t* node;
  
  clean_old_connections(r);

  CACHE_LOCK();
  node = find_node(r, key);
  if(node == NULL) {
    node = insert_node(r, key);
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                         "Upload Progress: Added upload with id=%s to list.", key);
    upload_progress_context_t *ctx = (upload_progress_context_t*)apr_pcalloc(r->pool, sizeof(upload_progress_context_t));
    ctx->node = node;
    ctx->r = r;
    CACHE_UNLOCK();
    apr_pool_cleanup_register(r->pool, ctx, upload_progress_cleanup, apr_pool_cleanup_null);
    return OK;
  }
  CACHE_UNLOCK();
  return OK;
}
static int track_upload_progress(ap_filter_t *f, apr_bucket_brigade *bb,
                           ap_input_mode_t mode, apr_read_type_e block,
                           apr_off_t readbytes)
{
    apr_status_t rv;
    upload_progress_node_t *node;
    ServerConfig* config = get_server_config(f->r);
    
     if ((rv = ap_get_brigade(f->next, bb, mode, block,
                                 readbytes)) != APR_SUCCESS) {
       return rv;
     }

    apr_off_t length;
    apr_brigade_length(bb, 1, &length);
    const char* id = get_progress_id(f->r);
    if(id == NULL) 
        return APR_SUCCESS;

    CACHE_LOCK();
    node = find_node(f->r, id);
    CACHE_UNLOCK();
    if(node == NULL) {
      return APR_SUCCESS;
    } else {
      CACHE_LOCK();
      node->received += (int)length;
      int upload_time = time(NULL) - node->started_at;
      if(upload_time > 0) {
        node->speed = (int)(node->received / upload_time);
      }
      CACHE_UNLOCK();
    }
    
    return APR_SUCCESS;
}