/* Copyright (C) 2021 Open Information Security Foundation
 *
 * You can copy, redistribute or modify this Program under the terms of
 * the GNU General Public License version 2 as published by the Free
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#ifndef __APP_LAYER_HTP_RANGE_H__
#define __APP_LAYER_HTP_RANGE_H__

#include "suricata-common.h"

#include "util-thash.h"
#include "rust.h"

void HttpRangeContainersInit(void);
void HttpRangeContainersDestroy(void);
uint32_t HttpRangeContainersTimeoutHash(const SCTime_t ts);

// linked list of ranges : buffer with offset
typedef struct HttpRangeContainerBuffer {
    /** red and black tree */
    RB_ENTRY(HttpRangeContainerBuffer) rb;
    /** allocated buffer */
    uint8_t *buffer;
    /** length of buffer */
    uint64_t buflen;
    /** the start of the range (offset relative to the absolute beginning of the file) */
    uint64_t start;
    /** offset of bytes written in buffer (relative to the start of the range) */
    uint64_t offset;
    /** number of gaped bytes */
    uint64_t gap;
} HttpRangeContainerBuffer;

int HttpRangeContainerBufferCompare(HttpRangeContainerBuffer *a, HttpRangeContainerBuffer *b);

RB_HEAD(HTTP_RANGES, HttpRangeContainerBuffer);
RB_PROTOTYPE(HTTP_RANGES, HttpRangeContainerBuffer, rb, HttpRangeContainerBufferCompare);

/** Item in hash table for a file in multiple ranges
 * Thread-safety is ensured with the thread-safe hash table cf THashData
 * The number of use is increased for each flow opening a new HttpRangeContainerBlock
 * until it closes this HttpRangeContainerBlock
 * The design goal is to have concurrency only on opening and closing a range request
 * and have a lock-free data structure belonging to one Flow
 * (see HttpRangeContainerBlock below)
 * for every append in between (we suppose we have many appends per range request)
 */
typedef struct HttpRangeContainerFile {
    /** key for hashtable */
    uint8_t *key;
    /** key length */
    uint32_t len;
    /** expire time in epoch */
    uint32_t expire;
    /** pointer to hashtable data, for locking and use count */
    THashData *hdata;
    /** total expected size of the file in ranges */
    uint64_t totalsize;
    /** size of the file after last sync */
    uint64_t lastsize;
    /** streaming buffer config for files below */
    const StreamingBufferConfig *sbcfg;
    /** file container, with only one file */
    FileContainer *files;
    /** red and black tree list of ranges which came out of order */
    struct HTTP_RANGES fragment_tree;
    /** file flags */
    uint16_t flags;
    /** error condition for this range. Its up to timeout handling to cleanup */
    bool error;
} HttpRangeContainerFile;

/** A structure representing a single range request :
 * either skipping, buffering, or appending
 * As this belongs to a flow, appending data to it is ensured to be thread-safe
 * Only one block per file has the pointer to the container
 */
typedef struct HttpRangeContainerBlock {
    /** state where we skip content */
    uint64_t toskip;
    /** current out of order range to write into */
    HttpRangeContainerBuffer *current;
    /** pointer to the main file container, where to directly append data */
    HttpRangeContainerFile *container;
    /** file container we are owning for now */
    FileContainer *files;
} HttpRangeContainerBlock;

int HttpRangeAppendData(const StreamingBufferConfig *sbcfg, HttpRangeContainerBlock *c,
        const uint8_t *data, uint32_t len);
File *HttpRangeClose(
        const StreamingBufferConfig *sbcfg, HttpRangeContainerBlock *c, uint16_t flags);

// HttpRangeContainerBlock but trouble with headers inclusion order
HttpRangeContainerBlock *HttpRangeContainerOpenFile(const unsigned char *key, uint32_t keylen,
        const Flow *f, const HTTPContentRange *cr, const StreamingBufferConfig *sbcfg,
        const unsigned char *name, uint16_t name_len, uint16_t flags, const unsigned char *data,
        uint32_t data_len);

void HttpRangeFreeBlock(HttpRangeContainerBlock *b);

#endif /* __APP_LAYER_HTP_RANGE_H__ */
