Request: new request session flag to mark those files opened by FDT

This patch aims to fix a potential DDoS problem that can be caused
in the server quering repetitive non-existent resources.

When serving a static file, the core use Vhost FDT mechanism, but if
it sends a static error page it does a direct open(2). When closing
the resources for the same request it was just calling mk_vhost_close()
which did not clear properly the file descriptor.

This patch adds a new field on the struct session_request called 'fd_is_fdt',
which contains MK_TRUE or MK_FALSE depending of how fd_file was opened.

Thanks to Matthew Daley <mattd@bugfuzz.com> for report and troubleshoot this
problem.

Signed-off-by: Eduardo Silva <eduardo@monkey.io>
int mk_handler_write(int socket, struct client_session *cs)
{
    int final_status = 0;
    struct session_request *sr_node;
    struct mk_list *sr_list;

    if (mk_list_is_empty(&cs->request_list) == 0) {
        if (mk_request_parse(cs) != 0) {
            return -1;
        }
    }

    sr_list = &cs->request_list;

    sr_node = mk_list_entry_first(sr_list, struct session_request, _head);

    if (sr_node->bytes_to_send > 0) {
        /* Request with data to send by static file sender */
        final_status = mk_http_send_file(cs, sr_node);
    }
    else if (sr_node->bytes_to_send < 0) {
        final_status = mk_request_process(cs, sr_node);
    }

    /*
     * If we got an error, we don't want to parse
     * and send information for another pipelined request
     */
    if (final_status > 0) {
        return final_status;
    }
    else {
        /* STAGE_40, request has ended */
        mk_plugin_stage_run(MK_PLUGIN_STAGE_40, socket,
                            NULL, cs, sr_node);
        switch (final_status) {
        case EXIT_NORMAL:
        case EXIT_ERROR:
            if (sr_node->close_now == MK_TRUE) {
                return -1;
            }
            break;
        case EXIT_ABORT:
            return -1;
        }
    }

    /*
     * If we are here, is because all pipelined request were
     * processed successfully, let's return 0
     */
    return 0;
}

int mk_handler_write(int socket, struct client_session *cs)
{
    int final_status = 0;
    struct session_request *sr_node;
    struct mk_list *sr_list;

    if (mk_list_is_empty(&cs->request_list) == 0) {
        if (mk_request_parse(cs) != 0) {
            return -1;
        }
    }

    sr_list = &cs->request_list;

    sr_node = mk_list_entry_first(sr_list, struct session_request, _head);

    if (sr_node->bytes_to_send > 0) {
        /* Request with data to send by static file sender */
        final_status = mk_http_send_file(cs, sr_node);
    }
    else if (sr_node->bytes_to_send < 0) {
        final_status = mk_request_process(cs, sr_node);
    }

    /*
     * If we got an error, we don't want to parse
     * and send information for another pipelined request
     */
    if (final_status > 0) {
        return final_status;
    }
    else {
        /* STAGE_40, request has ended */
        mk_plugin_stage_run(MK_PLUGIN_STAGE_40, socket,
                            NULL, cs, sr_node);
        switch (final_status) {
        case EXIT_NORMAL:
        case EXIT_ERROR:
            if (sr_node->close_now == MK_TRUE) {
                return -1;
            }
            break;
        case EXIT_ABORT:
            return -1;
        }
    }

    /*
     * If we are here, is because all pipelined request were
     * processed successfully, let's return 0
     */
    return 0;
}
