diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | cnpreq.go | 132 |
2 files changed, 133 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0e0b57 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/cnp-req diff --git a/cnpreq.go b/cnpreq.go new file mode 100644 index 0000000..f0efb7c --- /dev/null +++ b/cnpreq.go @@ -0,0 +1,132 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "os" + "path" + "strings" + + "contnet.org/lib/cnp-go" +) + +var prog = path.Base(os.Args[0]) + +func checkerr(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "%s: error: %s\n", prog, err) + os.Exit(1) + } +} + +type requestPrinter struct { + out io.Writer + body io.Reader + n int64 + notFirst bool +} + +func (p *requestPrinter) Read(b []byte) (int, error) { + n, err := p.body.Read(b) + p.n += int64(n) + if n > 0 && !p.notFirst { + p.out.Write([]byte("> ")) + p.notFirst = true + } + p.out.Write(bytes.Replace(b[:n], []byte("\n"), []byte("\n> "), -1)) + if err == io.EOF && p.n > 0 { + p.out.Write([]byte("\n")) + } + return n, err +} + +func send(r *cnp.Request, printReq, printHdr, printBody bool) { + if printReq { + if r.Body != nil { + if _, ok := r.Header.Parameters["length"]; !ok { + r.ComputeLength() + } + + fmt.Fprint(os.Stderr, "> ") + r.Header.Write(os.Stderr) + r.Body = &requestPrinter{out: os.Stderr, body: io.LimitReader(r.Body, r.Length())} + } else { + fmt.Fprint(os.Stderr, "> ") + r.Header.Write(os.Stderr) + } + } + + resp, err := cnp.Send(r) + checkerr(err) + defer resp.Close() + + if printHdr { + checkerr(resp.Header.Write(os.Stdout)) + } + + if printBody { + l := resp.Length() + if l > 0 { + resp.Body = io.LimitReader(resp.Body, l) + } + _, err = io.Copy(os.Stdout, resp.Body) + checkerr(err) + } +} + +func usage(f io.Writer) { + fmt.Fprintf(f, "%s: usage: %s {URL|HOST/PATH} [PARAM=VALUE ...]\n", prog, prog) +} + +func main() { + printReq := flag.Bool("v", false, "print the request header and body") + printHdr := flag.Bool("i", false, "print the response header") + noBody := flag.Bool("I", false, "only print the response header without the body") + rawParams := flag.Bool("r", false, "treat parameters as raw CNP-encoded parameters") + readBody := flag.Bool("b", false, "read body data from stdin") + body := flag.String("B", "", "body data") + + flag.Parse() + + if flag.NArg() < 1 { + usage(os.Stderr) + os.Exit(2) + } + + var req *cnp.Request + var err error + if strings.HasPrefix(flag.Arg(0), "cnp:") || strings.HasPrefix(flag.Arg(0), "/") { + req, err = cnp.NewRequestURL(flag.Arg(0), nil) + } else { + ss := strings.SplitN(flag.Arg(0), "/", 2) + if len(ss) != 2 { + usage(os.Stderr) + os.Exit(2) + } + req, err = cnp.NewRequest(ss[0], "/"+ss[1], nil) + } + checkerr(err) + + req.Body = strings.NewReader(*body) + if *readBody { + req.Body = os.Stdin + } + + for _, arg := range flag.Args()[1:] { + ss := strings.SplitN(arg, "=", 2) + if len(ss) != 2 { + checkerr(fmt.Errorf("invalid parameter: %q", arg)) + } + if *rawParams { + ss[0], err = cnp.Unescape([]byte(ss[0])) + checkerr(err) + ss[1], err = cnp.Unescape([]byte(ss[1])) + checkerr(err) + } + req.SetParam(ss[0], ss[1]) + } + + send(req, *printReq, *printHdr || *noBody, !*noBody) +} |