mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-10-09 00:02:50 -04:00
637 lines
28 KiB
Ruby
637 lines
28 KiB
Ruby
require "optparse"
|
|
require "uri"
|
|
require "json"
|
|
|
|
def parse_options(command, options)
|
|
OptionParser.new do |opts|
|
|
options[:urls] = []
|
|
options[:require] = []
|
|
options[:plugins] = []
|
|
options[:plugin_options] = Hash.new { |hs, k| hs[k] = {} }
|
|
options[:options] = {
|
|
ssl: {},
|
|
timeout: {},
|
|
headers: {},
|
|
}
|
|
# opts.on("--abstract-unix-socket PATH") do |path|# TODO: Connect via abstract Unix domain socket
|
|
# end
|
|
# opts.on("--alt-svc FILE") do |path| # TODO: nable alt-svc with this cache file
|
|
# end
|
|
# opts.on("--anyauth") do # Pick any authentication method
|
|
# end
|
|
# opts.on("-a", "--append") do # Append to target file when uploading
|
|
# end
|
|
opts.on("--basic") do # Use HTTP Basic Authentication
|
|
options[:auth] = :basic_authentication
|
|
options[:auth_method] = :basic_auth
|
|
end
|
|
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}))"
|
|
end
|
|
opts.on("--capath DIR") do |path|
|
|
options[:options][:ssl][:ca_path] = path
|
|
end # CA directory to verify peer against
|
|
opts.on("-E", "--cert <certificate[:password]>") do |cert|
|
|
(options[:options][:ssl][:certificate] ||= {})[:cert] = "OpenSSL::X509::Certificate.new(File.read(#{cert.inspect}))"
|
|
end # Client certificate file and password
|
|
# opts.on("--cert-status") # Verify the status of the server certificate
|
|
# opts.on("--cert-type TYPE") # Certificate file type (DER/PEM/ENG)
|
|
opts.on("--ciphers CIPHERLIST") do |ciphers| # SSL ciphers to use
|
|
options[:options][:ssl][:ciphers] = ciphers.inspect
|
|
end
|
|
opts.on("--compressed") do
|
|
options[:plugins] << :compression
|
|
end # Request compressed response
|
|
# opts.on("--compressed-ssh") # Enable SSH compression
|
|
# opts.on("-K, --config FILE") # Read config from a file
|
|
opts.on("--connect-timeout SECS") do |timeout|
|
|
options[:options][:timeout][:connect_timeout] = Float(timeout)
|
|
end # Maximum time allowed for connection
|
|
opts.on("--connect-to HOST1:PORT1:HOST2:PORT2") do |ios| # Connect to host
|
|
options[:options][:io] = ios.split(":").each_slice(2).map{|*a|a.join(":s")}
|
|
end
|
|
opts.on("-C", "--continue-at OFFSET") # Resumed transfer offset
|
|
opts.on("-b", "--cookie DATAORFILENAME") do |cookie|
|
|
options[:options][:headers][:cookie] = cookie
|
|
end # Send cookies from string/file
|
|
# opts.on("-c", "--cookie-jar FILENAME") # TODO: Write cookies to <filename> after operation
|
|
# opts.on("--create-dirs") # Create necessary local directory hierarchy
|
|
# 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("-d", "--data DATA") do |data|
|
|
data = data.sub(/\A'(.*)'\z/, '\\1')
|
|
options[:verb] ||= :post
|
|
if data.start_with?("@")
|
|
options[:body] = "File.open(#{data[1..-1].inspect})"
|
|
else
|
|
k, v = data.split("=")
|
|
if 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 # HTTP POST data
|
|
opts.on("--data-ascii DATA") do |data|
|
|
data = data.sub(/\A'(.*)'\z/, '\\1')
|
|
options[:verb] ||= :post
|
|
if data.start_with?("@")
|
|
options[:body] = "File.open(#{data[1..-1].inspect})"
|
|
else
|
|
k, v = data.split("=")
|
|
if 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 # HTTP POST ASCII data
|
|
opts.on("--data-binary DATA") do |data|
|
|
data = data.sub(/\A'(.*)'\z/, '\\1')
|
|
options[:verb] ||= :post
|
|
if data.start_with?("@")
|
|
options[:body] = "File.open(#{data[1..-1].inspect})"
|
|
else
|
|
k, v = data.split("=")
|
|
if 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 # HTTP POST binary data
|
|
opts.on("--data-raw DATA") do |data|
|
|
options[:form] ||= []
|
|
k, v = data.split("=")
|
|
if 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
|
|
opts.on("--data-urlencode DATA") do |data|
|
|
options[:verb] ||= :post
|
|
options[:require] << "cgi"
|
|
options[:body] = "CGI.escape(#{data.inspect})"
|
|
end # HTTP POST data url encoded
|
|
# opts.on("--delegation LEVEL") # GSS-API delegation permission
|
|
opts.on("--digest") do
|
|
options[:auth] = :digest_authentication
|
|
options[:auth_method] = :digest_auth
|
|
end # Use HTTP Digest Authentication
|
|
# opts.on("-q", "--disable") # Disable .curlrc
|
|
# opts.on("--disable-eprt") # Inhibit using EPRT or LPRT
|
|
# opts.on("--disable-epsv") # Inhibit using EPSV
|
|
# opts.on("--disallow-username-in-url") # Disallow username in url
|
|
# opts.on("--dns-interface INTERFACE") # Interface to use for DNS requests
|
|
# opts.on("--dns-ipv4-addr ADDRESS") # IPv4 address to use for DNS requests
|
|
# opts.on("--dns-ipv6-addr ADDRESS") # IPv6 address to use for DNS requests
|
|
# opts.on("--dns-servers ADDRESSES") # DNS server addrs to use
|
|
opts.on("--doh-url URL") do |uri|
|
|
options[:options][:resolver_options] = { resolver_class: :https, uri: uri }
|
|
end # Resolve host names over DOH
|
|
# opts.on("-D", "--dump-header FILENAME") # Write the received headers to <filename>
|
|
# opts.on("--egd-file FILE") # EGD socket path for random data
|
|
opts.on("--expect100-timeout SECONDS") do |secs|
|
|
options[:plugins] << :expect
|
|
options[:plugin_options][:expect][:expect_timeout] = Float(secs)
|
|
end # How long to wait for 100-continue
|
|
opts.on("-f", "--fail") # Fail silently (no output at all) on HTTP errors
|
|
opts.on("--fail-early") do
|
|
options[:raise_for_status] = true
|
|
end # Fail on first transfer error, do not continue
|
|
# opts.on("--false-start") # Enable TLS False Start
|
|
opts.on("-F", "--form NAME=CONTENT") do |data|
|
|
data = URI.decode_www_form(data)
|
|
data.each do |_, val|
|
|
if val[0] = "@"
|
|
options[:plugins] << :multipart
|
|
val.replace("File.open(#{val.slice(1..-1).inspect})")
|
|
end
|
|
end
|
|
|
|
options[:verb] ||= :post
|
|
options[:form] ||= []
|
|
options[:form] += data
|
|
end # Specify multipart MIME data
|
|
opts.on("--form-string <NAME=STRING") do |data|
|
|
options[:verb] ||= :post
|
|
options[:form] ||= []
|
|
options[:form] += URI.decode_www_form(data)
|
|
end # Specify multipart MIME data
|
|
# opts.on("--ftp-account DATA") # Account data string
|
|
# opts.on("--ftp-alternative-to-user <command> String to replace USER [name]
|
|
# opts.on("--ftp-create-dirs Create the remote dirs if not present
|
|
# opts.on("--ftp-method <method> Control CWD usage
|
|
# opts.on("--ftp-pasv Use PASV/EPSV instead of PORT
|
|
# opts.on("-P, --ftp-port <address> Use PORT instead of PASV
|
|
# opts.on("--ftp-pret Send PRET before PASV
|
|
# opts.on("--ftp-skip-pasv-ip Skip the IP address for PASV
|
|
# opts.on("--ftp-ssl-ccc Send CCC after authenticating
|
|
# opts.on("--ftp-ssl-ccc-mode <active/passive> Set CCC mode
|
|
# opts.on("--ftp-ssl-control Require SSL/TLS for FTP login, clear for transfer
|
|
opts.on("-G", "--get") do
|
|
options[:verb] = :get
|
|
end # Put the post data in the URL and use GET
|
|
# opts.on("-g", "--globoff") # Disable URL sequences and ranges using {} and []
|
|
# opts.on("--happy-eyeballs-timeout-ms MILLISECONDS") # TODO: How long to wait in milliseconds for IPv6 before trying IPv4
|
|
# opts.on("--haproxy-protocol Send HAProxy PROXY protocol v1 header
|
|
opts.on("-I", "--head") do
|
|
options[:verb] = :head
|
|
options[:options][:debug_level] = 1
|
|
end # Show document info only
|
|
opts.on("-H", "--header HEADER/@FILE") do |data|
|
|
k, v = data[1..-2].split(/ *: */)
|
|
v = v.start_with?("@") ? "File.open(#{v[1..-1].inspect})" : v
|
|
|
|
options[:options][:headers][k.downcase] = v
|
|
end # Pass custom header(s) to server
|
|
# opts.on("-h, --help This help text
|
|
# opts.on("--hostpubmd5 <md5> Acceptable MD5 hash of the host public key
|
|
# opts.on("--http0.9 Allow HTTP 0.9 responses
|
|
# opts.on("-0, --http1.0 Use HTTP 1.0
|
|
opts.on("--http1.1") do
|
|
options[:options][:ssl][:alpn_protocols] = %w[http/1.1]
|
|
end # Use HTTP 1.1
|
|
opts.on("--http2") do
|
|
options[:plugins] << :h2c
|
|
end # Use HTTP 2
|
|
opts.on("--http2-prior-knowledge") do
|
|
options[:options][:fallback_protocol] = "h2"
|
|
end # Use HTTP 2 without HTTP/1.1 Upgrade
|
|
# opts.on("--ignore-content-length") # TODO: Ignore the size of the remote resource
|
|
# opts.on("-i", "--include") # TODO: Include protocol response headers in the output
|
|
opts.on("-k", "--insecure") do
|
|
options[:options][:ssl][:verify_mode] = "OpenSSL::SSL::VERIFY_NONE"
|
|
end # Allow insecure server connections when using SSL
|
|
# opts.on("--interface NAME") # Use network INTERFACE (or address)
|
|
opts.on("-4", "--ipv4") do
|
|
options[:options][:ip_families] = "Socket::AF_INET"
|
|
end # Resolve names to IPv4 addresses
|
|
opts.on("-6", "--ipv6") do
|
|
options[:options][:ip_families] = "Socket::AF_INET6"
|
|
end # Resolve names to IPv6 addresses
|
|
# opts.on("-j", "--junk-session-cookies") # Ignore session cookies read from file
|
|
opts.on("--keepalive-time SECONDS", Integer) do |timeout|
|
|
options[:options][:timeout][:keepalive_timeout] = timeout
|
|
end # Interval time for keepalive probes
|
|
opts.on("--key KEY") do |key|
|
|
(options[:options][:ssl][:certificate] ||= {})[:key] = OpenSSL::PKey::PKey.new(File.read(key))
|
|
end # Private key file name
|
|
# opts.on("--key-type TYPE") # Private key file type (DER/PEM/ENG)
|
|
# opts.on("--krb <level> Enable Kerberos with security <level>
|
|
# opts.on("--libcurl <file> Dump libcurl equivalent code of this command line
|
|
# opts.on("--limit-rate <speed> Limit transfer speed to RATE
|
|
# opts.on("-l, --list-only List only mode
|
|
# opts.on("--local-port <num/range> Force use of RANGE for local port numbers
|
|
opts.on("-L", "--location") do
|
|
options[:plugins] << :follow_redirects
|
|
end # Follow redirects
|
|
# opts.on("--location-trusted") # TODO: Like --location, and send auth to other hosts
|
|
# opts.on("--login-options <options> Server login options
|
|
# opts.on("--mail-auth <address> Originator address of the original email
|
|
# opts.on("--mail-from <address> Mail from this address
|
|
# opts.on("--mail-rcpt <address> Mail to this address
|
|
# opts.on("-M, --manual Display the full manual
|
|
opts.on("--max-filesize BYTES") # Maximum file size to download
|
|
opts.on("--max-redirs NUM", Integer) do |max|
|
|
options[:plugins] << :retries
|
|
options[:plugin_options][:retries][:max_retries] = max
|
|
end # Maximum number of redirects allowed
|
|
# opts.on("-m", "--max-time SECONDS") # TODO: Maximum time allowed for the transfer
|
|
# opts.on("--metalink Process given URLs as metalink XML file
|
|
opts.on("--negotiate") do
|
|
options[:auth] = :ntlm_authentication
|
|
end # Use HTTP Negotiate (SPNEGO) authentication
|
|
# opts.on("-n, --netrc Must read .netrc for user name and password
|
|
# opts.on("--netrc-file <filename> Specify FILE for netrc
|
|
# opts.on("--netrc-optional Use either .netrc or URL
|
|
# opts.on("-:, --next Make next URL use its separate set of options
|
|
opts.on("--no-alpn") do # Disable the ALPN TLS extension
|
|
options[:options][:ssl][:alpn_protocols] = []
|
|
end
|
|
# opts.on("-N, --no-buffer Disable buffering of the output stream
|
|
# opts.on("--no-keepalive") # TODO: Disable TCP keepalive on the connection
|
|
# opts.on("--no-npn Disable the NPN TLS extension
|
|
# opts.on("--no-sessionid") # Disable SSL session-ID reusing
|
|
opts.on("--noproxy NOPROXYLIST") # List of hosts which do not use proxy
|
|
opts.on("--ntlm") do
|
|
options[:plugins] << :ntlm_authentication
|
|
end # Use HTTP NTLM authentication
|
|
# opts.on("--ntlm-wb") # Use HTTP NTLM authentication with winbind
|
|
opts.on("--oauth2-bearer TOKEN") do |token|
|
|
options[:options][:headers]["authorization"] = "Bearer #{token}"
|
|
end # OAuth 2 Bearer Token
|
|
opts.on("-o", "--output FILE") do |path|
|
|
options[:copy_to] = path
|
|
end # Write to file instead of stdout
|
|
opts.on("--pass PHRASE") do |pass|
|
|
if !options.key?(:auth)
|
|
options[:auth] = :basic_authentication
|
|
options[:auth_method] = :basic_auth
|
|
end
|
|
options[:password] = pass
|
|
end# Pass phrase for the private key
|
|
# opts.on("--path-as-is Do not squash .. sequences in URL path
|
|
# opts.on("--pinnedpubkey <hashes> FILE/HASHES Public key to verify peer against
|
|
opts.on("--post301") # Do not switch to GET after following a 301
|
|
opts.on("--post302") # Do not switch to GET after following a 302
|
|
opts.on("--post303") # Do not switch to GET after following a 303
|
|
# opts.on("--preproxy [protocol://]host[:port] Use this proxy first
|
|
# opts.on("-#, --progress-bar Display transfer progress as a bar
|
|
# opts.on("--proto <protocols> Enable/disable PROTOCOLS
|
|
# opts.on("--proto-default <protocol> Use PROTOCOL for any URL missing a scheme
|
|
# opts.on("--proto-redir <protocols> Enable/disable PROTOCOLS on redirect
|
|
opts.on("-x", "--proxy [protocol://]host[:port]") do |proxy|
|
|
options[:plugins] << :proxy
|
|
options[:plugin_options][:proxy][:uri] = proxy
|
|
end # Use this proxy
|
|
# opts.on("--proxy-anyauth Pick any proxy authentication method
|
|
opts.on("--proxy-basic") do
|
|
options[:plugins] << :proxy
|
|
options[:plugin_options][:proxy][:authentication] = :basic
|
|
end # Use Basic authentication on the proxy
|
|
# opts.on("--proxy-cacert <file> CA certificate to verify peer against for proxy
|
|
# opts.on("--proxy-capath <dir> CA directory to verify peer against for proxy
|
|
# opts.on("--proxy-cert <cert[:passwd]> Set client certificate for proxy
|
|
# opts.on("--proxy-cert-type <type> Client certificate type for HTTPS proxy
|
|
# opts.on("--proxy-ciphers <list> SSL ciphers to use for proxy
|
|
# opts.on("--proxy-crlfile <file> Set a CRL list for proxy
|
|
# opts.on("--proxy-digest Use Digest authentication on the proxy
|
|
opts.on("--proxy-header <header/@file>") do |header|
|
|
end # Pass custom header(s) to proxy
|
|
opts.on("--proxy-insecure") # Do HTTPS proxy connections without verifying the proxy
|
|
opts.on("--proxy-key <key>") # Private key for HTTPS proxy
|
|
opts.on("--proxy-key-type <type>") # Private key file type for proxy
|
|
opts.on("--proxy-negotiate") # Use HTTP Negotiate (SPNEGO) authentication on the proxy
|
|
opts.on("--proxy-ntlm") # Use NTLM authentication on the proxy
|
|
opts.on("--proxy-pass <phrase>") do |pass|
|
|
options[:plugins] << :proxy
|
|
options[:plugin_options][:proxy][:password] = pass
|
|
end # Pass phrase for the private key for HTTPS proxy
|
|
# opts.on("--proxy-pinnedpubkey <hashes> FILE/HASHES public key to verify proxy with
|
|
# opts.on("--proxy-service-name <name> SPNEGO proxy service name
|
|
# opts.on("--proxy-ssl-allow-beast Allow security flaw for interop for HTTPS proxy
|
|
# opts.on("--proxy-tls13-ciphers <ciphersuite list> TLS 1.3 proxy cipher suites
|
|
# opts.on("--proxy-tlsauthtype <type> TLS authentication type for HTTPS proxy
|
|
# opts.on("--proxy-tlspassword <string> TLS password for HTTPS proxy
|
|
# opts.on("--proxy-tlsuser <name> TLS username for HTTPS proxy
|
|
# opts.on("--proxy-tlsv1 Use TLSv1 for HTTPS proxy
|
|
opts.on("-U", "--proxy-user <user:password>") do |user|
|
|
options[:plugins] << :proxy
|
|
options[:plugin_options][:proxy][:username] = user
|
|
end # Proxy user and password
|
|
# opts.on("--proxy1.0 <host[:port]> Use HTTP/1.0 proxy on given port
|
|
# opts.on("-p", "--proxytunnel") Operate through an HTTP proxy tunnel (using CONNECT)
|
|
# opts.on("--pubkey <key> SSH Public key file name
|
|
# opts.on("-Q, --quote Send command(s) to server before transfer
|
|
# opts.on("--random-file <file> File for reading random data from
|
|
opts.on("-r", "--range RANGE") # Retrieve only the bytes within RANGE
|
|
opts.on("--raw") # Do HTTP "raw"; no transfer decoding
|
|
opts.on("-e", "--referer URL") do |url|
|
|
options[:options][:headers]["referer"] = url
|
|
end # Referrer URL
|
|
# opts.on("-J", "--remote-header-name Use the header-provided filename
|
|
# opts.on("-O, --remote-name Write output to a file named as the remote file
|
|
# opts.on("--remote-name-all Use the remote file name for all URLs
|
|
# opts.on("-R, --remote-time Set the remote file's time on the local output
|
|
opts.on("-X", "--request <command>") do |verb|
|
|
options[:verb] = verb.downcase.to_sym
|
|
end # Specify request command to use
|
|
# opts.on("--request-target Specify the target for this request
|
|
opts.on("--resolve <host:port:address[,address]...>") # Resolve the host+port to this address
|
|
opts.on("--retry <num>", Integer) do |num|
|
|
options[:plugins] << :retries
|
|
# options[:options]
|
|
end # Retry request if transient problems occur
|
|
opts.on("--retry-connrefused") # Retry on connection refused (use with --retry)
|
|
opts.on("--retry-delay <seconds>", Integer) do |delay|
|
|
options[:plugins] << :retries
|
|
options[:plugin_options][:retries][:retry_after] = delay
|
|
|
|
end # Wait time between retries
|
|
# opts.on("--retry-max-time <seconds> Retry only within this period
|
|
# opts.on("--sasl-ir Enable initial response in SASL authentication
|
|
# opts.on("--service-name <name> SPNEGO service name
|
|
# opts.on("-S, --show-error Show error even when -s is used
|
|
# opts.on("-s, --silent Silent mode
|
|
opts.on("--socks4 <host[:port]>") do |socks_authority|
|
|
options[:plugins] << :proxy
|
|
options[:plugin_options][:proxy][:uri] = "socks4://#{socks_authority}"
|
|
end # SOCKS4 proxy on given host + port
|
|
opts.on("--socks4a <host[:port]>") do |socks_authority|
|
|
options[:plugins] << :proxy
|
|
options[:plugin_options][:proxy][:uri] = "socks4a://#{socks_authority}"
|
|
end # SOCKS4a proxy on given host + port
|
|
opts.on("--socks5 <host[:port]>") do |socks_authority|
|
|
options[:plugins] << :proxy
|
|
options[:plugin_options][:proxy][:uri] = "socks5://#{socks_authority}"
|
|
end # SOCKS5 proxy on given host + port
|
|
opts.on("--socks5-basic") do |pass|
|
|
options[:plugins] << :proxy
|
|
options[:plugin_options][:proxy][:password] = pass
|
|
end # Enable username/password auth for SOCKS5 proxies
|
|
# opts.on("--socks5-gssapi Enable GSS-API auth for SOCKS5 proxies
|
|
# opts.on("--socks5-gssapi-nec Compatibility with NEC SOCKS5 server
|
|
# opts.on("--socks5-gssapi-service <name> SOCKS5 proxy service name for GSS-API
|
|
opts.on("--socks5-hostname <host[:port]>") # SOCKS5 proxy, pass host name to proxy
|
|
# opts.on("-Y, --speed-limit <speed> Stop transfers slower than this
|
|
# opts.on("-y, --speed-time <seconds> Trigger 'speed-limit' abort after this time
|
|
# opts.on("--ssl Try SSL/TLS
|
|
# opts.on("--ssl-allow-beast Allow security flaw to improve interop
|
|
# opts.on("--ssl-no-revoke Disable cert revocation checks (Schannel)
|
|
# opts.on("--ssl-reqd Require SSL/TLS
|
|
opts.on("-2", "--sslv2") do
|
|
options[:options][:ssl][:min_version] = ":SSL2"
|
|
end # Use SSLv2
|
|
opts.on("-3", "--sslv3") do
|
|
options[:options][:ssl][:min_version] = ":SSL3"
|
|
end # Use SSLv3
|
|
opts.on("--stderr") # Where to redirect stderr
|
|
# opts.on("--styled-output Enable styled output for HTTP headers
|
|
# opts.on("--suppress-connect-headers Suppress proxy CONNECT response headers
|
|
# opts.on("--tcp-fastopen Use TCP Fast Open
|
|
# opts.on("--tcp-nodelay Use the TCP_NODELAY option
|
|
# opts.on("-t, --telnet-option <opt=val> Set telnet option
|
|
# opts.on("--tftp-blksize <value> Set TFTP BLKSIZE option
|
|
# opts.on("--tftp-no-options Do not send any TFTP options
|
|
# opts.on("-z, --time-cond <time> Transfer based on a time condition
|
|
opts.on("--tls-max <VERSION>") do |version|
|
|
options[:options][:ssl][:max_version] = version.sub("v", "").sub(".", "_").upcase.to_sym
|
|
end # Set maximum allowed TLS version
|
|
# opts.on("--tls13-ciphers <list of TLS 1.3 ciphersuites> TLS 1.3 cipher suites to use
|
|
# opts.on("--tlsauthtype <type> TLS authentication type
|
|
# opts.on("--tlspassword TLS password
|
|
# opts.on("--tlsuser <name> TLS user name
|
|
opts.on("-1", "--tlsv1") do
|
|
options[:options][:ssl][:min_version] = ":TLS1_0"
|
|
end # Use TLSv1.0 or greater
|
|
opts.on("--tlsv1.0") do
|
|
options[:options][:ssl][:min_version] = ":TLS1_0"
|
|
end # Use TLSv1.0 or greater
|
|
opts.on("--tlsv1.1") do
|
|
options[:options][:ssl][:min_version] = ":TLS1_1"
|
|
end # Use TLSv1.1 or greater
|
|
opts.on("--tlsv1.2") do
|
|
options[:options][:ssl][:min_version] = ":TLS1_2"
|
|
end # Use TLSv1.2 or greater
|
|
opts.on("--tlsv1.3") do
|
|
options[:options][:ssl][:min_version] = ":TLS1_3"
|
|
end # Use TLSv1.3 or greater
|
|
opts.on("--tr-encoding") # Request compressed transfer encoding
|
|
# opts.on("--trace <file> Write a debug trace to FILE
|
|
# opts.on("--trace-ascii <file> Like --trace, but without hex output
|
|
# opts.on("--trace-time Add time stamps to trace/verbose output
|
|
opts.on("--unix-socket <path>") do |path|
|
|
options[:options][:transport] = :unix
|
|
options[:options][:addresses] = [path]
|
|
end # Connect through this Unix domain socket
|
|
opts.on("-T", "--upload-file <file>") do |path|
|
|
options[:verb] = :put
|
|
options[:body] = Pathname.open(path)
|
|
end # Transfer local FILE to destination
|
|
opts.on("--url <url>") do |url|
|
|
options[:urls] << url
|
|
end # URL to work with
|
|
opts.on("-B", "--use-ascii") # Use ASCII/text transfer
|
|
opts.on("-u <user:password>", "--user <user:password>") do |user_pass|
|
|
if !options.key?(:auth)
|
|
options[:auth] = :basic_authentication
|
|
options[:auth_method] = :basic_auth
|
|
end
|
|
user, pass = user_pass.sub(/\A"(.*)"\z/, '\\1').split(":")
|
|
options[:username] = user
|
|
options[:password] = pass
|
|
end # Server user and password
|
|
opts.on("-A", "--user-agent <name>") do |uagent|
|
|
options[:options][:headers]["user-agent"] = uagent
|
|
end # Send User-Agent <name> to server
|
|
opts.on("-v", "--verbose") do
|
|
options[:options][:debug_level] = 2
|
|
end # Make the operation more talkative
|
|
# opts.on("-V", "--version") # Show version number and quit
|
|
# opts.on("-w", "--write-out <format> Use output FORMAT after completion
|
|
# opts.on("--xattr Store metadata in extended file attributes
|
|
end.parse(command)
|
|
end
|
|
|
|
def to_ruby(urls, options)
|
|
template = <<-HTTPX.slice(0..-2)
|
|
require "httpx"
|
|
|
|
HTTPX
|
|
|
|
# load extra deps
|
|
options[:require].uniq.each do |lib|
|
|
template.prepend("require \"#{lib}\"\n")
|
|
end
|
|
|
|
if options[:options][:headers]["content-type"] == "application/json" && options.key?(:body)
|
|
begin
|
|
options[:json] = JSON.parse(options[:body])
|
|
options.delete(:body)
|
|
options[:options][:headers].delete("content-type")
|
|
rescue JSON::ParserError
|
|
end
|
|
end
|
|
|
|
|
|
# handle general options
|
|
with_options = options[:options].map do |k, v|
|
|
next if %i[body form json].include?(k)
|
|
|
|
unless v.respond_to?(:empty?) && v.empty?
|
|
v = case k
|
|
when :ssl, :timeout
|
|
vstr = "{"
|
|
vstr += v.map do |sk, sv|
|
|
" #{sk}: #{sv}"
|
|
end.join(",")
|
|
vstr += " }"
|
|
vstr
|
|
# when :headers
|
|
else
|
|
v.inspect
|
|
end
|
|
|
|
"#{k}: #{v}"
|
|
end
|
|
end.compact
|
|
|
|
|
|
|
|
if (http_custom = !options[:plugins].empty? || options[:auth] || !with_options.empty?)
|
|
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}"
|
|
end
|
|
template += ")\n\t"
|
|
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
|
|
template += "\nresponse = "
|
|
template += http_custom ? "http" : "HTTPX"
|
|
template += ".#{options.fetch(:verb, :get)}("
|
|
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
|
|
if options.key?(:body)
|
|
template += ", body: #{options[:body]}"
|
|
elsif options.key?(:json)
|
|
template += ", json: #{options[:json]}"
|
|
elsif options.key?(:form)
|
|
template += ", form: {"
|
|
template += options[:form].map do |k, v|
|
|
"#{k.inspect} => #{v.inspect}"
|
|
end.join(", ")
|
|
template += "}"
|
|
end
|
|
template += ")\n"
|
|
|
|
template += "response.raise_for_status\n" if options[:raise_for_status]
|
|
template += "puts response.to_s"
|
|
template
|
|
end
|
|
|
|
def to_httpx
|
|
on_txt_change = lambda do |evt|
|
|
command = `#{evt}.target.value`
|
|
unless command.start_with?("curl ")
|
|
`document.getElementById('curl-command-output').style.display = "none"`
|
|
return
|
|
end
|
|
|
|
%x{
|
|
var output = document.getElementById('curl-command-output');
|
|
output.classList.remove("error");
|
|
}
|
|
begin
|
|
options = {}
|
|
urls = parse_options(command.slice(5..-1).scan(/"[^"]+"|'[^']+'|\S+/), options)
|
|
result = to_ruby(urls, options)
|
|
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument => error
|
|
result = error.message
|
|
result.sub("invalid", "unsupported")
|
|
`output.classList.add("error")`
|
|
end
|
|
|
|
%x{
|
|
output.innerHTML = #{result};
|
|
output.style.display = "block";
|
|
}
|
|
end
|
|
|
|
%x{
|
|
var input = document.getElementById('curl-command-input');
|
|
input.addEventListener('input', on_txt_change, false);
|
|
input.addEventListener('change', on_txt_change, false);
|
|
}
|
|
end
|
|
|
|
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__
|
|
# command = ARGV.first
|
|
|
|
# return unless command.start_with?("curl ")
|
|
|
|
# options = {}
|
|
|
|
# urls = parse_options(command.slice(5..-1).split(/ +/), options)
|
|
# puts to_ruby(urls, options)
|
|
# end |