sparte/sparte.go

160 lines
3.2 KiB
Go

package main
import (
"bufio"
"fmt"
"io/ioutil"
"net"
"net/url"
"os"
"strconv"
"strings"
)
// SpartanURL parses u and calls SpartanParsedURL with the parsed url
func SpartanURL(u, input string) bool {
parsed, err := url.Parse(u)
if err != nil {
fmt.Println("invalid url")
fmt.Println(err.Error())
return false
}
if parsed.Host == "" {
parsed, err = url.Parse("spartan://" + u)
// this is repeated, ugh!
if err != nil {
fmt.Println("invalid url")
fmt.Println(err.Error())
return false
}
}
if parsed.Scheme != "spartan" {
fmt.Println("Unsupported scheme")
return false
}
return SpartanParsedURL(parsed, input)
}
// SpartanParsedURL fetches u and displays the page
func SpartanParsedURL(u *url.URL, input string) bool {
host := u.Host
if u.Port() == "" {
host += ":300"
}
// Connect to server
conn, err := net.Dial("tcp", host)
if err != nil {
fmt.Println("unable to connect to " + u.Host)
fmt.Println(err.Error())
return false
}
defer conn.Close()
// Send request
path := u.Path
if u.Path == "" {
path = "/"
}
conn.Write([]byte(fmt.Sprintf("%s %s %d\r\n", u.Hostname(), path, len(input))))
conn.Write([]byte(input))
// Receive and parse response header
reader := bufio.NewReader(conn)
header, err := reader.ReadString(byte('\n'))
if err != nil {
fmt.Println("Error reading response header")
return false
}
// Parse header
statusParts := strings.SplitN(header, " ", 2)
status, err := strconv.Atoi(statusParts[0])
if err != nil {
fmt.Println("invalid status code:" + statusParts[0])
return false
}
meta := strings.Trim(statusParts[1], "\r\n")
// Handle status
switch status {
case 2:
// TODO: handle mime type
bodyBytes, err := ioutil.ReadAll(reader)
if err != nil {
fmt.Println("Error reading body")
return false
}
body := string(bodyBytes)
fmt.Print(body)
case 3:
SpartanURL("spartan://"+host+meta, "")
case 4:
fmt.Println("Error: " + meta)
case 5:
fmt.Println("Server error: " + meta)
}
return true
}
func Usage() {
fmt.Fprintln(os.Stderr, `
Usage: sparte [--help] [URL] [-i INPUT] [-f FILE]
--help usage
-i send INPUT to URL
-f send contents of FILE as input to URL (use - for reading stdin pipe)
`)
return
}
func main() {
args := os.Args[1:]
if len(args) == 0 {
Usage()
return
}
url := args[0]
input := ""
for i, arg := range args {
if arg == "--help" {
Usage()
return
}
if arg == "-i" || arg == "-f" {
if len(args) < i+2 || (i == 0 && len(args) < i+3) {
fmt.Println("input argument or URL missing")
return
}
url = args[i-1]
if arg == "-i" {
input = args[i+1]
break
}
// arg must be -f
file := args[i+1]
var inputBytes []byte
var err error
if file == "-" {
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) != 0 {
fmt.Println("nothing in stdin")
os.Exit(1)
}
inputBytes, err = ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintln(os.Stderr, "Error reading stdin")
os.Exit(1)
}
} else {
inputBytes, err = ioutil.ReadFile(args[i+1])
if err != nil {
fmt.Fprintln(os.Stderr, "Error reading file")
os.Exit(1)
}
}
input = string(inputBytes)
break
// ignoring other args if any
}
}
SpartanURL(url, input)
}