135 lines
3.1 KiB
Go
135 lines
3.1 KiB
Go
//go:build linux || freebsd
|
|
// +build linux freebsd
|
|
|
|
package gemini
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"math/big"
|
|
"net"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"codeberg.org/petrbalvin/goget/internal/core"
|
|
)
|
|
|
|
type stringWriter struct{ sb *strings.Builder }
|
|
|
|
func (w *stringWriter) Write(p []byte) (int, error) { return w.sb.Write(p) }
|
|
|
|
func testTLSConfig() *tls.Config {
|
|
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
tmpl := &x509.Certificate{
|
|
SerialNumber: big.NewInt(1),
|
|
Subject: pkix.Name{CommonName: "localhost"},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().Add(time.Hour),
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
}
|
|
certDER, _ := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
|
|
return &tls.Config{
|
|
Certificates: []tls.Certificate{{
|
|
Certificate: [][]byte{certDER},
|
|
PrivateKey: key,
|
|
}},
|
|
}
|
|
}
|
|
|
|
func TestGeminiSuccess(t *testing.T) {
|
|
cfg := testTLSConfig()
|
|
listener, err := tls.Listen("tcp", "127.0.0.1:0", cfg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer listener.Close()
|
|
port := listener.Addr().(*net.TCPAddr).Port
|
|
|
|
go func() {
|
|
conn, _ := listener.Accept()
|
|
if conn == nil {
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
r := bufio.NewReader(conn)
|
|
r.ReadString('\n')
|
|
conn.Write([]byte("20 text/gemini\r\n# Hello\n\nContent.\n"))
|
|
}()
|
|
|
|
proto := NewProtocol()
|
|
proto.TLSInsecure = true
|
|
u, _ := url.Parse("gemini://127.0.0.1:" + strconv.Itoa(port) + "/")
|
|
var buf strings.Builder
|
|
_, err = proto.Download(context.Background(), &core.DownloadRequest{
|
|
URL: u,
|
|
Writer: &stringWriter{&buf},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(buf.String(), "Hello") {
|
|
t.Errorf("got %q", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestGeminiTemporaryFailure(t *testing.T) {
|
|
cfg := testTLSConfig()
|
|
listener, _ := tls.Listen("tcp", "127.0.0.1:0", cfg)
|
|
defer listener.Close()
|
|
port := listener.Addr().(*net.TCPAddr).Port
|
|
|
|
go func() {
|
|
conn, _ := listener.Accept()
|
|
if conn == nil {
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
r := bufio.NewReader(conn)
|
|
r.ReadString('\n')
|
|
conn.Write([]byte("40 overloaded\r\n"))
|
|
}()
|
|
|
|
proto := NewProtocol()
|
|
proto.TLSInsecure = true
|
|
u, _ := url.Parse("gemini://127.0.0.1:" + strconv.Itoa(port) + "/")
|
|
_, err := proto.Download(context.Background(), &core.DownloadRequest{URL: u})
|
|
if err == nil {
|
|
t.Error("expected temporary failure error")
|
|
}
|
|
}
|
|
|
|
func TestGeminiPermanentFailure(t *testing.T) {
|
|
cfg := testTLSConfig()
|
|
listener, _ := tls.Listen("tcp", "127.0.0.1:0", cfg)
|
|
defer listener.Close()
|
|
port := listener.Addr().(*net.TCPAddr).Port
|
|
|
|
go func() {
|
|
conn, _ := listener.Accept()
|
|
if conn == nil {
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
r := bufio.NewReader(conn)
|
|
r.ReadString('\n')
|
|
conn.Write([]byte("51 not found\r\n"))
|
|
}()
|
|
|
|
proto := NewProtocol()
|
|
proto.TLSInsecure = true
|
|
u, _ := url.Parse("gemini://127.0.0.1:" + strconv.Itoa(port) + "/")
|
|
_, err := proto.Download(context.Background(), &core.DownloadRequest{URL: u})
|
|
if err == nil {
|
|
t.Error("expected permanent failure error")
|
|
}
|
|
}
|