several improvs, added pre-baked commands

This commit is contained in:
HoneyryderChuck 2022-01-29 19:01:19 +00:00
parent 07dc8726d4
commit d239e40b72
4 changed files with 146 additions and 50 deletions

View File

@ -1,3 +1,7 @@
.buttons {
display: block;
text-align: center;
}
.btn { .btn {
display: inline-block; display: inline-block;
padding: 0.75em 1.25em; padding: 0.75em 1.25em;
@ -17,6 +21,12 @@
svg { svg {
vertical-align: -1px; vertical-align: -1px;
} }
&.btn-small {
background-color: green;
border-radius: 0.2em;
padding: 0 0.4em;
}
} }
.badge { .badge {

View File

@ -57,10 +57,11 @@
code, #curl-command { code, #curl-command {
border-radius: 10px; border-radius: 10px;
padding: 10px; padding: 0 10px;
font-weight: bold; font-weight: bold;
background-color: black; background-color: black;
color: white; color: white;
text-align: justify;
} }
#curl-command:before { #curl-command:before {
@ -69,19 +70,33 @@
} }
#curl-command input, #curl-command input,
#curl-command textarea { #curl-command pre {
background: transparent; background: transparent;
border: none; border: none;
font-size: 20px;
}
#curl-command input {
padding: 10px;
width: 95%; width: 95%;
} }
#curl-command textarea { #curl-command pre {
width: 100%;
background-image: radial-gradient(
rgba(0, 70, 0, 0.75), black 120%
);
padding-top: 10px; padding-top: 10px;
font-family: monospace; font-family: Inconsolata, monospace;
text-shadow: 0 0 5px #C8C8C8;
color: #87cefa; color: #87cefa;
&.error { &.error {
color: red; color: red;
} }
&::selection {
background: #0080FF;
text-shadow: none;
}
} }
} }

View File

@ -23,9 +23,17 @@ layout: default
</div> </div>
<div class="code-preview"> <div class="code-preview">
<p><span class="text">Convert curl commands into httpx</span></p> <p><span class="text">Convert curl commands into httpx</span></p>
<div class="buttons">
<button class="btn btn-small" onclick="javascript: setexample('simple')">Simple</button>
<button class="btn btn-small" onclick="javascript: setexample('basicauth')">Basic Auth</button>
<button class="btn btn-small" onclick="javascript: setexample('json')">JSON</button>
<button class="btn btn-small" onclick="javascript: setexample('complexjson')">Complex JSON</button>
<button class="btn btn-small" onclick="javascript: setexample('formdata')">Form Data</button>
</div>
<label for="curl-command-input">"insert curl command here..."</label>
<div id="curl-command"> <div id="curl-command">
<input id="curl-command-input" type="text" placeholder="insert curl command here..."/> <input id="curl-command-input" type="text" placeholder="curl http://..."/>
<textarea id="curl-command-output" rows=0 style="display:none"></textarea> <pre id="curl-command-output" style="display:none"></pre>
</div> </div>
</div> </div>
<a class="btn" href="{{ '/posts/index.html' | prepend: site.baseurl }}"> <a class="btn" href="{{ '/posts/index.html' | prepend: site.baseurl }}">

View File

