void uwsgi_close_request(struct wsgi_request *wsgi_req) {

	int waitpid_status;
	int tmp_id;
	uint64_t tmp_rt, rss = 0, vsz = 0;
#ifdef __linux__
	uint64_t uss = 0, pss = 0;
#endif

	// apply transformations
	if (wsgi_req->transformations) {
		if (uwsgi_apply_final_transformations(wsgi_req) == 0) {
			if (wsgi_req->transformed_chunk && wsgi_req->transformed_chunk_len > 0) {
				uwsgi_response_write_body_do(wsgi_req, wsgi_req->transformed_chunk, wsgi_req->transformed_chunk_len);
			}
		}
		uwsgi_free_transformations(wsgi_req);
	}

	// check if headers should be sent
	if (wsgi_req->headers) {
		if (!wsgi_req->headers_sent && !wsgi_req->headers_size && !wsgi_req->response_size) {
			uwsgi_response_write_headers_do(wsgi_req);
		}
		uwsgi_buffer_destroy(wsgi_req->headers);
	}

	uint64_t end_of_request = uwsgi_micros();
	wsgi_req->end_of_request = end_of_request;

	if (!wsgi_req->do_not_account_avg_rt) {
		tmp_rt = wsgi_req->end_of_request - wsgi_req->start_of_request;
		uwsgi.workers[uwsgi.mywid].running_time += tmp_rt;
		uwsgi.workers[uwsgi.mywid].avg_response_time = (uwsgi.workers[uwsgi.mywid].avg_response_time + tmp_rt) / 2;
	}

	// get memory usage
	if (uwsgi.logging_options.memory_report || uwsgi.force_get_memusage) {
		get_memusage(&rss, &vsz);
		uwsgi.workers[uwsgi.mywid].vsz_size = vsz;
		uwsgi.workers[uwsgi.mywid].rss_size = rss;
	}

#ifdef __linux__
	if (uwsgi.logging_options.memory_report || uwsgi.reload_on_uss || uwsgi.reload_on_pss) {
		get_memusage_extra(&uss, &pss);
		uwsgi.workers[uwsgi.mywid].uss_size = uss;
		uwsgi.workers[uwsgi.mywid].pss_size = pss;
	}
#endif

	if (!wsgi_req->do_not_account) {
		uwsgi.workers[0].requests++;
		uwsgi.workers[uwsgi.mywid].requests++;
		uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].requests++;
		uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].write_errors += wsgi_req->write_errors;
		uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].read_errors += wsgi_req->read_errors;
		// this is used for MAX_REQUESTS
		uwsgi.workers[uwsgi.mywid].delta_requests++;
	}

#ifdef UWSGI_ROUTING
	// apply final routes after accounting
	uwsgi_apply_final_routes(wsgi_req);
