#include <stdlib.h>
#include <gtest/gtest.h>

#include <http2_stream.h>
#include <http2_common.h>

#include <nghttp2/nghttp2.h>
#include <event2/buffer.h>
#include "test_http2_stream.h"
bool g_print_to_stderr = true;

/*********    stub function   ******************************/
const char * tfe_version()
{
	return NULL;
}

void tfe_stream_resume(const struct tfe_stream * stream)
{
	return;
}

void tfe_stream_suspend(const struct tfe_stream * stream, enum tfe_conn_dir by)
{
	return;
}

int tfe_stream_write(const struct tfe_stream * stream, enum tfe_conn_dir dir, const unsigned char * data, size_t size)
{
	return 0;
}

void tfe_stream_kill(const struct tfe_stream * stream)
{}

int tfe_stream_action_set_opt(const struct tfe_stream * stream, enum tfe_stream_action_opt type,
	void * value, size_t size)
{
	return 0;
}

void tfe_stream_detach(const struct tfe_stream * stream)
{
	return;
}
/*********    stub function   ******************************/

/* Magic Header : PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n */
static uint8_t kMagicHello[] = {
        0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
        0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
        0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
};

#define MAGIC_FRAME_LENGTH      24


#if 1
void UT_Parse_ReqHeaders(nghttp2_session *as_server)
{
	int stream_id = 1;

	struct tfe_h2_session *h2_session = (struct tfe_h2_session *)nghttp2_session_get_stream_user_data(as_server, stream_id);
	ASSERT_TRUE(h2_session != NULL);

	struct tfe_h2_half_private *half_private_req = h2_session->req;
	struct tfe_http_half *tfe_half = &half_private_req->half_public;
	struct tfe_http_req_spec *req_spec = &tfe_half->req_spec;

	/* PUBLIC FIELD */
	EXPECT_EQ(req_spec->method, NGHTTP2_METHOD_GET);
	EXPECT_STREQ(req_spec->uri, "/");
	EXPECT_STREQ(req_spec->url, "www.jd.com/");
	EXPECT_STREQ(req_spec->host, "www.jd.com");

	/* Header Field */
	struct http_field_name field_name;
	void * __iterator = NULL;
	const char * hdr_value = NULL;

	/*read User-Agent*/
	field_name.field_id = TFE_HTTP_USER_AGENT;
	hdr_value = tfe_http_field_read((const struct tfe_http_half*)tfe_half,
									&field_name);
	EXPECT_STREQ(hdr_value,
		"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36");

	/* method */
	hdr_value = tfe_http_field_iterate((const struct tfe_http_half*)tfe_half, &__iterator, &field_name);
	EXPECT_EQ(field_name.field_id, TFE_HTTP_UNKNOWN_FIELD);
	EXPECT_STREQ(hdr_value, "GET");

	/* Host */
	hdr_value = tfe_http_field_iterate((const struct tfe_http_half*)tfe_half, &__iterator, &field_name);
	EXPECT_EQ(field_name.field_id, TFE_HTTP_HOST);
	EXPECT_STREQ(hdr_value, "www.jd.com");

	/* scheme */
	hdr_value = tfe_http_field_iterate((const struct tfe_http_half*)tfe_half, &__iterator, &field_name);
	EXPECT_EQ(field_name.field_id, TFE_HTTP_UNKNOWN_FIELD);
	EXPECT_STREQ(hdr_value, "https");

	/*write **/
	field_name.field_id = TFE_HTTP_DATE;
	field_name.field_name = "date";
	int xret = tfe_http_field_write(tfe_half, &field_name, "20181018");
	EXPECT_EQ(xret, 0);

	/*read TFE_HTTP_DATE **/
	field_name.field_id = TFE_HTTP_HOST;
	hdr_value = tfe_http_field_read((const struct tfe_http_half*)tfe_half, &field_name);
	EXPECT_STREQ(hdr_value, "www.jd.com");

	/*rewrite date **/
	field_name.field_id = TFE_HTTP_DATE;
	field_name.field_name = "date";
	xret = tfe_http_field_write(tfe_half, &field_name, NULL); // delete field_name
	xret = tfe_http_field_write(tfe_half, &field_name, "201x101x");
	EXPECT_EQ(xret, 0);

    field_name.field_id = TFE_HTTP_DATE;
	hdr_value = tfe_http_field_read((const struct tfe_http_half*)tfe_half, &field_name);
	EXPECT_STREQ(hdr_value, "201x101x");
    xret = tfe_http_field_write(tfe_half, &field_name, NULL); // delete field_name

    return;
}