@ -22,6 +22,7 @@ def parse_options(command, options)
# end # end
opts.on("--basic") do # Use HTTP Basic Authentication opts.on("--basic") do # Use HTTP Basic Authentication
options[:auth] = :basic_authentication options[:auth] = :basic_authentication
options[:auth_method] = :basic_auth
end end
opts.on("--cacert FILE") do |path| # CA certificate to verify peer against opts.on("--cacert FILE") do |path| # CA certificate to verify peer against
options[:options][:ssl][:ca_file] = "OpenSSL::X509::Certificate.new(File.read(#{path.inspect}))" options[:options][:ssl][:ca_file] = "OpenSSL::X509::Certificate.new(File.read(#{path.inspect}))"
@ -57,43 +58,62 @@ def parse_options(command, options)
# opts.on("--crlf") # Convert LF to CRLF in upload # opts.on("--crlf") # Convert LF to CRLF in upload
# opts.on("--crlfile FILE") # Get a CRL list in PEM format from the given file # opts.on("--crlfile FILE") # Get a CRL list in PEM format from the given file
opts.on("-d", "--data DATA") do |data| opts.on("-d", "--data DATA") do |data|
data = data.sub(/\A'(.*)'\z/, '\\1')
options[:verb] ||= :post options[:verb] ||= :post
if data.start_with?("@") if data.start_with?("@")
options[:body] = "File.open(#{data[1..-1].inspect})" options[:body] = "File.open(#{data[1..-1].inspect})"
else else
options[:form] ||= []
k, v = data.split("=") k, v = data.split("=")
v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v.inspect if v
options[:form] << [k, v] options[:form] ||= []
v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v
options[:form] << [k, v]
else
options[:body] = k
end
end end
end # HTTP POST data end # HTTP POST data
opts.on("--data-ascii DATA") do |data| opts.on("--data-ascii DATA") do |data|
data = data.sub(/\A'(.*)'\z/, '\\1')
options[:verb] ||= :post options[:verb] ||= :post
if data.start_with?("@") if data.start_with?("@")
options[:body] = "File.open(#{data[1..-1].inspect})" options[:body] = "File.open(#{data[1..-1].inspect})"
else else
options[:form] ||= []
k, v = data.split("=") k, v = data.split("=")
v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v.inspect if v
options[:form] << [k, v] options[:form] ||= []
v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v
options[:form] << [k, v]
else
options[:body] = k
end
end end
end # HTTP POST ASCII data end # HTTP POST ASCII data
opts.on("--data-binary DATA") do |data| opts.on("--data-binary DATA") do |data|
data = data.sub(/\A'(.*)'\z/, '\\1')
options[:verb] ||= :post options[:verb] ||= :post
if data.start_with?("@") if data.start_with?("@")
options[:body] = "File.open(#{data[1..-1].inspect})" options[:body] = "File.open(#{data[1..-1].inspect})"
else else
options[:form] ||= []
k, v = data.split("=") k, v = data.split("=")
v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v.inspect if v
options[:form] << [k, v] options[:form] ||= []
v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v
options[:form] << [k, v]
else
options[:body] = k
end
end end
end # HTTP POST binary data end # HTTP POST binary data
opts.on("--data-raw DATA") do |data| opts.on("--data-raw DATA") do |data|
options[:form] ||= [] options[:form] ||= []
k, v = data.split("=") k, v = data.split("=")
v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v.inspect if v
options[:form] << [k, v] v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v
options[:form] << [k, v]
else
options[:body] = k
end
end # HTTP POST data, '@' allowed end # HTTP POST data, '@' allowed
opts.on("--data-urlencode DATA") do |data| opts.on("--data-urlencode DATA") do |data|
options[:verb] ||= :post options[:verb] ||= :post
@ -103,6 +123,7 @@ def parse_options(command, options)
# opts.on("--delegation LEVEL") # GSS-API delegation permission # opts.on("--delegation LEVEL") # GSS-API delegation permission
opts.on("--digest") do opts.on("--digest") do
options[:auth] = :digest_authentication options[:auth] = :digest_authentication
options[:auth_method] = :digest_auth
end # Use HTTP Digest Authentication end # Use HTTP Digest Authentication
# opts.on("-q", "--disable") # Disable .curlrc # opts.on("-q", "--disable") # Disable .curlrc
# opts.on("--disable-eprt") # Inhibit using EPRT or LPRT # opts.on("--disable-eprt") # Inhibit using EPRT or LPRT
@ -164,8 +185,8 @@ def parse_options(command, options)
options[:options][:debug_level] = 1 options[:options][:debug_level] = 1
end # Show document info only end # Show document info only
opts.on("-H", "--header HEADER/@FILE") do |data| opts.on("-H", "--header HEADER/@FILE") do |data|
k, v = data.split("/") k, v = data[1..-2].split(/ *: */)
v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v.inspect v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v
options[:options][:headers][k] = v options[:options][:headers][k] = v
end # Pass custom header(s) to server end # Pass custom header(s) to server
@ -249,7 +270,10 @@ def parse_options(command, options)
options[:copy_to] = path options[:copy_to] = path
end # Write to file instead of stdout end # Write to file instead of stdout
opts.on("--pass PHRASE") do |pass| opts.on("--pass PHRASE") do |pass|
options[:auth] ||= :basic_authentication if !options.key?(:auth)
options[:auth] = :basic_authentication
options[:auth_method] = :basic_auth
end
options[:password] = pass options[:password] = pass
end# Pass phrase for the private key end# Pass phrase for the private key
# opts.on("--path-as-is Do not squash .. sequences in URL path # opts.on("--path-as-is Do not squash .. sequences in URL path
@ -414,9 +438,12 @@ def parse_options(command, options)
options[:urls] << url options[:urls] << url
end # URL to work with end # URL to work with
opts.on("-B", "--use-ascii") # Use ASCII/text transfer opts.on("-B", "--use-ascii") # Use ASCII/text transfer
opts.on("-u", "--user <user:password>") do |user_pass| opts.on("-u <user:password>", "--user <user:password>") do |user_pass|
options[:auth] ||= :basic_authentication if !options.key?(:auth)
user, pass = user_pass.split(":") options[:auth] = :basic_authentication
options[:auth_method] = :basic_auth
end
user, pass = user_pass.sub(/\A"(.*)"\z/, '\\1').split(":")
options[:username] = user options[:username] = user
options[:password] = pass options[:password] = pass
end # Server user and password end # Server user and password
@ -436,7 +463,6 @@ def to_ruby(urls, options)
template = <<-HTTPX.slice(0..-2) template = <<-HTTPX.slice(0..-2)
require "httpx" require "httpx"
http = HTTPX
HTTPX HTTPX
# load extra deps # load extra deps
@ -444,47 +470,58 @@ http = HTTPX
template.prepend("require \"#{lib}\"\n") template.prepend("require \"#{lib}\"\n")
end end
# load plugins
options[:plugins].each do |plugin|
template += ".plugin(#{plugin.inspect}"
options[:plugin_options][plugin].each do |key, value|
template += ", #{key}: #{value.inspect}"
end
template += ")"
end
# handle auth
if (auth = options[:auth])
template += ".plugin(#{auth.inspect})." \
"#{auth}(#{options[:username].inspect}, " \
"#{options[:password].inspect})"
end
# handle general options # handle general options
with_options = options[:options].map do |k, v| with_options = options[:options].map do |k, v|
next if %i[body form json].include?(k) next if %i[body form json].include?(k)
"#{k}: #{v.inspect}" unless v.respond_to?(:empty?) && v.empty? "#{k.inspect} => #{v.inspect}" unless v.respond_to?(:empty?) && v.empty?
end.compact end.compact
if not with_options.empty? if (http_custom = !options[:plugins].empty? || options[:auth] || !with_options.empty?)
template += ".with(#{with_options.join(', ')})" template += "\nhttp = HTTPX"
end
# load plugins
options[:plugins].each do |plugin|
template += ".plugin(:#{plugin}"
options[:plugin_options][plugin].each do |key, value|
template += ", #{key}: #{value.inspect}"
end
template += ")\n\t"
end end
# handle auth
if (auth = options[:auth])
template += ".plugin(:#{auth})\n\t" \
".#{options.fetch(:auth_method, auth)}(#{options[:username].inspect}, " \
"#{options[:password].inspect})\n\t"
end
# handle general options
if !with_options.empty?
template += ".with(#{with_options.join(', ')})\n\t"
end
# send request # send request
template += "\nresponse = http" template += "\nresponse = "
template += http_custom ? "http" : "HTTPX"
template += ".#{options.fetch(:verb, :get)}(" template += ".#{options.fetch(:verb, :get)}("
template += (urls + options[:urls]).map(&:inspect).join(", ") template += (urls + options[:urls]).map do |h|
h = h.sub(/\A"(.*)"\z/, '\\1')
(h.start_with?("http://") || h.start_with?("https://")) ?
h : "http://#{h}"
end.map(&:inspect).join(", ")
# send body # send body
if options.key?(:body) if options.key?(:body)
template += ", body: #{options[:body].inspect}" template += ", body: #{options[:body]}"
end end
if options.key?(:form) if options.key?(:form)
template += ", form: {" template += ", form: {"
template += options[:form].map do |k, v| template += options[:form].map do |k, v|
"#{k}: #{v}" "#{k.inspect} => #{v.inspect}"
end.join(", ") end.join(", ")
template += "}" template += "}"
end end
@ -509,7 +546,7 @@ def to_httpx
} }
begin begin
options = {} options = {}
urls = parse_options(command.slice(5..-1).split(/ +/), options) urls = parse_options(command.slice(5..-1).scan(/"[^"]+"|'[^']+'|\S+/), options)
result = to_ruby(urls, options) result = to_ruby(urls, options)
rescue OptionParser::InvalidOption => error rescue OptionParser::InvalidOption => error
result = error.message result = error.message
@ -517,21 +554,47 @@ def to_httpx
`output.classList.add("error")` `output.classList.add("error")`
end end
`console.log(#{result})`
%x{ %x{
output.value = #{result}; output.innerHTML = #{result};
output.rows = #{result.lines.size};
output.style.display = "block"; output.style.display = "block";
} }
end end
%x{ %x{
var input = document.getElementById('curl-command-input'); var input = document.getElementById('curl-command-input');
input.addEventListener('input', on_txt_change, false);
input.addEventListener('change', on_txt_change, false); input.addEventListener('change', on_txt_change, false);
} }
end end
to_httpx to_httpx
EXAMPLES = {
"simple" => "curl echoip.com",
"basicauth" => %Q{curl https://api.example.com/surprise -u banana:coconuts -d "sample data"},
"json" => %Q{curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer b7d03a6947b217efb6f3ec3bd3504582" -d '{"type":"A","name":"www","data":"162.10.66.0","priority":null,"port":null,"weight":null}' "https://api.digitalocean.com/v2/domains/example.com/records"},
"complexjson" => %Q{curl -u "demo:" -X POST -H "Content-Type: application/json" -d '{"array": [1,2,3], "object": {"foo": "bar", "nested": {"baz": true}}}' https://example.com/},
"formdata" => %Q{
curl -X POST https://api.easypost.com/v2/shipments \
-u API_KEY: \
-d 'shipment[to_address][id]=adr_HrBKVA85' \
-d 'shipment[from_address][id]=adr_VtuTOj7o' \
-d 'shipment[parcel][id]=prcl_WDv2VzHp' \
-d 'shipment[is_return]=true' \
-d 'shipment[customs_info][id]=cstinfo_bl5sE20Y'}
}
setexample = lambda do |ex|
%x{
var input = document.getElementById('curl-command-input');
input.value = #{EXAMPLES[ex]};
var event = new Event("input");
input.dispatchEvent(event);
}
end
`window.setexample = setexample`
# if $0 == __FILE__ # if $0 == __FILE__
# command = ARGV.first # command = ARGV.first