/*
 * Copyright 2025 Vsevolod Stakhov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef HTTP_H_
#define HTTP_H_

/**
 * @file http.h
 *
 * This is an interface for HTTP client and conn.
 * This code uses HTTP parser written by Joyent Inc based on nginx code.
 */

#include "config.h"
#include "http_context.h"
#include "fstring.h"
#include "ref.h"
#include "http_message.h"
#include "http_util.h"
#include "addr.h"

#include "contrib/libev/ev.h"

#ifdef __cplusplus
extern "C" {
#endif

enum rspamd_http_connection_type {
	RSPAMD_HTTP_SERVER,
	RSPAMD_HTTP_CLIENT
};

struct rspamd_http_header;
struct rspamd_http_message;
struct rspamd_http_connection_private;
struct rspamd_http_connection;
struct rspamd_http_connection_router;
struct rspamd_http_connection_entry;
struct rspamd_keepalive_hash_key;

struct rspamd_storage_shmem {
	char *shm_name;
	ref_entry_t ref;
};

/**
 * Legacy spamc protocol
 */
#define RSPAMD_HTTP_FLAG_SPAMC (1 << 0)
/**
 * Store body of the message in a shared memory segment
 */
#define RSPAMD_HTTP_FLAG_SHMEM (1 << 2)
/**
 * Store body of the message in an immutable shared memory segment
 */
#define RSPAMD_HTTP_FLAG_SHMEM_IMMUTABLE (1 << 3)
/**
 * Body has been set for a message
 */
#define RSPAMD_HTTP_FLAG_HAS_BODY (1 << 5)
/**
 * Do not verify server's certificate
 */
#define RSPAMD_HTTP_FLAG_SSL_NOVERIFY (1 << 6)
/**
 * Body has been set for a message
 */
#define RSPAMD_HTTP_FLAG_HAS_HOST_HEADER (1 << 7)
/**
 * Connection header has been set for a message
 */
#define RSPAMD_HTTP_FLAG_HAS_CONNECTION_HEADER (1 << 8)
/**
 * Message is intended for SSL connection
 */
#define RSPAMD_HTTP_FLAG_WANT_SSL (1 << 9)
/**
 * Options for HTTP connection
 */
enum rspamd_http_options {
	RSPAMD_HTTP_BODY_PARTIAL = 1,           /**< Call body handler on all body data portions */
	RSPAMD_HTTP_CLIENT_SIMPLE = 1u << 1,    /**< Read HTTP client reply automatically */
	RSPAMD_HTTP_CLIENT_ENCRYPTED = 1u << 2, /**< Encrypt data for client */
	RSPAMD_HTTP_CLIENT_SHARED = 1u << 3,    /**< Store reply in shared memory */
	RSPAMD_HTTP_REQUIRE_ENCRYPTION = 1u << 4,
	RSPAMD_HTTP_CLIENT_KEEP_ALIVE = 1u << 5,
	RSPAMD_HTTP_CLIENT_SSL = 1u << 6u,
};

typedef int (*rspamd_http_body_handler_t)(struct rspamd_http_connection *conn,
										  struct rspamd_http_message *msg,
										  const char *chunk,
										  gsize len);

typedef void (*rspamd_http_error_handler_t)(struct rspamd_http_connection *conn,
											GError *err);

typedef int (*rspamd_http_finish_handler_t)(struct rspamd_http_connection *conn,
											struct rspamd_http_message *msg);

/**
 * HTTP connection structure
 */
struct rspamd_http_connection {
	struct rspamd_http_connection_private *priv;
	rspamd_http_body_handler_t body_handler;
	rspamd_http_error_handler_t error_handler;
	rspamd_http_finish_handler_t finish_handler;
	gpointer ud;
	const char *log_tag;
	/* Used for keepalive */
	struct rspamd_keepalive_hash_key *keepalive_hash_key;
	gsize max_size;
	unsigned opts;
	enum rspamd_http_connection_type type;
	gboolean finished;
	int fd;
	int ref;
};

/**
 * Creates a new HTTP server connection from an opened FD returned by accept function
 * @param ctx
 * @param fd
 * @param body_handler
 * @param error_handler
 * @param finish_handler
 * @param opts
 * @return
 */
struct rspamd_http_connection *rspamd_http_connection_new_server(
	struct rspamd_http_context *ctx,
	int fd,
	rspamd_http_body_handler_t body_handler,
	rspamd_http_error_handler_t error_handler,
	rspamd_http_finish_handler_t finish_handler,
	unsigned opts);

/**
 * Creates or reuses a new keepalive client connection identified by hostname and inet_addr
 * @param ctx
 * @param body_handler
 * @param error_handler
 * @param finish_handler
 * @param addr
 * @param host
 * @return
 */
struct rspamd_http_connection *rspamd_http_connection_new_client_keepalive(
	struct rspamd_http_context *ctx,
	rspamd_http_body_handler_t body_handler,
	rspamd_http_error_handler_t error_handler,
	rspamd_http_finish_handler_t finish_handler,
	unsigned opts,
	rspamd_inet_addr_t *addr,
	const char *host);

/**
 * Creates an ordinary connection using the address specified (if proxy is not set)
 * @param ctx
 * @param body_handler
 * @param error_handler
 * @param finish_handler
 * @param opts
 * @param addr
 * @return
 */
struct rspamd_http_connection *rspamd_http_connection_new_client(
	struct rspamd_http_context *ctx,
	rspamd_http_body_handler_t body_handler,
	rspamd_http_error_handler_t error_handler,
	rspamd_http_finish_handler_t finish_handler,
	unsigned opts,
	rspamd_inet_addr_t *addr);

/**
 * Set per-request staged timeouts. Pass 0 to keep defaults.
 */
void rspamd_http_connection_set_timeouts(struct rspamd_http_connection *conn,
										 ev_tstamp connect_timeout,
										 ev_tstamp ssl_timeout,
										 ev_tstamp write_timeout,
										 ev_tstamp read_timeout);

/**
 * Set per-request keepalive tuning. Pass 0 to keep defaults/disable limit.
 */
void rspamd_http_connection_set_keepalive_tuning(struct rspamd_http_connection *conn,
												 double connection_ttl,
												 double idle_timeout,
												 unsigned int max_reuse);

/* Helpers used by keepalive context */
void rspamd_http_connection_keepalive_note_put(struct rspamd_http_connection *conn,
											   double now_ts);
void rspamd_http_connection_keepalive_note_reuse(struct rspamd_http_connection *conn);
gboolean rspamd_http_connection_keepalive_is_valid(struct rspamd_http_connection *conn,
												   double now_ts,
												   double default_ttl,
												   unsigned int default_max_reuse);
double rspamd_http_connection_keepalive_idle_timeout(struct rspamd_http_connection *conn,
													 double default_idle);

/**
 * Creates an ordinary client connection using ready file descriptor (ignores proxy)
 * @param ctx
 * @param body_handler
 * @param error_handler
 * @param finish_handler
 * @param opts
 * @param addr
 * @return
 */
struct rspamd_http_connection *rspamd_http_connection_new_client_socket(
	struct rspamd_http_context *ctx,
	rspamd_http_body_handler_t body_handler,
	rspamd_http_error_handler_t error_handler,
	rspamd_http_finish_handler_t finish_handler,
	unsigned opts,
	int fd);

/**
 * Set key pointed by an opaque pointer
 * @param conn connection structure
 * @param key opaque key structure
 */
void rspamd_http_connection_set_key(struct rspamd_http_connection *conn,
									struct rspamd_cryptobox_keypair *key);

/**
 * Transfer ownership on socket to an HTTP connection
 * @param conn
 */
void rspamd_http_connection_own_socket(struct rspamd_http_connection *conn);

/**
 * Get peer's public key
 * @param conn connection structure
 * @return pubkey structure or NULL
 */
const struct rspamd_cryptobox_pubkey *rspamd_http_connection_get_peer_key(
	struct rspamd_http_connection *conn);

/**
 * Returns TRUE if a connection is encrypted
 * @param conn
 * @return
 */
gboolean rspamd_http_connection_is_encrypted(struct rspamd_http_connection *conn);

/**
 * Handle a request using socket fd and user data ud
 * @param conn connection structure
 * @param ud opaque user data
 * @param fd fd to read/write
 */
void rspamd_http_connection_read_message(
	struct rspamd_http_connection *conn,
	gpointer ud,
	ev_tstamp timeout);

void rspamd_http_connection_read_message_shared(
	struct rspamd_http_connection *conn,
	gpointer ud,
	ev_tstamp timeout);

/**
 * Send reply using initialised connection
 * @param conn connection structure
 * @param msg HTTP message
 * @param ud opaque user data
 * @param fd fd to read/write
 */
gboolean rspamd_http_connection_write_message(
	struct rspamd_http_connection *conn,
	struct rspamd_http_message *msg,
	const char *host,
	const char *mime_type,
	gpointer ud,
	ev_tstamp timeout);

gboolean rspamd_http_connection_write_message_shared(
	struct rspamd_http_connection *conn,
	struct rspamd_http_message *msg,
	const char *host,
	const char *mime_type,
	gpointer ud,
	ev_tstamp timeout);

/**
 * Free connection structure
 * @param conn
 */
void rspamd_http_connection_free(struct rspamd_http_connection *conn);

/**
 * Increase refcount for a connection
 * @param conn
 * @return
 */
static inline struct rspamd_http_connection *
rspamd_http_connection_ref(struct rspamd_http_connection *conn)
{
	conn->ref++;
	return conn;
}

/**
 * Decrease a refcount for a connection and free it if refcount is equal to zero
 * @param conn
 */
static void
rspamd_http_connection_unref(struct rspamd_http_connection *conn)
{
	if (--conn->ref <= 0) {
		rspamd_http_connection_free(conn);
	}
}

/**
 * Reset connection for a new request
 * @param conn
 */
void rspamd_http_connection_reset(struct rspamd_http_connection *conn);

/**
 * Sets global maximum size for HTTP message being processed
 * @param sz
 */
void rspamd_http_connection_set_max_size(struct rspamd_http_connection *conn,
										 gsize sz);

void rspamd_http_connection_disable_encryption(struct rspamd_http_connection *conn);

/**
 * Accept SSL on an existing server connection. Performs the SSL handshake
 * asynchronously and then starts HTTP message reading.
 * @param conn server connection (already created via rspamd_http_connection_new_server)
 * @param ssl_ctx server SSL context (from rspamd_init_ssl_ctx_server)
 * @param ud opaque user data
 * @param timeout handshake/IO timeout
 * @return TRUE if handshake was initiated
 */
gboolean rspamd_http_connection_accept_ssl(struct rspamd_http_connection *conn,
										   gpointer ssl_ctx,
										   gpointer ud,
										   ev_tstamp timeout);

/**
 * Accept SSL on an existing server connection with shared memory body.
 * Same as rspamd_http_connection_accept_ssl but starts reading in shared mode.
 */
gboolean rspamd_http_connection_accept_ssl_shared(struct rspamd_http_connection *conn,
												  gpointer ssl_ctx,
												  gpointer ud,
												  ev_tstamp timeout);

#ifdef __cplusplus
}
#endif

#endif /* HTTP_H_ */