void UT_Parse_RespHeaders(nghttp2_session *as_client)
{
	int stream_id = 1;

	struct tfe_h2_session *h2_session = (struct tfe_h2_session *)nghttp2_session_get_stream_user_data(as_client, stream_id);
	ASSERT_TRUE(h2_session != NULL);

	struct tfe_h2_half_private *half_private_req = h2_session->resp;
	struct tfe_http_half *tfe_half = &half_private_req->half_public;
	struct tfe_http_resp_spec *resp_spec = &tfe_half->resp_spec;

	/* PUBLIC FIELD */
	EXPECT_EQ(resp_spec->resp_code, 200);
	EXPECT_STREQ(resp_spec->content_type, "text/html; charset=utf-8");
	EXPECT_STREQ(resp_spec->content_encoding, "gzip");

	/* Header Field */
	struct http_field_name field_name;
	void * __iterator = NULL;
	const char * hdr_value = NULL;

	/* resp code */
	hdr_value = tfe_http_field_iterate((const struct tfe_http_half*)tfe_half, &__iterator, &field_name);
	EXPECT_EQ(field_name.field_id, TFE_HTTP_UNKNOWN_FIELD);
	EXPECT_STREQ(hdr_value, "200");

	/*server*/
	hdr_value = tfe_http_field_iterate((const struct tfe_http_half*)tfe_half, &__iterator, &field_name);
	EXPECT_EQ(field_name.field_id, TFE_HTTP_SERVER);
	EXPECT_STREQ(hdr_value, "JDWS/2.0");

	/*Date*/
	hdr_value = tfe_http_field_iterate((const struct tfe_http_half*)tfe_half, &__iterator, &field_name);
	EXPECT_EQ(field_name.field_id, TFE_HTTP_DATE);
	EXPECT_STREQ(hdr_value, "Mon, 24 Sep 2018 13:16:55 GMT");

	field_name.field_id = TFE_HTTP_CONT_TYPE;
	hdr_value = tfe_http_field_read((const struct tfe_http_half*)tfe_half, &field_name);
	EXPECT_STREQ(hdr_value, "text/html; charset=utf-8");

	field_name.field_id = TFE_HTTP_CONT_ENCODING;
	hdr_value = tfe_http_field_read((const struct tfe_http_half*)tfe_half, &field_name);
	EXPECT_STREQ(hdr_value, "gzip");

	field_name.field_id = TFE_HTTP_DATE;
	hdr_value = tfe_http_field_read((const struct tfe_http_half*)tfe_half, &field_name);
	EXPECT_STREQ(hdr_value, "Mon, 24 Sep 2018 13:16:55 GMT");

	field_name.field_id = TFE_HTTP_EXPIRES;
	hdr_value = tfe_http_field_read((const struct tfe_http_half*)tfe_half, &field_name);
	EXPECT_STREQ(hdr_value, "Mon, 24 Sep 2018 13:17:22 GMT");

	return;
}

TEST(Http2StreamParser, GetFrameWithMagic)
{
	int readlen = 0;
	struct stream_tap_info_t *tapinfo = ALLOC(struct stream_tap_info_t, 1);
	assert(tapinfo);

	tapinfo->h2_stream_info = tfe_session_info_init();
	ASSERT_FALSE(tapinfo->h2_stream_info == NULL);

	struct tfe_h2_stream *h2_stream_info = tapinfo->h2_stream_info;

	/*Http2 protocol**/
	EXPECT_EQ(memcmp(magic_headers, kMagicHello, MAGIC_FRAME_LENGTH), 0);

	/*Recv data magic**/
	readlen = nghttp2_session_mem_recv(h2_stream_info->as_server, magic_headers, sizeof(magic_headers));
	EXPECT_GT(readlen, 0);

    sess_data_ctx_fini(tapinfo->h2_stream_info, NULL, 0);
    free(tapinfo->h2_stream_info);
    free(tapinfo);
}

