/*
 * Copyright (c) 2020-2026 Free Software Foundation, Inc.
 *
 * This file is part of Wget
 *
 * Wget is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Wget 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
 * along with Wget  If not, see <https://www.gnu.org/licenses/>.
 *
 *
 * Tests interruptions to responses received by wget and continuing afterwards
 */

#include <config.h>

#include <stdlib.h> // exit()
#include <string.h> // memset()
#include "libtest.h"

#define DUMMYDATA_SIZE 1024
#define INTERRUPT_AFTER_NBYTES 512
#define TIMESTAMP 1000000000

int main(void)
{
	static char data1[DUMMYDATA_SIZE + 1];
	memset(data1, '@', sizeof(data1) - 1);

	static char data2[DUMMYDATA_SIZE + 1];
	memset(data2, '-', sizeof(data2) - 1);

	static char data1_interrupted[INTERRUPT_AFTER_NBYTES + 1];
	memset(data1_interrupted, '@', sizeof(data1_interrupted) - 1);

	static char data12_merged[DUMMYDATA_SIZE + 1];
	memcpy(data12_merged, data1, INTERRUPT_AFTER_NBYTES);
	memcpy(data12_merged + INTERRUPT_AFTER_NBYTES, data2, INTERRUPT_AFTER_NBYTES);


	wget_test_url_t urls[]={
		{	.name = "/file1.bin",
			.code = "200 Dontcare",
			.body = data1,
			.headers = {
				"Content-Type: application/octet-stream",
			},
			.interrupt_response_mode = INTERRUPT_RESPONSE_DISABLED
		},
		{	.name = "/file2.bin",
			.code = "200 Dontcare",
			.body = data1,
			.headers = {
				"Content-Type: application/octet-stream",
			},
			.interrupt_response_mode = INTERRUPT_RESPONSE_DURING_BODY,
			.interrupt_response_after_nbytes = 0
		},
		{	.name = "/file3.bin",
			.code = "200 Dontcare",
			.body = data1,
			.headers = {
				"Content-Type: application/octet-stream",
			},
			.interrupt_response_mode = INTERRUPT_RESPONSE_DURING_BODY,
			.interrupt_response_after_nbytes = INTERRUPT_AFTER_NBYTES
		},
		{	.name = "/file4.bin",
			.code = "200 Dontcare",
			.modified = TIMESTAMP,
			.body = data1,
			.headers = {
				"Content-Type: application/octet-stream",
				"Last-Modified: Sun, 09 Sep 2001 01:46:40 GMT", // =1000000000
			},
			.interrupt_response_mode = INTERRUPT_RESPONSE_DURING_BODY,
			.interrupt_response_after_nbytes = INTERRUPT_AFTER_NBYTES
		}
	};

	wget_test_start_server(
		WGET_TEST_RESPONSE_URLS, &urls, countof(urls),
		WGET_TEST_FEATURE_MHD,
		WGET_TEST_SKIP_H2,
		0);

	// Test interrupt request disabled
	wget_test(
		WGET_TEST_OPTIONS, "",
		WGET_TEST_REQUEST_URL, "file1.bin",
		WGET_TEST_EXPECTED_ERROR_CODE, 0,
		WGET_TEST_EXPECTED_FILES, &(wget_test_file_t []) {
			{ "file1.bin", data1 },
			{	NULL } },
		0);

	// Test interrupt connection immediately
	wget_test(
		WGET_TEST_OPTIONS, "--tries=2 --waitretry=1",
		WGET_TEST_REQUEST_URL, "file2.bin",
		WGET_TEST_EXPECTED_ERROR_CODE, 7,
		WGET_TEST_EXPECTED_FILES, &(wget_test_file_t []) {
			{ "file2.bin", "" },
			{	NULL } },
		0);

	// test interrupting connection during headers transfer
	wget_test(
//		WGET_TEST_EXECUTABLE, "/usr/bin/wget",
		WGET_TEST_OPTIONS, "--tries=2 --waitretry=1",
		WGET_TEST_REQUEST_URL, "file3.bin",
		WGET_TEST_EXPECTED_ERROR_CODE, 7,
		WGET_TEST_EXPECTED_FILES, &(wget_test_file_t []) {
			{ "file3.bin", data1_interrupted },
			{	NULL } },
		0);

	// disable the interrupt response mode to test continue
	urls[2].interrupt_response_mode = INTERRUPT_RESPONSE_DISABLED;
	urls[2].body = data2;

	// test continue after interrupt
	// Wget1.x fails here
	wget_test(
//		WGET_TEST_EXECUTABLE, "/usr/bin/wget -d",
		WGET_TEST_OPTIONS, "-c --tries=2 --waitretry=1",
		WGET_TEST_REQUEST_URL, "file3.bin",
		WGET_TEST_EXPECTED_ERROR_CODE, 0,
		WGET_TEST_EXISTING_FILES, &(wget_test_file_t []) {
			{ "file3.bin", data1_interrupted },
			{	NULL } },
		WGET_TEST_EXPECTED_FILES, &(wget_test_file_t []) {
			{ "file3.bin", data12_merged },
			{	NULL } },
		0);

	// Testing interrupting responses while timestamping is active
	// Expect that timestamp of partially finished file will match server minus one due to interruption
	// Wget1.x leaves the partial file with a current timestamp (ignoring Last-Modified header)
	wget_test(
//		WGET_TEST_EXECUTABLE, "/usr/bin/wget",
		WGET_TEST_OPTIONS, "-N --tries=2 --waitretry=1",
		WGET_TEST_REQUEST_URL, "file4.bin",
		WGET_TEST_EXPECTED_ERROR_CODE, 7,
		WGET_TEST_EXPECTED_FILES, &(wget_test_file_t []) {
			{ "file4.bin", data1_interrupted, TIMESTAMP - 1 },
			{	NULL } },
		0);

	// disable the interrupt response mode to test continue
	urls[3].interrupt_response_mode = INTERRUPT_RESPONSE_DISABLED;
	urls[3].body = data2;

	// Testing continue interrupted response while timestamping is active
	// With resume (-c), If-Modified-Since is NOT sent (to avoid 304 responses).
	// Instead, only the Range header is sent, so the server returns 206 Partial Content.
	// The partial body is appended to the existing truncated file.
	wget_test(
//		WGET_TEST_EXECUTABLE, "/usr/bin/wget",
		WGET_TEST_OPTIONS, "-c -N",
		WGET_TEST_REQUEST_URL, "file4.bin",
		WGET_TEST_EXPECTED_ERROR_CODE, 0,
		WGET_TEST_EXISTING_FILES, &(wget_test_file_t []) {
			{ "file4.bin", data1_interrupted, TIMESTAMP },
			{	NULL } },
		WGET_TEST_EXPECTED_FILES, &(wget_test_file_t []) {
			{ "file4.bin", data12_merged, TIMESTAMP },
			{	NULL } },
		0);

	// Testing resume without timestamp (file timestamp differs by 1s)
	// With resume (-c), If-Modified-Since is NOT sent (to avoid 304 responses).
	// Instead, only the Range header is sent, so the server returns 206 Partial Content.
	// The partial body is appended to the existing truncated file.
	wget_test(
//		WGET_TEST_EXECUTABLE, "/usr/bin/wget",
		WGET_TEST_OPTIONS, "-c -N",
		WGET_TEST_REQUEST_URL, "file4.bin",
		WGET_TEST_EXPECTED_ERROR_CODE, 0,
		WGET_TEST_EXISTING_FILES, &(wget_test_file_t []) {
			{ "file4.bin", data1_interrupted, TIMESTAMP - 1 }, // 1s older than on server
			{	NULL } },
		WGET_TEST_EXPECTED_FILES, &(wget_test_file_t []) {
			{ "file4.bin", data12_merged, TIMESTAMP },
			{	NULL } },
		0);

	exit(EXIT_SUCCESS);
}
