123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- // Copyright 2015 bat authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License"): you may
- // not use this file except in compliance with the License. You may obtain
- // a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- // License for the specific language governing permissions and limitations
- // under the License.
- // Bat is a Go implemented CLI cURL-like tool for humans
- // bat [flags] [METHOD] URL [ITEM [ITEM]]
- package main
- import (
- "bytes"
- "crypto/tls"
- "encoding/json"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net/http"
- "net/url"
- "os"
- "path/filepath"
- "runtime"
- "strconv"
- "strings"
- )
- const (
- version = "0.1.0"
- printReqHeader uint8 = 1 << (iota - 1)
- printReqBody
- printRespHeader
- printRespBody
- )
- var (
- ver bool
- form bool
- pretty bool
- download bool
- insecureSSL bool
- auth string
- proxy string
- printV string
- printOption uint8
- body string
- bench bool
- benchN int
- benchC int
- isjson = flag.Bool("json", true, "Send the data as a JSON object")
- method = flag.String("method", "GET", "HTTP method")
- URL = flag.String("url", "", "HTTP request URL")
- jsonmap map[string]interface{}
- contentJsonRegex = `application/(.*)json`
- )
- func init() {
- flag.BoolVar(&ver, "v", false, "Print Version Number")
- flag.BoolVar(&ver, "version", false, "Print Version Number")
- flag.BoolVar(&pretty, "pretty", true, "Print Json Pretty Format")
- flag.BoolVar(&pretty, "p", true, "Print Json Pretty Format")
- flag.StringVar(&printV, "print", "A", "Print request and response")
- flag.BoolVar(&form, "form", false, "Submitting as a form")
- flag.BoolVar(&form, "f", false, "Submitting as a form")
- flag.BoolVar(&download, "download", false, "Download the url content as file")
- flag.BoolVar(&download, "d", false, "Download the url content as file")
- flag.BoolVar(&insecureSSL, "insecure", false, "Allow connections to SSL sites without certs")
- flag.BoolVar(&insecureSSL, "i", false, "Allow connections to SSL sites without certs")
- flag.StringVar(&auth, "auth", "", "HTTP authentication username:password, USER[:PASS]")
- flag.StringVar(&auth, "a", "", "HTTP authentication username:password, USER[:PASS]")
- flag.StringVar(&proxy, "proxy", "", "Proxy host and port, PROXY_URL")
- flag.BoolVar(&bench, "bench", false, "Sends bench requests to URL")
- flag.BoolVar(&bench, "b", false, "Sends bench requests to URL")
- flag.IntVar(&benchN, "b.N", 1000, "Number of requests to run")
- flag.IntVar(&benchC, "b.C", 100, "Number of requests to run concurrently.")
- flag.StringVar(&body, "body", "", "Raw data send as body")
- jsonmap = make(map[string]interface{})
- }
- func parsePrintOption(s string) {
- if strings.ContainsRune(s, 'A') {
- printOption = printReqHeader | printReqBody | printRespHeader | printRespBody
- return
- }
- if strings.ContainsRune(s, 'H') {
- printOption |= printReqHeader
- }
- if strings.ContainsRune(s, 'B') {
- printOption |= printReqBody
- }
- if strings.ContainsRune(s, 'h') {
- printOption |= printRespHeader
- }
- if strings.ContainsRune(s, 'b') {
- printOption |= printRespBody
- }
- return
- }
- func main() {
- log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds)
- flag.Usage = usage
- flag.Parse()
- args := flag.Args()
- if len(args) > 0 {
- args = filter(args)
- }
- if ver {
- fmt.Println("Version:", version)
- os.Exit(2)
- }
- parsePrintOption(printV)
- if printOption&printReqBody != printReqBody {
- defaultSetting.DumpBody = false
- }
- var stdin []byte
- if runtime.GOOS != "windows" {
- fi, err := os.Stdin.Stat()
- if err != nil {
- panic(err)
- }
- if fi.Size() != 0 {
- stdin, err = ioutil.ReadAll(os.Stdin)
- if err != nil {
- log.Fatal("Read from Stdin", err)
- }
- }
- }
- if *URL == "" {
- usage()
- }
- if strings.HasPrefix(*URL, ":") {
- urlb := []byte(*URL)
- if *URL == ":" {
- *URL = "http://localhost/"
- } else if len(*URL) > 1 && urlb[1] != '/' {
- *URL = "http://localhost" + *URL
- } else {
- *URL = "http://localhost" + string(urlb[1:])
- }
- }
- if !strings.HasPrefix(*URL, "http://") && !strings.HasPrefix(*URL, "https://") {
- *URL = "http://" + *URL
- }
- u, err := url.Parse(*URL)
- if err != nil {
- log.Fatal(err)
- }
- if auth != "" {
- userpass := strings.Split(auth, ":")
- if len(userpass) == 2 {
- u.User = url.UserPassword(userpass[0], userpass[1])
- } else {
- u.User = url.User(auth)
- }
- }
- *URL = u.String()
- httpreq := getHTTP(*method, *URL, args)
- if u.User != nil {
- password, _ := u.User.Password()
- httpreq.GetRequest().SetBasicAuth(u.User.Username(), password)
- }
- // Insecure SSL Support
- if insecureSSL {
- httpreq.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
- }
- // Proxy Support
- if proxy != "" {
- purl, err := url.Parse(proxy)
- if err != nil {
- log.Fatal("Proxy Url parse err", err)
- }
- httpreq.SetProxy(http.ProxyURL(purl))
- } else {
- eurl, err := http.ProxyFromEnvironment(httpreq.GetRequest())
- if err != nil {
- log.Fatal("Environment Proxy Url parse err", err)
- }
- httpreq.SetProxy(http.ProxyURL(eurl))
- }
- if body != "" {
- httpreq.Body(body)
- }
- if len(stdin) > 0 {
- var j interface{}
- d := json.NewDecoder(bytes.NewReader(stdin))
- d.UseNumber()
- err = d.Decode(&j)
- if err != nil {
- httpreq.Body(stdin)
- } else {
- httpreq.JsonBody(j)
- }
- }
- // AB bench
- if bench {
- httpreq.Debug(false)
- RunBench(httpreq)
- return
- }
- res, err := httpreq.Response()
- if err != nil {
- log.Fatalln("can't get the url", err)
- }
- // download file
- if download {
- var fl string
- if disposition := res.Header.Get("Content-Disposition"); disposition != "" {
- fls := strings.Split(disposition, ";")
- for _, f := range fls {
- f = strings.TrimSpace(f)
- if strings.HasPrefix(f, "filename=") {
- // Remove 'filename='
- f = strings.TrimLeft(f, "filename=")
- // Remove quotes and spaces from either end
- f = strings.TrimLeft(f, "\"' ")
- fl = strings.TrimRight(f, "\"' ")
- }
- }
- }
- if fl == "" {
- _, fl = filepath.Split(u.Path)
- }
- fd, err := os.OpenFile(fl, os.O_RDWR|os.O_CREATE, 0666)
- if err != nil {
- log.Fatal("can't create file", err)
- }
- if runtime.GOOS != "windows" {
- fmt.Println(Color(res.Proto, Magenta), Color(res.Status, Green))
- for k, v := range res.Header {
- fmt.Println(Color(k, Gray), ":", Color(strings.Join(v, " "), Cyan))
- }
- } else {
- fmt.Println(res.Proto, res.Status)
- for k, v := range res.Header {
- fmt.Println(k, ":", strings.Join(v, " "))
- }
- }
- fmt.Println("")
- contentLength := res.Header.Get("Content-Length")
- var total int64
- if contentLength != "" {
- total, _ = strconv.ParseInt(contentLength, 10, 64)
- }
- fmt.Printf("Downloading to \"%s\"\n", fl)
- pb := NewProgressBar(total)
- pb.Start()
- multiWriter := io.MultiWriter(fd, pb)
- _, err = io.Copy(multiWriter, res.Body)
- if err != nil {
- log.Fatal("Can't Write the body into file", err)
- }
- pb.Finish()
- defer fd.Close()
- defer res.Body.Close()
- return
- }
- if runtime.GOOS != "windows" {
- fi, err := os.Stdout.Stat()
- if err != nil {
- panic(err)
- }
- if fi.Mode()&os.ModeDevice == os.ModeDevice {
- var dumpHeader, dumpBody []byte
- dump := httpreq.DumpRequest()
- dps := strings.Split(string(dump), "\n")
- for i, line := range dps {
- if len(strings.Trim(line, "\r\n ")) == 0 {
- dumpHeader = []byte(strings.Join(dps[:i], "\n"))
- dumpBody = []byte(strings.Join(dps[i:], "\n"))
- break
- }
- }
- if printOption&printReqHeader == printReqHeader {
- fmt.Println(ColorfulRequest(string(dumpHeader)))
- fmt.Println("")
- }
- if printOption&printReqBody == printReqBody {
- if string(dumpBody) != "\r\n" {
- fmt.Println(string(dumpBody))
- fmt.Println("")
- }
- }
- if printOption&printRespHeader == printRespHeader {
- fmt.Println(Color(res.Proto, Magenta), Color(res.Status, Green))
- for k, v := range res.Header {
- fmt.Printf("%s: %s\n", Color(k, Gray), Color(strings.Join(v, " "), Cyan))
- }
- fmt.Println("")
- }
- if printOption&printRespBody == printRespBody {
- body := formatResponseBody(res, httpreq, pretty)
- fmt.Println(ColorfulResponse(body, res.Header.Get("Content-Type")))
- }
- } else {
- body := formatResponseBody(res, httpreq, pretty)
- _, err = os.Stdout.WriteString(body)
- if err != nil {
- log.Fatal(err)
- }
- }
- } else {
- var dumpHeader, dumpBody []byte
- dump := httpreq.DumpRequest()
- dps := strings.Split(string(dump), "\n")
- for i, line := range dps {
- if len(strings.Trim(line, "\r\n ")) == 0 {
- dumpHeader = []byte(strings.Join(dps[:i], "\n"))
- dumpBody = []byte(strings.Join(dps[i:], "\n"))
- break
- }
- }
- if printOption&printReqHeader == printReqHeader {
- fmt.Println(string(dumpHeader))
- fmt.Println("")
- }
- if printOption&printReqBody == printReqBody {
- fmt.Println(string(dumpBody))
- fmt.Println("")
- }
- if printOption&printRespHeader == printRespHeader {
- fmt.Println(res.Proto, res.Status)
- for k, v := range res.Header {
- fmt.Println(k, ":", strings.Join(v, " "))
- }
- fmt.Println("")
- }
- if printOption&printRespBody == printRespBody {
- body := formatResponseBody(res, httpreq, pretty)
- fmt.Println(body)
- }
- }
- }
- var usageinfo string = `bat is a Go implemented CLI cURL-like tool for humans.
- Usage:
- bat [flags] [METHOD] URL [ITEM [ITEM]]
- flags:
- -a, -auth=USER[:PASS] Pass a username:password pair as the argument
- -b, -bench=false Sends bench requests to URL
- -b.N=1000 Number of requests to run
- -b.C=100 Number of requests to run concurrently
- -body="" Send RAW data as body
- -f, -form=false Submitting the data as a form
- -j, -json=true Send the data in a JSON object
- -p, -pretty=true Print Json Pretty Format
- -i, -insecure=false Allow connections to SSL sites without certs
- -proxy=PROXY_URL Proxy with host and port
- -print="A" String specifying what the output should contain, default will print all information
- "H" request headers
- "B" request body
- "h" response headers
- "b" response body
- -v, -version=true Show Version Number
- METHOD:
- bat defaults to either GET (if there is no request data) or POST (with request data).
- URL:
- The only information needed to perform a request is a URL. The default scheme is http://,
- which can be omitted from the argument; example.org works just fine.
- ITEM:
- Can be any of:
- Query string key=value
- Header key:value
- Post data key=value
- File upload key@/path/file
- Example:
- bat beego.me
- more help information please refer to https://github.com/astaxie/bat
- `
- func usage() {
- fmt.Println(usageinfo)
- os.Exit(2)
- }
|