TEST(Http2StreamParser, GetFrameWithHeader_01)
{
	int readlen = 0;
	struct stream_tap_info_t *tapinfo = ALLOC(struct stream_tap_info_t, 1);
	assert(tapinfo);

	tapinfo->h2_stream_info = tfe_session_info_init();
	ASSERT_FALSE(tapinfo->h2_stream_info == NULL);

	struct tfe_h2_stream *h2_stream_info = tapinfo->h2_stream_info;

	/*Recv data magic**/
	readlen = nghttp2_session_mem_recv(h2_stream_info->as_server, magic_headers, sizeof(magic_headers));
	EXPECT_GT(readlen, 0);

	/*Recv request Headers**/
	readlen = nghttp2_session_mem_recv(h2_stream_info->as_server, request_get_headers, sizeof(request_get_headers));
	EXPECT_GT(readlen, 0);

	UT_Parse_ReqHeaders(h2_stream_info->as_server);

    sess_data_ctx_fini(tapinfo->h2_stream_info, NULL, 0);
    free(tapinfo->h2_stream_info);
    free(tapinfo);
}

TEST(Http2StreamParser, GetFrameWithHeader_02)
{
	int readlen = 0;
	struct stream_tap_info_t *tapinfo = ALLOC(struct stream_tap_info_t, 1);
	assert(tapinfo);

	tapinfo->h2_stream_info = tfe_session_info_init();
	ASSERT_FALSE(tapinfo->h2_stream_info == NULL);

	struct tfe_h2_stream *h2_stream_info = tapinfo->h2_stream_info;

	/*Recv data magic**/
	readlen = nghttp2_session_mem_recv(h2_stream_info->as_server, magic_headers, sizeof(magic_headers));
	EXPECT_GT(readlen, 0);

	/*Recv request Headers**/
	readlen = nghttp2_session_mem_recv(h2_stream_info->as_server, request_get_headers, sizeof(request_get_headers));
	EXPECT_GT(readlen, 0);

	/*Send data message**/
	//enum tfe_stream_action stream_action = ACTION_FORWARD_DATA;
	//stream_action = nghttp2_client_mem_send(h2_stream_info);
	//EXPECT_EQ(stream_action, ACTION_DROP_DATA);

    sess_data_ctx_fini(tapinfo->h2_stream_info, NULL, 0);
    free(tapinfo->h2_stream_info);
    free(tapinfo);
}

TEST(Http2StreamParser, RespFrameWithHead_01)
{
	int readlen = 0;
	struct stream_tap_info_t *tapinfo = ALLOC(struct stream_tap_info_t, 1);
	assert(tapinfo);

	tapinfo->h2_stream_info = tfe_session_info_init();
	ASSERT_FALSE(tapinfo->h2_stream_info == NULL);

	struct tfe_h2_stream *h2_stream_info = tapinfo->h2_stream_info;

	/*Recv data magic**/
	readlen = nghttp2_session_mem_recv(h2_stream_info->as_server, magic_headers, sizeof(magic_headers));
	EXPECT_GT(readlen, 0);

	/*Recv request Headers**/
	readlen = nghttp2_session_mem_recv(h2_stream_info->as_server, request_get_headers, sizeof(request_get_headers));
	EXPECT_GT(readlen, 0);

	/*Send data head message**/
	//enum tfe_stream_action stream_action = ACTION_FORWARD_DATA;
	//stream_action = nghttp2_client_mem_send(h2_stream_info);
	//EXPECT_EQ(stream_action, ACTION_DROP_DATA);

	/*Recv response settings*/
	readlen = nghttp2_session_mem_recv(h2_stream_info->as_client, response_settings, sizeof(response_settings));
	EXPECT_GT(readlen, 0);

	/*Recv response Header*/
	readlen = nghttp2_session_mem_recv(h2_stream_info->as_client, response_header2, sizeof(response_header2));
	EXPECT_GT(readlen, 0);
	UT_Parse_RespHeaders(h2_stream_info->as_client);

    sess_data_ctx_fini(tapinfo->h2_stream_info, NULL, 0);
    free(tapinfo->h2_stream_info);
    free(tapinfo);
}
#endif