#endif

	// close socket and free parsers-allocated memory
	close_and_free_request(wsgi_req);

	// after_request hook
	if (!wsgi_req->is_raw && uwsgi.p[wsgi_req->uh->modifier1]->after_request)
		uwsgi.p[wsgi_req->uh->modifier1]->after_request(wsgi_req);

	// after_request custom hooks
	struct uwsgi_string_list *usl = NULL;
	uwsgi_foreach(usl, uwsgi.after_request_hooks) {
		void (*func) (struct wsgi_request *) = (void (*)(struct wsgi_request *)) usl->custom_ptr;
		func(wsgi_req);
	}

	if (uwsgi.threads > 1) {
		// now the thread can die...
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &tmp_id);
	}

	// leave harakiri mode
	if (uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].harakiri > 0) {
		set_harakiri(wsgi_req, 0);
	}

	// leave user harakiri mode
	if (uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].user_harakiri > 0) {
		set_user_harakiri(wsgi_req, 0);
	}

	if (!wsgi_req->do_not_account) {
		// this is racy in multithread mode
		if (wsgi_req->response_size > 0) {
			uwsgi.workers[uwsgi.mywid].tx += wsgi_req->response_size;
		}
		if (wsgi_req->headers_size > 0) {
			uwsgi.workers[uwsgi.mywid].tx += wsgi_req->headers_size;
		}
	}

	// defunct process reaper
	if (uwsgi.reaper == 1) {
		while (waitpid(WAIT_ANY, &waitpid_status, WNOHANG) > 0);
	}

	// free logvars
	struct uwsgi_logvar *lv = wsgi_req->logvars;
	while (lv) {
		struct uwsgi_logvar *ptr = lv;
		lv = lv->next;
		free(ptr);
	}

	// free additional headers
	struct uwsgi_string_list *ah = wsgi_req->additional_headers;
	while (ah) {
		struct uwsgi_string_list *ptr = ah;
		ah = ah->next;
		free(ptr->value);
		free(ptr);
	}
	// free remove headers
	ah = wsgi_req->remove_headers;
	while (ah) {
		struct uwsgi_string_list *ptr = ah;
		ah = ah->next;
		free(ptr->value);
		free(ptr);
	}

	// free chunked input
	if (wsgi_req->chunked_input_buf) {
		uwsgi_buffer_destroy(wsgi_req->chunked_input_buf);
	}

	if (wsgi_req->body_chunked_buf) {
		uwsgi_buffer_destroy(wsgi_req->body_chunked_buf);
	}

	// free websocket engine
	if (wsgi_req->websocket_buf) {
		uwsgi_buffer_destroy(wsgi_req->websocket_buf);
	}
	if (wsgi_req->websocket_send_buf) {
		uwsgi_buffer_destroy(wsgi_req->websocket_send_buf);
	}


	// reset request
	wsgi_req->uh->_pktsize = 0;
	tmp_id = wsgi_req->async_id;
	memset(wsgi_req, 0, sizeof(struct wsgi_request));
	// some plugins expected async_id to be defined before setup
	wsgi_req->async_id = tmp_id;
	// yes, this is pretty useless but we cannot ensure all of the plugin have the same behaviour
	uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].in_request = 0;

	if (uwsgi.max_requests > 0 && uwsgi.workers[uwsgi.mywid].delta_requests >= (uwsgi.max_requests + ((uwsgi.mywid-1) * uwsgi.max_requests_delta))
	    && (end_of_request - (uwsgi.workers[uwsgi.mywid].last_spawn * 1000000) >= uwsgi.min_worker_lifetime * 1000000)) {
		goodbye_cruel_world("max requests reached (%llu >= %llu)",
			(unsigned long long) uwsgi.workers[uwsgi.mywid].delta_requests,
			(unsigned long long) (uwsgi.max_requests + ((uwsgi.mywid-1) * uwsgi.max_requests_delta))
		);
	}

	if (uwsgi.reload_on_as && (rlim_t) vsz >= uwsgi.reload_on_as && (end_of_request - (uwsgi.workers[uwsgi.mywid].last_spawn * 1000000) >= uwsgi.min_worker_lifetime * 1000000)) {
		goodbye_cruel_world("reload-on-as limit reached (%llu >= %llu)",
			(unsigned long long) (rlim_t) vsz,
			(unsigned long long) uwsgi.reload_on_as
		);
	}

	if (uwsgi.reload_on_rss && (rlim_t) rss >= uwsgi.reload_on_rss && (end_of_request - (uwsgi.workers[uwsgi.mywid].last_spawn * 1000000) >= uwsgi.min_worker_lifetime * 1000000)) {
		goodbye_cruel_world("reload-on-rss limit reached (%llu >= %llu)",
			(unsigned long long) (rlim_t) rss,
			(unsigned long long) uwsgi.reload_on_rss
		);
	}

#ifdef __linux__
	if (uwsgi.reload_on_uss && (rlim_t) uss >= uwsgi.reload_on_uss && (end_of_request - (uwsgi.workers[uwsgi.mywid].last_spawn * 1000000) >= uwsgi.min_worker_lifetime * 1000000)) {
		goodbye_cruel_world("reload-on-uss limit reached (%llu >= %llu)",
			(unsigned long long) (rlim_t) uss,
			(unsigned long long) uwsgi.reload_on_uss
		);
	}

	if (uwsgi.reload_on_pss && (rlim_t) pss >= uwsgi.reload_on_pss && (end_of_request - (uwsgi.workers[uwsgi.mywid].last_spawn * 1000000) >= uwsgi.min_worker_lifetime * 1000000)) {
		goodbye_cruel_world("reload-on-pss limit reached (%llu >= %llu)",
			(unsigned long long) (rlim_t) pss,
			(unsigned long long) uwsgi.reload_on_pss
		);
	}
#endif

	// after the first request, if i am a vassal, signal Emperor about my loyalty
	if (uwsgi.has_emperor && !uwsgi.loyal) {
		uwsgi_log("announcing my loyalty to the Emperor...\n");
		char byte = 17;
		if (write(uwsgi.emperor_fd, &byte, 1) != 1) {
			uwsgi_error("write()");
		}
		uwsgi.loyal = 1;
	}

#ifdef __linux__
#ifdef MADV_MERGEABLE
	// run the ksm mapper
	if (uwsgi.linux_ksm > 0 && (uwsgi.workers[uwsgi.mywid].requests % uwsgi.linux_ksm) == 0) {
		uwsgi_linux_ksm_map();
	}
#endif
#endif

}