package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"time"
)

// namaVersiTLS menerjemahkan kode versi TLS menjadi nama yang mudah dibaca.
func namaVersiTLS(v uint16) string {
	switch v {
	case tls.VersionTLS12:
		return "TLS 1.2"
	case tls.VersionTLS13:
		return "TLS 1.3"
	default:
		return fmt.Sprintf("versi 0x%04x", v)
	}
}

func main() {
	// httptest membuat sertifikat self-signed otomatis khusus untuk demo lokal.
	ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "data rahasia dari server")
	}))
	ts.EnableHTTP2 = true
	ts.StartTLS()
	defer ts.Close()

	// Bagian 1: klien yang memercayai sertifikat server.
	klien := ts.Client()

	resp, err := klien.Get(ts.URL)
	if err != nil {
		fmt.Println("permintaan HTTPS gagal:", err)
		return
	}
	isi, _ := io.ReadAll(resp.Body)
	resp.Body.Close()

	fmt.Println("status respons HTTPS:", resp.Status)
	fmt.Println("protokol yang dinegosiasikan:", resp.Proto)
	fmt.Println("koneksi terenkripsi TLS:", resp.TLS != nil)
	fmt.Println("versi TLS:", namaVersiTLS(resp.TLS.Version))
	fmt.Println("isi respons:", strings.TrimSpace(string(isi)))

	// Bagian 2: klien tanpa CA tepercaya ditolak saat verifikasi sertifikat.
	klienPolos := &http.Client{Timeout: 2 * time.Second}
	_, err = klienPolos.Get(ts.URL)
	fmt.Println("klien tanpa CA tepercaya ditolak:", err != nil)
	var otoritas x509.UnknownAuthorityError
	fmt.Println("penyebab sertifikat tidak dikenal:", errors.As(err, &otoritas))

	// Bagian 3: klien yang dibatasi maksimum TLS 1.2.
	pool := x509.NewCertPool()
	pool.AddCert(ts.Certificate())

	klien12 := &http.Client{
		Timeout: 2 * time.Second,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				MaxVersion: tls.VersionTLS12,
				RootCAs:    pool,
			},
		},
	}

	resp2, err := klien12.Get(ts.URL)
	if err != nil {
		fmt.Println("permintaan TLS 1.2 gagal:", err)
		return
	}
	io.Copy(io.Discard, resp2.Body)
	resp2.Body.Close()

	fmt.Println("klien dibatasi TLS 1.2, status:", resp2.Status)
	fmt.Println("versi TLS hasil negosiasi:", namaVersiTLS(resp2.TLS.Version))

	// Bagian 4: bentuk ringkas httptest.NewTLSServer.
	tsRingkas := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "versi ringkas")
	}))
	defer tsRingkas.Close()

	resp3, err := tsRingkas.Client().Get(tsRingkas.URL)
	if err != nil {
		fmt.Println("permintaan ke NewTLSServer gagal:", err)
		return
	}
	io.Copy(io.Discard, resp3.Body)
	resp3.Body.Close()
	fmt.Println("NewTLSServer + client bawaan:", resp3.Status, "|", resp3.Proto, "|", namaVersiTLS(resp3.TLS.Version))
}
