forked from nojima/httpie-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
156 lines (136 loc) · 3.74 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package httpie
import (
"bufio"
"bytes"
"fmt"
"net/http"
"net/http/httputil"
"os"
"github.com/nojima/httpie-go/exchange"
"github.com/nojima/httpie-go/flags"
"github.com/nojima/httpie-go/input"
"github.com/nojima/httpie-go/output"
"github.com/pkg/errors"
)
type Options struct {
// Transport is applied to the underlying HTTP client. Use to mock or
// intercept network traffic. If nil, http.DefaultTransport will be cloned.
Transport http.RoundTripper
}
func Main(options *Options) error {
// Parse flags
args, usage, optionSet, err := flags.Parse(os.Args)
if err != nil {
return err
}
inputOptions := optionSet.InputOptions
exchangeOptions := optionSet.ExchangeOptions
exchangeOptions.Transport = options.Transport
outputOptions := optionSet.OutputOptions
// Parse positional arguments
in, err := input.ParseArgs(args, os.Stdin, &inputOptions)
if _, ok := errors.Cause(err).(*input.UsageError); ok {
usage.PrintUsage(os.Stderr)
return err
}
if err != nil {
return err
}
// Send request and receive response
status, err := Exchange(in, &exchangeOptions, &outputOptions)
if err != nil {
return err
}
if exchangeOptions.CheckStatus {
os.Exit(getExitStatus(status))
}
return nil
}
func getExitStatus(statusCode int) int {
if 300 <= statusCode && statusCode < 600 {
return statusCode / 100
}
return 0
}
func Exchange(in *input.Input, exchangeOptions *exchange.Options, outputOptions *output.Options) (int, error) {
// Prepare printer
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
printer := output.NewPrinter(writer, outputOptions)
// Build HTTP request
request, err := exchange.BuildHTTPRequest(in, exchangeOptions)
if err != nil {
return -1, err
}
// Print HTTP request
if outputOptions.PrintRequestHeader || outputOptions.PrintRequestBody {
// `request` does not contain HTTP headers that HttpClient.Do adds.
// We can get these headers by DumpRequestOut and ReadRequest.
dump, err := httputil.DumpRequestOut(request, true)
if err != nil {
return -1, err // should not happen
}
r, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(dump)))
if err != nil {
return -1, err // should not happen
}
defer r.Body.Close()
// ReadRequest deletes Host header. We must restore it.
if request.Host != "" {
r.Header.Set("Host", request.Host)
} else {
r.Header.Set("Host", request.URL.Host)
}
if outputOptions.PrintRequestHeader {
if err := printer.PrintRequestLine(r); err != nil {
return -1, err
}
if err := printer.PrintHeader(r.Header); err != nil {
return -1, err
}
}
if outputOptions.PrintRequestBody {
if err := printer.PrintBody(r.Body, r.Header.Get("Content-Type")); err != nil {
return -1, err
}
}
fmt.Fprintln(writer)
writer.Flush()
}
// Send HTTP request and receive HTTP request
httpClient, err := exchange.BuildHTTPClient(exchangeOptions)
if err != nil {
return -1, err
}
resp, err := httpClient.Do(request)
if err != nil {
return -1, errors.Wrap(err, "sending HTTP request")
}
defer resp.Body.Close()
if outputOptions.PrintResponseHeader {
if err := printer.PrintStatusLine(resp.Proto, resp.Status, resp.StatusCode); err != nil {
return -1, err
}
if err := printer.PrintHeader(resp.Header); err != nil {
return -1, err
}
writer.Flush()
}
if outputOptions.Download {
file := output.NewFileWriter(in.URL, outputOptions)
if err := printer.PrintDownload(resp.ContentLength, file.Filename()); err != nil {
return -1, err
}
writer.Flush()
if err = file.Download(resp); err != nil {
return -1, err
}
} else {
if outputOptions.PrintResponseBody {
if err := printer.PrintBody(resp.Body, resp.Header.Get("Content-Type")); err != nil {
return -1, err
}
}
}
return resp.StatusCode, nil
}