aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gemini.go82
-rw-r--r--gemini_test.go43
-rw-r--r--main.go69
3 files changed, 135 insertions, 59 deletions
diff --git a/gemini.go b/gemini.go
new file mode 100644
index 0000000..8594de4
--- /dev/null
+++ b/gemini.go
@@ -0,0 +1,82 @@
+package main
+
+import (
+ "crypto/tls"
+ "fmt"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+)
+
+const GeminiNetwork = "tcp"
+const GeminiPort = 1965
+const GeminiPrefix = "gemini://"
+
+func FormatRequest(request string) []byte {
+ if !strings.HasPrefix(request, GeminiPrefix) {
+ request = GeminiPrefix + request
+ }
+ request = request + "\r\n"
+ return []byte(request)
+}
+
+type GeminiConn struct {
+ host string
+ config *tls.Config
+}
+
+func NewGeminiConn(request string, port int) (*GeminiConn, error) {
+ if !strings.HasPrefix(request, "gemini://") {
+ request = "gemini://" + request
+ }
+
+ u, err := url.Parse(request)
+ if err != nil {
+ return nil, err
+ }
+
+ conf := tls.Config{
+ InsecureSkipVerify: true,
+ }
+
+ if strings.Contains(u.Host, ":") {
+ return &GeminiConn{u.Host, &conf}, nil
+ } else {
+ return &GeminiConn{u.Host + ":" + strconv.Itoa(port), &conf}, nil
+ }
+}
+
+func (c *GeminiConn) Get(request []byte, out *os.File) error {
+ conn, err := tls.Dial(GeminiNetwork, c.host, c.config)
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+
+ n, err := conn.Write(request)
+ if err != nil {
+ return err
+ }
+
+ buf := make([]byte, 1024)
+
+ for {
+ n, err = conn.Read(buf)
+ if err != nil {
+ return err
+ }
+ written, err := out.Write(buf[:n])
+ if written != n {
+ return fmt.Errorf("Got %d bytes, but only wrote %d bytes", n, written)
+ }
+ if err != nil {
+ return err
+ }
+
+ if n == 0 {
+ break
+ }
+ }
+ return nil
+}
diff --git a/gemini_test.go b/gemini_test.go
new file mode 100644
index 0000000..e029c04
--- /dev/null
+++ b/gemini_test.go
@@ -0,0 +1,43 @@
+package main
+
+import (
+ "bytes"
+ "strconv"
+ "testing"
+)
+
+func TestNewGeminiConn(t *testing.T) {
+ tables := []struct {
+ url string
+ port int
+ host string
+ }{
+ {"hostname.com", GeminiPort, "hostname.com:" + strconv.Itoa(GeminiPort)},
+ {"hostname.com", 1234, "hostname.com:1234"},
+ }
+
+ for _, table := range tables {
+ conn, err := NewGeminiConn(table.url, table.port)
+ if err != nil {
+ t.Fatalf("NewGeminiConn error: %s", err.Error())
+ }
+ if conn.host != table.host {
+ t.Fatalf("NewGeminiConn error: wrong hostname %s", conn.host)
+ }
+ }
+}
+
+func TestFormatRequest(t *testing.T) {
+ tables := []struct {
+ input string
+ output []byte
+ }{
+ {"hostname.com", []byte("gemini://hostname.com\r\n")},
+ }
+
+ for _, table := range tables {
+ if !bytes.Equal(FormatRequest(table.input), table.output) {
+ t.Fatalf("FormatRequest failed on: %s\n", table.input)
+ }
+ }
+}
diff --git a/main.go b/main.go
index 2667f2a..22d9fbf 100644
--- a/main.go
+++ b/main.go
@@ -1,29 +1,14 @@
package main
import (
- "crypto/tls"
"flag"
"fmt"
"log"
- "net/url"
"os"
- "strconv"
- "strings"
)
-var port = 1965
-
-func get_host(path string) (string, error) {
- if !strings.HasPrefix(path, "gemini://") {
- path = "gemini://" + path
- }
- url, err := url.Parse(path)
- if err != nil {
- return "", err
- }
-
- return url.Host + ":" + strconv.Itoa(port), nil
-}
+var host = ""
+var port = GeminiPort
func usage() {
fmt.Printf("Usage: %s [options] url\n", os.Args[0])
@@ -32,7 +17,10 @@ func usage() {
func main() {
flag.Usage = usage
+ flag.StringVar(&host, "h", host, "server host name")
+ flag.StringVar(&host, "host", host, "server host name")
flag.IntVar(&port, "p", port, "server port number")
+ flag.IntVar(&port, "port", port, "server port number")
flag.Parse()
if flag.NArg() != 1 {
fmt.Printf("Got %d arguments, expected 1\n", flag.NArg())
@@ -40,52 +28,15 @@ func main() {
os.Exit(1)
}
- url, err := get_host(flag.Arg(0))
- if err != nil {
- panic("Error parsing hostname: " + err.Error())
- }
- request := flag.Arg(0) + "\r\n"
- if !strings.HasPrefix(request, "gemini://") {
- request = "gemini://" + request
- }
-
- conf := tls.Config{
- InsecureSkipVerify: true,
- }
-
- log.Println("connecting to: " + url)
- log.Println("request: " + request)
-
- conn, err := tls.Dial("tcp", url, &conf)
-
+ conn, err := NewGeminiConn(flag.Arg(0), port)
if err != nil {
- panic("failed to connect: " + err.Error())
- } else {
- log.Println("connected to " + url)
+ log.Fatalf("Error parsing hostname: %s\n", err.Error())
}
- defer conn.Close()
+ log.Printf("connecting to: %s\n", conn.host)
- n, err := conn.Write([]byte(request))
+ err = conn.Get(FormatRequest(flag.Arg(0)), os.Stdout)
if err != nil {
- log.Println(n, err)
- return
+ log.Fatalf("Error: %s\n", err.Error())
}
-
- buf := make([]byte, 1024)
-
- for {
- n, err = conn.Read(buf)
- if err != nil {
- log.Println(n, err)
- return
- } else {
- println(string(buf[:n]))
- }
-
- if n == 0 {
- break
- }
- }
-
}