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) }