TEST(Http2StreamParser, FrameWithJdData)
{
	int len = 0;
	const uint8_t *data[] = {jd_data_00, jd_data_01, jd_data_02, jd_data_03,
				             jd_data_04, jd_data_05, jd_data_06, jd_data_07};

	size_t datalen[] = {sizeof(jd_data_00), sizeof(jd_data_01), sizeof(jd_data_02),
					 sizeof(jd_data_03), sizeof(jd_data_04), sizeof(jd_data_05),
					 sizeof(jd_data_06), sizeof(jd_data_07)};

	enum tfe_stream_action __attribute__((__unused__))action;

	struct stream_tap_info_t *tapinfo = ALLOC(struct stream_tap_info_t, 1);
	assert(tapinfo);

	tapinfo->h2_stream_info = tfe_session_info_init();
	ASSERT_FALSE(tapinfo->h2_stream_info == NULL);

	struct tfe_h2_stream *h2_stream_info = tapinfo->h2_stream_info;

	for (len = 0; len <= 7; len++){
		if (len > 0 && len < 4){
			printf("Proc down stream(%d)\n", len);
			action = detect_down_stream_protocol(h2_stream_info, NULL, 0, data[len], datalen[len]);
		}else{
			printf("Proc up stream(%d)\n", len);
			action = detect_up_stream_protocol(h2_stream_info, NULL, 0, data[len], datalen[len]);
		}
	}

    sess_data_ctx_fini(tapinfo->h2_stream_info, NULL, 0);
    free(tapinfo->h2_stream_info);
	free(tapinfo);
}

TEST(UI_TEST_INFLATE_GZIP, inflate_01)
{
	char *uncompr = NULL;
	int  ret = 0, uncompr_len = 0;
    struct z_stream_st *inflate = NULL;
    ret = inflate_read(ut_gip_01, sizeof(ut_gip_01), &uncompr, &uncompr_len, &inflate, 2);

	EXPECT_EQ(ret, Z_STREAM_END);
	EXPECT_EQ(uncompr_len, sizeof(ut_ungip_01));
	EXPECT_EQ(memcmp(uncompr, ut_ungip_01, sizeof(ut_ungip_01)), 0);
    inflate_finished(&inflate);

    free(uncompr);
	uncompr = NULL;
}

TEST(UI_TEST_INFLATE_GZIP, inflate_02)
{
    struct z_stream_st *inflate = NULL;
    int ret = 0, half = 0;
    int size = sizeof(ut_gip_01);
	half = size / 2;

	char *uncompr1 = NULL; int uncompr_len1 = 0;
	ret = inflate_read(ut_gip_01, half, &uncompr1, &uncompr_len1, &inflate, 2);
	EXPECT_EQ(ret, Z_OK);

	char *uncompr2 = NULL; int uncompr_len2 = 0;
	ret = inflate_read(ut_gip_01 + half, size - half, &uncompr2, &uncompr_len2, &inflate, 2);
	EXPECT_EQ(ret, Z_STREAM_END);

    char uncompr[1024] = {0};
	memcpy(uncompr, uncompr1, uncompr_len1);
    memcpy(uncompr + uncompr_len1, uncompr2, uncompr_len2);
	EXPECT_EQ(uncompr_len1 + uncompr_len2, sizeof(ut_ungip_01));
	EXPECT_EQ(memcmp(uncompr, ut_ungip_01, sizeof(ut_ungip_01)), 0);

    inflate_finished(&inflate);
    free(uncompr1);
	uncompr1 = NULL;
	free(uncompr2);
	uncompr2 = NULL;
}

TEST(UI_TEST_DEFLATE_GZIP, deflate_01)
{
	unsigned char *dest = NULL;
	int  ret = 0, dlen = 0;
	struct evbuffer * buf = evbuffer_new();
	int size = sizeof(ut_ungip_01);

    struct z_stream_st *deflate = NULL;
    ret = deflate_write(&deflate, ut_ungip_01, size, buf, 2, 1);
	EXPECT_EQ(ret, Z_OK);
    deflate_finished(&deflate);

    dest = evbuffer_pullup(buf, -1);
	dlen = evbuffer_get_length(buf);

	char *uncompr = NULL;
	int uncompr_len = 0;
    struct z_stream_st *inflate = NULL;
    ret = inflate_read(dest, dlen, &uncompr, &uncompr_len, &inflate, 2);
    EXPECT_EQ(ret, Z_STREAM_END);
	EXPECT_EQ(uncompr_len, sizeof(ut_ungip_01));
	EXPECT_EQ(memcmp(uncompr, ut_ungip_01, sizeof(ut_ungip_01)), 0);

    inflate_finished(&inflate);
    free(uncompr);
	uncompr = NULL;
	evbuffer_free(buf);
}

