content: package io.activej;
  
  import io.activej.dns.DnsClient;
  import io.activej.http.*;
  import io.activej.reactor.Reactor;
  import io.activej.reactor.nio.NioReactor;
  import io.activej.test.rules.ByteBufRule;
  import io.activej.test.rules.EventloopRule;
  import org.junit.Before;
  import org.junit.ClassRule;
  import org.junit.Test;
  
  import java.io.IOException;
  import java.nio.charset.Charset;
  import java.nio.charset.StandardCharsets;
  import java.time.Duration;
  import java.time.Instant;
  import java.time.LocalDate;
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.List;
  
  import static io.activej.http.HttpHeaderValue.*;
  import static io.activej.http.HttpHeaders.*;
  import static io.activej.http.HttpUtils.inetAddress;
  import static io.activej.promise.TestUtils.await;
  import static io.activej.test.TestUtils.getFreePort;
  import static java.time.ZoneOffset.UTC;
  import static java.util.stream.Collectors.toSet;
  import static org.junit.Assert.assertEquals;
  
  public final class HttpApiTest {
  	@ClassRule
  	public static final EventloopRule eventloopRule = new EventloopRule();
  
  	@ClassRule
  	public static final ByteBufRule byteBufRule = new ByteBufRule();
  
  	private HttpServer server;
  	private IHttpClient client;
  
  	private int port;
  
  	// request
  	private final List<AcceptMediaType> requestAcceptContentTypes = new ArrayList<>();
  	private final List<AcceptCharset> requestAcceptCharsets = new ArrayList<>();
  	private final Instant requestDate = createDate(1999, 1, 1);
  	private final Instant dateIMS = createDate(2011, 3, 4);
  	private final Instant dateIUMS = createDate(2012, 5, 6);
  	private final MediaType requestMime = MediaTypes.ANY_TEXT;
  	private final ContentType requestContentType = ContentType.of(requestMime);
  	private final List<HttpCookie> requestCookies = new ArrayList<>();
  
  	// response
  	private final Instant responseDate = createDate(2000, 11, 17);
  	private final Instant expiresDate = createDate(2011, 2, 22);
  	private final Instant lastModified = createDate(2099, 11, 13);
  	private final MediaType responseMime = MediaType.of("font/woff2");
  	private final Charset responseCharset = StandardCharsets.UTF_16LE;
  	private final ContentType responseContentType = ContentType.of(responseMime, responseCharset);
  	private final List<HttpCookie> responseCookies = new ArrayList<>();
  	private final int age = 10_000;
  
  	@Before
  	public void setUp() {
  		port = getFreePort();
  		NioReactor reactor = Reactor.getCurrentReactor();
  		server = HttpServer.builder(reactor,
  				request -> {
  					testRequest(request);
  					return createResponse().toPromise();
  				})
  			.withListenPort(port)
  			.build();
  
  		DnsClient dnsClient = DnsClient.create(reactor, inetAddress("8.8.8.8"));
  		client = HttpClient.create(reactor, dnsClient);
  
  		// setup request and response data
  		requestAcceptContentTypes.add(AcceptMediaType.of(MediaTypes.ANY_AUDIO, 90));
  		requestAcceptContentTypes.add(AcceptMediaType.of(MediaTypes.ANY));
  		requestAcceptContentTypes.add(AcceptMediaType.of(MediaTypes.ATOM));
  		requestAcceptContentTypes.add(AcceptMediaType.of(MediaType.of("hello/world")));
  
  		requestAcceptCharsets.add(AcceptCharset.of(StandardCharsets.UTF_8));
  		requestAcceptCharsets.add(AcceptCharset.of(Charset.forName("ISO-8859-5"), 10));
  		requestAcceptCharsets.add(AcceptCharset.of(Charset.forName("ISO-8859-2"), 10));
  		requestAcceptCharsets.add(AcceptCharset.of(Charset.forName("ISO-8859-3"), 10));
  
  		HttpCookie cookie2 = HttpCookie.of("name1", "value1");
  		requestCookies.add(cookie2);
  		HttpCookie cookie3 = HttpCookie.of("name3");
  		requestCookies.add(cookie3);
  
  		HttpCookie cookie1 = HttpCookie.of("name2", "value2");
  		cookie1.setMaxAge(Duration.ofSeconds(123));
  		cookie1.setExpirationDate(Instant.now());
  		cookie1.setPath("/");
  		responseCookies.add(cookie1);
  	}
  
  	@Test
  	public void test() throws IOException {
  		server.listen();
  		await(client.request(createRequest())
  			.whenResult(this::testResponse)
  			.whenComplete(server::close));
  	}
  
  	private HttpResponse createResponse() {
  		return HttpResponse.ok200()
  			.withHeader(DATE, ofInstant(responseDate))
  			.withHeader(EXPIRES, ofInstant(expiresDate))
  			.withHeader(CONTENT_TYPE, ofContentType(responseContentType))
  			.withHeader(LAST_MODIFIED, ofInstant(lastModified))
  			.withHeader(AGE, ofDecimal(age))
  			.withCookies(responseCookies)
  			.build();
  	}
  
  	private HttpRequest createRequest() {
  		return HttpRequest.get("http://127.0.0.1:" + port)
  			.withHeader(ACCEPT, ofAcceptMediaTypes(requestAcceptContentTypes))
  			.withHeader(ACCEPT_CHARSET, ofAcceptCharsets(requestAcceptCharsets))
  			.withHeader(DATE, ofInstant(requestDate))
  			.withHeader(CONTENT_TYPE, ofContentType(requestContentType))
  			.withHeader(IF_MODIFIED_SINCE, ofInstant(dateIMS))
  			.withHeader(IF_UNMODIFIED_SINCE, ofInstant(dateIUMS))
  			.initialize(httpRequestBuilder -> requestCookies.forEach(httpRequestBuilder::withCookie))
  			.build();
  	}
  
  	@SuppressWarnings("ConstantConditions")
  	private void testResponse(HttpResponse response) {
  		assertEquals(responseContentType, response.getHeader(CONTENT_TYPE, HttpHeaderValue::toContentType));
  		assertEquals(responseCookies, new ArrayList<>(response.getCookies().values()));
  		assertEquals(responseDate, response.getHeader(DATE, HttpHeaderValue::toInstant));
  		assertEquals(age, (int) response.getHeader(AGE, HttpHeaderValue::toPositiveInt));
  		assertEquals(expiresDate, response.getHeader(EXPIRES, HttpHeaderValue::toInstant));
  		assertEquals(lastModified, response.getHeader(LAST_MODIFIED, HttpHeaderValue::toInstant));
  	}
  
  	private void testRequest(HttpRequest request) {
  		assertEquals(requestAcceptContentTypes, request.getHeader(ACCEPT, HttpHeaderValue::toAcceptContentTypes));
  		assertEquals(requestAcceptCharsets, request.getHeader(ACCEPT_CHARSET, HttpHeaderValue::toAcceptCharsets));
  		assertEquals(requestDate, request.getHeader(DATE, HttpHeaderValue::toInstant));
  		assertEquals(dateIMS, request.getHeader(IF_MODIFIED_SINCE, HttpHeaderValue::toInstant));
  		assertEquals(dateIUMS, request.getHeader(IF_UNMODIFIED_SINCE, HttpHeaderValue::toInstant));
  		assertEquals(requestContentType, request.getHeader(CONTENT_TYPE, HttpHeaderValue::toContentType));
  		assertEquals(requestCookies.stream().map(HttpCookie::getValue).collect(toSet()), new HashSet<>(request.getCookies().values()));
  	}
  
  	private static Instant createDate(int year, int month, int day) {
  		return LocalDate.of(year, month, day).atStartOfDay().toInstant(UTC);
  	}
  }
  