httpdisk 0.1.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/httpdisk/cli.rb DELETED
@@ -1,218 +0,0 @@
1
- require 'faraday-cookie_jar'
2
- require 'faraday_middleware'
3
- require 'ostruct'
4
-
5
- module HTTPDisk
6
- # Command line httpdisk command.
7
- class Cli
8
- attr_reader :options
9
-
10
- # for --expires
11
- UNITS = {
12
- s: 1,
13
- m: 60,
14
- h: 60 * 60,
15
- d: 24 * 60 * 60,
16
- w: 7 * 24 * 60 * 60,
17
- y: 365 * 7 * 24 * 60 * 60,
18
- }.freeze
19
-
20
- def initialize(options)
21
- @options = options
22
- end
23
-
24
- # we have a very liberal retry policy
25
- RETRY_OPTIONS = {
26
- methods: %w[delete get head options patch post put trace],
27
- retry_statuses: (400..600).to_a,
28
- retry_if: ->(_env, _err) { true },
29
- }.freeze
30
-
31
- # Make the request (or print status)
32
- def run
33
- # short circuit --status
34
- if options[:status]
35
- status
36
- return
37
- end
38
-
39
- # create Faraday client
40
- faraday = create_faraday
41
-
42
- # run request
43
- response = faraday.run_request(request_method, request_url, request_body, request_headers)
44
- if response.status >= 400
45
- raise CliError, "the requested URL returned error: #{response.status} #{response.reason_phrase}"
46
- end
47
-
48
- # output
49
- if options[:output]
50
- File.open(options[:output], 'w') { output(response, _1) }
51
- else
52
- output(response, $stdout)
53
- end
54
- end
55
-
56
- def create_faraday
57
- Faraday.new do
58
- # connection settings
59
- _1.proxy = proxy if options[:proxy]
60
- _1.options.timeout = options[:max_time] if options[:max_time]
61
-
62
- # cookie middleware
63
- _1.use :cookie_jar
64
-
65
- # BEFORE httpdisk so each redirect segment is cached
66
- _1.response :follow_redirects
67
-
68
- # httpdisk
69
- _1.use :httpdisk, client_options
70
-
71
- # AFTER httpdisk so transient failures are not cached
72
- if options[:retry]
73
- _1.request :retry, RETRY_OPTIONS.merge(max: options[:retry])
74
- end
75
- end
76
- end
77
-
78
- # Support for --status
79
- def status
80
- # build env
81
- env = Faraday::Env.new.tap do
82
- _1.method = request_method
83
- _1.request_body = request_body
84
- _1.request_headers = request_headers
85
- _1.url = request_url
86
- end
87
-
88
- # now print status
89
- client = HTTPDisk::Client.new(nil, client_options)
90
- client.status(env).each do
91
- puts "#{_1}: #{_2.inspect}"
92
- end
93
- end
94
-
95
- # Output response to f
96
- def output(response, f)
97
- if options[:include]
98
- f.puts "HTTPDISK #{response.status} #{response.reason_phrase}"
99
- response.headers.each { f.puts("#{_1}: #{_2}") }
100
- f.puts
101
- end
102
- f.write(response.body)
103
- end
104
-
105
- #
106
- # request_XXX
107
- #
108
-
109
- # HTTP method (get, post, etc.)
110
- def request_method
111
- method = if options[:request]
112
- options[:request]
113
- elsif options[:data]
114
- 'post'
115
- end
116
- method ||= 'get'
117
- method = method.downcase.to_sym
118
-
119
- if !Faraday::Connection::METHODS.include?(method)
120
- raise CliError, "invalid --request #{method.inspect}"
121
- end
122
- method
123
- end
124
-
125
- # Request url
126
- def request_url
127
- url = options[:url]
128
- # recover from missing http:
129
- if url !~ %r{^https?://}i
130
- if url =~ %r{^\w+://}
131
- raise CliError, 'only http/https supported'
132
- end
133
- url = "http://#{url}"
134
- end
135
- URI.parse(url)
136
- rescue URI::InvalidURIError
137
- raise CliError, "invalid url #{url.inspect}"
138
- end
139
-
140
- # Request body
141
- def request_body
142
- options[:data]
143
- end
144
-
145
- # Request headers
146
- def request_headers
147
- {}.tap do |headers|
148
- if options[:user_agent]
149
- headers['User-Agent'] = options[:user_agent]
150
- end
151
-
152
- options[:header].each do |header|
153
- key, value = header.split(': ', 2)
154
- if !key || !value || key.empty? || value.empty?
155
- raise CliError, "invalid --header #{header.inspect}"
156
- end
157
- headers[key] = value
158
- end
159
- end
160
- end
161
-
162
- #
163
- # helpers
164
- #
165
-
166
- # Options to HTTPDisk::Client
167
- def client_options
168
- {}.tap do |client_options|
169
- client_options[:dir] = options[:dir]
170
- if options[:expires]
171
- seconds = parse_expires(options[:expires])
172
- if !seconds
173
- raise CliError, "invalid --expires #{options[:expires].inspect}"
174
- end
175
- client_options[:expires_in] = seconds
176
- end
177
- client_options[:force] = options[:force]
178
- client_options[:force_errors] = options[:force_errors]
179
- end
180
- end
181
-
182
- # Return validated --proxy flag if present
183
- def proxy
184
- return if !options[:proxy]
185
-
186
- proxy = parse_proxy(options[:proxy])
187
- raise CliError, "--proxy should be host[:port], not #{options[:proxy].inspect}" if !proxy
188
- proxy
189
- end
190
-
191
- # Parse --expires flag
192
- def parse_expires(s)
193
- m = s.match(/^(\d+)([smhdwy])?$/)
194
- return if !m
195
-
196
- num, unit = m[1].to_i, (m[2] || 's').to_sym
197
- return if !UNITS.key?(unit)
198
-
199
- num * UNITS[unit]
200
- end
201
-
202
- # Parse --proxy flag
203
- def parse_proxy(proxy_flag)
204
- host, port = proxy_flag.split(':', 2)
205
- return if !host || host.empty?
206
- return if port&.empty?
207
-
208
- URI.parse('http://placeholder').tap do
209
- begin
210
- _1.host = host
211
- _1.port = port if port
212
- rescue URI::InvalidComponentError
213
- return
214
- end
215
- end.to_s
216
- end
217
- end
218
- end
@@ -1,54 +0,0 @@
1
- # manually load dependencies here since this is loaded standalone by bin
2
- require 'httpdisk/error'
3
- require 'httpdisk/version'
4
- require 'slop'
5
-
6
- module HTTPDisk
7
- # Slop parsing. This is broken out so we can run without require 'httpdisk'.
8
- module CliSlop
9
- def self.slop(args)
10
- slop = Slop.parse(args) do |o|
11
- o.banner = 'httpdisk [options] [url]'
12
-
13
- # similar to curl
14
- o.separator 'Similar to curl:'
15
- o.string '-d', '--data', 'HTTP POST data'
16
- o.array '-H', '--header', 'pass custom header(s) to server', delimiter: nil
17
- o.boolean '-i', '--include', 'include response headers in the output'
18
- o.integer '-m', '--max-time', 'maximum time allowed for the transfer'
19
- o.string '-o', '--output', 'write to file instead of stdout'
20
- o.string '-x', '--proxy', 'use host[:port] as proxy'
21
- o.string '-X', '--request', 'HTTP method to use'
22
- o.integer '--retry', 'retry request if problems occur'
23
- o.boolean '-s', '--silent', "silent mode (don't print errors)"
24
- o.string '-A', '--user-agent', 'send User-Agent to server'
25
-
26
- # from httpdisk
27
- o.separator 'Specific to httpdisk:'
28
- o.string '--dir', 'httpdisk cache directory (defaults to ~/httpdisk)'
29
- o.string '--expires', 'when to expire cached requests (ex: 1h, 2d, 3w)'
30
- o.boolean '--force', "don't read anything from cache (but still write)"
31
- o.boolean '--force-errors', "don't read errors from cache (but still write)"
32
- o.boolean '--status', 'show status for a url in the cache'
33
-
34
- # generic
35
- o.boolean '--version', 'show version' do
36
- puts "httpdisk #{HTTPDisk::VERSION}"
37
- exit
38
- end
39
- o.on '--help', 'show this help' do
40
- puts o
41
- exit
42
- end
43
- end
44
-
45
- raise Slop::Error, '' if args.empty?
46
- raise Slop::Error, 'no URL specified' if slop.args.empty?
47
- raise Slop::Error, 'more than one URL specified' if slop.args.length > 1
48
-
49
- slop.to_h.tap do
50
- _1[:url] = slop.args.first
51
- end
52
- end
53
- end
54
- end