TEST(UI_TEST_DEFLATE_GZIP, deflate_02)
{
	unsigned char *dest = NULL;
	int ret = 0, half = 0, dlen = 0;

	int size = sizeof(ut_ungip_01);
	half = size / 2;

	struct evbuffer * buf = evbuffer_new();
    struct z_stream_st *deflate = NULL;
    /* First frag */
	ret = deflate_write(&deflate, ut_ungip_01, half, buf, 2, 0);
	EXPECT_EQ(ret, Z_OK);

	/* Last frag */
	ret = deflate_write(&deflate, ut_ungip_01 + half, size - half, buf, 2, 0);
	EXPECT_EQ(ret, Z_OK);

	/* End frag */
	ret = deflate_write(&deflate, NULL, 0, buf, 2, 1);
	EXPECT_EQ(ret, Z_OK);
    deflate_finished(&deflate);

    dest = evbuffer_pullup(buf, -1);
	dlen = evbuffer_get_length(buf);

	char *uncompr = NULL;
	int uncompr_len = 0;
    struct z_stream_st *inflate = NULL;
    ret = inflate_read(dest, dlen, &uncompr, &uncompr_len, &inflate, 2);
	EXPECT_EQ(ret, Z_STREAM_END);
	EXPECT_EQ(uncompr_len, sizeof(ut_ungip_01));
	EXPECT_EQ(memcmp(uncompr, ut_ungip_01, sizeof(ut_ungip_01)), 0);
    inflate_finished(&inflate);

    free(uncompr);
	uncompr = NULL;
	evbuffer_free(buf);
}

TEST(UI_TEST_DEFLATE_BR, deflate_01)
{
	unsigned char *dest = NULL;
	int  ret = 0, dlen = 0;
	struct evbuffer * buf = evbuffer_new();
	int size = sizeof(ut_ungip_01);

    struct z_stream_st *deflate = NULL;
    ret = deflate_write(&deflate, ut_ungip_01, size, buf, HTTP2_CONTENT_ENCODING_BR, 1);
	EXPECT_EQ(ret, Z_OK);
    deflate_finished(&deflate);

    dest = evbuffer_pullup(buf, -1);
	dlen = evbuffer_get_length(buf);

	char *uncompr = NULL;
	int uncompr_len = 0;
    struct z_stream_st *inflate = NULL;
    ret = inflate_read(dest, dlen, &uncompr, &uncompr_len, &inflate, HTTP2_CONTENT_ENCODING_BR);
	EXPECT_EQ(ret, Z_STREAM_END);
	EXPECT_EQ(uncompr_len, sizeof(ut_ungip_01));
	EXPECT_EQ(memcmp(uncompr, ut_ungip_01, sizeof(ut_ungip_01)), 0);
    inflate_finished(&inflate);

    free(uncompr);
	uncompr = NULL;
	evbuffer_free(buf);
}

TEST(UI_TEST_DEFLATE_BR, deflate_02)
{
	unsigned char *dest = NULL;
	int ret = 0, half = 0, dlen = 0;

	int size = sizeof(ut_ungip_01);
	half = size / 2;

	struct evbuffer * buf = evbuffer_new();
    struct z_stream_st *deflate = NULL;
    /* First frag */
	ret = deflate_write(&deflate, ut_ungip_01, half, buf, HTTP2_CONTENT_ENCODING_BR, 0);
	EXPECT_EQ(ret, Z_OK);

	/* Last frag */
	ret = deflate_write(&deflate, ut_ungip_01 + half, size - half, buf, HTTP2_CONTENT_ENCODING_BR, 0);
	EXPECT_EQ(ret, Z_OK);

	/* End frag */
	ret = deflate_write(&deflate, NULL, 0, buf, HTTP2_CONTENT_ENCODING_BR, 1);
	EXPECT_EQ(ret, Z_OK);
    deflate_finished(&deflate);

    dest = evbuffer_pullup(buf, -1);
	dlen = evbuffer_get_length(buf);

	char *uncompr = NULL;
	int uncompr_len = 0;
    struct z_stream_st *inflate = NULL;
    ret = inflate_read(dest, dlen, &uncompr, &uncompr_len, &inflate, HTTP2_CONTENT_ENCODING_BR);
	EXPECT_EQ(ret, Z_STREAM_END);
	EXPECT_EQ(uncompr_len, sizeof(ut_ungip_01));
	EXPECT_EQ(memcmp(uncompr, ut_ungip_01, sizeof(ut_ungip_01)), 0);
    inflate_finished(&inflate);

    free(uncompr);
	uncompr = NULL;
	evbuffer_free(buf);
}

int main(int argc, char ** argv)
{
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}
