# frozen_string_literal: true
#
# ronin-support-web - A web support library for ronin-rb.
#
# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
#
# ronin-support-web is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-support-web is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-support-web. If not, see .
#
require 'ronin/support/web/agent'
module Ronin
module Support
module Web
class Agent
#
# Provides helper methods for performing high-level web requests.
#
# ## Features
#
# * Automatically follows redirects.
# * Provides high-level methods for requesting and parsing HTML, XML, or
# JSON.
# * Maintains a persistent connection pool.
#
# ## Anti-Features
#
# * Does not cache files or write to the disk.
# * Does not evaluate JavaScript.
#
module Mixin
#
# The web agent object.
#
# @return [Agent]
#
def web_agent
@web_agent ||= Agent.new
end
#
# @!macro request_kwargs
# @option kwargs [String, nil] :query
# The query-string to append to the request path.
#
# @option kwargs [Hash, nil] :query_params
# The query-params to append to the request path.
#
# @option kwargs [String, nil] :user
# The user to authenticate as.
#
# @option kwargs [String, nil] :password
# The password to authenticate with.
#
# @option kwargs [Hash{Symbol,String => String}, nil] :headers
# Additional HTTP headers to use for the request.
#
# @option kwargs [String, :text, :xml, :html, :json, nil] :content_type
# The `Content-Type` header value for the request.
# If a Symbol is given it will be resolved to a common MIME type:
# * `:text` - `text/plain`
# * `:xml` - `text/xml`
# * `:html` - `text/html`
# * `:json` - `application/json`
#
# @option kwargs [String, :text, :xml, :html, :json, nil] :accept
# The `Accept` header value for the request.
# If a Symbol is given it will be resolved to a common MIME type:
# * `:text` - `text/plain`
# * `:xml` - `text/xml`
# * `:html` - `text/html`
# * `:json` - `application/json`
#
# @option kwargs [String, Hash{String => String}, Ronin::Support::Network::HTTP::Cookie, nil] :cookie
# Additional `Cookie` header.
# * If a `Hash` is given, it will be converted to a `String` using
# [Ronin::Support::Network::HTTP::Cookie](https://ronin-rb.dev/docs/ronin-support/Ronin/Support/Network/HTTP/Cookie.html).
# * If the cookie value is empty, the `Cookie` header will not be
# set.
#
# @option kwargs [String, nil] :body
# The body of the request.
#
# @option kwargs [Hash, String, nil] :form_data
# The form data that may be sent in the body of the request.
#
# @option kwargs [#to_json, nil] :json
# The JSON data that will be sent in the body of the request.
# Will also default the `Content-Type` header to
# `application/json`, unless already set.
#
#
# Gets a URL and returns the response.
#
# @param [URI::HTTP, Addressable::URI, String] url
# The URL to create the HTTP GET request for.
#
# @!macro request_kwargs
#
# @yield [response]
# If a block is given it will be passed the received HTTP response.
#
# @yieldparam [Net::HTTPResponse] response
# The received HTTP response object.
#
# @return [Net::HTTPResponse]
# The HTTP response object.
#
# @raise [TooManyRedirects]
# Maximum number of redirects reached.
#
# @note This method will follow redirects by default.
#
# @example
# response = web_get('https://example.com/')
# # => #
#
def web_get(url,**kwargs,&block)
web_agent.get(url,**kwargs,&block)
end
alias get web_get
#
# Gets the URL and returns the parsed HTML.
#
# @param [URI::HTTP, Addressable::URI, String] url
# The URL to create the HTTP GET request for.
#
# @!macro request_kwargs
#
# @return [Nokogiri::HTML::Document]
# The parsed HTML response.
#
# @raise [ContentTypeError]
# Did not receive a response with a `Content-Type` of `text/html`.
#
# @raise [TooManyRedirects]
# Maximum number of redirects reached.
#
# @note This method will follow redirects by default.
#
# @example
# doc = web_get_html('https://example.com/page.html')
# # => #
#
def web_get_html(url,**kwargs)
web_agent.get_html(url,**kwargs)
end
alias get_html web_get_html
#
# Gets the URL and returns the parsed XML.
#
# @param [URI::HTTP, Addressable::URI, String] url
# The URL to create the HTTP GET request for.
#
# @!macro request_kwargs
#
# @return [Nokogiri::XML::Document]
# The parsed XML response.
#
# @raise [ContentTypeError]
# Did not receive a response with a `Content-Type` of `text/xml`.
#
# @raise [TooManyRedirects]
# Maximum number of redirects reached.
#
# @note This method will follow redirects by default.
#
# @example
# doc = web_get_xml('https://example.com/data.xml')
# # => #
#
def web_get_xml(url,**kwargs)
web_agent.get_xml(url,**kwargs)
end
alias get_xml web_get_xml
#
# Gets the URL and returns the parsed JSON.
#
# @param [URI::HTTP, Addressable::URI, String] url
# The URL to create the HTTP GET request for.
#
# @!macro request_kwargs
#
# @return [Hash{String => Object}, Array]
# The parsed JSON.
#
# @raise [ContentTypeError]
# Did not receive a response with a `Content-Type` of
# `application/json`.
#
# @raise [TooManyRedirects]
# Maximum number of redirects reached.
#
# @note This method will follow redirects by default.
#
# @example
# json = web_get_json('https://example.com/data.json')
# # => {...}
#
def web_get_json(url,**kwargs)
web_agent.get_json(url,**kwargs)
end
alias get_json web_get_json
#
# Performs an HTTP POST to the URL.
#
# @param [URI::HTTP, Addressable::URI, String] url
# The URL to create the HTTP GET request for.
#
# @!macro request_kwargs
#
# @yield [response]
# If a block is given it will be passed the received HTTP response.
#
# @yieldparam [Net::HTTPResponse] response
# The received HTTP response object.
#
# @return [Net::HTTPResponse]
# The HTTP response object.
#
# @raise [TooManyRedirects]
# Maximum number of redirects reached.
#
# @note
# If the response is an HTTP redirect, then {#get} will be called to
# follow any redirects.
#
# @example
# response = web_post('https://example.com/form', form_data: {'foo' => 'bar'})
# # => #
#
def web_post(url,**kwargs,&block)
web_agent.post(url,**kwargs,&block)
end
alias post web_post
#
# Performs an HTTP POST to the URL and parses the HTML response.
#
# @param [URI::HTTP, Addressable::URI, String] url
# The URL to create the HTTP POST request for.
#
# @!macro request_kwargs
#
# @return [Nokogiri::HTML::Document]
# The parsed HTML response.
#
# @raise [TooManyRedirects]
# Maximum number of redirects reached.
#
# @raise [ContentTypeError]
# Did not receive a response with a `Content-Type` of
# `text/html`.
#
# @note
# If the response is an HTTP redirect, then {#get} will be called to
# follow any redirects.
#
# @example Send a POST request and parses the HTML response:
# doc = web_post_html 'https://example.com/form', form_data: {foo: 'bar'})
# # => #
#
def web_post_html(url,**kwargs)
web_agent.post_html(url,**kwargs)
end
alias post_html web_post_html
#
# Performs an HTTP POST to the URL and parses the XML response.
#
# @param [URI::HTTP, Addressable::URI, String] url
# The URL to create the HTTP POST request for.
#
# @!macro request_kwargs
#
# @return [Nokogiri::XML::Document]
# The parsed XML response.
#
# @raise [TooManyRedirects]
# Maximum number of redirects reached.
#
# @raise [ContentTypeError]
# Did not receive a response with a `Content-Type` of
# `text/xml`.
#
# @note
# If the response is an HTTP redirect, then {#get} will be called to
# follow any redirects.
#
# @example Send a POST request to the form and parses the XML response:
# doc = web_post_xml 'https://example.com/form', form_data: {foo: 'bar'}
# # => #
#
def web_post_xml(url,**kwargs)
web_agent.post_xml(url,**kwargs)
end
alias post_xml web_post_xml
#
# Performs an HTTP POST to the URL and parses the JSON response.
#
# @param [URI::HTTP, Addressable::URI, String] url
# The URL to create the HTTP POST request for.
#
# @!macro request_kwargs
#
# @return [Hash{String => Object}, Array]
# The parses JSON response.
#
# @raise [TooManyRedirects]
# Maximum number of redirects reached.
#
# @raise [ContentTypeError]
# Did not receive a response with a `Content-Type` of
# `application/json`.
#
# @note
# If the response is an HTTP redirect, then {#get} will be called to
# follow any redirects.
#
# @example Send a POST request to the form and parse the JSON response:
# json = web_post_json 'https://example.com/form', form_data: {foo: 'bar'}
# # => {...}
#
# @example Send a POST request containing JSON and parse the JSON response:
# json = web_post_json 'https://example.com/api/end-point', json: {foo: 'bar'}
# # => {...}
#
def web_post_json(url,**kwargs)
web_agent.post_json(url,**kwargs)
end
alias post_json web_post_json
end
end
end
end
end