pax_global_header00006660000000000000000000000064136702244150014516gustar00rootroot0000000000000052 comment=0e8b82730a143c07c919bb8773f017338f637016 httparty-0.18.1/000077500000000000000000000000001367022441500134645ustar00rootroot00000000000000httparty-0.18.1/.editorconfig000066400000000000000000000005371367022441500161460ustar00rootroot00000000000000; This file is for unifying the coding style for different editors and IDEs. ; More information at http://EditorConfig.org root = true [*] end_of_line = lf trim_trailing_whitespace = true [**.rb] indent_size = 2 indent_style = spaces insert_final_newline = true [**.xml] trim_trailing_whitespace = false [**.html] trim_trailing_whitespace = false httparty-0.18.1/.gitignore000066400000000000000000000001361367022441500154540ustar00rootroot00000000000000Gemfile.lock .DS_Store .yardoc/ doc/ tmp/ log/ pkg/ *.swp /.bundle .rvmrc coverage *.gem .ideahttparty-0.18.1/.rubocop.yml000066400000000000000000000041321367022441500157360ustar00rootroot00000000000000inherit_from: .rubocop_todo.yml # Offense count: 963 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/StringLiterals: Enabled: false # Offense count: 327 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles. Style/SpaceInsideHashLiteralBraces: Enabled: false # Offense count: 33 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. Style/SpaceInsideBlockBraces: Enabled: false # Offense count: 1 # Cop supports --auto-correct. Style/SpaceBeforeSemicolon: Enabled: false # Offense count: 20 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/SignalException: Enabled: false # Offense count: 1 # Configuration parameters: Methods. Style/SingleLineBlockParams: Enabled: false # Offense count: 6 # Cop supports --auto-correct. Style/PerlBackrefs: Enabled: false # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: AllowAsExpressionSeparator. Style/Semicolon: Enabled: false # Offense count: 77 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/BracesAroundHashParameters: Enabled: false # Offense count: 36 Style/Documentation: Enabled: false # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. Style/RegexpLiteral: Enabled: false # Offense count: 5 # Cop supports --auto-correct. Style/NumericLiterals: MinDigits: 6 # Offense count: 4 # Cop supports --auto-correct. Lint/UnusedMethodArgument: Enabled: false # Offense count: 11 # Cop supports --auto-correct. Lint/UnusedBlockArgument: Enabled: false # Offense count: 1 Lint/Void: Enabled: false # Offense count: 22 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/IndentHash: Enabled: false # Offense count: 7 # Configuration parameters: MinBodyLength. Style/GuardClause: Enabled: false httparty-0.18.1/.rubocop_todo.yml000066400000000000000000000053651367022441500167740ustar00rootroot00000000000000# This configuration was generated by `rubocop --auto-gen-config` # on 2015-04-24 07:22:28 +0200 using RuboCop version 0.30.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 33 Lint/AmbiguousRegexpLiteral: Enabled: false # Offense count: 1 # Configuration parameters: AlignWith, SupportedStyles. Lint/EndAlignment: Enabled: false # Offense count: 1 Lint/SuppressedException: Enabled: false # Offense count: 5 Lint/UselessAssignment: Enabled: false # Offense count: 23 Metrics/AbcSize: Max: 86 # Offense count: 1 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 285 # Offense count: 8 Metrics/CyclomaticComplexity: Max: 17 # Offense count: 332 # Configuration parameters: AllowURI, URISchemes. Metrics/LineLength: Max: 266 # Offense count: 17 # Configuration parameters: CountComments. Metrics/MethodLength: Max: 39 # Offense count: 8 Metrics/PerceivedComplexity: Max: 20 # Offense count: 1 Style/AccessorMethodName: Enabled: false # Offense count: 1 Style/AsciiComments: Enabled: false # Offense count: 14 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods. Style/BlockDelimiters: Enabled: false # Offense count: 2 Style/CaseEquality: Enabled: false # Offense count: 3 # Configuration parameters: IndentWhenRelativeTo, SupportedStyles, IndentOneStep. Style/CaseIndentation: Enabled: false # Offense count: 4 # Configuration parameters: EnforcedStyle, SupportedStyles. Style/ClassAndModuleChildren: Enabled: false # Offense count: 7 Style/ConstantName: Enabled: false # Offense count: 2 Style/EachWithObject: Enabled: false # Offense count: 2 # Cop supports --auto-correct. Style/ElseAlignment: Enabled: false # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/FirstParameterIndentation: Enabled: false # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues. Style/HashSyntax: Enabled: false # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: MaxLineLength. Style/IfUnlessModifier: Enabled: false # Offense count: 11 # Cop supports --auto-correct. Style/Lambda: Enabled: false # Offense count: 1 # Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles. Style/Next: Enabled: false # Offense count: 2 # Configuration parameters: EnforcedStyle, SupportedStyles. Style/RaiseArgs: Enabled: false httparty-0.18.1/.simplecov000066400000000000000000000000411367022441500154610ustar00rootroot00000000000000SimpleCov.start "test_frameworks"httparty-0.18.1/.travis.yml000066400000000000000000000002551367022441500155770ustar00rootroot00000000000000language: ruby rvm: - 2.0.0 - 2.1.10 - 2.2.10 - 2.3.8 - 2.4.5 - 2.5.3 - 2.6.1 bundler_args: --without development before_install: gem install bundler -v '< 2' httparty-0.18.1/CONTRIBUTING.md000066400000000000000000000020441367022441500157150ustar00rootroot00000000000000# Contributing * Contributions will not be accepted without tests. * Please post unconfirmed bugs to the mailing list first: https://groups.google.com/forum/#!forum/httparty-gem * Don't change the version. The maintainers will handle that when they release. * Always provide as much information and reproducibility as possible when filing an issue or submitting a pull request. ## Workflow * Fork the project. * Run `bundle` * Run `bundle exec rake` * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Run `bundle exec rake` (No, REALLY :)) * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. ## Help and Docs * https://groups.google.com/forum/#!forum/httparty-gem * http://stackoverflow.com/questions/tagged/httparty * http://rdoc.info/projects/jnunemaker/httparty httparty-0.18.1/Changelog.md000066400000000000000000000526551367022441500157120ustar00rootroot00000000000000## 0.18.1 * [Rename cop Lint/HandleExceptions to Lint/SuppressedException](https://github.com/jnunemaker/httparty/pull/699). * [Encode keys in query params](https://github.com/jnunemaker/httparty/pull/698). * [Fixed SSL doc example](https://github.com/jnunemaker/httparty/pull/692). * [Add a build status badge](https://github.com/jnunemaker/httparty/pull/701). ## 0.18.0 * [Support gzip/deflate transfer encoding when explicit headers are set](https://github.com/jnunemaker/httparty/pull/678). * [Support edge case cookie format with a blank attribute](https://github.com/jnunemaker/httparty/pull/685). ## 0.17.3 0.17.2 is broken https://github.com/jnunemaker/httparty/issues/681 ## 0.17.2 * [Add Response#nil? deprecetion warning](https://github.com/jnunemaker/httparty/pull/680) ## 0.17.1 * [Pass options to dynamic block headers](https://github.com/jnunemaker/httparty/pull/661) * [Normalize urls with URI adapter to allow International Domain Names support](https://github.com/jnunemaker/httparty/pull/668) * [Add max_retries support](https://github.com/jnunemaker/httparty/pull/660) * [Minize gem size by removing test files](https://github.com/jnunemaker/httparty/pull/658) ## 0.17.0 * [Fix encoding of streamed chunk](https://github.com/jnunemaker/httparty/pull/644) * [Avoid modifying frozen strings](https://github.com/jnunemaker/httparty/pull/649) * [Expose .connection on fragment block param](https://github.com/jnunemaker/httparty/pull/648) * [Add support for `Net::HTTP#write_timeout` method (Ruby 2.6.0)](https://github.com/jnunemaker/httparty/pull/647) ## 0.16.4 * [Add support for Ruby 2.6](https://github.com/jnunemaker/httparty/pull/636) * [Fix a few multipart issues](https://github.com/jnunemaker/httparty/pull/626) * [Improve a memory usage for https requests](https://github.com/jnunemaker/httparty/pull/625) * [Add response code to streamed body](https://github.com/jnunemaker/httparty/pull/588) ## 0.16.3 * [Add Logstash-compatible formatter](https://github.com/jnunemaker/httparty/pull/612) * [Add support for headers specified with symbols](https://github.com/jnunemaker/httparty/pull/622) * [Fix response object marshalling](https://github.com/jnunemaker/httparty/pull/618) * [Add ability to send multipart, without passing file](https://github.com/jnunemaker/httparty/pull/615) * [Fix detection of content_type for multipart payload](https://github.com/jnunemaker/httparty/pull/616) * [Process dynamic headers before making actual request](https://github.com/jnunemaker/httparty/pull/606) * [Fix multipart uploads with ActionDispatch::Http::UploadedFile TempFile by using original_filename](https://github.com/jnunemaker/httparty/pull/598) * [Added support for lock and unlock http requests](https://github.com/jnunemaker/httparty/pull/596) ## 0.16.2 * [Support ActionDispatch::Http::UploadedFile again](https://github.com/jnunemaker/httparty/pull/585) ## 0.16.1 * [Parse content with application/hal+json content type as JSON](https://github.com/jnunemaker/httparty/pull/573) * [Convert objects to string when concatenating in multipart stuff](https://github.com/jnunemaker/httparty/pull/575) * [Fix multipart to set its header even when other headers are provided](https://github.com/jnunemaker/httparty/pull/576) ## 0.16.0 * [Add multipart support](https://github.com/jnunemaker/httparty/pull/569) ## 0.15.7 Fixed * [Add Response#pretty_print | Restore documented behavior](https://github.com/jnunemaker/httparty/pull/570) * [Add ability to parse response from JSONAPI ](https://github.com/jnunemaker/httparty/pull/553) ## 0.15.6 Fixed * [Encoding and content type stuff](https://github.com/jnunemaker/httparty/pull/543) ## 0.15.5 Fixed * [Use non-destructive gsub](https://github.com/jnunemaker/httparty/pull/540) ## 0.15.4 Fixed * Prevent gsub errors with different encodings. * Prevent passing nil to encode_body. ## 0.15.3 Fixed * [Fix processing nil body for HEAD requests](https://github.com/jnunemaker/httparty/pull/530). * Add missing require to headers.rb (33439a8). ## 0.15.2 Fixed * Remove symlink from specs. It was reportedly still getting bundled with gem. ## 0.15.1 Fixed * Stop including test files in gem. Fixes installation issues on windows due to symlink in spec dir. ## 0.15.0 Breaking Changes * require Ruby >= 2.0.0 Fixed * [fix numerous bugs](https://github.com/jnunemaker/httparty/pull/513) * [handle utf-8 bom for json parsing](https://github.com/jnunemaker/httparty/pull/520) * [do not overwrite default headers unless specified](https://github.com/jnunemaker/httparty/pull/518) ## 0.14.0 Breaking Changes * None Added * [added status predicate methods to Response#respond_to?](https://github.com/jnunemaker/httparty/pull/482) * [support for MKCOL method](https://github.com/jnunemaker/httparty/pull/465) * one fewer dependency: [remove json gem from gemspec](https://github.com/jnunemaker/httparty/pull/464) * [optional raising exception on certain status codes](https://github.com/jnunemaker/httparty/pull/455) Fixed * [allow empty array to be used as param](https://github.com/jnunemaker/httparty/pull/477) * [stop mutating cookie hash](https://github.com/jnunemaker/httparty/pull/460) ## 0.13.7 aka "party not as hard" * remove post install emoji as it caused installation issues for some people ## 0.13.6 * avoid calling String#strip on invalid Strings * preserve request method on 307 and 308 redirects * output version with --version for command line bin * maintain head request method across redirects by default * add support for RFC2617 MD5-sess algorithm type * add party popper emoji to post install message ## 0.13.5 * allow setting a custom URI adapter ## 0.13.4 * correct redirect url for redirect paths without leading slash * remove core_extensions.rb as backwards compat for ruby 1.8 not needed * replace URI.encode with ERB::Util.url_encode * allow the response to be tapped ## 0.13.3 * minor improvement * added option to allow for streaming large files without loading them into memory (672cdae) ## 0.13.2 * minor improvement * [Set correct path on redirect to filename](https://github.com/jnunemaker/httparty/pull/337) * ensure logger works with curl format ## 0.13.1 2014-04-08 * new * [Added ability to specify a body_stream in HttpRequest](https://github.com/jnunemaker/httparty/pull/275) * [Added read_timeout and open_timeout options](https://github.com/jnunemaker/httparty/pull/278) * change * [Initialize HTTParty requests with an URI object and a String](https://github.com/jnunemaker/httparty/pull/274) * minor improvement * [Add stackexchange API example](https://github.com/jnunemaker/httparty/pull/280) ## 0.13.0 2014-02-14 * new * [Add CSV support](https://github.com/jnunemaker/httparty/pull/269) * [Allows PKCS12 client certificates](https://github.com/jnunemaker/httparty/pull/246) * bug fix * [Digest auth no longer fails when multiple headers are sent by the server](https://github.com/jnunemaker/httparty/pull/272) * [Use 'Basement.copy' when calling 'HTTParty.copy'](https://github.com/jnunemaker/httparty/pull/268) * [No longer appends ampersand when queries are embedded in paths](https://github.com/jnunemaker/httparty/pull/252) * change * [Merge - instead of overwrite - default headers with request provided headers](https://github.com/jnunemaker/httparty/pull/270) * [Modernize respond_to implementations to support second param](https://github.com/jnunemaker/httparty/pull/264) * [Sort query parameters by key before processing](https://github.com/jnunemaker/httparty/pull/245) * minor improvement * [Add HTTParty::Error base class](https://github.com/jnunemaker/httparty/pull/260) ## 0.12.0 2013-10-10 * new * [Added initial logging support](https://github.com/jnunemaker/httparty/pull/243) * [Add support for local host and port binding](https://github.com/jnunemaker/httparty/pull/238) * [content_type_charset_support](https://github.com/jnunemaker/httparty/commit/82e351f0904e8ecc856015ff2854698a2ca47fbc) * bug fix * [No longer attempt to decompress the body on HEAD requests](https://github.com/jnunemaker/httparty/commit/f2b8cc3d49e0e9363d7054b14f30c340d7b8e7f1) * [Adding java check in aliasing of multiple choices](https://github.com/jnunemaker/httparty/pull/204/commits) * change * [MIME-type files of javascript are returned as a string instead of JSON](https://github.com/jnunemaker/httparty/pull/239) * [Made SSL connections use the system certificate store by default](https://github.com/jnunemaker/httparty/pull/226) * [Do not pass proxy options to Net::HTTP connection if not specified](https://github.com/jnunemaker/httparty/pull/222) * [Replace multi_json with stdlib json](https://github.com/jnunemaker/httparty/pull/214) * [Require Ruby >= 1.9.3] * [Response returns array of returned cookie strings](https://github.com/jnunemaker/httparty/pull/218) * [Allow '=' within value of a cookie] * minor improvements * [Improve documentation of ssl_ca_file, ssl_ca_path](https://github.com/jnunemaker/httparty/pull/223) * [Fix example URLs](https://github.com/jnunemaker/httparty/pull/232) ## 0.11.0 2013-04-10 * new * [Add COPY http request handling](https://github.com/jnunemaker/httparty/pull/190) * [Ruby 2.0 tests](https://github.com/jnunemaker/httparty/pull/194) * [Ruby >= 2.0.0 support both multiple_choice? and multiple_choices?] * bug fix * [Maintain blocks passed to 'perform' in redirects](https://github.com/jnunemaker/httparty/pull/191) * [Fixed nc value being quoted, this was against spec](https://github.com/jnunemaker/httparty/pull/196) * [Request#uri no longer duplicates non-relative-path params](https://github.com/jnunemaker/httparty/pull/189) * change * [Client-side-only cookie attributes are removed: case-insensitive](https://github.com/jnunemaker/httparty/pull/188) ## 0.10.2 2013-01-26 * bug fix * [hash_conversions misnamed variable](https://github.com/jnunemaker/httparty/pull/187) ## 0.10.1 2013-01-26 * new * [Added support for MOVE requests](https://github.com/jnunemaker/httparty/pull/183) * [Bump multi xml version](https://github.com/jnunemaker/httparty/pull/181) ## 0.10.0 2013-01-10 * changes * removed yaml support because of security risk (see rails yaml issues) ## 0.9.0 2012-09-07 * new * [support for connection adapters](https://github.com/jnunemaker/httparty/pull/157) * [allow ssl_version on ruby 1.9](https://github.com/jnunemaker/httparty/pull/159) * bug fixes * [don't treat port 4430 as ssl](https://github.com/jnunemaker/httparty/commit/a296b1c97f83d7dcc6ef85720a43664c265685ac) * [deep clone default options](https://github.com/jnunemaker/httparty/commit/f74227d30f9389b4b23a888c9af49fb9b8248e1f) * a few net digest auth fixes ## 0.8.3 2012-04-21 * new * [lazy parsing of responses](https://github.com/jnunemaker/httparty/commit/9fd5259c8dab00e426082b66af44ede2c9068f45) * [add support for PATCH requests](https://github.com/jnunemaker/httparty/commit/7ab6641e37a9e31517e46f6124f38c615395d38a) * bug fixes * [subclasses no longer override superclass options](https://github.com/jnunemaker/httparty/commit/682af8fbf672e7b3009e650da776c85cdfe78d39) ## 0.8.2 2012-04-12 * new * add -r to make CLI return failure code if status >= 400 * allow blank username from CLI * bug fixes * return nil for null body * automatically deflate responses with a Content-Encoding: x-gzip header * Do not HEAD on POST request with digest authentication * add support for proxy authentication * fix posting data with CLI * require rexml/document if xml format from CLI * support for fragmented responses ## 0.8.1 2011-10-05 * bug fixes * content-encoding header should be removed when automatically inflating the body ## 0.8.0 2011-09-13 * new * switch to multi json/xml for parsing by default * bug fixes * fix redirects to relative uri's ## 0.7.8 2011-06-06 * bug fix * Make response honor respond to * net http timeout can also be a float ## 0.7.7 2011-04-16 * bug fix * Fix NoMethodError when using the NON_RAILS_QUERY_STRING_NORMALIZER with a hash whose key is a symbol and value is nil ## 0.7.5 2011-04-16 * bug fix * caused issue with latest rubygems ## 0.7.4 2011-02-13 * bug fixes * Set VERIFY_NONE when using https. Ruby 1.9.2 no longer sets this for us. gh-67 ## 0.7.3 2011-01-20 * bug fixes * Fix digest auth for unspecified quality of protection (bjoernalbers, mtrudel, dwo) ## 0.7.2 2011-01-20 * bug fixes * Fix gem dependencies ## 0.7.1 2011-01-19 * bug fixes * Fix uninitialized constant HTTParty::Response::Net in 1.9.2 (cap10morgan) * Other fixes for 1.9.2, full suite still fails (cap10morgan) ## 0.7.0 2011-01-18 * minor enhancements * Added query methods for HTTP status codes, i.e. response.success? response.created? (thanks citizenparker) * Added support for ssl_ca_file and ssl_ca_path (dlitz) * Allow custom query string normalization. gh-8 * Unlock private keys with password (freerange) * Added high level request documentation (phildarnowsky) * Added basic post example (pbuckley) * Response object has access to its corresponding request object * Added example of siginin into tripit.com * Added option to follow redirects (rkj). gh-56 * bug fixes * Fixed superclass mismatch exception while running tests (thanks dlitz http://github.com/dlitz/httparty/commit/48224f0615b32133afcff4718ad426df7a4b401b) ## 0.6.1 2010-07-07 * minor enhancements * updated to crack 0.1.8 * bug fixes * subclasses always merge into the parent's default_options and default_cookies (l4rk). * subclasses play nicely with grand parents. gh-49 ## 0.6.0 2010-06-13 * major enhancements * Digest Auth (bartiaco, sbecker, gilles, and aaronrussell) * Maintain HTTP method across redirects (bartiaco and sbecker) * HTTParty::Response#response returns the Net::HTTPResponse object * HTTParty::Response#headers returns a HTTParty::Response::Headers object which quacks like a Hash + Net::HTTPHeader. The #headers method continues to be backwards-compatible with the old Hash return value but may become deprecated in the future. * minor enhancements * Update crack requirement to version 0.1.7 You may still get a warning because Crack's version constant is out of date * Timeout option can be set for all requests using HTTParty.default_timeout (taazza) * Closed #38 "headers hash should downcase keys so canonical header name can be used" * Closed #40 "Gzip response" wherein gziped and deflated responses are automatically inflated. (carsonmcdonald) ## 0.5.2 2010-01-31 * minor enhancements * Update crack requirement to version 0.1.6 ## 0.5.1 2010-01-30 * bug fixes * Handle 304 response correctly by returning the HTTParty::Response object instead of redirecting (seth and hellvinz) * Only redirect 300 responses if the header contains a Location * Don't append empty query strings to the uri. Closes #31 * When no_follow is enabled, only raise the RedirectionTooDeep exception when a response tries redirecting. Closes #28 * major enhancements * Removed rubygems dependency. I suggest adding rubygems to RUBYOPT if this causes problems for you. $ export RUBYOPT='rubygems' * HTTParty#debug_output prints debugging information for the current request (iwarshak) * HTTParty#no_follow now available as a class-level option. Sets whether or not to follow redirects. * minor enhancements * HTTParty::VERSION now available * Update crack requirement to version 0.1.5 ## 0.5.0 2009-12-07 * bug fixes * inheritable attributes no longer mutable by subclasses (yyyc514) * namespace BasicObject within HTTParty to avoid class name collisions (eric) * major enhancements * Custom Parsers via class or proc * Deprecation warning on HTTParty::AllowedFormats moved to HTTParty::Parser::SupportedFormats * minor enhancements * Curl inspired output when using the binary in verbose mode (alexvollmer) * raise UnsupportedURIScheme when scheme is not HTTP or HTTPS (djspinmonkey) * Allow SSL for ports other than 443 when scheme is HTTPS (stefankroes) * Accept PEM certificates via HTTParty#pem (chrislo) * Support HEAD and OPTION verbs (grempe) * Verify SSL certificates when providing a PEM file (collectiveidea/danielmorrison) ## 0.4.5 2009-09-12 * bug fixes * Fixed class-level headers overwritten by cookie management code. Closes #19 * Fixed "superclass mismatch for class BlankSlate" error. Closes #20 * Fixed reading files as post data from the command line (vesan) * minor enhancements * Timeout option added; will raise a Timeout::Error after the timeout has elapsed (attack). Closes #17 HTTParty.get "http://github.com", timeout: 1 * Building gem with Jeweler ## 0.4.4 2009-07-19 * 2 minor update * :query no longer sets form data. Use body and set content type to application/x-www-form-urlencoded if you need it. :query was wrong for that. * Fixed a bug in the cookies class method that caused cookies to be forgotten after the first request. * Also, some general cleanup of tests and such. ## 0.4.3 2009-04-23 * 1 minor update * added message to the response object ## 0.4.2 2009-03-30 * 2 minor changes * response code now returns an integer instead of a string (jqr) * rubyforge project setup for crack so i'm now depending on that instead of jnunemaker-crack ## 0.4.1 2009-03-29 * 1 minor fix * gem 'jnunemaker-crack' instead of gem 'crack' ## 0.4.0 2009-03-29 * 1 minor change * Switched xml and json parsing to crack (same code as before just moved to gem for easier reuse in other projects) ## 0.3.1 2009-02-10 * 1 minor fix, 1 minor enhancement * Fixed unescaping umlauts (siebertm) * Added yaml response parsing (Miha Filej) ## 0.3.0 2009-01-31 * 1 major enhancement, 1 bug fix * JSON gem no longer a requirement. It was conflicting with rails json stuff so I just stole ActiveSupport's json decoding and bundled it with HTTParty. * Fixed bug where query strings were being duplicated on redirects * Added a bunch of specs and moved some code around. ## 0.2.10 2009-01-29 * 1 minor enhancement * Made encoding on query parameters treat everything except URI::PATTERN::UNRESERVED as UNSAFE to force encoding of '+' character (Julian Russell) ## 0.2.9 2009-01-29 * 3 minor enhancements * Added a 'headers' accessor to the response with a hash of any HTTP headers. (Don Peterson) * Add support for a ":cookies" option to be used at the class level, or as an option on any individual call. It should be passed a hash, which will be converted to the proper format and added to the request headers when the call is made. (Don Peterson) * Refactored several specs and added a full suite of cucumber features (Don Peterson) ## 0.2.8 2009-01-28 * 1 major fix * fixed major bug with response where it wouldn't iterate or really work at all with parsed responses ## 0.2.7 2009-01-28 * 2 minor fixes, 2 minor enhancements, 2 major enhancements * fixed undefined method add_node for nil class error that occasionally happened (juliocesar) * Handle nil or unexpected values better when typecasting. (Brian Landau) * More robust handling of mime types (Alex Vollmer) * Fixed support for specifying headers and added support for basic auth to CLI. (Alex Vollmer) * Added first class response object that includes original body and status code (Alex Vollmer) * Now parsing all response types as some non-200 responses provide important information, this means no more exception raising (Alex Vollmer) ## 0.2.6 2009-01-05 * 1 minor bug fix * added explicit require of time as Time#parse failed outside of rails (willcodeforfoo) ## 0.2.5 2009-01-05 * 1 major enhancement * Add command line interface to HTTParty (Alex Vollmer) ## 0.2.4 2008-12-23 * 1 bug fix * Fixed that mimetype detection was failing if no mimetype was returned from service (skippy) ## 0.2.3 2008-12-23 * 1 bug fix * Fixed typecasting class variable naming issue ## 0.2.2 2008-12-08 * 1 bug fix * Added the missing core extension hash method to_xml_attributes ## 0.2.1 2008-12-08 * 1 bug fix * Fixed that HTTParty was borking ActiveSupport and as such Rails (thanks to Rob Sanheim) ## 0.2.0 2008-12-07 * 1 major enhancement * Removed ActiveSupport as a dependency. Now requires json gem for json deserialization and uses an included class to do the xml parsing. ## 0.1.8 2008-11-30 * 3 major enhancements * Moved base_uri normalization into request class and out of httparty module, fixing the problem where base_uri was not always being normalized. * Stupid simple support for HTTParty.get/post/put/delete. (jqr) * Switched gem management to Echoe from newgem. ## 0.1.7 2008-11-30 * 1 major enhancement * fixed multiple class definitions overriding each others options ## 0.1.6 2008-11-26 * 1 major enhancement * now passing :query to set_form_data if post request to avoid content length errors ## 0.1.5 2008-11-14 * 2 major enhancements * Refactored send request method out into its own object. * Added :html format if you just want to do that. ## 0.1.4 2008-11-08 * 3 major enhancements: * Removed some cruft * Added ability to follow redirects automatically and turn that off (Alex Vollmer) ## 0.1.3 2008-08-22 * 3 major enhancements: * Added http_proxy key for setting proxy server and port (francxk@gmail.com) * Now raises exception when http error occurs (francxk@gmail.com) * Changed auto format detection from file extension to response content type (Jay Pignata) ## 0.1.2 2008-08-09 * 1 major enhancement: * default_params were not being appended to query string if option[:query] was blank ## 0.1.1 2008-07-30 * 2 major enhancement: * Added :basic_auth key for options when making a request * :query and :body both now work with query string or hash ## 0.1.0 2008-07-27 * 1 major enhancement: * Initial release httparty-0.18.1/Gemfile000066400000000000000000000005561367022441500147650ustar00rootroot00000000000000source 'https://rubygems.org' gemspec gem 'rake' gem 'mongrel', '1.2.0.pre2' group :development do gem 'guard' gem 'guard-rspec' gem 'guard-bundler' end group :test do gem 'rspec', '~> 3.4' gem 'simplecov', require: false gem 'aruba' gem 'cucumber', '~> 2.3' gem 'webmock' gem 'addressable' end group :development, :test do gem 'pry' end httparty-0.18.1/Guardfile000066400000000000000000000005141367022441500153110ustar00rootroot00000000000000rspec_options = { version: 1, all_after_pass: false, all_on_start: false } guard 'rspec', rspec_options do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { "spec" } end guard 'bundler' do watch('Gemfile') watch(/^.+\.gemspec/) end httparty-0.18.1/MIT-LICENSE000066400000000000000000000020411367022441500151150ustar00rootroot00000000000000Copyright (c) 2008 John Nunemaker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.httparty-0.18.1/README.md000066400000000000000000000045541367022441500147530ustar00rootroot00000000000000# httparty [![Build Status](https://travis-ci.org/jnunemaker/httparty.svg?branch=master)](https://travis-ci.org/jnunemaker/httparty) Makes http fun again! Ain't no party like a httparty, because a httparty don't stop. ## Install ``` gem install httparty ``` ## Requirements * Ruby 2.0.0 or higher * multi_xml * You like to party! ## Examples ```ruby # Use the class methods to get down to business quickly response = HTTParty.get('http://api.stackexchange.com/2.2/questions?site=stackoverflow') puts response.body, response.code, response.message, response.headers.inspect # Or wrap things up in your own class class StackExchange include HTTParty base_uri 'api.stackexchange.com' def initialize(service, page) @options = { query: { site: service, page: page } } end def questions self.class.get("/2.2/questions", @options) end def users self.class.get("/2.2/users", @options) end end stack_exchange = StackExchange.new("stackoverflow", 1) puts stack_exchange.questions puts stack_exchange.users ``` See the [examples directory](http://github.com/jnunemaker/httparty/tree/master/examples) for even more goodies. ## Command Line Interface httparty also includes the executable `httparty` which can be used to query web services and examine the resulting output. By default it will output the response as a pretty-printed Ruby object (useful for grokking the structure of output). This can also be overridden to output formatted XML or JSON. Execute `httparty --help` for all the options. Below is an example of how easy it is. ``` httparty "https://api.stackexchange.com/2.2/questions?site=stackoverflow" ``` ## Help and Docs * [Docs](https://github.com/jnunemaker/httparty/tree/master/docs) * https://groups.google.com/forum/#!forum/httparty-gem * https://www.rubydoc.info/github/jnunemaker/httparty * http://stackoverflow.com/questions/tagged/httparty ## Contributing * Fork the project. * Run `bundle` * Run `bundle exec rake` * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Run `bundle exec rake` (No, REALLY :)) * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. httparty-0.18.1/Rakefile000066400000000000000000000003031367022441500151250ustar00rootroot00000000000000begin require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) rescue LoadError end require 'cucumber/rake/task' Cucumber::Rake::Task.new(:features) task default: [:spec, :features] httparty-0.18.1/bin/000077500000000000000000000000001367022441500142345ustar00rootroot00000000000000httparty-0.18.1/bin/httparty000077500000000000000000000060261367022441500160450ustar00rootroot00000000000000#!/usr/bin/env ruby require "optparse" require "pp" $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "/../lib")) require "httparty" opts = { action: :get, headers: {}, verbose: false } OptionParser.new do |o| o.banner = "USAGE: #{$PROGRAM_NAME} [options] [url]" o.on("-f", "--format [FORMAT]", "Output format to use instead of pretty-print ruby: " \ "plain, csv, json or xml") do |f| opts[:output_format] = f.downcase.to_sym end o.on("-a", "--action [ACTION]", "HTTP action: get (default), post, put, delete, head, or options") do |a| opts[:action] = a.downcase.to_sym end o.on("-d", "--data [BODY]", "Data to put in request body (prefix with '@' for file)") do |d| if d =~ /^@/ opts[:body] = open(d[1..-1]).read else opts[:body] = d end end o.on("-H", "--header [NAME:VALUE]", "Additional HTTP headers in NAME:VALUE form") do |h| abort "Invalid header specification, should be Name:Value" unless h =~ /.+:.+/ name, value = h.split(':') opts[:headers][name.strip] = value.strip end o.on("-v", "--verbose", "If set, print verbose output") do |v| opts[:verbose] = true end o.on("-u", "--user [CREDS]", "Use basic authentication. Value should be user:password") do |u| abort "Invalid credentials format. Must be user:password" unless u =~ /.*:.+/ user, password = u.split(':') opts[:basic_auth] = { username: user, password: password } end o.on("-r", "--response-code", "Command fails if response code >= 400") do opts[:response_code] = true end o.on("-h", "--help", "Show help documentation") do |h| puts o exit end o.on("--version", "Show HTTParty version") do |ver| puts "Version: #{HTTParty::VERSION}" exit end end.parse! if ARGV.empty? STDERR.puts "You need to provide a URL" STDERR.puts "USAGE: #{$PROGRAM_NAME} [options] [url]" end def dump_headers(response) resp_type = Net::HTTPResponse::CODE_TO_OBJ[response.code.to_s] puts "#{response.code} #{resp_type.to_s.sub(/^Net::HTTP/, '')}" response.headers.each do |n, v| puts "#{n}: #{v}" end puts end if opts[:verbose] puts "#{opts[:action].to_s.upcase} #{ARGV.first}" opts[:headers].each do |n, v| puts "#{n}: #{v}" end puts end response = HTTParty.send(opts[:action], ARGV.first, opts) if opts[:output_format].nil? dump_headers(response) if opts[:verbose] pp response else print_format = opts[:output_format] dump_headers(response) if opts[:verbose] case opts[:output_format] when :json begin require 'json' puts JSON.pretty_generate(response.parsed_response) rescue LoadError puts YAML.dump(response) rescue JSON::JSONError puts response.inspect end when :xml require 'rexml/document' REXML::Document.new(response.body).write(STDOUT, 2) puts when :csv require 'csv' puts CSV.parse(response.body).map(&:to_s) else puts response end end exit false if opts[:response_code] && response.code >= 400 httparty-0.18.1/cucumber.yml000066400000000000000000000000441367022441500160120ustar00rootroot00000000000000default: features --format progress httparty-0.18.1/docs/000077500000000000000000000000001367022441500144145ustar00rootroot00000000000000httparty-0.18.1/docs/README.md000066400000000000000000000044521367022441500157000ustar00rootroot00000000000000# httparty Makes http fun again! ## Table of contents - [Parsing JSON](#parsing-json) - [Working with SSL](#working-with-ssl) ## Parsing JSON If the response Content Type is `application/json`, HTTParty will parse the response and return Ruby objects such as a hash or array. The default behavior for parsing JSON will return keys as strings. This can be supressed with the `format` option. To get hash keys as symbols: ```ruby response = HTTParty.get('http://example.com', format: :plain) JSON.parse response, symbolize_names: true ``` ## Working with SSL You can use this guide to work with SSL certificates. #### Using `pem` option ```ruby # Use this example if you are using a pem file class Client include HTTParty base_uri "https://example.com" pem File.read("#{File.expand_path('.')}/path/to/certs/cert.pem"), "123456" end ``` #### Using `pkcs12` option ```ruby # Use this example if you are using a pkcs12 file class Client include HTTParty base_uri "https://example.com" pkcs12 File.read("#{File.expand_path('.')}/path/to/certs/cert.p12"), "123456" end ``` #### Using `ssl_ca_file` option ```ruby # Use this example if you are using a pkcs12 file class Client include HTTParty base_uri "https://example.com" ssl_ca_file "#{File.expand_path('.')}/path/to/certs/cert.pem" end ``` #### Using `ssl_ca_path` option ```ruby # Use this example if you are using a pkcs12 file class Client include HTTParty base_uri "https://example.com" ssl_ca_path '/path/to/certs' end ``` You can also include this options with the call: ```ruby class Client include HTTParty base_uri "https://example.com" def self.fetch get("/resources", pem: File.read("#{File.expand_path('.')}/path/to/certs/cert.pem"), pem_password: "123456") end end ``` ### Avoid SSL verification In some cases you may want to skip SSL verification, because the entity that issue the certificate is not a valid one, but you still want to work with it. You can achieve this through: ```ruby # Skips SSL certificate verification class Client include HTTParty base_uri "https://example.com" pem File.read("#{File.expand_path('.')}/path/to/certs/cert.pem"), "123456" def self.fetch get("/resources", verify: false) # You can also use something like: # get("resources", verify_peer: false) end end ``` httparty-0.18.1/examples/000077500000000000000000000000001367022441500153025ustar00rootroot00000000000000httparty-0.18.1/examples/README.md000066400000000000000000000046561367022441500165740ustar00rootroot00000000000000## Examples * [Amazon Book Search](aaws.rb) * Httparty included into poro class * Uses `get` requests * Transforms query params to uppercased params * [Google Search](google.rb) * Httparty included into poro class * Uses `get` requests * [Crack Custom Parser](crack.rb) * Creates a custom parser for XML using crack gem * Uses `get` request * [Create HTML Nokogiri parser](nokogiri_html_parser.rb) * Adds Html as a format * passed the body of request to Nokogiri * [More Custom Parsers](custom_parsers.rb) * Create an additional parser for atom or make it the ONLY parser * [Basic Auth, Delicious](delicious.rb) * Basic Auth, shows how to merge those into options * Uses `get` requests * [Passing Headers, User Agent](headers_and_user_agents.rb) * Use the class method of Httparty * Pass the User-Agent in the headers * Uses `get` requests * [Basic Post Request](basic.rb) * Httparty included into poro class * Uses `post` requests * [Access Rubyurl Shortener](rubyurl.rb) * Httparty included into poro class * Uses `post` requests * [Add a custom log file](logging.rb) * create a log file and have httparty log requests * [Accessing StackExchange](stackexchange.rb) * Httparty included into poro class * Creates methods for different endpoints * Uses `get` requests * [Accessing Tripit](tripit_sign_in.rb) * Httparty included into poro class * Example of using `debug_output` to see headers/urls passed * Getting and using Cookies * Uses `get` requests * [Accessing Twitter](twitter.rb) * Httparty included into poro class * Basic Auth * Loads settings from a config file * Uses `get` requests * Uses `post` requests * [Accessing WhoIsMyRep](whoismyrep.rb) * Httparty included into poro class * Uses `get` requests * Two ways to pass params to get, inline on the url or in query hash * [Rescue Json Error](rescue_json.rb) * Rescue errors due to parsing response * [Download file using stream mode](stream_download.rb) * Uses `get` requests * Uses `stream_body` mode * Download file without using the memory * [Microsoft graph](microsoft_graph.rb) * Basic Auth * Uses `post` requests * Uses multipart * [Multipart](multipart.rb) * Multipart data upload _(with and without file)_ * [Uploading File](body_stream.rb) * Uses `body_stream` to upload file * [Accessing x509 Peer Certificate](peer_cert.rb) * Provides access to the server's TLS certificate httparty-0.18.1/examples/aaws.rb000066400000000000000000000020131367022441500165560ustar00rootroot00000000000000require 'rubygems' require 'active_support' dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' config = YAML.load(File.read(File.join(ENV['HOME'], '.aaws'))) module AAWS class Book include HTTParty base_uri 'http://ecs.amazonaws.com' default_params Service: 'AWSECommerceService', Operation: 'ItemSearch', SearchIndex: 'Books' def initialize(key) self.class.default_params AWSAccessKeyId: key end def search(options = {}) raise ArgumentError, 'You must search for something' if options[:query].blank? # amazon uses nasty camelized query params options[:query] = options[:query].inject({}) { |h, q| h[q[0].to_s.camelize] = q[1]; h } # make a request and return the items (NOTE: this doesn't handle errors at this point) self.class.get('/onca/xml', options)['ItemSearchResponse']['Items'] end end end aaws = AAWS::Book.new(config[:access_key]) pp aaws.search(query: { title: 'Ruby On Rails' }) httparty-0.18.1/examples/basic.rb000066400000000000000000000014471367022441500167160ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' # You can also use post, put, delete, head, options in the same fashion response = HTTParty.get('https://api.stackexchange.com/2.2/questions?site=stackoverflow') puts response.body, response.code, response.message, response.headers.inspect # An example post to a minimal rails app in the development environment # Note that "skip_before_filter :verify_authenticity_token" must be set in the # "pears" controller for this example class Partay include HTTParty base_uri 'http://localhost:3000' end options = { body: { pear: { # your resource foo: '123', # your columns/data bar: 'second', baz: 'last thing' } } } pp Partay.post('/pears.xml', options) httparty-0.18.1/examples/body_stream.rb000066400000000000000000000004521367022441500201400ustar00rootroot00000000000000# To upload file to a server use :body_stream HTTParty.put( 'http://localhost:3000/train', body_stream: File.open('sample_configs/config_train_server_md.yml', 'r') ) # Actually, it works with any IO object HTTParty.put( 'http://localhost:3000/train', body_stream: StringIO.new('foo') ) httparty-0.18.1/examples/crack.rb000066400000000000000000000006541367022441500167170ustar00rootroot00000000000000require 'rubygems' require 'crack' dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' class Rep include HTTParty parser( proc do |body, format| Crack::XML.parse(body) end ) end pp Rep.get('http://whoismyrepresentative.com/getall_mems.php?zip=46544') pp Rep.get('http://whoismyrepresentative.com/getall_mems.php', query: { zip: 46544 }) httparty-0.18.1/examples/custom_parsers.rb000066400000000000000000000022021367022441500206740ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' class ParseAtom include HTTParty # Support Atom along with the default parsers: xml, json, etc. class Parser::Atom < HTTParty::Parser SupportedFormats.merge!({"application/atom+xml" => :atom}) protected # perform atom parsing on body def atom body.to_atom end end parser Parser::Atom end class OnlyParseAtom include HTTParty # Only support Atom class Parser::OnlyAtom < HTTParty::Parser SupportedFormats = { "application/atom+xml" => :atom } protected # perform atom parsing on body def atom body.to_atom end end parser Parser::OnlyAtom end class SkipParsing include HTTParty # Parse the response body however you like class Parser::Simple < HTTParty::Parser def parse body end end parser Parser::Simple end class AdHocParsing include HTTParty parser( proc do |body, format| case format when :json body.to_json when :xml body.to_xml else body end end ) end httparty-0.18.1/examples/delicious.rb000066400000000000000000000022231367022441500176060ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' config = YAML.load(File.read(File.join(ENV['HOME'], '.delicious'))) class Delicious include HTTParty base_uri 'https://api.del.icio.us/v1' def initialize(u, p) @auth = { username: u, password: p } end # query params that filter the posts are: # tag (optional). Filter by this tag. # dt (optional). Filter by this date (CCYY-MM-DDThh:mm:ssZ). # url (optional). Filter by this url. # ie: posts(query: {tag: 'ruby'}) def posts(options = {}) options.merge!({ basic_auth: @auth }) self.class.get('/posts/get', options) end # query params that filter the posts are: # tag (optional). Filter by this tag. # count (optional). Number of items to retrieve (Default:15, Maximum:100). def recent(options = {}) options.merge!({ basic_auth: @auth }) self.class.get('/posts/recent', options) end end delicious = Delicious.new(config['username'], config['password']) pp delicious.posts(query: { tag: 'ruby' }) pp delicious.recent delicious.recent['posts']['post'].each { |post| puts post['href'] } httparty-0.18.1/examples/google.rb000066400000000000000000000006021367022441500171010ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' class Google include HTTParty format :html end # google.com redirects to www.google.com so this is live test for redirection pp Google.get('http://google.com') puts '', '*' * 70, '' # check that ssl is requesting right pp Google.get('https://www.google.com') httparty-0.18.1/examples/headers_and_user_agents.rb000066400000000000000000000007021367022441500224620ustar00rootroot00000000000000# To send custom user agents to identify your application to a web service (or mask as a specific browser for testing), send "User-Agent" as a hash to headers as shown below. dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' response = HTTParty.get('http://example.com', { headers: {"User-Agent" => "Httparty"}, debug_output: STDOUT, # To show that User-Agent is Httparty }) httparty-0.18.1/examples/logging.rb000066400000000000000000000017421367022441500172610ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'logger' require 'pp' my_logger = Logger.new STDOUT my_logger.info "Logging can be used on the main HTTParty class. It logs redirects too." HTTParty.get "http://google.com", logger: my_logger my_logger.info '*' * 70 my_logger.info "It can be used also on a custom class." class Google include HTTParty logger ::Logger.new STDOUT end Google.get "http://google.com" my_logger.info '*' * 70 my_logger.info "The default formatter is :apache. The :curl formatter can also be used." my_logger.info "You can tell which method to call on the logger too. It is info by default." HTTParty.get "http://google.com", logger: my_logger, log_level: :debug, log_format: :curl my_logger.info '*' * 70 my_logger.info "These configs are also available on custom classes." class Google include HTTParty logger ::Logger.new(STDOUT), :debug, :curl end Google.get "http://google.com" httparty-0.18.1/examples/microsoft_graph.rb000066400000000000000000000030221367022441500210120ustar00rootroot00000000000000require 'httparty' class MicrosoftGraph MS_BASE_URL = "https://login.microsoftonline.com".freeze TOKEN_REQUEST_PATH = "oauth2/v2.0/token".freeze def initialize(tenant_id) @tenant_id = tenant_id end # Make a request to the Microsoft Graph API, for instance https://graph.microsoft.com/v1.0/users def request(url) return false unless (token = bearer_token) response = HTTParty.get( url, headers: { Authorization: "Bearer #{token}" } ) return false unless response.code == 200 return JSON.parse(response.body) end private # A post to the Microsoft Graph to get a bearer token for the specified tenant. In this example # our Rails application has already been given permission to request these tokens by the admin of # the specified tenant_id. # # See here for more information https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_service # # This request also makes use of the multipart/form-data post body. def bearer_token response = HTTParty.post( "#{MS_BASE_URL}/#{@tenant_id}/#{TOKEN_REQUEST_PATH}", multipart: true, body: { client_id: Rails.application.credentials[Rails.env.to_sym][:microsoft_client_id], client_secret: Rails.application.credentials[Rails.env.to_sym][:microsoft_client_secret], scope: 'https://graph.microsoft.com/.default', grant_type: 'client_credentials' } ) return false unless response.code == 200 JSON.parse(response.body)['access_token'] end end httparty-0.18.1/examples/multipart.rb000066400000000000000000000006561367022441500176570ustar00rootroot00000000000000# If you are uploading file in params, multipart will used as content-type automatically HTTParty.post( 'http://localhost:3000/user', body: { name: 'Foo Bar', email: 'example@email.com', avatar: File.open('/full/path/to/avatar.jpg') } ) # However, you can force it yourself HTTParty.post( 'http://localhost:3000/user', multipart: true, body: { name: 'Foo Bar', email: 'example@email.com' } ) httparty-0.18.1/examples/nokogiri_html_parser.rb000066400000000000000000000005361367022441500220540ustar00rootroot00000000000000require 'rubygems' require 'nokogiri' dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' class HtmlParserIncluded < HTTParty::Parser def html Nokogiri::HTML(body) end end class Page include HTTParty parser HtmlParserIncluded end pp Page.get('http://www.google.com') httparty-0.18.1/examples/peer_cert.rb000066400000000000000000000004431367022441500176000ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') peer_cert = nil HTTParty.get("https://www.example.com") do |fragment| peer_cert ||= fragment.connection.peer_cert end puts "The server's certificate expires #{peer_cert.not_after}" httparty-0.18.1/examples/rescue_json.rb000066400000000000000000000012061367022441500201450ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') # Take note of the "; 1" at the end of the following line. It's required only if # running this in IRB, because IRB will try to inspect the variable named # "request", triggering the exception. request = HTTParty.get 'https://rubygems.org/api/v1/versions/doesnotexist.json' ; 1 # Check an exception due to parsing the response # because HTTParty evaluate the response lazily begin request.inspect # This would also suffice by forcing the request to be parsed: # request.parsed_response rescue => e puts "Rescued #{e.inspect}" end httparty-0.18.1/examples/rubyurl.rb000066400000000000000000000005311367022441500173320ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' class Rubyurl include HTTParty base_uri 'rubyurl.com' def self.shorten(website_url) post('/api/links.json', query: { link: { website_url: website_url } }) end end pp Rubyurl.shorten('http://istwitterdown.com/') httparty-0.18.1/examples/stackexchange.rb000066400000000000000000000010251367022441500204350ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' class StackExchange include HTTParty base_uri 'api.stackexchange.com' def initialize(service, page) @options = { query: { site: service, page: page } } end def questions self.class.get("/2.2/questions", @options) end def users self.class.get("/2.2/users", @options) end end stack_exchange = StackExchange.new("stackoverflow", 1) pp stack_exchange.questions pp stack_exchange.users httparty-0.18.1/examples/stream_download.rb000066400000000000000000000013631367022441500210140ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' # download file linux-4.6.4.tar.xz without using the memory response = nil filename = "linux-4.6.4.tar.xz" url = "https://cdn.kernel.org/pub/linux/kernel/v4.x/#{filename}" File.open(filename, "w") do |file| response = HTTParty.get(url, stream_body: true) do |fragment| if [301, 302].include?(fragment.code) print "skip writing for redirect" elsif fragment.code == 200 print "." file.write(fragment) else raise StandardError, "Non-success status code while streaming #{fragment.code}" end end end puts pp "Success: #{response.success?}" pp File.stat(filename).inspect File.unlink(filename) httparty-0.18.1/examples/tripit_sign_in.rb000066400000000000000000000021431367022441500206500ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') class TripIt include HTTParty base_uri 'https://www.tripit.com' debug_output def initialize(email, password) @email = email get_response = self.class.get('/account/login') get_response_cookie = parse_cookie(get_response.headers['Set-Cookie']) post_response = self.class.post( '/account/login', body: { login_email_address: email, login_password: password }, headers: {'Cookie' => get_response_cookie.to_cookie_string } ) @cookie = parse_cookie(post_response.headers['Set-Cookie']) end def account_settings self.class.get('/account/edit', headers: { 'Cookie' => @cookie.to_cookie_string }) end def logged_in? account_settings.include? "You're logged in as #{@email}" end private def parse_cookie(resp) cookie_hash = CookieHash.new resp.get_fields('Set-Cookie').each { |c| cookie_hash.add_cookies(c) } cookie_hash end end tripit = TripIt.new('email', 'password') puts "Logged in: #{tripit.logged_in?}" httparty-0.18.1/examples/twitter.rb000066400000000000000000000017201367022441500173310ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' config = YAML.load(File.read(File.join(ENV['HOME'], '.twitter'))) class Twitter include HTTParty base_uri 'twitter.com' def initialize(u, p) @auth = {username: u, password: p} end # which can be :friends, :user or :public # options[:query] can be things like since, since_id, count, etc. def timeline(which = :friends, options = {}) options.merge!({ basic_auth: @auth }) self.class.get("/statuses/#{which}_timeline.json", options) end def post(text) options = { query: { status: text }, basic_auth: @auth } self.class.post('/statuses/update.json', options) end end twitter = Twitter.new(config['email'], config['password']) pp twitter.timeline # pp twitter.timeline(:friends, query: {since_id: 868482746}) # pp twitter.timeline(:friends, query: 'since_id=868482746') # pp twitter.post('this is a test of 0.2.0') httparty-0.18.1/examples/whoismyrep.rb000066400000000000000000000004711367022441500200370ustar00rootroot00000000000000dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) require File.join(dir, 'httparty') require 'pp' class Rep include HTTParty end pp Rep.get('http://whoismyrepresentative.com/getall_mems.php?zip=46544') pp Rep.get('http://whoismyrepresentative.com/getall_mems.php', query: { zip: 46544 }) httparty-0.18.1/features/000077500000000000000000000000001367022441500153025ustar00rootroot00000000000000httparty-0.18.1/features/basic_authentication.feature000066400000000000000000000016751367022441500230500ustar00rootroot00000000000000Feature: Basic Authentication As a developer I want to be able to use a service that requires Basic Authentication Because that is not an uncommon requirement Scenario: Passing no credentials to a page requiring Basic Authentication Given a restricted page at '/basic_auth.html' When I call HTTParty#get with '/basic_auth.html' Then it should return a response with a 401 response code Scenario: Passing proper credentials to a page requiring Basic Authentication Given a remote service that returns 'Authenticated Page' And that service is accessed at the path '/basic_auth.html' And that service is protected by Basic Authentication And that service requires the username 'jcash' with the password 'maninblack' When I call HTTParty#get with '/basic_auth.html' and a basic_auth hash: | username | password | | jcash | maninblack | Then the return value should match 'Authenticated Page' httparty-0.18.1/features/command_line.feature000066400000000000000000000102611367022441500213040ustar00rootroot00000000000000@command_line Feature: Command Line As a developer I want to be able to harness the power of HTTParty from the command line Because that would make quick testing and debugging easy Scenario: Show help information When I run `httparty --help` Then the output should contain "-f, --format [FORMAT]" Scenario: Show current version When I run `httparty --version` Then the output should contain "Version:" And the output should not contain "You need to provide a URL" Scenario: Make a get request Given a remote deflate service on port '4001' And the response from the service has a body of 'GET request' And that service is accessed at the path '/fun' When I run `httparty http://0.0.0.0:4001/fun` Then the output should contain "GET request" Scenario: Make a post request Given a remote deflate service on port '4002' And the response from the service has a body of 'POST request' And that service is accessed at the path '/fun' When I run `httparty http://0.0.0.0:4002/fun --action post --data "a=1&b=2"` Then the output should contain "POST request" Scenario: Make a put request Given a remote deflate service on port '4003' And the response from the service has a body of 'PUT request' And that service is accessed at the path '/fun' When I run `httparty http://0.0.0.0:4003/fun --action put --data "a=1&b=2"` Then the output should contain "PUT request" Scenario: Make a delete request Given a remote deflate service on port '4004' And the response from the service has a body of 'DELETE request' And that service is accessed at the path '/fun' When I run `httparty http://0.0.0.0:4004/fun --action delete` Then the output should contain "DELETE request" Scenario: Set a verbose mode Given a remote deflate service on port '4005' And the response from the service has a body of 'Some request' And that service is accessed at the path '/fun' When I run `httparty http://0.0.0.0:4005/fun --verbose` Then the output should contain "content-length" Scenario: Use service with basic authentication Given a remote deflate service on port '4006' And the response from the service has a body of 'Successfull authentication' And that service is accessed at the path '/fun' And that service is protected by Basic Authentication And that service requires the username 'user' with the password 'pass' When I run `httparty http://0.0.0.0:4006/fun --user 'user:pass'` Then the output should contain "Successfull authentication" Scenario: Get response in plain format Given a remote deflate service on port '4007' And the response from the service has a body of 'Some request' And that service is accessed at the path '/fun' When I run `httparty http://0.0.0.0:4007/fun --format plain` Then the output should contain "Some request" Scenario: Get response in json format Given a remote deflate service on port '4008' Given a remote service that returns '{ "jennings": "waylon", "cash": "johnny" }' And that service is accessed at the path '/service.json' And the response from the service has a Content-Type of 'application/json' When I run `httparty http://0.0.0.0:4008/service.json --format json` Then the output should contain '"jennings": "waylon"' Scenario: Get response in xml format Given a remote deflate service on port '4009' Given a remote service that returns 'waylon jennings' And that service is accessed at the path '/service.xml' And the response from the service has a Content-Type of 'text/xml' When I run `httparty http://0.0.0.0:4009/service.xml --format xml` Then the output should contain "" Scenario: Get response in csv format Given a remote deflate service on port '4010' Given a remote service that returns: """ "Last Name","Name" "jennings","waylon" "cash","johnny" """ And that service is accessed at the path '/service.csv' And the response from the service has a Content-Type of 'application/csv' When I run `httparty http://0.0.0.0:4010/service.csv --format csv` Then the output should contain '["Last Name", "Name"]' httparty-0.18.1/features/deals_with_http_error_codes.feature000066400000000000000000000022401367022441500244250ustar00rootroot00000000000000Feature: Deals with HTTP error codes As a developer I want to be informed of non-successful responses Because sometimes thing explode And I should probably know what happened Scenario: A response of '404 - Not Found' Given a remote service that returns a 404 status code And that service is accessed at the path '/404_service.html' When I call HTTParty#get with '/404_service.html' Then it should return a response with a 404 response code Scenario: A response of '500 - Internal Server Error' Given a remote service that returns a 500 status code And that service is accessed at the path '/500_service.html' When I call HTTParty#get with '/500_service.html' Then it should return a response with a 500 response code Scenario: A non-successful response where I need the body Given a remote service that returns a 400 status code And the response from the service has a body of 'Bad response' And that service is accessed at the path '/400_service.html' When I call HTTParty#get with '/400_service.html' Then it should return a response with a 400 response code And the return value should match 'Bad response' httparty-0.18.1/features/digest_authentication.feature000066400000000000000000000031141367022441500232340ustar00rootroot00000000000000Feature: Digest Authentication As a developer I want to be able to use a service that requires Digest Authentication Because that is not an uncommon requirement Scenario: Passing no credentials to a page requiring Digest Authentication Given a restricted page at '/digest_auth.html' When I call HTTParty#get with '/digest_auth.html' Then it should return a response with a 401 response code Scenario: Passing proper credentials to a page requiring Digest Authentication Given a remote service that returns 'Digest Authenticated Page' And that service is accessed at the path '/digest_auth.html' And that service is protected by Digest Authentication And that service requires the username 'jcash' with the password 'maninblack' When I call HTTParty#get with '/digest_auth.html' and a digest_auth hash: | username | password | | jcash | maninblack | Then the return value should match 'Digest Authenticated Page' Scenario: Passing proper credentials to a page requiring Digest Authentication using md5-sess algorithm Given a remote service that returns 'Digest Authenticated Page Using MD5-sess' And that service is accessed at the path '/digest_auth.html' And that service is protected by MD5-sess Digest Authentication And that service requires the username 'jcash' with the password 'maninblack' When I call HTTParty#get with '/digest_auth.html' and a digest_auth hash: | username | password | | jcash | maninblack | Then the return value should match 'Digest Authenticated Page Using MD5-sess' httparty-0.18.1/features/handles_compressed_responses.feature000066400000000000000000000036061367022441500246270ustar00rootroot00000000000000Feature: Handles Compressed Responses In order to save bandwidth As a developer I want to leverage Net::Http's built in transparent support for gzip and deflate content encoding Scenario: Supports deflate encoding Given a remote deflate service And the response from the service has a body of '

Some HTML

' And that service is accessed at the path '/deflate_service.html' When I call HTTParty#get with '/deflate_service.html' Then the return value should match '

Some HTML

' And it should return a response without a content-encoding Scenario: Supports gzip encoding Given a remote gzip service And the response from the service has a body of '

Some HTML

' And that service is accessed at the path '/gzip_service.html' When I call HTTParty#get with '/gzip_service.html' Then the return value should match '

Some HTML

' And it should return a response without a content-encoding Scenario: Supports gzip encoding with explicit header set Given a remote gzip service And the response from the service has a body of '

Some HTML

' And that service is accessed at the path '/gzip_service.html' When I set my HTTParty header 'User-Agent' to value 'Party' And I call HTTParty#get with '/gzip_service.html' Then the return value should match '

Some HTML

' And it should return a response without a content-encoding Scenario: Supports deflate encoding with explicit header set Given a remote deflate service And the response from the service has a body of '

Some HTML

' And that service is accessed at the path '/deflate_service.html' When I set my HTTParty header 'User-Agent' to value 'Party' And I call HTTParty#get with '/deflate_service.html' Then the return value should match '

Some HTML

' And it should return a response without a content-encoding httparty-0.18.1/features/handles_multiple_formats.feature000066400000000000000000000045131367022441500237460ustar00rootroot00000000000000Feature: Handles Multiple Formats As a developer I want to be able to consume remote services of many different formats And I want those formats to be automatically detected and handled Because web services take many forms And I don't want to have to do any extra work Scenario: An HTML service Given a remote service that returns '

Some HTML

' And that service is accessed at the path '/html_service.html' And the response from the service has a Content-Type of 'text/html' When I call HTTParty#get with '/html_service.html' Then it should return a String And the return value should match '

Some HTML

' Scenario: A CSV service Given a remote service that returns: """ "Last Name","Name" "jennings","waylon" "cash","johnny" """ And that service is accessed at the path '/service.csv' And the response from the service has a Content-Type of 'application/csv' When I call HTTParty#get with '/service.csv' Then it should return an Array equaling: | Last Name | Name | | jennings | waylon | | cash | johnny | Scenario: A JSON service Given a remote service that returns '{ "jennings": "waylon", "cash": "johnny" }' And that service is accessed at the path '/service.json' And the response from the service has a Content-Type of 'application/json' When I call HTTParty#get with '/service.json' Then it should return a Hash equaling: | key | value | | jennings | waylon | | cash | johnny | Scenario: An XML Service Given a remote service that returns 'waylon jennings' And that service is accessed at the path '/service.xml' And the response from the service has a Content-Type of 'text/xml' When I call HTTParty#get with '/service.xml' Then it should return a Hash equaling: | key | value | | singer | waylon jennings | Scenario: A Javascript remote file Given a remote service that returns '$(function() { alert("hi"); });' And that service is accessed at the path '/service.js' And the response from the service has a Content-Type of 'application/javascript' When I call HTTParty#get with '/service.js' Then it should return a String And the return value should match '$(function() { alert("hi"); });' httparty-0.18.1/features/steps/000077500000000000000000000000001367022441500164405ustar00rootroot00000000000000httparty-0.18.1/features/steps/env.rb000066400000000000000000000007521367022441500175610ustar00rootroot00000000000000require 'mongrel' require './lib/httparty' require 'rspec/expectations' require 'aruba/cucumber' def run_server(port) @host_and_port = "0.0.0.0:#{port}" @server = Mongrel::HttpServer.new("0.0.0.0", port) @server.run @request_options = {} end def new_port server = TCPServer.new('0.0.0.0', nil) port = server.addr[1] ensure server.close end Before('~@command_line') do port = ENV["HTTPARTY_PORT"] || new_port run_server(port) end After do @server.stop if @server end httparty-0.18.1/features/steps/httparty_response_steps.rb000066400000000000000000000035221367022441500240020ustar00rootroot00000000000000# Not needed anymore in ruby 2.0, but needed to resolve constants # in nested namespaces. This is taken from rails :) def constantize(camel_cased_word) names = camel_cased_word.split('::') names.shift if names.empty? || names.first.empty? constant = Object names.each do |name| constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name) end constant end Then /it should return an? ([\w\:]+)$/ do |class_string| expect(@response_from_httparty.parsed_response).to be_a(Object.const_get(class_string)) end Then /the return value should match '(.*)'/ do |expected_text| expect(@response_from_httparty.parsed_response).to eq(expected_text) end Then /it should return a Hash equaling:/ do |hash_table| expect(@response_from_httparty.parsed_response).to be_a(Hash) expect(@response_from_httparty.keys.length).to eq(hash_table.rows.length) hash_table.hashes.each do |pair| key, value = pair["key"], pair["value"] expect(@response_from_httparty.keys).to include(key) expect(@response_from_httparty[key]).to eq(value) end end Then /it should return an Array equaling:/ do |array| expect(@response_from_httparty.parsed_response).to be_a(Array) expect(@response_from_httparty.parsed_response).to eq(array.raw) end Then /it should return a response with a (\d+) response code/ do |code| expect(@response_from_httparty.code).to eq(code.to_i) end Then /it should return a response without a content\-encoding$/ do expect(@response_from_httparty.headers['content-encoding']).to be_nil end Then /it should raise (?:an|a) ([\w:]+) exception/ do |exception| expect(@exception_from_httparty).to_not be_nil expect(@exception_from_httparty).to be_a constantize(exception) end Then /it should not raise (?:an|a) ([\w:]+) exception/ do |exception| expect(@exception_from_httparty).to be_nil end httparty-0.18.1/features/steps/httparty_steps.rb000066400000000000000000000030451367022441500220640ustar00rootroot00000000000000When /^I set my HTTParty timeout option to (\d+)$/ do |timeout| @request_options[:timeout] = timeout.to_i end When /^I set my HTTParty open_timeout option to (\d+)$/ do |timeout| @request_options[:open_timeout] = timeout.to_i end When /^I set my HTTParty read_timeout option to (\d+)$/ do |timeout| @request_options[:read_timeout] = timeout.to_i end When /^I set my HTTParty header '(.*)' to value '(.*)'$/ do |name, value| @request_options[:headers] ||= {} @request_options[:headers][name] = value end When /I call HTTParty#get with '(.*)'$/ do |url| begin @response_from_httparty = HTTParty.get("http://#{@host_and_port}#{url}", @request_options) rescue HTTParty::RedirectionTooDeep, Timeout::Error => e @exception_from_httparty = e end end When /^I call HTTParty#head with '(.*)'$/ do |url| begin @response_from_httparty = HTTParty.head("http://#{@host_and_port}#{url}", @request_options) rescue HTTParty::RedirectionTooDeep, Timeout::Error => e @exception_from_httparty = e end end When /I call HTTParty#get with '(.*)' and a basic_auth hash:/ do |url, auth_table| h = auth_table.hashes.first @response_from_httparty = HTTParty.get( "http://#{@host_and_port}#{url}", basic_auth: { username: h["username"], password: h["password"] } ) end When /I call HTTParty#get with '(.*)' and a digest_auth hash:/ do |url, auth_table| h = auth_table.hashes.first @response_from_httparty = HTTParty.get( "http://#{@host_and_port}#{url}", digest_auth: { username: h["username"], password: h["password"] } ) end httparty-0.18.1/features/steps/mongrel_helper.rb000066400000000000000000000075061367022441500217770ustar00rootroot00000000000000require 'base64' class BasicMongrelHandler < Mongrel::HttpHandler attr_accessor :content_type, :custom_headers, :response_body, :response_code, :preprocessor, :username, :password def initialize @content_type = "text/html" @response_body = "" @response_code = 200 @custom_headers = {} end def process(request, response) instance_eval(&preprocessor) if preprocessor reply_with(response, response_code, response_body) end def reply_with(response, code, response_body) response.start(code) do |head, body| head["Content-Type"] = content_type custom_headers.each { |k, v| head[k] = v } body.write(response_body) end end end class DeflateHandler < BasicMongrelHandler def process(request, response) accept_encoding = request.params["HTTP_ACCEPT_ENCODING"] if accept_encoding.nil? || !accept_encoding.include?('deflate') reply_with(response, 406, 'No deflate accept encoding found in request') else response.start do |head, body| head['Content-Encoding'] = 'deflate' body.write Zlib::Deflate.deflate(response_body) end end end end class GzipHandler < BasicMongrelHandler def process(request, response) accept_encoding = request.params["HTTP_ACCEPT_ENCODING"] if accept_encoding.nil? || !accept_encoding.include?('gzip') reply_with(response, 406, 'No gzip accept encoding found in request') else response.start do |head, body| head['Content-Encoding'] = 'gzip' body.write gzip(response_body) end end end protected def gzip(string) sio = StringIO.new('', 'r+') gz = Zlib::GzipWriter.new sio gz.write string gz.finish sio.rewind sio.read end end module BasicAuthentication def self.extended(base) base.custom_headers["WWW-Authenticate"] = 'Basic Realm="Super Secret Page"' end def process(request, response) if authorized?(request) super else reply_with(response, 401, "Incorrect. You have 20 seconds to comply.") end end def authorized?(request) request.params["HTTP_AUTHORIZATION"] == "Basic " + Base64.encode64("#{@username}:#{@password}").strip end end module DigestAuthentication def self.extended(base) base.custom_headers["WWW-Authenticate"] = 'Digest realm="testrealm@host.com",qop="auth,auth-int",nonce="nonce",opaque="opaque"' end def process(request, response) if authorized?(request) super else reply_with(response, 401, "Incorrect. You have 20 seconds to comply.") end end def authorized?(request) request.params["HTTP_AUTHORIZATION"] =~ /Digest.*uri=/ end end module DigestAuthenticationUsingMD5Sess NONCE = 'nonce' REALM = 'testrealm@host.com' QOP = 'auth,auth-int' def self.extended(base) base.custom_headers["WWW-Authenticate"] = %(Digest realm="#{REALM}",qop="#{QOP}",algorithm="MD5-sess",nonce="#{NONCE}",opaque="opaque"') end def process(request, response) if authorized?(request) super else reply_with(response, 401, "Incorrect. You have 20 seconds to comply.") end end def md5(str) Digest::MD5.hexdigest(str) end def authorized?(request) auth = request.params["HTTP_AUTHORIZATION"] params = {} auth.to_s.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }.gsub(/(\w+)=([^,]*)/) { params[$1] = $2 } a1a = [@username,REALM,@password].join(':') a1 = [md5(a1a),NONCE,params['cnonce'] ].join(':') a2 = [ request.params["REQUEST_METHOD"], request.params["REQUEST_URI"] ] .join(':') expected_response = md5( [md5(a1), NONCE, params['nc'], params['cnonce'], QOP, md5(a2)].join(':') ) expected_response == params['response'] end end def new_mongrel_redirector(target_url, relative_path = false) target_url = "http://#{@host_and_port}#{target_url}" unless relative_path Mongrel::RedirectHandler.new(target_url) end httparty-0.18.1/features/steps/remote_service_steps.rb000066400000000000000000000053501367022441500232210ustar00rootroot00000000000000Given /a remote service that returns '(.*)'/ do |response_body| @handler = BasicMongrelHandler.new step "the response from the service has a body of '#{response_body}'" end Given /^a remote service that returns:$/ do |response_body| @handler = BasicMongrelHandler.new @handler.response_body = response_body end Given /a remote service that returns a (\d+) status code/ do |code| @handler = BasicMongrelHandler.new @handler.response_code = code end Given /that service is accessed at the path '(.*)'/ do |path| @server.register(path, @handler) end Given /^that service takes (\d+) (.*) to generate a response$/ do |time, unit| time = time.to_i time *= 60 if unit =~ /minute/ @server_response_time = time @handler.preprocessor = proc { sleep time } end Given /^a remote deflate service$/ do @handler = DeflateHandler.new end Given /^a remote deflate service on port '(\d+)'/ do |port| run_server(port) @handler = DeflateHandler.new end Given /^a remote gzip service$/ do @handler = GzipHandler.new end Given /the response from the service has a Content-Type of '(.*)'/ do |content_type| @handler.content_type = content_type end Given /the response from the service has a body of '(.*)'/ do |response_body| @handler.response_body = response_body end Given /the url '(.*)' redirects to '(.*)'/ do |redirection_url, target_url| @server.register redirection_url, new_mongrel_redirector(target_url) end Given /that service is protected by Basic Authentication/ do @handler.extend BasicAuthentication end Given /that service is protected by Digest Authentication/ do @handler.extend DigestAuthentication end Given /that service is protected by MD5-sess Digest Authentication/ do @handler.extend DigestAuthenticationUsingMD5Sess end Given /that service requires the username '(.*)' with the password '(.*)'/ do |username, password| @handler.username = username @handler.password = password end # customize aruba cucumber step Then /^the output should contain '(.*)'$/ do |expected| expect(all_commands.map(&:output).join("\n")).to match_output_string(expected) end Given /a restricted page at '(.*)'/ do |url| steps " Given a remote service that returns 'A response I will never see' And that service is accessed at the path '#{url}' And that service is protected by Basic Authentication And that service requires the username 'something' with the password 'secret' " end # This joins the server thread, and halts cucumber, so you can actually hit the # server with a browser. Runs until you kill it with Ctrl-c Given /I want to hit this in a browser/ do @server.acceptor.join end Then /I wait for the server to recover/ do timeout = @request_options[:timeout] || 0 sleep @server_response_time - timeout end httparty-0.18.1/features/supports_read_timeout_option.feature000066400000000000000000000012001367022441500247000ustar00rootroot00000000000000Feature: Supports the read timeout option In order to handle inappropriately slow response times As a developer I want my request to raise an exception after my specified read_timeout as elapsed Scenario: A long running response Given a remote service that returns '

Some HTML

' And that service is accessed at the path '/long_running_service.html' And that service takes 2 seconds to generate a response When I set my HTTParty read_timeout option to 1 And I call HTTParty#get with '/long_running_service.html' Then it should raise a Timeout::Error exception And I wait for the server to recover httparty-0.18.1/features/supports_redirection.feature000066400000000000000000000016541367022441500231530ustar00rootroot00000000000000Feature: Supports Redirection As a developer I want to work with services that may redirect me And I want it to follow a reasonable number of redirects Because sometimes web services do that Scenario: A service that redirects once Given a remote service that returns 'Service Response' And that service is accessed at the path '/landing_service.html' And the url '/redirector.html' redirects to '/landing_service.html' When I call HTTParty#get with '/redirector.html' Then the return value should match 'Service Response' # TODO: Look in to why this actually fails... Scenario: A service that redirects to a relative URL Scenario: A service that redirects infinitely Given the url '/first.html' redirects to '/second.html' And the url '/second.html' redirects to '/first.html' When I call HTTParty#get with '/first.html' Then it should raise an HTTParty::RedirectionTooDeep exception httparty-0.18.1/features/supports_timeout_option.feature000066400000000000000000000011611367022441500237130ustar00rootroot00000000000000Feature: Supports the timeout option In order to handle inappropriately slow response times As a developer I want my request to raise an exception after my specified timeout as elapsed Scenario: A long running response Given a remote service that returns '

Some HTML

' And that service is accessed at the path '/long_running_service.html' And that service takes 2 seconds to generate a response When I set my HTTParty timeout option to 1 And I call HTTParty#get with '/long_running_service.html' Then it should raise a Timeout::Error exception And I wait for the server to recover httparty-0.18.1/httparty.gemspec000066400000000000000000000021561367022441500167140ustar00rootroot00000000000000# -*- encoding: utf-8 -*- $LOAD_PATH.push File.expand_path("../lib", __FILE__) require "httparty/version" Gem::Specification.new do |s| s.name = "httparty" s.version = HTTParty::VERSION s.platform = Gem::Platform::RUBY s.licenses = ['MIT'] s.authors = ["John Nunemaker", "Sandro Turriate"] s.email = ["nunemaker@gmail.com"] s.homepage = "https://github.com/jnunemaker/httparty" s.summary = 'Makes http fun! Also, makes consuming restful web services dead easy.' s.description = 'Makes http fun! Also, makes consuming restful web services dead easy.' s.required_ruby_version = '>= 2.0.0' s.add_dependency 'multi_xml', ">= 0.5.2" s.add_dependency('mime-types', "~> 3.0") # If this line is removed, all hard partying will cease. s.post_install_message = "When you HTTParty, you must party hard!" all_files = `git ls-files`.split("\n") test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.files = all_files - test_files s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } s.require_paths = ["lib"] end httparty-0.18.1/lib/000077500000000000000000000000001367022441500142325ustar00rootroot00000000000000httparty-0.18.1/lib/httparty.rb000066400000000000000000000526641367022441500164530ustar00rootroot00000000000000require 'pathname' require 'net/http' require 'net/https' require 'uri' require 'zlib' require 'multi_xml' require 'mime/types' require 'json' require 'csv' require 'httparty/module_inheritable_attributes' require 'httparty/cookie_hash' require 'httparty/net_digest_auth' require 'httparty/version' require 'httparty/connection_adapter' require 'httparty/logger/logger' require 'httparty/request/body' require 'httparty/response_fragment' require 'httparty/text_encoder' require 'httparty/headers_processor' # @see HTTParty::ClassMethods module HTTParty def self.included(base) base.extend ClassMethods base.send :include, ModuleInheritableAttributes base.send(:mattr_inheritable, :default_options) base.send(:mattr_inheritable, :default_cookies) base.instance_variable_set("@default_options", {}) base.instance_variable_set("@default_cookies", CookieHash.new) end # == Common Request Options # Request methods (get, post, patch, put, delete, head, options) all take a common set of options. These are: # # [:+body+:] Body of the request. If passed an object that responds to #to_hash, will try to normalize it first, by default passing it to ActiveSupport::to_params. Any other kind of object will get used as-is. # [:+http_proxyaddr+:] Address of proxy server to use. # [:+http_proxyport+:] Port of proxy server to use. # [:+http_proxyuser+:] User for proxy server authentication. # [:+http_proxypass+:] Password for proxy server authentication. # [:+limit+:] Maximum number of redirects to follow. Takes precedences over :+no_follow+. # [:+query+:] Query string, or an object that responds to #to_hash representing it. Normalized according to the same rules as :+body+. If you specify this on a POST, you must use an object which responds to #to_hash. See also HTTParty::ClassMethods.default_params. # [:+timeout+:] Timeout for opening connection and reading data. # [:+local_host+:] Local address to bind to before connecting. # [:+local_port+:] Local port to bind to before connecting. # [:+body_stream+:] Allow streaming to a REST server to specify a body_stream. # [:+stream_body+:] Allow for streaming large files without loading them into memory. # [:+multipart+:] Force content-type to be multipart # # There are also another set of options with names corresponding to various class methods. The methods in question are those that let you set a class-wide default, and the options override the defaults on a request-by-request basis. Those options are: # * :+base_uri+: see HTTParty::ClassMethods.base_uri. # * :+basic_auth+: see HTTParty::ClassMethods.basic_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError. # * :+debug_output+: see HTTParty::ClassMethods.debug_output. # * :+digest_auth+: see HTTParty::ClassMethods.digest_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError. # * :+format+: see HTTParty::ClassMethods.format. # * :+headers+: see HTTParty::ClassMethods.headers. Must be a an object which responds to #to_hash. # * :+maintain_method_across_redirects+: see HTTParty::ClassMethods.maintain_method_across_redirects. # * :+no_follow+: see HTTParty::ClassMethods.no_follow. # * :+parser+: see HTTParty::ClassMethods.parser. # * :+uri_adapter+: see HTTParty::ClassMethods.uri_adapter # * :+connection_adapter+: see HTTParty::ClassMethods.connection_adapter. # * :+pem+: see HTTParty::ClassMethods.pem. # * :+query_string_normalizer+: see HTTParty::ClassMethods.query_string_normalizer # * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file. # * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path. module ClassMethods # Turns on logging # # class Foo # include HTTParty # logger Logger.new('http_logger'), :info, :apache # end def logger(logger, level = :info, format = :apache) default_options[:logger] = logger default_options[:log_level] = level default_options[:log_format] = format end # Raises HTTParty::ResponseError if response's code matches this statuses # # class Foo # include HTTParty # raise_on [404, 500] # end def raise_on(codes = []) default_options[:raise_on] = *codes end # Allows setting http proxy information to be used # # class Foo # include HTTParty # http_proxy 'http://foo.com', 80, 'user', 'pass' # end def http_proxy(addr = nil, port = nil, user = nil, pass = nil) default_options[:http_proxyaddr] = addr default_options[:http_proxyport] = port default_options[:http_proxyuser] = user default_options[:http_proxypass] = pass end # Allows setting a base uri to be used for each request. # Will normalize uri to include http, etc. # # class Foo # include HTTParty # base_uri 'twitter.com' # end def base_uri(uri = nil) return default_options[:base_uri] unless uri default_options[:base_uri] = HTTParty.normalize_base_uri(uri) end # Allows setting basic authentication username and password. # # class Foo # include HTTParty # basic_auth 'username', 'password' # end def basic_auth(u, p) default_options[:basic_auth] = {username: u, password: p} end # Allows setting digest authentication username and password. # # class Foo # include HTTParty # digest_auth 'username', 'password' # end def digest_auth(u, p) default_options[:digest_auth] = {username: u, password: p} end # Do not send rails style query strings. # Specifically, don't use bracket notation when sending an array # # For a query: # get '/', query: {selected_ids: [1,2,3]} # # The default query string looks like this: # /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3 # # Call `disable_rails_query_string_format` to transform the query string # into: # /?selected_ids=1&selected_ids=2&selected_ids=3 # # @example # class Foo # include HTTParty # disable_rails_query_string_format # end def disable_rails_query_string_format query_string_normalizer Request::NON_RAILS_QUERY_STRING_NORMALIZER end # Allows setting default parameters to be appended to each request. # Great for api keys and such. # # class Foo # include HTTParty # default_params api_key: 'secret', another: 'foo' # end def default_params(h = {}) raise ArgumentError, 'Default params must be an object which responds to #to_hash' unless h.respond_to?(:to_hash) default_options[:default_params] ||= {} default_options[:default_params].merge!(h) end # Allows setting a default timeout for all HTTP calls # Timeout is specified in seconds. # # class Foo # include HTTParty # default_timeout 10 # end def default_timeout(value) validate_timeout_argument(__method__, value) default_options[:timeout] = value end # Allows setting a default open_timeout for all HTTP calls in seconds # # class Foo # include HTTParty # open_timeout 10 # end def open_timeout(value) validate_timeout_argument(__method__, value) default_options[:open_timeout] = value end # Allows setting a default read_timeout for all HTTP calls in seconds # # class Foo # include HTTParty # read_timeout 10 # end def read_timeout(value) validate_timeout_argument(__method__, value) default_options[:read_timeout] = value end # Allows setting a default write_timeout for all HTTP calls in seconds # Supported by Ruby > 2.6.0 # # class Foo # include HTTParty # write_timeout 10 # end def write_timeout(value) validate_timeout_argument(__method__, value) default_options[:write_timeout] = value end # Set an output stream for debugging, defaults to $stderr. # The output stream is passed on to Net::HTTP#set_debug_output. # # class Foo # include HTTParty # debug_output $stderr # end def debug_output(stream = $stderr) default_options[:debug_output] = stream end # Allows setting HTTP headers to be used for each request. # # class Foo # include HTTParty # headers 'Accept' => 'text/html' # end def headers(h = nil) if h raise ArgumentError, 'Headers must be an object which responds to #to_hash' unless h.respond_to?(:to_hash) default_options[:headers] ||= {} default_options[:headers].merge!(h.to_hash) else default_options[:headers] || {} end end def cookies(h = {}) raise ArgumentError, 'Cookies must be an object which responds to #to_hash' unless h.respond_to?(:to_hash) default_cookies.add_cookies(h) end # Proceed to the location header when an HTTP response dictates a redirect. # Redirects are always followed by default. # # @example # class Foo # include HTTParty # base_uri 'http://google.com' # follow_redirects true # end def follow_redirects(value = true) default_options[:follow_redirects] = value end # Allows setting the format with which to parse. # Must be one of the allowed formats ie: json, xml # # class Foo # include HTTParty # format :json # end def format(f = nil) if f.nil? default_options[:format] else parser(Parser) if parser.nil? default_options[:format] = f validate_format end end # Declare whether or not to follow redirects. When true, an # {HTTParty::RedirectionTooDeep} error will raise upon encountering a # redirect. You can then gain access to the response object via # HTTParty::RedirectionTooDeep#response. # # @see HTTParty::ResponseError#response # # @example # class Foo # include HTTParty # base_uri 'http://google.com' # no_follow true # end # # begin # Foo.get('/') # rescue HTTParty::RedirectionTooDeep => e # puts e.response.body # end def no_follow(value = false) default_options[:no_follow] = value end # Declare that you wish to maintain the chosen HTTP method across redirects. # The default behavior is to follow redirects via the GET method, except # if you are making a HEAD request, in which case the default is to # follow all redirects with HEAD requests. # If you wish to maintain the original method, you can set this option to true. # # @example # class Foo # include HTTParty # base_uri 'http://google.com' # maintain_method_across_redirects true # end def maintain_method_across_redirects(value = true) default_options[:maintain_method_across_redirects] = value end # Declare that you wish to resend the full HTTP request across redirects, # even on redirects that should logically become GET requests. # A 303 redirect in HTTP signifies that the redirected url should normally # retrieved using a GET request, for instance, it is the output of a previous # POST. maintain_method_across_redirects respects this behavior, but you # can force HTTParty to resend_on_redirect even on 303 responses. # # @example # class Foo # include HTTParty # base_uri 'http://google.com' # resend_on_redirect # end def resend_on_redirect(value = true) default_options[:resend_on_redirect] = value end # Allows setting a PEM file to be used # # class Foo # include HTTParty # pem File.read('/home/user/my.pem'), "optional password" # end def pem(pem_contents, password = nil) default_options[:pem] = pem_contents default_options[:pem_password] = password end # Allows setting a PKCS12 file to be used # # class Foo # include HTTParty # pkcs12 File.read('/home/user/my.p12'), "password" # end def pkcs12(p12_contents, password) default_options[:p12] = p12_contents default_options[:p12_password] = password end # Override the way query strings are normalized. # Helpful for overriding the default rails normalization of Array queries. # # For a query: # get '/', query: {selected_ids: [1,2,3]} # # The default query string normalizer returns: # /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3 # # Let's change it to this: # /?selected_ids=1&selected_ids=2&selected_ids=3 # # Pass a Proc to the query normalizer which accepts the yielded query. # # @example Modifying Array query strings # class ServiceWrapper # include HTTParty # # query_string_normalizer proc { |query| # query.map do |key, value| # value.map {|v| "#{key}=#{v}"} # end.join('&') # } # end # # @param [Proc] normalizer custom query string normalizer. # @yield [Hash, String] query string # @yieldreturn [Array] an array that will later be joined with '&' def query_string_normalizer(normalizer) default_options[:query_string_normalizer] = normalizer end # Allows setting of SSL version to use. This only works in Ruby 1.9+. # You can get a list of valid versions from OpenSSL::SSL::SSLContext::METHODS. # # class Foo # include HTTParty # ssl_version :SSLv3 # end def ssl_version(version) default_options[:ssl_version] = version end # Allows setting of SSL ciphers to use. This only works in Ruby 1.9+. # You can get a list of valid specific ciphers from OpenSSL::Cipher.ciphers. # You also can specify a cipher suite here, listed here at openssl.org: # http://www.openssl.org/docs/apps/ciphers.html#CIPHER_SUITE_NAMES # # class Foo # include HTTParty # ciphers "RC4-SHA" # end def ciphers(cipher_names) default_options[:ciphers] = cipher_names end # Allows setting an OpenSSL certificate authority file. The file # should contain one or more certificates in PEM format. # # Setting this option enables certificate verification. All # certificates along a chain must be available in ssl_ca_file or # ssl_ca_path for verification to succeed. # # # class Foo # include HTTParty # ssl_ca_file '/etc/ssl/certs/ca-certificates.crt' # end def ssl_ca_file(path) default_options[:ssl_ca_file] = path end # Allows setting an OpenSSL certificate authority path (directory). # # Setting this option enables certificate verification. All # certificates along a chain must be available in ssl_ca_file or # ssl_ca_path for verification to succeed. # # class Foo # include HTTParty # ssl_ca_path '/etc/ssl/certs/' # end def ssl_ca_path(path) default_options[:ssl_ca_path] = path end # Allows setting a custom parser for the response. # # class Foo # include HTTParty # parser Proc.new {|data| ...} # end def parser(custom_parser = nil) if custom_parser.nil? default_options[:parser] else default_options[:parser] = custom_parser validate_format end end # Allows setting a custom URI adapter. # # class Foo # include HTTParty # uri_adapter Addressable::URI # end def uri_adapter(uri_adapter) raise ArgumentError, 'The URI adapter should respond to #parse' unless uri_adapter.respond_to?(:parse) default_options[:uri_adapter] = uri_adapter end # Allows setting a custom connection_adapter for the http connections # # @example # class Foo # include HTTParty # connection_adapter Proc.new {|uri, options| ... } # end # # @example provide optional configuration for your connection_adapter # class Foo # include HTTParty # connection_adapter Proc.new {|uri, options| ... }, {foo: :bar} # end # # @see HTTParty::ConnectionAdapter def connection_adapter(custom_adapter = nil, options = nil) if custom_adapter.nil? default_options[:connection_adapter] else default_options[:connection_adapter] = custom_adapter default_options[:connection_adapter_options] = options end end # Allows making a get request to a url. # # class Foo # include HTTParty # end # # # Simple get with full url # Foo.get('http://foo.com/resource.json') # # # Simple get with full url and query parameters # # ie: http://foo.com/resource.json?limit=10 # Foo.get('http://foo.com/resource.json', query: {limit: 10}) def get(path, options = {}, &block) perform_request Net::HTTP::Get, path, options, &block end # Allows making a post request to a url. # # class Foo # include HTTParty # end # # # Simple post with full url and setting the body # Foo.post('http://foo.com/resources', body: {bar: 'baz'}) # # # Simple post with full url using :query option, # # which appends the parameters to the URI. # Foo.post('http://foo.com/resources', query: {bar: 'baz'}) def post(path, options = {}, &block) perform_request Net::HTTP::Post, path, options, &block end # Perform a PATCH request to a path def patch(path, options = {}, &block) perform_request Net::HTTP::Patch, path, options, &block end # Perform a PUT request to a path def put(path, options = {}, &block) perform_request Net::HTTP::Put, path, options, &block end # Perform a DELETE request to a path def delete(path, options = {}, &block) perform_request Net::HTTP::Delete, path, options, &block end # Perform a MOVE request to a path def move(path, options = {}, &block) perform_request Net::HTTP::Move, path, options, &block end # Perform a COPY request to a path def copy(path, options = {}, &block) perform_request Net::HTTP::Copy, path, options, &block end # Perform a HEAD request to a path def head(path, options = {}, &block) ensure_method_maintained_across_redirects options perform_request Net::HTTP::Head, path, options, &block end # Perform an OPTIONS request to a path def options(path, options = {}, &block) perform_request Net::HTTP::Options, path, options, &block end # Perform a MKCOL request to a path def mkcol(path, options = {}, &block) perform_request Net::HTTP::Mkcol, path, options, &block end def lock(path, options = {}, &block) perform_request Net::HTTP::Lock, path, options, &block end def unlock(path, options = {}, &block) perform_request Net::HTTP::Unlock, path, options, &block end attr_reader :default_options private def validate_timeout_argument(timeout_type, value) raise ArgumentError, "#{ timeout_type } must be an integer or float" unless value && (value.is_a?(Integer) || value.is_a?(Float)) end def ensure_method_maintained_across_redirects(options) unless options.key?(:maintain_method_across_redirects) options[:maintain_method_across_redirects] = true end end def perform_request(http_method, path, options, &block) #:nodoc: options = ModuleInheritableAttributes.hash_deep_dup(default_options).merge(options) HeadersProcessor.new(headers, options).call process_cookies(options) Request.new(http_method, path, options).perform(&block) end def process_cookies(options) #:nodoc: return unless options[:cookies] || default_cookies.any? options[:headers] ||= headers.dup options[:headers]["cookie"] = cookies.merge(options.delete(:cookies) || {}).to_cookie_string end def validate_format if format && parser.respond_to?(:supports_format?) && !parser.supports_format?(format) supported_format_names = parser.supported_formats.map(&:to_s).sort.join(', ') raise UnsupportedFormat, "'#{format.inspect}' Must be one of: #{supported_format_names}" end end end def self.normalize_base_uri(url) #:nodoc: normalized_url = url.dup use_ssl = (normalized_url =~ /^https/) || (normalized_url =~ /:443\b/) ends_with_slash = normalized_url =~ /\/$/ normalized_url.chop! if ends_with_slash normalized_url.gsub!(/^https?:\/\//i, '') "http#{'s' if use_ssl}://#{normalized_url}" end class Basement #:nodoc: include HTTParty end def self.get(*args, &block) Basement.get(*args, &block) end def self.post(*args, &block) Basement.post(*args, &block) end def self.patch(*args, &block) Basement.patch(*args, &block) end def self.put(*args, &block) Basement.put(*args, &block) end def self.delete(*args, &block) Basement.delete(*args, &block) end def self.move(*args, &block) Basement.move(*args, &block) end def self.copy(*args, &block) Basement.copy(*args, &block) end def self.head(*args, &block) Basement.head(*args, &block) end def self.options(*args, &block) Basement.options(*args, &block) end end require 'httparty/hash_conversions' require 'httparty/utils' require 'httparty/exceptions' require 'httparty/parser' require 'httparty/request' require 'httparty/response' httparty-0.18.1/lib/httparty/000077500000000000000000000000001367022441500161115ustar00rootroot00000000000000httparty-0.18.1/lib/httparty/connection_adapter.rb000066400000000000000000000203361367022441500223010ustar00rootroot00000000000000module HTTParty # Default connection adapter that returns a new Net::HTTP each time # # == Custom Connection Factories # # If you like to implement your own connection adapter, subclassing # HTTParty::ConnectionAdapter will make it easier. Just override # the #connection method. The uri and options attributes will have # all the info you need to construct your http connection. Whatever # you return from your connection method needs to adhere to the # Net::HTTP interface as this is what HTTParty expects. # # @example log the uri and options # class LoggingConnectionAdapter < HTTParty::ConnectionAdapter # def connection # puts uri # puts options # Net::HTTP.new(uri) # end # end # # @example count number of http calls # class CountingConnectionAdapter < HTTParty::ConnectionAdapter # @@count = 0 # # self.count # @@count # end # # def connection # self.count += 1 # super # end # end # # === Configuration # There is lots of configuration data available for your connection adapter # in the #options attribute. It is up to you to interpret them within your # connection adapter. Take a look at the implementation of # HTTParty::ConnectionAdapter#connection for examples of how they are used. # The keys used in options are # * :+timeout+: timeout in seconds # * :+open_timeout+: http connection open_timeout in seconds, overrides timeout if set # * :+read_timeout+: http connection read_timeout in seconds, overrides timeout if set # * :+write_timeout+: http connection write_timeout in seconds, overrides timeout if set (Ruby >= 2.6.0 required) # * :+debug_output+: see HTTParty::ClassMethods.debug_output. # * :+cert_store+: contains certificate data. see method 'attach_ssl_certificates' # * :+pem+: contains pem client certificate data. see method 'attach_ssl_certificates' # * :+p12+: contains PKCS12 client client certificate data. see method 'attach_ssl_certificates' # * :+verify+: verify the server’s certificate against the ca certificate. # * :+verify_peer+: set to false to turn off server verification but still send client certificate # * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file. # * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path. # * :+ssl_version+: SSL versions to allow. see method 'attach_ssl_certificates' # * :+ciphers+: The list of SSL ciphers to support # * :+connection_adapter_options+: contains the hash you passed to HTTParty.connection_adapter when you configured your connection adapter # * :+local_host+: The local address to bind to # * :+local_port+: The local port to bind to # * :+http_proxyaddr+: HTTP Proxy address # * :+http_proxyport+: HTTP Proxy port # * :+http_proxyuser+: HTTP Proxy user # * :+http_proxypass+: HTTP Proxy password # # === Inherited methods # * :+clean_host+: Method used to sanitize host names class ConnectionAdapter # Private: Regex used to strip brackets from IPv6 URIs. StripIpv6BracketsRegex = /\A\[(.*)\]\z/ OPTION_DEFAULTS = { verify: true, verify_peer: true } # Public def self.call(uri, options) new(uri, options).connection end def self.default_cert_store @default_cert_store ||= OpenSSL::X509::Store.new.tap do |cert_store| cert_store.set_default_paths end end attr_reader :uri, :options def initialize(uri, options = {}) uri_adapter = options[:uri_adapter] || URI raise ArgumentError, "uri must be a #{uri_adapter}, not a #{uri.class}" unless uri.is_a? uri_adapter @uri = uri @options = OPTION_DEFAULTS.merge(options) end def connection host = clean_host(uri.host) port = uri.port || (uri.scheme == 'https' ? 443 : 80) if options.key?(:http_proxyaddr) http = Net::HTTP.new( host, port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass] ) else http = Net::HTTP.new(host, port) end http.use_ssl = ssl_implied?(uri) attach_ssl_certificates(http, options) if add_timeout?(options[:timeout]) http.open_timeout = options[:timeout] http.read_timeout = options[:timeout] from_ruby_version('2.6.0', option: :write_timeout, warn: false) do http.write_timeout = options[:timeout] end end if add_timeout?(options[:read_timeout]) http.read_timeout = options[:read_timeout] end if add_timeout?(options[:open_timeout]) http.open_timeout = options[:open_timeout] end if add_timeout?(options[:write_timeout]) from_ruby_version('2.6.0', option: :write_timeout) do http.write_timeout = options[:write_timeout] end end if add_max_retries?(options[:max_retries]) from_ruby_version('2.5.0', option: :max_retries) do http.max_retries = options[:max_retries] end end if options[:debug_output] http.set_debug_output(options[:debug_output]) end if options[:ciphers] http.ciphers = options[:ciphers] end # Bind to a specific local address or port # # @see https://bugs.ruby-lang.org/issues/6617 if options[:local_host] from_ruby_version('2.0.0', option: :local_host) do http.local_host = options[:local_host] end end if options[:local_port] from_ruby_version('2.0.0', option: :local_port) do http.local_port = options[:local_port] end end http end private def from_ruby_version(ruby_version, option: nil, warn: true) if RUBY_VERSION >= ruby_version yield elsif warn Kernel.warn("Warning: option #{ option } requires Ruby version #{ ruby_version } or later") end end def add_timeout?(timeout) timeout && (timeout.is_a?(Integer) || timeout.is_a?(Float)) end def add_max_retries?(max_retries) max_retries && max_retries.is_a?(Integer) && max_retries >= 0 end def clean_host(host) strip_ipv6_brackets(host) end def strip_ipv6_brackets(host) StripIpv6BracketsRegex =~ host ? $1 : host end def ssl_implied?(uri) uri.port == 443 || uri.scheme == 'https' end def verify_ssl_certificate? !(options[:verify] == false || options[:verify_peer] == false) end def attach_ssl_certificates(http, options) if http.use_ssl? if options.fetch(:verify, true) http.verify_mode = OpenSSL::SSL::VERIFY_PEER if options[:cert_store] http.cert_store = options[:cert_store] else # Use the default cert store by default, i.e. system ca certs http.cert_store = self.class.default_cert_store end else http.verify_mode = OpenSSL::SSL::VERIFY_NONE end # Client certificate authentication # Note: options[:pem] must contain the content of a PEM file having the private key appended if options[:pem] http.cert = OpenSSL::X509::Certificate.new(options[:pem]) http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password]) http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE end # PKCS12 client certificate authentication if options[:p12] p12 = OpenSSL::PKCS12.new(options[:p12], options[:p12_password]) http.cert = p12.certificate http.key = p12.key http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE end # SSL certificate authority file and/or directory if options[:ssl_ca_file] http.ca_file = options[:ssl_ca_file] http.verify_mode = OpenSSL::SSL::VERIFY_PEER end if options[:ssl_ca_path] http.ca_path = options[:ssl_ca_path] http.verify_mode = OpenSSL::SSL::VERIFY_PEER end # This is only Ruby 1.9+ if options[:ssl_version] && http.respond_to?(:ssl_version=) http.ssl_version = options[:ssl_version] end end end end end httparty-0.18.1/lib/httparty/cookie_hash.rb000066400000000000000000000010531367022441500207110ustar00rootroot00000000000000class HTTParty::CookieHash < Hash #:nodoc: CLIENT_COOKIES = %w(path expires domain path secure httponly samesite) def add_cookies(data) case data when Hash merge!(data) when String data.split('; ').each do |cookie| key, value = cookie.split('=', 2) self[key.to_sym] = value if key end else raise "add_cookies only takes a Hash or a String" end end def to_cookie_string select { |k, v| !CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join("; ") end end httparty-0.18.1/lib/httparty/exceptions.rb000066400000000000000000000022731367022441500206230ustar00rootroot00000000000000module HTTParty # @abstact Exceptions raised by HTTParty inherit from Error class Error < StandardError; end # Exception raised when you attempt to set a non-existent format class UnsupportedFormat < Error; end # Exception raised when using a URI scheme other than HTTP or HTTPS class UnsupportedURIScheme < Error; end # @abstract Exceptions which inherit from ResponseError contain the Net::HTTP # response object accessible via the {#response} method. class ResponseError < Error # Returns the response of the last request # @return [Net::HTTPResponse] A subclass of Net::HTTPResponse, e.g. # Net::HTTPOK attr_reader :response # Instantiate an instance of ResponseError with a Net::HTTPResponse object # @param [Net::HTTPResponse] def initialize(response) @response = response super(response) end end # Exception that is raised when request has redirected too many times. # Calling {#response} returns the Net:HTTP response object. class RedirectionTooDeep < ResponseError; end # Exception that is raised when request redirects and location header is present more than once class DuplicateLocationHeader < ResponseError; end end httparty-0.18.1/lib/httparty/hash_conversions.rb000066400000000000000000000041451367022441500220150ustar00rootroot00000000000000require 'erb' module HTTParty module HashConversions # @return This hash as a query string # # @example # { name: "Bob", # address: { # street: '111 Ruby Ave.', # city: 'Ruby Central', # phones: ['111-111-1111', '222-222-2222'] # } # }.to_params # #=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111 Ruby Ave." def self.to_params(hash) hash.to_hash.map { |k, v| normalize_param(k, v) }.join.chop end # @param key The key for the param. # @param value The value for the param. # # @return This key value pair as a param # # @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&" def self.normalize_param(key, value) normalized_keys = normalize_keys(key, value) normalized_keys.flatten.each_slice(2).inject('') do |string, (k, v)| string + "#{ERB::Util.url_encode(k)}=#{ERB::Util.url_encode(v.to_s)}&" end end def self.normalize_keys(key, value) stack = [] normalized_keys = [] if value.respond_to?(:to_ary) if value.empty? normalized_keys << ["#{key}[]", ''] else normalized_keys = value.to_ary.flat_map do |element| normalize_keys("#{key}[]", element) end end elsif value.respond_to?(:to_hash) stack << [key, value.to_hash] else normalized_keys << [key.to_s, value] end stack.each do |parent, hash| hash.each do |child_key, child_value| if child_value.respond_to?(:to_hash) stack << ["#{parent}[#{child_key}]", child_value.to_hash] elsif child_value.respond_to?(:to_ary) child_value.to_ary.each do |v| normalized_keys << normalize_keys("#{parent}[#{child_key}][]", v).flatten end else normalized_keys << normalize_keys("#{parent}[#{child_key}]", child_value).flatten end end end normalized_keys end end end httparty-0.18.1/lib/httparty/headers_processor.rb000066400000000000000000000014661367022441500221570ustar00rootroot00000000000000module HTTParty class HeadersProcessor attr_reader :headers, :options def initialize(headers, options) @headers = headers @options = options end def call return unless options[:headers] options[:headers] = headers.merge(options[:headers]) if headers.any? options[:headers] = Utils.stringify_keys(process_dynamic_headers) end private def process_dynamic_headers options[:headers].each_with_object({}) do |header, processed_headers| key, value = header processed_headers[key] = if value.respond_to?(:call) value.arity == 0 ? value.call : value.call(options) else value end end end end end httparty-0.18.1/lib/httparty/logger/000077500000000000000000000000001367022441500173705ustar00rootroot00000000000000httparty-0.18.1/lib/httparty/logger/apache_formatter.rb000066400000000000000000000017041367022441500232230ustar00rootroot00000000000000module HTTParty module Logger class ApacheFormatter #:nodoc: TAG_NAME = HTTParty.name attr_accessor :level, :logger def initialize(logger, level) @logger = logger @level = level.to_sym end def format(request, response) @request = request @response = response logger.public_send level, message end private attr_reader :request, :response def message "[#{TAG_NAME}] [#{current_time}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} " end def current_time Time.now.strftime("%Y-%m-%d %H:%M:%S %z") end def http_method request.http_method.name.split("::").last.upcase end def path request.path.to_s end def content_length response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length'] end end end end httparty-0.18.1/lib/httparty/logger/curl_formatter.rb000066400000000000000000000041751367022441500227540ustar00rootroot00000000000000module HTTParty module Logger class CurlFormatter #:nodoc: TAG_NAME = HTTParty.name OUT = '>'.freeze IN = '<'.freeze attr_accessor :level, :logger def initialize(logger, level) @logger = logger @level = level.to_sym @messages = [] end def format(request, response) @request = request @response = response log_request log_response logger.public_send level, messages.join("\n") end private attr_reader :request, :response attr_accessor :messages def log_request log_url log_headers log_query log OUT, request.raw_body if request.raw_body log OUT end def log_response log IN, "HTTP/#{response.http_version} #{response.code}" log_response_headers log IN, "\n#{response.body}" log IN end def log_url http_method = request.http_method.name.split("::").last.upcase uri = if request.options[:base_uri] request.options[:base_uri] + request.path.path else request.path.to_s end log OUT, "#{http_method} #{uri}" end def log_headers return unless request.options[:headers] && request.options[:headers].size > 0 log OUT, 'Headers: ' log_hash request.options[:headers] end def log_query return unless request.options[:query] log OUT, 'Query: ' log_hash request.options[:query] end def log_response_headers headers = response.respond_to?(:headers) ? response.headers : response response.each_header do |response_header| log IN, "#{response_header.capitalize}: #{headers[response_header]}" end end def log_hash(hash) hash.each { |k, v| log(OUT, "#{k}: #{v}") } end def log(direction, line = '') messages << "[#{TAG_NAME}] [#{current_time}] #{direction} #{line}" end def current_time Time.now.strftime("%Y-%m-%d %H:%M:%S %z") end end end end httparty-0.18.1/lib/httparty/logger/logger.rb000066400000000000000000000014331367022441500211750ustar00rootroot00000000000000require 'httparty/logger/apache_formatter' require 'httparty/logger/curl_formatter' require 'httparty/logger/logstash_formatter' module HTTParty module Logger def self.formatters @formatters ||= { :curl => Logger::CurlFormatter, :apache => Logger::ApacheFormatter, :logstash => Logger::LogstashFormatter, } end def self.add_formatter(name, formatter) raise HTTParty::Error.new("Log Formatter with name #{name} already exists") if formatters.include?(name) formatters.merge!(name.to_sym => formatter) end def self.build(logger, level, formatter) level ||= :info formatter ||= :apache logger_klass = formatters[formatter] || Logger::ApacheFormatter logger_klass.new(logger, level) end end end httparty-0.18.1/lib/httparty/logger/logstash_formatter.rb000066400000000000000000000025631367022441500236320ustar00rootroot00000000000000module HTTParty module Logger class LogstashFormatter #:nodoc: TAG_NAME = HTTParty.name attr_accessor :level, :logger def initialize(logger, level) @logger = logger @level = level.to_sym end def format(request, response) @request = request @response = response logger.public_send level, logstash_message end private attr_reader :request, :response def logstash_message { '@timestamp' => current_time, '@version' => 1, 'content_length' => content_length || '-', 'http_method' => http_method, 'message' => message, 'path' => path, 'response_code' => response.code, 'severity' => level, 'tags' => [TAG_NAME], }.to_json end def message "[#{TAG_NAME}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} " end def current_time Time.now.strftime("%Y-%m-%d %H:%M:%S %z") end def http_method @http_method ||= request.http_method.name.split("::").last.upcase end def path @path ||= request.path.to_s end def content_length @content_length ||= response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length'] end end end end httparty-0.18.1/lib/httparty/module_inheritable_attributes.rb000066400000000000000000000027361367022441500245470ustar00rootroot00000000000000module HTTParty module ModuleInheritableAttributes #:nodoc: def self.included(base) base.extend(ClassMethods) end # borrowed from Rails 3.2 ActiveSupport def self.hash_deep_dup(hash) duplicate = hash.dup duplicate.each_pair do |key, value| if value.is_a?(Hash) duplicate[key] = hash_deep_dup(value) elsif value.is_a?(Proc) duplicate[key] = value.dup else duplicate[key] = value end end duplicate end module ClassMethods #:nodoc: def mattr_inheritable(*args) @mattr_inheritable_attrs ||= [:mattr_inheritable_attrs] @mattr_inheritable_attrs += args args.each do |arg| module_eval %(class << self; attr_accessor :#{arg} end) end @mattr_inheritable_attrs end def inherited(subclass) super @mattr_inheritable_attrs.each do |inheritable_attribute| ivar = "@#{inheritable_attribute}" subclass.instance_variable_set(ivar, instance_variable_get(ivar).clone) if instance_variable_get(ivar).respond_to?(:merge) method = <<-EOM def self.#{inheritable_attribute} duplicate = ModuleInheritableAttributes.hash_deep_dup(#{ivar}) #{ivar} = superclass.#{inheritable_attribute}.merge(duplicate) end EOM subclass.class_eval method end end end end end end httparty-0.18.1/lib/httparty/net_digest_auth.rb000066400000000000000000000063241367022441500216110ustar00rootroot00000000000000require 'digest/md5' require 'net/http' module Net module HTTPHeader def digest_auth(username, password, response) authenticator = DigestAuthenticator.new( username, password, @method, @path, response ) authenticator.authorization_header.each do |v| add_field('Authorization', v) end authenticator.cookie_header.each do |v| add_field('Cookie', v) end end class DigestAuthenticator def initialize(username, password, method, path, response_header) @username = username @password = password @method = method @path = path @response = parse(response_header) @cookies = parse_cookies(response_header) end def authorization_header @cnonce = md5(random) header = [ %(Digest username="#{@username}"), %(realm="#{@response['realm']}"), %(nonce="#{@response['nonce']}"), %(uri="#{@path}"), %(response="#{request_digest}") ] header << %(algorithm="#{@response['algorithm']}") if algorithm_present? if qop_present? fields = [ %(cnonce="#{@cnonce}"), %(qop="#{@response['qop']}"), "nc=00000001" ] fields.each { |field| header << field } end header << %(opaque="#{@response['opaque']}") if opaque_present? header end def cookie_header @cookies end private def parse(response_header) header = response_header['www-authenticate'] header = header.gsub(/qop=(auth(?:-int)?)/, 'qop="\\1"') header =~ /Digest (.*)/ params = {} if $1 non_quoted = $1.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 } non_quoted.gsub(/(\w+)=([^,]*)/) { params[$1] = $2 } end params end def parse_cookies(response_header) return [] unless response_header['Set-Cookie'] cookies = response_header['Set-Cookie'].split('; ') cookies.reduce([]) do |ret, cookie| ret << cookie ret end cookies end def opaque_present? @response.key?('opaque') && !@response['opaque'].empty? end def qop_present? @response.key?('qop') && !@response['qop'].empty? end def random format "%x", (Time.now.to_i + rand(65535)) end def request_digest a = [md5(a1), @response['nonce'], md5(a2)] a.insert(2, "00000001", @cnonce, @response['qop']) if qop_present? md5(a.join(":")) end def md5(str) Digest::MD5.hexdigest(str) end def algorithm_present? @response.key?('algorithm') && !@response['algorithm'].empty? end def use_md5_sess? algorithm_present? && @response['algorithm'] == 'MD5-sess' end def a1 a1_user_realm_pwd = [@username, @response['realm'], @password].join(':') if use_md5_sess? [ md5(a1_user_realm_pwd), @response['nonce'], @cnonce ].join(':') else a1_user_realm_pwd end end def a2 [@method, @path].join(":") end end end end httparty-0.18.1/lib/httparty/parser.rb000066400000000000000000000076171367022441500177450ustar00rootroot00000000000000module HTTParty # The default parser used by HTTParty, supports xml, json, html, csv and # plain text. # # == Custom Parsers # # If you'd like to do your own custom parsing, subclassing HTTParty::Parser # will make that process much easier. There are a few different ways you can # utilize HTTParty::Parser as a superclass. # # @example Intercept the parsing for all formats # class SimpleParser < HTTParty::Parser # def parse # perform_parsing # end # end # # @example Add the atom format and parsing method to the default parser # class AtomParsingIncluded < HTTParty::Parser # SupportedFormats.merge!( # {"application/atom+xml" => :atom} # ) # # def atom # perform_atom_parsing # end # end # # @example Only support the atom format # class ParseOnlyAtom < HTTParty::Parser # SupportedFormats = {"application/atom+xml" => :atom} # # def atom # perform_atom_parsing # end # end # # @abstract Read the Custom Parsers section for more information. class Parser SupportedFormats = { 'text/xml' => :xml, 'application/xml' => :xml, 'application/json' => :json, 'application/vnd.api+json' => :json, 'application/hal+json' => :json, 'text/json' => :json, 'application/javascript' => :plain, 'text/javascript' => :plain, 'text/html' => :html, 'text/plain' => :plain, 'text/csv' => :csv, 'application/csv' => :csv, 'text/comma-separated-values' => :csv } # The response body of the request # @return [String] attr_reader :body # The intended parsing format for the request # @return [Symbol] e.g. :json attr_reader :format # Instantiate the parser and call {#parse}. # @param [String] body the response body # @param [Symbol] format the response format # @return parsed response def self.call(body, format) new(body, format).parse end # @return [Hash] the SupportedFormats hash def self.formats const_get(:SupportedFormats) end # @param [String] mimetype response MIME type # @return [Symbol] # @return [nil] mime type not supported def self.format_from_mimetype(mimetype) formats[formats.keys.detect {|k| mimetype.include?(k)}] end # @return [Array] list of supported formats def self.supported_formats formats.values.uniq end # @param [Symbol] format e.g. :json, :xml # @return [Boolean] def self.supports_format?(format) supported_formats.include?(format) end def initialize(body, format) @body = body @format = format end # @return [Object] the parsed body # @return [nil] when the response body is nil, an empty string, spaces only or "null" def parse return nil if body.nil? return nil if body == "null" return nil if body.valid_encoding? && body.strip.empty? if body.valid_encoding? && body.encoding == Encoding::UTF_8 @body = body.gsub(/\A#{UTF8_BOM}/, '') end if supports_format? parse_supported_format else body end end protected def xml MultiXml.parse(body) end UTF8_BOM = "\xEF\xBB\xBF".freeze def json JSON.parse(body, :quirks_mode => true, :allow_nan => true) end def csv CSV.parse(body) end def html body end def plain body end def supports_format? self.class.supports_format?(format) end def parse_supported_format send(format) rescue NoMethodError => e raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format.", e.backtrace end end end httparty-0.18.1/lib/httparty/request.rb000066400000000000000000000264521367022441500201370ustar00rootroot00000000000000require 'erb' module HTTParty class Request #:nodoc: SupportedHTTPMethods = [ Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Patch, Net::HTTP::Put, Net::HTTP::Delete, Net::HTTP::Head, Net::HTTP::Options, Net::HTTP::Move, Net::HTTP::Copy, Net::HTTP::Mkcol, Net::HTTP::Lock, Net::HTTP::Unlock, ] SupportedURISchemes = ['http', 'https', 'webcal', nil] NON_RAILS_QUERY_STRING_NORMALIZER = proc do |query| Array(query).sort_by { |a| a[0].to_s }.map do |key, value| if value.nil? key.to_s elsif value.respond_to?(:to_ary) value.to_ary.map {|v| "#{key}=#{ERB::Util.url_encode(v.to_s)}"} else HashConversions.to_params(key => value) end end.flatten.join('&') end JSON_API_QUERY_STRING_NORMALIZER = proc do |query| Array(query).sort_by { |a| a[0].to_s }.map do |key, value| if value.nil? key.to_s elsif value.respond_to?(:to_ary) values = value.to_ary.map{|v| ERB::Util.url_encode(v.to_s)} "#{key}=#{values.join(',')}" else HashConversions.to_params(key => value) end end.flatten.join('&') end attr_accessor :http_method, :options, :last_response, :redirect, :last_uri attr_reader :path def initialize(http_method, path, o = {}) @changed_hosts = false @credentials_sent = false self.http_method = http_method self.options = { limit: o.delete(:no_follow) ? 1 : 5, assume_utf16_is_big_endian: true, default_params: {}, follow_redirects: true, parser: Parser, uri_adapter: URI, connection_adapter: ConnectionAdapter }.merge(o) self.path = path set_basic_auth_from_uri end def path=(uri) uri_adapter = options[:uri_adapter] @path = if uri.is_a?(uri_adapter) uri elsif String.try_convert(uri) uri_adapter.parse(uri).normalize else raise ArgumentError, "bad argument (expected #{uri_adapter} object or URI string)" end end def request_uri(uri) if uri.respond_to? :request_uri uri.request_uri else uri.path end end def uri if redirect && path.relative? && path.path[0] != "/" last_uri_host = @last_uri.path.gsub(/[^\/]+$/, "") path.path = "/#{path.path}" if last_uri_host[-1] != "/" path.path = last_uri_host + path.path end if path.relative? && path.host new_uri = options[:uri_adapter].parse("#{@last_uri.scheme}:#{path}").normalize elsif path.relative? new_uri = options[:uri_adapter].parse("#{base_uri}#{path}").normalize else new_uri = path.clone end # avoid double query string on redirects [#12] unless redirect new_uri.query = query_string(new_uri) end unless SupportedURISchemes.include? new_uri.scheme raise UnsupportedURIScheme, "'#{new_uri}' Must be HTTP, HTTPS or Generic" end @last_uri = new_uri end def base_uri if redirect base_uri = "#{@last_uri.scheme}://#{@last_uri.host}" base_uri += ":#{@last_uri.port}" if @last_uri.port != 80 base_uri else options[:base_uri] && HTTParty.normalize_base_uri(options[:base_uri]) end end def format options[:format] || (format_from_mimetype(last_response['content-type']) if last_response) end def parser options[:parser] end def connection_adapter options[:connection_adapter] end def perform(&block) validate setup_raw_request chunked_body = nil current_http = http self.last_response = current_http.request(@raw_request) do |http_response| if block chunks = [] http_response.read_body do |fragment| encoded_fragment = encode_text(fragment, http_response['content-type']) chunks << encoded_fragment if !options[:stream_body] block.call ResponseFragment.new(encoded_fragment, http_response, current_http) end chunked_body = chunks.join end end handle_host_redirection if response_redirects? result = handle_unauthorized result ||= handle_response(chunked_body, &block) result end def handle_unauthorized(&block) return unless digest_auth? && response_unauthorized? && response_has_digest_auth_challenge? return if @credentials_sent @credentials_sent = true perform(&block) end def raw_body @raw_request.body end private def http connection_adapter.call(uri, options) end def credentials (options[:basic_auth] || options[:digest_auth]).to_hash end def username credentials[:username] end def password credentials[:password] end def normalize_query(query) if query_string_normalizer query_string_normalizer.call(query) else HashConversions.to_params(query) end end def query_string_normalizer options[:query_string_normalizer] end def setup_raw_request if options[:headers].respond_to?(:to_hash) headers_hash = options[:headers].to_hash else headers_hash = nil end @raw_request = http_method.new(request_uri(uri), headers_hash) @raw_request.body_stream = options[:body_stream] if options[:body_stream] if options[:body] body = Body.new( options[:body], query_string_normalizer: query_string_normalizer, force_multipart: options[:multipart] ) if body.multipart? content_type = "multipart/form-data; boundary=#{body.boundary}" @raw_request['Content-Type'] = content_type end @raw_request.body = body.call end if options[:basic_auth] && send_authorization_header? @raw_request.basic_auth(username, password) @credentials_sent = true end setup_digest_auth if digest_auth? && response_unauthorized? && response_has_digest_auth_challenge? end def digest_auth? !!options[:digest_auth] end def response_unauthorized? !!last_response && last_response.code == '401' end def response_has_digest_auth_challenge? !last_response['www-authenticate'].nil? && last_response['www-authenticate'].length > 0 end def setup_digest_auth @raw_request.digest_auth(username, password, last_response) end def query_string(uri) query_string_parts = [] query_string_parts << uri.query unless uri.query.nil? if options[:query].respond_to?(:to_hash) query_string_parts << normalize_query(options[:default_params].merge(options[:query].to_hash)) else query_string_parts << normalize_query(options[:default_params]) unless options[:default_params].empty? query_string_parts << options[:query] unless options[:query].nil? end query_string_parts.reject!(&:empty?) unless query_string_parts == [""] query_string_parts.size > 0 ? query_string_parts.join('&') : nil end def assume_utf16_is_big_endian options[:assume_utf16_is_big_endian] end def handle_response(body, &block) if response_redirects? options[:limit] -= 1 if options[:logger] logger = HTTParty::Logger.build(options[:logger], options[:log_level], options[:log_format]) logger.format(self, last_response) end self.path = last_response['location'] self.redirect = true if last_response.class == Net::HTTPSeeOther unless options[:maintain_method_across_redirects] && options[:resend_on_redirect] self.http_method = Net::HTTP::Get end elsif last_response.code != '307' && last_response.code != '308' unless options[:maintain_method_across_redirects] self.http_method = Net::HTTP::Get end end capture_cookies(last_response) perform(&block) else body ||= last_response.body body = body.nil? ? body : encode_text(body, last_response['content-type']) Response.new(self, last_response, lambda { parse_response(body) }, body: body) end end def handle_host_redirection check_duplicate_location_header redirect_path = options[:uri_adapter].parse(last_response['location']).normalize return if redirect_path.relative? || path.host == redirect_path.host @changed_hosts = true end def check_duplicate_location_header location = last_response.get_fields('location') if location.is_a?(Array) && location.count > 1 raise DuplicateLocationHeader.new(last_response) end end def send_authorization_header? !@changed_hosts end def response_redirects? case last_response when Net::HTTPNotModified # 304 false when Net::HTTPRedirection options[:follow_redirects] && last_response.key?('location') end end def parse_response(body) parser.call(body, format) end def capture_cookies(response) return unless response['Set-Cookie'] cookies_hash = HTTParty::CookieHash.new cookies_hash.add_cookies(options[:headers].to_hash['Cookie']) if options[:headers] && options[:headers].to_hash['Cookie'] response.get_fields('Set-Cookie').each { |cookie| cookies_hash.add_cookies(cookie) } options[:headers] ||= {} options[:headers]['Cookie'] = cookies_hash.to_cookie_string end # Uses the HTTP Content-Type header to determine the format of the # response It compares the MIME type returned to the types stored in the # SupportedFormats hash def format_from_mimetype(mimetype) if mimetype && parser.respond_to?(:format_from_mimetype) parser.format_from_mimetype(mimetype) end end def validate raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0 raise ArgumentError, 'only get, post, patch, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method) raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].respond_to?(:to_hash) raise ArgumentError, 'only one authentication method, :basic_auth or :digest_auth may be used at a time' if options[:basic_auth] && options[:digest_auth] raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].respond_to?(:to_hash) raise ArgumentError, ':digest_auth must be a hash' if options[:digest_auth] && !options[:digest_auth].respond_to?(:to_hash) raise ArgumentError, ':query must be hash if using HTTP Post' if post? && !options[:query].nil? && !options[:query].respond_to?(:to_hash) end def post? Net::HTTP::Post == http_method end def set_basic_auth_from_uri if path.userinfo username, password = path.userinfo.split(':') options[:basic_auth] = {username: username, password: password} @credentials_sent = true end end def encode_text(text, content_type) TextEncoder.new( text, content_type: content_type, assume_utf16_is_big_endian: assume_utf16_is_big_endian ).call end end end httparty-0.18.1/lib/httparty/request/000077500000000000000000000000001367022441500176015ustar00rootroot00000000000000httparty-0.18.1/lib/httparty/request/body.rb000066400000000000000000000047131367022441500210700ustar00rootroot00000000000000require_relative 'multipart_boundary' module HTTParty class Request class Body def initialize(params, query_string_normalizer: nil, force_multipart: false) @params = params @query_string_normalizer = query_string_normalizer @force_multipart = force_multipart end def call if params.respond_to?(:to_hash) multipart? ? generate_multipart : normalize_query(params) else params end end def boundary @boundary ||= MultipartBoundary.generate end def multipart? params.respond_to?(:to_hash) && (force_multipart || has_file?(params)) end private def generate_multipart normalized_params = params.flat_map { |key, value| HashConversions.normalize_keys(key, value) } multipart = normalized_params.inject('') do |memo, (key, value)| memo += "--#{boundary}\r\n" memo += %(Content-Disposition: form-data; name="#{key}") # value.path is used to support ActionDispatch::Http::UploadedFile # https://github.com/jnunemaker/httparty/pull/585 memo += %(; filename="#{file_name(value)}") if file?(value) memo += "\r\n" memo += "Content-Type: #{content_type(value)}\r\n" if file?(value) memo += "\r\n" memo += file?(value) ? value.read : value.to_s memo += "\r\n" end multipart += "--#{boundary}--\r\n" end def has_file?(value) if value.respond_to?(:to_hash) value.to_hash.any? { |_, v| has_file?(v) } elsif value.respond_to?(:to_ary) value.to_ary.any? { |v| has_file?(v) } else file?(value) end end def file?(object) object.respond_to?(:path) && object.respond_to?(:read) end def normalize_query(query) if query_string_normalizer query_string_normalizer.call(query) else HashConversions.to_params(query) end end def content_type(object) return object.content_type if object.respond_to?(:content_type) mime = MIME::Types.type_for(object.path) mime.empty? ? 'application/octet-stream' : mime[0].content_type end def file_name(object) object.respond_to?(:original_filename) ? object.original_filename : File.basename(object.path) end attr_reader :params, :query_string_normalizer, :force_multipart end end end httparty-0.18.1/lib/httparty/request/multipart_boundary.rb000066400000000000000000000003151367022441500240510ustar00rootroot00000000000000require 'securerandom' module HTTParty class Request class MultipartBoundary def self.generate "------------------------#{SecureRandom.urlsafe_base64(12)}" end end end end httparty-0.18.1/lib/httparty/response.rb000066400000000000000000000105641367022441500203020ustar00rootroot00000000000000module HTTParty class Response < Object def self.underscore(string) string.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z])([A-Z])/, '\1_\2').downcase end def self._load(data) req, resp, parsed_resp, resp_body = Marshal.load(data) new(req, resp, -> { parsed_resp }, body: resp_body) end attr_reader :request, :response, :body, :headers def initialize(request, response, parsed_block, options = {}) @request = request @response = response @body = options[:body] || response.body @parsed_block = parsed_block @headers = Headers.new(response.to_hash) if request.options[:logger] logger = ::HTTParty::Logger.build( request.options[:logger], request.options[:log_level], request.options[:log_format] ) logger.format(request, self) end throw_exception end def parsed_response @parsed_response ||= @parsed_block.call end def code response.code.to_i end def http_version response.http_version end def tap yield self self end def inspect inspect_id = ::Kernel::format "%x", (object_id * 2) %(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>) end CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ CODES_TO_OBJ.each do |response_code, klass| name = klass.name.sub("Net::HTTP", '') name = "#{underscore(name)}?".to_sym define_method(name) do klass === response end end # Support old multiple_choice? method from pre 2.0.0 era. if ::RUBY_VERSION >= "2.0.0" && ::RUBY_PLATFORM != "java" alias_method :multiple_choice?, :multiple_choices? end # Support old status codes method from pre 2.6.0 era. if ::RUBY_VERSION >= "2.6.0" && ::RUBY_PLATFORM != "java" alias_method :gateway_time_out?, :gateway_timeout? alias_method :request_entity_too_large?, :payload_too_large? alias_method :request_time_out?, :request_timeout? alias_method :request_uri_too_long?, :uri_too_long? alias_method :requested_range_not_satisfiable?, :range_not_satisfiable? end def nil? warn_about_nil_deprecation response.nil? || response.body.nil? || response.body.empty? end def to_s if !response.nil? && !response.body.nil? && response.body.respond_to?(:to_s) response.body.to_s else inspect end end def pretty_print(pp) if !parsed_response.nil? && parsed_response.respond_to?(:pretty_print) parsed_response.pretty_print(pp) else super end end def display(port=$>) if !parsed_response.nil? && parsed_response.respond_to?(:display) parsed_response.display(port) elsif !response.nil? && !response.body.nil? && response.body.respond_to?(:display) response.body.display(port) else port.write(inspect) end end def respond_to_missing?(name, *args) return true if super parsed_response.respond_to?(name) || response.respond_to?(name) end def _dump(_level) Marshal.dump([request, response, parsed_response, body]) end protected def method_missing(name, *args, &block) if parsed_response.respond_to?(name) parsed_response.send(name, *args, &block) elsif response.respond_to?(name) response.send(name, *args, &block) else super end end def throw_exception if @request.options[:raise_on] && @request.options[:raise_on].include?(code) ::Kernel.raise ::HTTParty::ResponseError.new(@response), "Code #{code} - #{body}" end end private def warn_about_nil_deprecation trace_line = caller.reject { |line| line.include?('httparty') }.first warning = "[DEPRECATION] HTTParty will no longer override `response#nil?`. " \ "This functionality will be removed in future versions. " \ "Please, add explicit check `response.body.nil? || response.body.empty?`. " \ "For more info refer to: https://github.com/jnunemaker/httparty/issues/568\n" \ "#{trace_line}" warn(warning) end end end require 'httparty/response/headers' httparty-0.18.1/lib/httparty/response/000077500000000000000000000000001367022441500177475ustar00rootroot00000000000000httparty-0.18.1/lib/httparty/response/headers.rb000066400000000000000000000014241367022441500217100ustar00rootroot00000000000000require 'delegate' module HTTParty class Response #:nodoc: class Headers < ::SimpleDelegator include ::Net::HTTPHeader def initialize(header_values = nil) @header = {} if header_values header_values.each_pair do |k,v| if v.is_a?(Array) v.each do |sub_v| add_field(k, sub_v) end else add_field(k, v) end end end super(@header) end def ==(other) if other.is_a?(::Net::HTTPHeader) @header == other.instance_variable_get(:@header) elsif other.is_a?(Hash) @header == other || @header == Headers.new(other).instance_variable_get(:@header) end end end end end httparty-0.18.1/lib/httparty/response_fragment.rb000066400000000000000000000006641367022441500221650ustar00rootroot00000000000000require 'delegate' module HTTParty # Allow access to http_response and code by delegation on fragment class ResponseFragment < SimpleDelegator attr_reader :http_response, :connection def code @http_response.code.to_i end def initialize(fragment, http_response, connection) @fragment = fragment @http_response = http_response @connection = connection super fragment end end end httparty-0.18.1/lib/httparty/text_encoder.rb000066400000000000000000000032351367022441500211240ustar00rootroot00000000000000module HTTParty class TextEncoder attr_reader :text, :content_type, :assume_utf16_is_big_endian def initialize(text, assume_utf16_is_big_endian: true, content_type: nil) @text = text.dup @content_type = content_type @assume_utf16_is_big_endian = assume_utf16_is_big_endian end def call if can_encode? encoded_text else text end end private def can_encode? ''.respond_to?(:encoding) && charset end def encoded_text if 'utf-16'.casecmp(charset) == 0 encode_utf_16 else encode_with_ruby_encoding end end def encode_utf_16 if text.bytesize >= 2 if text.getbyte(0) == 0xFF && text.getbyte(1) == 0xFE return text.force_encoding("UTF-16LE") elsif text.getbyte(0) == 0xFE && text.getbyte(1) == 0xFF return text.force_encoding("UTF-16BE") end end if assume_utf16_is_big_endian # option text.force_encoding("UTF-16BE") else text.force_encoding("UTF-16LE") end end def encode_with_ruby_encoding # NOTE: This will raise an argument error if the # charset does not exist encoding = Encoding.find(charset) text.force_encoding(encoding.to_s) rescue ArgumentError text end def charset return nil if content_type.nil? if (matchdata = content_type.match(/;\s*charset\s*=\s*([^=,;"\s]+)/i)) return matchdata.captures.first end if (matchdata = content_type.match(/;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i)) return matchdata.captures.first.gsub(/\\(.)/, '\1') end end end end httparty-0.18.1/lib/httparty/utils.rb000066400000000000000000000004131367022441500175740ustar00rootroot00000000000000module HTTParty module Utils def self.stringify_keys(hash) return hash.transform_keys(&:to_s) if hash.respond_to?(:transform_keys) hash.each_with_object({}) do |(key, value), new_hash| new_hash[key.to_s] = value end end end end httparty-0.18.1/lib/httparty/version.rb000066400000000000000000000000511367022441500201170ustar00rootroot00000000000000module HTTParty VERSION = "0.18.1" end httparty-0.18.1/script/000077500000000000000000000000001367022441500147705ustar00rootroot00000000000000httparty-0.18.1/script/release000077500000000000000000000015511367022441500163400ustar00rootroot00000000000000#!/bin/sh #/ Usage: release #/ #/ Tag the version in the repo and push the gem. #/ set -e cd $(dirname "$0")/.. [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && { grep '^#/' <"$0"| cut -c4- exit 0 } gem_name=httparty # Build a new gem archive. rm -rf $gem_name-*.gem gem build -q $gem_name.gemspec # Make sure we're on the master branch. (git branch | grep -q '* master') || { echo "Only release from the master branch." exit 1 } # Figure out what version we're releasing. tag=v`ls $gem_name-*.gem | sed "s/^$gem_name-\(.*\)\.gem$/\1/"` echo "Releasing $tag" # Make sure we haven't released this version before. git fetch -t origin (git tag -l | grep -q "$tag") && { echo "Whoops, there's already a '${tag}' tag." exit 1 } # Tag it and bag it. gem push $gem_name-*.gem && git tag "$tag" && git push origin master && git push origin "$tag" httparty-0.18.1/spec/000077500000000000000000000000001367022441500144165ustar00rootroot00000000000000httparty-0.18.1/spec/fixtures/000077500000000000000000000000001367022441500162675ustar00rootroot00000000000000httparty-0.18.1/spec/fixtures/delicious.xml000066400000000000000000000163351367022441500210010ustar00rootroot00000000000000 httparty-0.18.1/spec/fixtures/empty.xml000066400000000000000000000000001367022441500201350ustar00rootroot00000000000000httparty-0.18.1/spec/fixtures/example.html000066400000000000000000000001731367022441500206110ustar00rootroot00000000000000 Example HTML

Example

httparty-0.18.1/spec/fixtures/ssl/000077500000000000000000000000001367022441500170705ustar00rootroot00000000000000httparty-0.18.1/spec/fixtures/ssl/generate.sh000077500000000000000000000023741367022441500212270ustar00rootroot00000000000000#!/bin/sh set -e if [ -d "generated" ] ; then echo >&2 "error: 'generated' directory already exists. Delete it first." exit 1 fi mkdir generated # Generate the CA private key and certificate openssl req -batch -subj '/CN=INSECURE Test Certificate Authority' -newkey rsa:4096 -new -x509 -days 999999 -keyout generated/ca.key -nodes -out generated/ca.crt # Create symlinks for ssl_ca_path openssl generated # Generate the server private key and self-signed certificate openssl req -batch -subj '/CN=localhost' -newkey rsa:4096 -new -x509 -days 999999 -keyout generated/server.key -nodes -out generated/selfsigned.crt # Generate certificate signing request with bogus hostname openssl req -batch -subj '/CN=bogo' -new -key generated/server.key -nodes -out generated/bogushost.csr # Sign the certificate requests openssl x509 -CA generated/ca.crt -CAkey generated/ca.key -set_serial 1 -in generated/selfsigned.crt -out generated/server.crt -clrext -extfile openssl-exts.cnf -extensions cert -days 999999 openssl x509 -req -CA generated/ca.crt -CAkey generated/ca.key -set_serial 1 -in generated/bogushost.csr -out generated/bogushost.crt -clrext -extfile openssl-exts.cnf -extensions cert -days 999999 # Remove certificate signing requests rm -f generated/*.csr httparty-0.18.1/spec/fixtures/ssl/generated/000077500000000000000000000000001367022441500210265ustar00rootroot00000000000000httparty-0.18.1/spec/fixtures/ssl/generated/bogushost.crt000066400000000000000000000034211367022441500235550ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFCjCCAvKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNJTlNF Q1VSRSBUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTAgFw0xOTAzMjAyMDI5NTFa GA80NzU3MDIxMzIwMjk1MVowDzENMAsGA1UEAwwEYm9nbzCCAiIwDQYJKoZIhvcN AQEBBQADggIPADCCAgoCggIBALavqYkWGfPALQaE23pt55NY/7zGeM7PTA7UthnI NjLdXU8wHETAJYCLnE8IXLPZVNUfTIi05Y80IdNIXhdK2ZoC+OAqefXCxA/QCBRc ocFkQVLN0Upv9x28dI/h49/mXtg//zxfkD5bbOuSy6dlTjvPdn6AfiVhTqIhGszB DKMOLL4NLfq8LS7Usj0KZipscWlj20j5SbwWFKqzzkxm9Kr5qvWH3wFlqSx5e5bz tH+CEn+Jem1D3tbru2W0uS/ty59xQZJ3Ga+WfrPKSmqUmEj1GveZIbIjkmlnpXT3 vgb6HUSNRglmGQ1SDOOj3qLfoQG9T4rvMS1m4Dco8araEEZJsNZpqouvA+dSluRj gpfz7BwtsfgF7zenYCtp/QSOU+wVBI8rwM9LE65PMWtehLo2aVUbRZZ55gwmlebO 2GB6A+ZQk7k2Bvp3U8ob4MZzbjT96aa4uVm8LoaYF9jV4gyfKZ4CB7FC/aYkOhzZ X6dkV2Y98DfixK5ewmEoKs44q5PQdRwQSp3qMISPu9TaE/2ylRjcBi2FO2dtDnHq kY+944D1x9OujeFG5qak7s+AlU2bGRwoYjRo9es6vGlIfws1ttkLOsU8HGizMXkt sZKDja+lS1vnYf790MUS4lI0xZSL3IVs+ihwDJ4/74sf6nZEmerd5S8leZQ5zyaP BpoNAgMBAAGjUDBOMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFH5EfRhO2HNbUhsB WRM+F5O1dkYbMB8GA1UdIwQYMBaAFALj11Spg/cQ5epRzUB+cNSHRZ4sMA0GCSqG SIb3DQEBCwUAA4ICAQDKZ6ifsFWV4xk9uC8vPpP4+Tn1FT1fGIqlfPmUFGxzhdR3 94p8MAiM2CR93zBJJAwMC8lPYjpDUACGzfGAmt5hIKs72c1E5zvOxw+54EieoKe9 1isKhJMTtfVqUsga7foZj8XoBv8VscFZOWBFc96vfkwg5vXkl/v8qk227ILKs2U+ Unp1iWT4uY5zofZvwFYKJd3JMAIQ7bkzuOtHJ2r7sCYoHG3B5aVPsA8uNbYC61ri 5PjAl5eSrluy5tnnbhJCrjtwJI/LF1Tq+le2MEJRt7z/n3rfbvcAlmM+4XOAWAKn fM5nbT5qfct65HIBaDjX2Kdd4eOSzUD8/XpPmUXyj7ZlQf0WdKKA1QoSLvOdhHW8 sZlWbFBwfXEU199Zd3EF6f3N3yLIAnAGjs8JEITHFma9TRJDXMF4/2Chg5/iI2f9 sQLVvv2zomstVCoOXFsDmVQmdxVO69wW2KFvucFT8DiJIRnL5x1ZIoG5aylHFdaV LAi6lIluj7ETZwP6tKKOzLHipx7Jr/CafpIcYBzgjODoBgW246v9P2VJtIi7mHus Cw4HkEzJSm4CZm36OIcMayvFYz0FBts61/GvXGFnglCWRvFZ+n795hoUM4XAuXZA 0fxMEH/ZCbILy63kqjGaaOaTk2+Yu2DSRccgKVH7IuxV9xE7AgO0JNTGCS5HNg== -----END CERTIFICATE----- httparty-0.18.1/spec/fixtures/ssl/generated/ca.crt000066400000000000000000000035271367022441500221320ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFPzCCAyegAwIBAgIUIV37QRfu4LcX78hw64FG5rAnZG8wDQYJKoZIhvcNAQEL BQAwLjEsMCoGA1UEAwwjSU5TRUNVUkUgVGVzdCBDZXJ0aWZpY2F0ZSBBdXRob3Jp dHkwIBcNMTkwMzIwMjAyOTI4WhgPNDc1NzAyMTMyMDI5MjhaMC4xLDAqBgNVBAMM I0lOU0VDVVJFIFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIICIjANBgkqhkiG 9w0BAQEFAAOCAg8AMIICCgKCAgEA/LOBASLxwrE1i3lhpS3yigbdcSZIoyEBqIF/ yMuWqVKr/l0CDPwlM/iwF0/sToBbFtx0bkKCq7ztURgw1ItYUar+mP+DL8Ku0Fxz ZVWL3EM1E2vduqJlvbpE6F5I2OU0UC0enSX5xg34vkHSQVGM2VJXNBM6a+0aYxeP o2KjfFOXnsFNVvlMpg2GNNuHwaUXoOhd26pp35QDsyyAPVtiRfU3idB+Pg+Hp3mI FJLHZyRXpVmw3hNlhl+Iga/H//x5n99Scr1CQyl8Z8EE5QKpPa9znjsPJNifSFud a1rBGRDizMfPfn+imW5zXSSFf/2tJnpzP9kaZT5hwXe4HLZbHaoYssOJdXBYDfx9 qdw4yrBKt14z5ZFDjCu7SelDnLtX1aZ/+V2RrkNHge/G54MrKdnAg2CbxeWdrir2 OxlCngzeU0aDuNJczH83054Em/uu3a7xJipuDDOYZe6HTSXHNJgIUtmZhvu07PlO 3tBLY/VxMhgzUvCVYCH05VoZgkmy21TRFu8mNn/+7HiHt5QGTKEqGdlFGJ4gcPFH bq2/dNb45hPr6d1iNC4UM5ERTMpU2rvNQQhQwPiS2fzXhE8BwLxfB94T6hnJpDhT u8tExckzRLfIgvexorSQFvYj2Lj5YjPdIHJq/e/XVSkp6h6H93T7EHycLlTP5HZ2 le6+d4ECAwEAAaNTMFEwHQYDVR0OBBYEFALj11Spg/cQ5epRzUB+cNSHRZ4sMB8G A1UdIwQYMBaAFALj11Spg/cQ5epRzUB+cNSHRZ4sMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQELBQADggIBAG5PduOpr9ZZbR+tcyQUR1kxoc+vZMYQEi8fQyv9 G8sL+/DRXj8fD0S/4aK+rSXuFMbQpL1njLIUxE2z2cCn6zOsXx1IY9rN5ryLSJad L4QXrPokNBi+HEGba7NvY98swD8PblUyeJqfi59pFj7nz4EcVhltVvgbYBMZUIPc IZBhyB7l9QaX5OZMGO1rJy8jxg8ng0vAliz+HQQavu8rNito90vpGvlXr1NQs11H SO4+lQMb8uuU4wqto+74gkLUNLsnAfAFWpIEV2JG+0SnVVbI2TFjvFiu7P/TTGxQ MRrzEVQAXRSIBC4JDXMBAeJRc8AUEK/RYg0BGmvkh3aZUIlkAmCFk1OqyuEYoEl+ 2DsH26Glk978W4glHQi3PgKKa7NSvsHSd4BPGG6Vjs7jBh/LcSnT3iH5pHKkI/Tt sFWUKQ4Yz8DdUR02f890hOdlHG+Hz8DY33fS/Z8WoidMOJM+LHEqT9R4RAJi+puK 6Z8OTBCCJYMEgPx08swJbe9r7Q98n77ud+qyKPa/5jNJG3JYGSzsqhsyJXzCaQCl v1qVrzQ+qidO+tcousDMzNZ+AsdnLOGZ+UvKdx+l3kItj4AkIu2fkJb8sC/UP7ar EswtH8vJb8w03L8K/44Ql0cTDzHNyBJ5q/8xL/2M/L572Y8wmUcVsoPPw6+Js7l/ pSY3 -----END CERTIFICATE----- httparty-0.18.1/spec/fixtures/ssl/generated/ca.key000066400000000000000000000063101367022441500221230ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQD8s4EBIvHCsTWL eWGlLfKKBt1xJkijIQGogX/Iy5apUqv+XQIM/CUz+LAXT+xOgFsW3HRuQoKrvO1R GDDUi1hRqv6Y/4Mvwq7QXHNlVYvcQzUTa926omW9ukToXkjY5TRQLR6dJfnGDfi+ QdJBUYzZUlc0Ezpr7RpjF4+jYqN8U5eewU1W+UymDYY024fBpReg6F3bqmnflAOz LIA9W2JF9TeJ0H4+D4eneYgUksdnJFelWbDeE2WGX4iBr8f//Hmf31JyvUJDKXxn wQTlAqk9r3OeOw8k2J9IW51rWsEZEOLMx89+f6KZbnNdJIV//a0menM/2RplPmHB d7gctlsdqhiyw4l1cFgN/H2p3DjKsEq3XjPlkUOMK7tJ6UOcu1fVpn/5XZGuQ0eB 78bngysp2cCDYJvF5Z2uKvY7GUKeDN5TRoO40lzMfzfTngSb+67drvEmKm4MM5hl 7odNJcc0mAhS2ZmG+7Ts+U7e0Etj9XEyGDNS8JVgIfTlWhmCSbLbVNEW7yY2f/7s eIe3lAZMoSoZ2UUYniBw8Udurb901vjmE+vp3WI0LhQzkRFMylTau81BCFDA+JLZ /NeETwHAvF8H3hPqGcmkOFO7y0TFyTNEt8iC97GitJAW9iPYuPliM90gcmr979dV KSnqHof3dPsQfJwuVM/kdnaV7r53gQIDAQABAoICAQDziStypPLJ527rE/f+8OEm FKelPHgUfuLSOrukEFEKrhoD8i7fxME17R4H2YarwRgIWD39ZSv5xwIPfXjR3dko G9tyKA2OIdnIBNFRf7hidoLYTMRL8eaLitCOAQ/DuGFKQ7GVUdv9+8kV0umG+cj8 SFayYTWUfdVIWpSbqZxVXVpqLXETuP8dqTsGBew3u5uh/081PG78gfFu5BxTBZcY RNNZhg2kUeMyi/WRnkN+K5AsUtwZqifV8IvmMDpXgkLUyKz012DcyUaT13mYG5Bv Wn/apqBZqksXuPNlWvlt5tAs+wQFrYxOwht8UI44Y4pT4v7fMaQ2noAnq/FL+pKj geLkTx6/9J1bN4bI1VnZmSSLVb2gC6XESaHO02vAzPlTtB//pomIgckEdCKQPEDI W58FiYumaIGf7jfXBz4oUW6YF2Tlm5SYUNCeF7Tnbdz/7vebodWENRMy1qhRB89x s8RDOwrGVt+JgQhDdnEOUh1SjlcYevBlGV0OaqXNV1XsSrJxVjWzWmmBJe7qvRdi pbYp6Kt+FZFDuimOyjiCPoiLBSoa5haQZLp8RMWa435fKGbddYryOtSb9CBxW/RP UnpulIiFELrO6sBKAlO9XSpsueSNersdZ6zhuET9JsWES33NDVJeNnLaZZILnGc9 zPnBB0dqueD6C+IDv/8AAQKCAQEA/q5IS13dFb1QXU0h7FvtO3FDlHGIPZUflde1 wsALUE4rqEMnHtntfvtHLiOhMHQ71MMv7l2GT4sRe6lsvVXnk1RUljhbj4rmEZVG fScCxauWkEMx9n6+Mw/TMCG8VEFDc7VWyuuPfQV7vt2vCLbz0gIFlLEIOSSquWet lWZfaBEexe8KEjZLqkMDKzSowVzGCgC3FWaxR4+lDHIn9IGLqGDfSYE65I3r6vYg nW45wOt5NbLiZf+RJycoBzm1PZl/PxZpSaROg0qDZX4D1LSc1gPDlLKe7sXRJEYK dZOko+k7fPjRisoYni5AZxhUwrkuTDX/bKR6f4/2kZKd2I53gQKCAQEA/gKYszzA g/2KyojTDN9uUmN4SThnFPJbJJoJHup4QL8EgNU0ytLt9GD05AuaPs0XFblCjNwY qJ64ouHHx8iXrBAA7yW6+BHswwH79S0cCXl6Xl1+2Uz1LYrogHvHSvzO2uNx1HOH v+bbKQ4LHIyKEOeNGD0JHIHckkZN0acjip7mnJJdPlCrP3REzzBLgeGnzljC2Ms2 d+8mUKBzfO7fIksTBkDrSGVLr5oQv/Fqe+8HSdT0hQkCI7Bo7x0OmrTliQwLTphL nw61y+5wpg88sYZM0SxdPp2TOvfgcoc7ZS0HrdYrIHDDCrV/JQvGQ0AByidbUBI3 7fm2Q7gIrjAAAQKCAQAohxlwDN7Kv9aTElwsnbBRvkNv0uVIT3u4P5xoAmGKhPYD j7Qg/7MAewInwHm9GTIQOINfHjjqXYoJsLtiIdJ5KnlPcmZ3oDXeZG/UKKoTRKvw BxFjVWX1ADauOSAcFEqklh3aqsOptH6tr99TtrF0IOg8cjOJzGDyoiIIXUMfb2ID Q3fJ0CQYUxOlA3s7UgUdwGFiIXZimeQ858md6iOMRuYhb1Qs3LzHJiWoh8re/VnL hszqSFIT9fIzvCYwSEXshyd4FZJ86BWix/vaFGfE0tKDzizmeEpAyHiPn1Aa2Vmj GIFX4bMrMNcE0OVkG03XyNv9sOrhc8pb/gXqWTmBAoIBAE9zWnX90621WXs+Tt1g 6a4FhPNKHBwWLmIFeELeThzaYrs1dRzX2ywsQ40s/+MS3VyjJOjQUzoy40e3XXjl CmP8YX5sC85aNPdOIJQwtutTvu3TSsEHbE0BfPXrQYv4BW+74rf0JwrkV7rAtMMK RolBFAX32Wi8SdTK/r5MDDbouvNQaK/8JYRkhr1Tutp2TbmiU9fhwDjFafOgLF9w jAS6/Mlg9vcfEAxuIT0Ycxkuy9XRMWaHSc8F99yK9y121bEHPmYoBsdKn5yZCU03 yOEyQ8bNnKDgQtQYAnFwUSi1bAh4y+aKvscTvCBHTY1tcOHda5dhC3N5PwRxhO2P AAECggEAP5JXVhNW5R7o4K5fx645lmN7TUZLJz81MAudO6LOF7fMFc8c1E1iuL3W QpMXRgXHqKzuvEirUNyqL/lysFruN9BQvdVes8tPzh0SlZbBgrNvWldsbALmi1mc 1Qet0HbcNK7gCh4tQWEyyrf6i1IYdCQ+QVZA99Rk/MP7IF5ASApUaC7jmwxD8Tci FQ6PhSSEKDc8VvW4ioezsNFWtNnpuLDK5wwYG4XUuEWFrDMaQH37RlAVQZgZcLKv RH9Y/h2RJ04xcvdD1tRHFRHyhm8fJU/XWhRdx/Px2Fe72Nm4Kj4Kv/ub6ppUFG6v iOi/KEjMxb+mQi4jBIKJq/nRXjglUg== -----END PRIVATE KEY----- httparty-0.18.1/spec/fixtures/ssl/generated/selfsigned.crt000066400000000000000000000034211367022441500236630ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFCzCCAvOgAwIBAgIUI1eh28OnE/T/HBS9/6g9f4q3p/0wDQYJKoZIhvcNAQEL BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE5MDMyMDIwMjkzN1oYDzQ3NTcw MjEzMjAyOTM3WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEB AQUAA4ICDwAwggIKAoICAQC2r6mJFhnzwC0GhNt6beeTWP+8xnjOz0wO1LYZyDYy 3V1PMBxEwCWAi5xPCFyz2VTVH0yItOWPNCHTSF4XStmaAvjgKnn1wsQP0AgUXKHB ZEFSzdFKb/cdvHSP4ePf5l7YP/88X5A+W2zrksunZU47z3Z+gH4lYU6iIRrMwQyj Diy+DS36vC0u1LI9CmYqbHFpY9tI+Um8FhSqs85MZvSq+ar1h98BZakseXuW87R/ ghJ/iXptQ97W67tltLkv7cufcUGSdxmvln6zykpqlJhI9Rr3mSGyI5JpZ6V0974G +h1EjUYJZhkNUgzjo96i36EBvU+K7zEtZuA3KPGq2hBGSbDWaaqLrwPnUpbkY4KX 8+wcLbH4Be83p2Araf0EjlPsFQSPK8DPSxOuTzFrXoS6NmlVG0WWeeYMJpXmzthg egPmUJO5Ngb6d1PKG+DGc240/emmuLlZvC6GmBfY1eIMnymeAgexQv2mJDoc2V+n ZFdmPfA34sSuXsJhKCrOOKuT0HUcEEqd6jCEj7vU2hP9spUY3AYthTtnbQ5x6pGP veOA9cfTro3hRuampO7PgJVNmxkcKGI0aPXrOrxpSH8LNbbZCzrFPBxoszF5LbGS g42vpUtb52H+/dDFEuJSNMWUi9yFbPoocAyeP++LH+p2RJnq3eUvJXmUOc8mjwaa DQIDAQABo1MwUTAdBgNVHQ4EFgQUfkR9GE7Yc1tSGwFZEz4Xk7V2RhswHwYDVR0j BBgwFoAUfkR9GE7Yc1tSGwFZEz4Xk7V2RhswDwYDVR0TAQH/BAUwAwEB/zANBgkq hkiG9w0BAQsFAAOCAgEAKMO0HD503riWKRaikCFoA4n7LMUWaTdvZR/CZ/ZX0HJC 1PwBwk67+jLfjDa82cPH2yoxrK4PG21hO/d2+mwgDUh4jzHuJyPAMO45Hhq9zr8K SOQ+CmS7UwJZtLqVAwrxEUjRFpKeBEenNAtkrsTsv/JIp3iyu2FqVAq5mh1X/gDH CsByht2QaDBpZuTXhD0S6miFg21VT2aG1G55pUljOb/LjqEqxqWDZonsOMj8buTo NtAOCjxKIKKJfIz7hFDy+9Kecy/rmidQnRiNBm1dOjN9gk23FPu9EOBriL1jigYJ 3ws6WlgCQhhsTE/mCPyfxHA+8+GdaCc/L4qSNnxzlqN9wuxCpKQvrT2kTXIcjqVB QZz/Ii8Y2ZQR9paY/xCov/mri97uXrE4L2A1CodjcO7UCvswXNHrxgkOgmknb5WC jwbPCSpiYqnjtPvXWj2wygDX3456zZ2J7H7aEUAmOJfaL/xZpxJmejHtYedYINPZ /Zsk1bJTTuStXCvCgIiHIb9ILD7mq2LUDAh+RxANtqW2OAUzvqD/Xkg6zl6utQwJ UbIN1h2o0hl3qHZztVTaHRaBLgaPObhe2kFV9TioAbnJ1t04s4Xjw6ZFT3xhaySi mt5SBtT6qTkMohfYzZOe5+TPIMw87a0J3NwQNLl8kvRnyg00gBY4VU+l6MVTzYs= -----END CERTIFICATE----- httparty-0.18.1/spec/fixtures/ssl/generated/server.crt000066400000000000000000000034261367022441500230530ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNJTlNF Q1VSRSBUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTAgFw0xOTAzMjAyMDI5NTFa GA80NzU3MDIxMzIwMjk1MVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkq hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtq+piRYZ88AtBoTbem3nk1j/vMZ4zs9M DtS2Gcg2Mt1dTzAcRMAlgIucTwhcs9lU1R9MiLTljzQh00heF0rZmgL44Cp59cLE D9AIFFyhwWRBUs3RSm/3Hbx0j+Hj3+Ze2D//PF+QPlts65LLp2VOO892foB+JWFO oiEazMEMow4svg0t+rwtLtSyPQpmKmxxaWPbSPlJvBYUqrPOTGb0qvmq9YffAWWp LHl7lvO0f4ISf4l6bUPe1uu7ZbS5L+3Ln3FBkncZr5Z+s8pKapSYSPUa95khsiOS aWeldPe+BvodRI1GCWYZDVIM46Peot+hAb1Piu8xLWbgNyjxqtoQRkmw1mmqi68D 51KW5GOCl/PsHC2x+AXvN6dgK2n9BI5T7BUEjyvAz0sTrk8xa16EujZpVRtFlnnm DCaV5s7YYHoD5lCTuTYG+ndTyhvgxnNuNP3ppri5WbwuhpgX2NXiDJ8pngIHsUL9 piQ6HNlfp2RXZj3wN+LErl7CYSgqzjirk9B1HBBKneowhI+71NoT/bKVGNwGLYU7 Z20OceqRj73jgPXH066N4UbmpqTuz4CVTZsZHChiNGj16zq8aUh/CzW22Qs6xTwc aLMxeS2xkoONr6VLW+dh/v3QxRLiUjTFlIvchWz6KHAMnj/vix/qdkSZ6t3lLyV5 lDnPJo8Gmg0CAwEAAaNQME4wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUfkR9GE7Y c1tSGwFZEz4Xk7V2RhswHwYDVR0jBBgwFoAUAuPXVKmD9xDl6lHNQH5w1IdFniww DQYJKoZIhvcNAQELBQADggIBAKlXVHoSPWaCh5nDbfe5fqfBxk4D0UV0a4t9tynG Lp3JkMZKwv5nY97l2b2WRPuYQVk9Dg9HISB+DB3ID9kMfxul3Nf/E4lBrF+VQkqO pw0VexiZt4XzWhzx5ZxKDKarmIa/WZms9AuGCa3kwtCopwTrq70AL6aIeJg93FJo qkisyvSX7EUg2i9vgV/QGth37WiJpcrcdUQFfXqjaID2gk+EfC6fNQP3eA5eTj9T Nn9Jssnkhu4EhVS+SiDKdI5F/xyAqZAyrkphZAztUtrcqUs3CPLFjI3c3bpU1LPF 9yCBajuLoubEtaTrkJGeney1CgEW3G2nca69LfCuJbwlIEKiAyw0t8/rBu4MeJy+ G6PO7py0xyOxFHQ4nmilEbeleSJfd2JssjUvDQAdS1K+MnQVWNk+aoF3fFjgBB4a MR7A6RYZ9fMjzJhgdA2Ucc5pO9BN/bIttkF4x7AHeV8WUrdCMC466fAdq4W4Tazh uMsQBqt7J9M28K2PICp3qUEMfY53tQZX39ablTSe/ZULTfjTG+36VPs6JLBCu9TV yPL0or3XqiFMUVxW2AN3uAW1LrklUavvJxGtq0h3DEA0e6KgFVWyP1GH9z2AMuuJ r2sqQdWajjDnX7A+irqMoXxpq5ZjMBZ+AZFB5VOcffVdr71p69EhG8snY33DjmUe kIdU -----END CERTIFICATE----- httparty-0.18.1/spec/fixtures/ssl/generated/server.key000066400000000000000000000063101367022441500230460ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC2r6mJFhnzwC0G hNt6beeTWP+8xnjOz0wO1LYZyDYy3V1PMBxEwCWAi5xPCFyz2VTVH0yItOWPNCHT SF4XStmaAvjgKnn1wsQP0AgUXKHBZEFSzdFKb/cdvHSP4ePf5l7YP/88X5A+W2zr ksunZU47z3Z+gH4lYU6iIRrMwQyjDiy+DS36vC0u1LI9CmYqbHFpY9tI+Um8FhSq s85MZvSq+ar1h98BZakseXuW87R/ghJ/iXptQ97W67tltLkv7cufcUGSdxmvln6z ykpqlJhI9Rr3mSGyI5JpZ6V0974G+h1EjUYJZhkNUgzjo96i36EBvU+K7zEtZuA3 KPGq2hBGSbDWaaqLrwPnUpbkY4KX8+wcLbH4Be83p2Araf0EjlPsFQSPK8DPSxOu TzFrXoS6NmlVG0WWeeYMJpXmzthgegPmUJO5Ngb6d1PKG+DGc240/emmuLlZvC6G mBfY1eIMnymeAgexQv2mJDoc2V+nZFdmPfA34sSuXsJhKCrOOKuT0HUcEEqd6jCE j7vU2hP9spUY3AYthTtnbQ5x6pGPveOA9cfTro3hRuampO7PgJVNmxkcKGI0aPXr OrxpSH8LNbbZCzrFPBxoszF5LbGSg42vpUtb52H+/dDFEuJSNMWUi9yFbPoocAye P++LH+p2RJnq3eUvJXmUOc8mjwaaDQIDAQABAoICAB4NZalMfnZvrbh90JEiVU8S +/b0+1iiF1P1QAObwPa7HadyNaRFq6Wq0craiE15ug/ZN0Dh6UWWgN398KSKRqOk S4as1iVMpe4n/bxDxMRIlsplW0GQi6lToCUvNspVXlLarEUlP1hSt+dE3cWqz6Td KxQCVT4W9NWM9piqRUbphQi6qE6v+ArVKIEMHeRqtHlu2Nr3T17mjv3J2G0PHr6l la8Qa+oR7DOJxH//lTjIj/x1BDPHT3gxjUFc6n3s82tGOVA70XjmG++9nmJMEYUm 9Qm4Iifo3CQEzd4hBxyktn3Zc6UGK6baUY59HZnTiev8VYJ4eLGMU+QfEypU9woh jF7fYgV0pmplA5uvyu1UHc2bqmQJNkyiSKf+kQn+Ooxg3qZ6PP4jMh/a3ma2Aq9b peDDJy1d4uqlvCwE7Rwe3wSpWktiQ5m4E03I+g4klo8WWtRf7dcsEbiV+6slTUnf 4Lm37reMal8SEuOLGsOTLL86dSxf6Xe+cWIB/j0j9OiChdybTHrbkcbmmWaw388T XPlNpnPDeFsVY36gtchQSCv4jWVBoTo31zdp49lywB7jeBLokePjVE9Xpp1RFxLm yCvzPEIIhM85Nv22g0LGbBluUVg5o+G8ZiuB5MAGQd7hhK3kTgOiHSKn6X6DgMMP 6O7/nBhH52IIHdRKbpXBAoIBAQDthYcg0pXdMhQjluuNt1HCAUSgPCv/Qd/rwxmy kCtmvPEDiFYhIUwIF1rs2wP87Gw8V2U/apu2wB5m1AwMNCAFOjL7zWjaXoxvYsnL xfsvMphtc6nluco8+AQs1t9tgXMtyTBce/9Tvym2oc1es7/6iedV6DHRW+g9Ix+W O7fLPlmSUWAsfcAoipecu6gEM5Qz3fEV4OI2Hh8ludadonoaTyUHYpi+4TPmztmG 9KTN8Og64584ZzYhOQFcdr509bZh/cEnqQ1oTeClHZh9VukMdpIA2qsZgXzkUKT6 8hl9KDO4RMGJZ88unb5yiWcXWfCLdlTU8VXUqRLkwMmHbRXRAoIBAQDE5gi+Ge3T P1aIi0UkwcXrvw/Xi8rZJinRqozOyjnXAi1Z7pi29OR+swWwaBSYP2SNBnVIrq6P wZdlIALdNbEU2lVcilzh5DiorIvbwW/a24V2RYeNEA+tWll9IMvLGclDJ1qQPFOE 9hMVJN55gCwiWE1eUl/xTyMSwSFWqMw97z8l1tG7XBJN042gPZL1TcLebqsYbfgh ZskCSeWki4TopMLf2sBKTK2KOxrPTVznv07mChKExj5jRyriYZGpwkXxKmnA8YA+ mAqtpGT2hcz44tTsfUNKWSZpYexf1WQfsZUUwshCLQc2QSVaCjY/DcpCrERoqxho ToYkBmYkSYN9AoIBAQDlAEJiIOaglvHXxmBhCAfpdnOF0b3Rot+TXU1fXaSarzvn 3gC8fG/jtTDS/+5+YiuQmepiFBUBQ4WMOpFLCs58e7pAD7EyKMpZcfOBZeZhgPR5 QnThqhkUY0MTQC+2UoKL+FeKM3recYZ0MNfioIdNtLu3leDCK0xj9YM0w2rWp8yQ R6jj/HHSJJ08XakGM/yFbUxNfb5b0vrQ0rXa0ZXL29aloigGkPYLaA9OEHz8mXIt pNwT81669U0cqtfByE0JeTBLXrAwijU2vKwS3EJg48iszib8tl2Fe0M1N0nf42zK EnBOXCnile/SWits1igXLgVoMS5BlKlEp6Ml0MShAoIBACTyra1OnExJ1GknCUCO hB9q6QQfnV8GRE6t3GJpnFT2eaAon8ulRMNpwKWLwmGXc7zq+1M6RFOBlnFJgoAU NTieh+onpKpW8zLjvhLsx5qgGvlIrtkPrIFxNN3AK73fBt9tIRdz2pBWxNnw4zaV kLKZM1uBxbM4kHJSf3kNj9YHcSgemZi4/E5SZn8tTshtYSXwSnb5G9jYutuNFmS8 MaKeWFs9Z1wJ637G/I7uC4MJaTrNpQ/5S3fBwRBeEsFyTUGmHdYw+0nDqi1Pbgq8 rOv/VMlT8C6hcA3SbFLHblRsNHeC3aVdYb8SlnnA9ND8O3orbWLeyGO6aH2WHdCt YtkCggEAJPfr8O129Nq7p0ke48Sh/jrrqJ0FwVdmEBYZWDkkjpp9bz493x+rFpDQ S8Qq3MOgO+BAHTBACh/Y2kHDUAr43ZNfusMd1WXJS9Y3ORVI3CPVDrVQpzsAX5t+ dCcdmHiWzFXob4yTKRcwNku44JoxWRA/pzSft/H5ha4UTwWxBPaIiDvvhsNX6GRU xqN8ZD5d9FH3Q4KJa5NI0j90gW6YbD8wnzSWcbOF1AqosjaZ/om9aT3eIYe6bhEX eqb2hwFVYfE6jaaIrhBtetyFEWinjfSFhOIxaZ8LFLVCqGeM2DNhF/PRQTMf42aE ISDbj3Gvk5Gx42y1LbTSnVg8SHm4Vw== -----END PRIVATE KEY----- httparty-0.18.1/spec/fixtures/ssl/openssl-exts.cnf000066400000000000000000000003341367022441500222240ustar00rootroot00000000000000[ca] basicConstraints=critical,CA:true subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always [cert] basicConstraints=critical,CA:false subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer httparty-0.18.1/spec/fixtures/tiny.gif000066400000000000000000000000321367022441500177340ustar00rootroot00000000000000GIF89a,;httparty-0.18.1/spec/fixtures/twitter.csv000066400000000000000000000004161367022441500205070ustar00rootroot00000000000000"name","url","id","description","protected","screen_name","followers_count","profile_image_url","location" "Magic 8 Bot",,"17656026","ask me a question","false","magic8bot","90","http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg",httparty-0.18.1/spec/fixtures/twitter.json000066400000000000000000000313121367022441500206640ustar00rootroot00000000000000[{"user":{"followers_count":1,"description":null,"url":null,"profile_image_url":"http:\/\/static.twitter.com\/images\/default_profile_normal.png","protected":false,"location":"Opera Plaza, California","screen_name":"Pyk","name":"Pyk","id":"7694602"},"text":"@lapilofu If you need a faster transfer, target disk mode should work too :)","truncated":false,"favorited":false,"in_reply_to_user_id":6128642,"created_at":"Sat Dec 06 21:29:14 +0000 2008","source":"twitterrific","in_reply_to_status_id":1042497164,"id":"1042500587"},{"user":{"followers_count":278,"description":"しがないプログラマ\/とりあえず宮子は俺の嫁\u2026いや、娘だ!\/Delphi&Pythonプログラマ修行中\/bot製作にハマりつつある。三つほど製作&構想中\/[eof]","url":"http:\/\/logiq.orz.hm\/","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/59588711\/l_ワ_l↑_normal.png","protected":false,"location":"ひだまり荘202号室","screen_name":"feiz","name":"azkn3","id":"14310520"},"text":"@y_benjo ちょー遅レスですがただのはだいろすぎる・・・ ( ll ワ ll )","truncated":false,"favorited":false,"in_reply_to_user_id":8428752,"created_at":"Sat Dec 06 21:29:14 +0000 2008","source":"twit","in_reply_to_status_id":1042479758,"id":"1042500586"},{"user":{"followers_count":1233,"description":""to understand one life you must swallow the world." I run refine+focus: a marketing agency working w\/ brands, media and VCs. http:\/\/tinyurl.com\/63mrn","url":"http:\/\/www.quiverandquill.com","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/53684650\/539059005_2a3b462d20_normal.jpg","protected":false,"location":"Cambridge, MA ","screen_name":"quiverandquill","name":"zach Braiker","id":"6845872"},"text":"@18percentgrey I didn't see Damon on Palin. i'll look on youtube. thx .Z","truncated":false,"favorited":false,"in_reply_to_user_id":10529932,"created_at":"Sat Dec 06 21:29:12 +0000 2008","source":"web","in_reply_to_status_id":1042499331,"id":"1042500584"},{"user":{"followers_count":780,"description":"Mein Blog ist unter http:\/\/blog.helmschrott.de zu finden. Unter http:\/\/blogalm.de kannst Du Deinen Blog eintragen!","url":"http:\/\/helmschrott.de\/blog","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/60997686\/avatar-250_normal.jpg","protected":false,"location":"Münchner Straße","screen_name":"helmi","name":"Frank Helmschrott","id":"867641"},"text":"@gigold auch mist oder?ich glaub ich fangs jetzt dann einfach mal an - wird schon vernünftige update-prozesse geben.","truncated":false,"favorited":false,"in_reply_to_user_id":959931,"created_at":"Sat Dec 06 21:29:11 +0000 2008","source":"twhirl","in_reply_to_status_id":1042500095,"id":"1042500583"},{"user":{"followers_count":63,"description":"Liberation from Misconceptions","url":"http:\/\/sexorcism.blogspot.com","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/63897302\/having-sex-on-bed_normal.jpg","protected":false,"location":"USA","screen_name":"Sexorcism","name":"Sexorcism","id":"16929435"},"text":"@thursdays_child someecards might.","truncated":false,"favorited":false,"in_reply_to_user_id":14484963,"created_at":"Sat Dec 06 21:29:13 +0000 2008","source":"twittergadget","in_reply_to_status_id":1042499777,"id":"1042500581"},{"user":{"followers_count":106,"description":"Researcher. Maître de Conférences - Sémiologue - Spécialiste des médias audiovisuels. Analyste des médias, de la télévision et de la presse people (gossip). ","url":"http:\/\/semioblog.over-blog.org\/","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/57988482\/Thomas_et_Vic_pour_promo_2_058_normal.JPG","protected":false,"location":"France","screen_name":"semioblog","name":"Virginie Spies","id":"10078802"},"text":"@richardvonstern on reparle de tout cela bientôt, si vous voulez vraiment m'aider","truncated":false,"favorited":false,"in_reply_to_user_id":15835317,"created_at":"Sat Dec 06 21:29:13 +0000 2008","source":"twitterrific","in_reply_to_status_id":1042357537,"id":"1042500580"},{"user":{"followers_count":26,"description":"","url":"","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/63461084\/November2ndpics_125_normal.jpg","protected":false,"location":"Louisville, Ky","screen_name":"scrapaunt","name":"scrapaunt","id":"16660671"},"text":"@NKOTB4LIFE Hope your neck feels better after your nap.","truncated":false,"favorited":false,"in_reply_to_user_id":16041403,"created_at":"Sat Dec 06 21:29:10 +0000 2008","source":"web","in_reply_to_status_id":1042450159,"id":"1042500579"},{"user":{"followers_count":245,"description":"Maui HI Real Estate Salesperson specializing in off the grid lifestyle","url":"http:\/\/www.eastmaui.com","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/59558480\/face2_normal.jpg","protected":false,"location":"north shore maui hawaii","screen_name":"mauihunter","name":"Georgina M. Hunter ","id":"16161708"},"text":"@BeeRealty http:\/\/twitpic.com\/qpog - It's a good safe place to lay - no dogs up there.","truncated":false,"favorited":false,"in_reply_to_user_id":15781063,"created_at":"Sat Dec 06 21:29:13 +0000 2008","source":"twitpic","in_reply_to_status_id":1042497815,"id":"1042500578"},{"user":{"followers_count":95,"description":"","url":"","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/66581657\/nose-pick_normal.jpg","protected":false,"location":"zoetermeer","screen_name":"GsKlukkluk","name":"Klukkluk","id":"14218588"},"text":"twit \/off zalige nacht!","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:14 +0000 2008","source":"web","in_reply_to_status_id":null,"id":"1042500577"},{"user":{"followers_count":33,"description":"Living in denial that I live in a podunk town, I spend my time in search of good music in LA. Pure city-dweller and puma. That's about it.","url":"","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/56024131\/Photo_145_normal.jpg","protected":false,"location":"Santa Barbara, CA","screen_name":"pumainthemvmt","name":"Valerie","id":"15266837"},"text":"I love my parents with all my heart, but sometimes they make me want to scratch my eyes out.","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:10 +0000 2008","source":"sms","in_reply_to_status_id":null,"id":"1042500576"},{"user":{"followers_count":99,"description":"大学生ですよ。Ad[es]er。趣味で辭書とか編輯してゐます。JavaScriptでゲーム書きたいけど時間ねえ。","url":"http:\/\/greengablez.net\/diary\/","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/60269370\/zonu_1_normal.gif","protected":false,"location":"Sapporo, Hokkaido, Japan","screen_name":"tadsan","name":"船越次男","id":"11637282"},"text":"リトル・プリンセスとだけ書かれたら小公女を連想するだろ、常識的に考へて。","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:11 +0000 2008","source":"tiitan","in_reply_to_status_id":null,"id":"1042500575"},{"user":{"followers_count":68,"description":"I love all things beer. What goes better with beer than Porn, nothig I tell ya nothing.","url":"","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/66479069\/Picture_9_normal.jpg","protected":false,"location":"Durant","screen_name":"Jeffporn","name":"Jeffporn","id":"14065262"},"text":"At Lefthand having milk stout on cask - Photo: http:\/\/bkite.com\/02PeH","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:10 +0000 2008","source":"brightkite","in_reply_to_status_id":null,"id":"1042500574"},{"user":{"followers_count":7,"description":"","url":"http:\/\/www.PeteKinser.com","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/65572489\/PeteKinser-close_normal.jpg","protected":false,"location":"Denver, CO","screen_name":"pkinser","name":"pkinser","id":"15570525"},"text":"Snooze is where it's at for brunch if you're ever in Denver. Yum.","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:11 +0000 2008","source":"fring","in_reply_to_status_id":null,"id":"1042500572"},{"user":{"followers_count":75,"description":"I am a gamer and this is my gaming account, check out my other Twitter account for non-gaming tweets.","url":"http:\/\/twitter.com\/Nailhead","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/56881055\/nailhead_184x184_normal.jpg","protected":false,"location":"Huntsville, AL","screen_name":"Nailhead_Gamer","name":"Eric Fullerton","id":"15487663"},"text":"Completed the epic quest line for the Death Knight. Now what? Outlands? I wish to skip Outlands please, thanks.","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:13 +0000 2008","source":"twitterfox","in_reply_to_status_id":null,"id":"1042500571"},{"user":{"followers_count":111,"description":"","url":"http:\/\/www.ns-tech.com\/blog\/geldred.nsf","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/63865052\/brak2_normal.JPG","protected":false,"location":"Cleveland OH","screen_name":"geldred","name":"geldred","id":"14093394"},"text":"I'm at Target Store - Avon OH (35830 Detroit Rd, Avon, OH 44011, USA) - http:\/\/bkite.com\/02PeI","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:13 +0000 2008","source":"brightkite","in_reply_to_status_id":null,"id":"1042500570"},{"user":{"followers_count":16,"description":"Soundtrack Composer\/Musician\/Producer","url":"http:\/\/www.reverbnation\/musicbystratos","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/63311865\/logo-stratos_normal.png","protected":false,"location":"Grove City, Ohio 43123","screen_name":"Stratos","name":"Bryan K Borgman","id":"756062"},"text":"is reminded how beautiful the world can be when it's blanketed by clean white snow.","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:13 +0000 2008","source":"web","in_reply_to_status_id":null,"id":"1042500569"},{"user":{"followers_count":7,"description":null,"url":null,"profile_image_url":"http:\/\/static.twitter.com\/images\/default_profile_normal.png","protected":false,"location":null,"screen_name":"garrettromine","name":"garrettromine","id":"16120885"},"text":"Go Julio","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:10 +0000 2008","source":"sms","in_reply_to_status_id":null,"id":"1042500568"},{"user":{"followers_count":111,"description":"WHAT IS HAPPNING IN THE WORLD??? SEE DIFFERENT NEWS STORIES FROM MANY SOURCES.","url":"http:\/\/henrynews.wetpaint.com","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/65118112\/2008-election-map-nytimes_normal.png","protected":false,"location":"","screen_name":"HenryNews","name":"HenryNews","id":"17398510"},"text":"Svindal completes double at Beaver Creek: Read full story for latest details. http:\/\/tinyurl.com\/6qugub","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:13 +0000 2008","source":"twitterfeed","in_reply_to_status_id":null,"id":"1042500567"},{"user":{"followers_count":34,"description":"I am a man of many bio's, scott bio's!","url":"http:\/\/flickr.com\/photos\/giantcandy","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/25680382\/Icon_for_Twitter_normal.jpg","protected":false,"location":"Loves Park, IL, USA","screen_name":"Pychobj2001","name":"William Boehm Jr","id":"809103"},"text":"I have a 3rd break light and the license plate lights are out...just replacing 1 plate light...abide by law just enough","truncated":false,"favorited":false,"in_reply_to_user_id":null,"created_at":"Sat Dec 06 21:29:10 +0000 2008","source":"twidroid","in_reply_to_status_id":null,"id":"1042500566"},{"user":{"followers_count":61,"description":"Wife. Designer. Green Enthusiast. New Homeowner. Pet Owner. Internet Addict.","url":"http:\/\/confessionsofadesignjunkie.blogspot.com\/","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/66044439\/n27310459_33432814_6743-1_normal.jpg","protected":false,"location":"Indiana","screen_name":"smquaseb","name":"Stacy","id":"15530992"},"text":"@Indygardener We still had a few people shoveling in our neighborhood - I didn't think it was enough to shovel, but keeps the kids busy.","truncated":false,"favorited":false,"in_reply_to_user_id":12811482,"created_at":"Sat Dec 06 21:29:13 +0000 2008","source":"web","in_reply_to_status_id":1042488558,"id":"1042500565"}]httparty-0.18.1/spec/fixtures/twitter.xml000066400000000000000000000416741367022441500205270ustar00rootroot00000000000000 Sun Dec 07 00:36:16 +0000 2008 1042729116 @sebbo Outlook not so good web false 1042716367 2989541 false 17656026 Magic 8 Bot magic8bot ask me a question http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg false 90 Sun Dec 07 00:36:14 +0000 2008 1042729115 Azdel Slade :friends from midian city http://bloghud.com/id/27312 web false false 801094 BlogHUD bloghud http://s3.amazonaws.com/twitter_production/profile_images/25235272/bloghud_twitter_normal.jpg false 313 Sun Dec 07 00:36:14 +0000 2008 1042729114 Reading: "The Reckoning - Debt Watchdogs - Tamed or Caught Napping? - Series - NYTimes.com" ( http://tinyurl.com/5754s6 ) twitthat false false 3512101 bill niubi in beijing learning socialism 2 prepare 4 return 2 us beijing http://s3.amazonaws.com/twitter_production/profile_images/53292616/realtwitterers_normal.jpg false 710 Sun Dec 07 00:36:14 +0000 2008 1042729113 Fianlly done and headed home! sms false false 13186842 Okthirddayfan Okthirddayfan Oklahoma! http://s3.amazonaws.com/twitter_production/profile_images/61414367/mecropped_normal.jpg http://thirddaypix.blogspot.com/ false 68 Sun Dec 07 00:36:16 +0000 2008 1042729112 Adobe Flashplayer 10 and Debug versions: http://www.adobe.com/support/flashplayer/downloads.html toro false false 15243380 cbrueggenolte cbrueggenolte 27, Male, Mac Geek, Developer Java &amp; Flex &amp; Air Aachen http://s3.amazonaws.com/twitter_production/profile_images/55929263/214508011845f026bfd407c_normal.jpg http://my.opera.com/carstenbrueggenolte false 16 Sun Dec 07 00:36:14 +0000 2008 1042729111 Done and done. twitterrific false false 10978752 Sergey Safonov iron_Lung I have my fingers in many pies. Moscow http://s3.amazonaws.com/twitter_production/profile_images/57841057/eat38_normal.gif http://www.flickr.com/photos/iron_Lung false 11 Sun Dec 07 00:36:14 +0000 2008 1042729110 Veja a tabela de preços do Acquaplay da Tecnisa aqui:http://tinyurl.com/acquaplaypreco web false false 13735402 Tecnisa S.A Tecnisa Mais construtora por m2 Faria Lima, 3144 - SP http://s3.amazonaws.com/twitter_production/profile_images/56572222/logo_normal.jpg http://www.tecnisa.com.br false 77 Sun Dec 07 00:36:16 +0000 2008 1042729108 devin harris has the flu. always have the memory of jordan scoring 50 on the knicks at the garden with the flu web false false 17930773 24 Seconds to Shoot NBA24sts NBA handicapping insight and analysis. las vegas http://s3.amazonaws.com/twitter_production/profile_images/66593862/fresno_normal.jpg false 1 Sun Dec 07 00:36:16 +0000 2008 1042729105 At Brandon and Shannon's holiday party.. twitterberry false false 5388602 Mike J. (Telligent) mjamrst Video Game Account Manager Palo Alto, CA http://s3.amazonaws.com/twitter_production/profile_images/66375174/Thanksgiving_11272008-048_normal.jpg http://www.telligent.com false 225 Sun Dec 07 00:36:13 +0000 2008 1042729104 Xinhua: Forty percent of Australian PM office' staff quit : CANBERRA, Dec. 7 (Xinhua) -- Only.. http://tinyurl.com/5gwotd twitterfeed false false 11566502 Headline News headlinenews http://s3.amazonaws.com/twitter_production/profile_images/41784602/1890_wires_normal.jpg false 575 Sun Dec 07 00:36:13 +0000 2008 1042729101 @hilarycassman soo funnny sms false 1042725825 17644455 false 17887548 katieballss katieballss http://s3.amazonaws.com/twitter_production/profile_images/66523737/th_Photo87_normal.jpg http://www.myspace.com/xxpeachxx101 false 23 Sun Dec 07 00:36:13 +0000 2008 1042729100 d'ora in poi, oltre al ferragosto, odiera' anche il natale e tutto il mese che ci gira intorno.. =.='' mobile false false 9719482 trotto trotto sociologo di formazione... uno dei tanti attivisti! :) Fano - Italy http://s3.amazonaws.com/twitter_production/profile_images/55603933/mybob-calimetux-1526_normal.png http://trotto1308.netsons.org/wordpress/ false 98 Sun Dec 07 00:36:13 +0000 2008 1042729099 Came across an ad on another site with a redneck looking santa... said "Bust Santa's zit and win a free ipod." Umm... disturbing much? web false false 9937182 froggybluesock froggybluesock Indiana http://s3.amazonaws.com/twitter_production/profile_images/35393032/about_me_normal.jpg http://www.footprintsonthemoon.com false 82 Sun Dec 07 00:36:17 +0000 2008 1042729098 nothing web false false 17932339 treblehook treblehook http://static.twitter.com/images/default_profile_normal.png false 0 Sun Dec 07 00:36:13 +0000 2008 1042729095 Setting up my new Windows Live profile web false false 17906075 Mark_Layton Mark_Layton http://static.twitter.com/images/default_profile_normal.png false 2 Sun Dec 07 00:36:13 +0000 2008 1042729092 me voy a sobar, a ver si mañana estoy mejor, wenas noches a tod@s web false false 14062655 tmaniak tmaniak http://s3.amazonaws.com/twitter_production/profile_images/51680481/BF-109_normal.jpg http://dhost.info/tmaniak false 10 Sun Dec 07 00:36:14 +0000 2008 1042729090 バイト延長戦入りましたー 店長が遅刻ってどうなんだ。しかも何回も natsuliphone false false 15618846 kabayaki kabayaki FPS(L4D、TF2、CS:S、BF)、TPS、RCG、マンガ、アニメ、デジモノが好きなとある学生 tokyo http://s3.amazonaws.com/twitter_production/profile_images/57305902/184_normal.jpg http://kabayakiya.blog43.fc2.com/ false 20 Sun Dec 07 00:36:13 +0000 2008 1042729089 just drove to southern california and joy of children hugging grandparents made it all worthwhile. heading to imedia in la quinta tomorrow. web false false 1630261 mark silva marksilva digital media http://realbranding.com Principal, Managing Director often san francisco http://s3.amazonaws.com/twitter_production/profile_images/29807902/silvasimpson3th_normal.jpg http://marksilva.com false 497 Sun Dec 07 00:36:14 +0000 2008 1042729088 @wholefoods would love to have a juicebar in one of your cambridge or boston locations web false 1041125103 15131310 false 15856582 Maura McGovern mmcgovern photographer with a day job in venture capital; obsessed with design blogs, long walks, yoga, social networking, people watching. tea and music are essential! Boston http://s3.amazonaws.com/twitter_production/profile_images/66110383/DSCN1740_normal.JPG http://lefteyephotography.blogspot.com false 244 Sun Dec 07 00:36:15 +0000 2008 1042729087 Going over the Advent lists. web false false 17122107 gsusan gsusan US American writer/sister/daughter/aunt/woman from New England living in SoCal. San Diego, CA USA http://s3.amazonaws.com/twitter_production/profile_images/63951854/susan_ocracoke_normal.jpg false 6 httparty-0.18.1/spec/fixtures/undefined_method_add_node_for_nil.xml000066400000000000000000000002411367022441500256340ustar00rootroot00000000000000 httparty-0.18.1/spec/httparty/000077500000000000000000000000001367022441500162755ustar00rootroot00000000000000httparty-0.18.1/spec/httparty/connection_adapter_spec.rb000066400000000000000000000522741367022441500235050ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::ConnectionAdapter do describe "initialization" do let(:uri) { URI 'http://www.google.com' } it "takes a URI as input" do HTTParty::ConnectionAdapter.new(uri) end it "raises an ArgumentError if the uri is nil" do expect { HTTParty::ConnectionAdapter.new(nil) }.to raise_error ArgumentError end it "raises an ArgumentError if the uri is a String" do expect { HTTParty::ConnectionAdapter.new('http://www.google.com') }.to raise_error ArgumentError end it "sets the uri" do adapter = HTTParty::ConnectionAdapter.new(uri) expect(adapter.uri).to be uri end it "also accepts an optional options hash" do HTTParty::ConnectionAdapter.new(uri, {}) end it "sets the options" do options = {foo: :bar} adapter = HTTParty::ConnectionAdapter.new(uri, options) expect(adapter.options.keys).to include(:verify, :verify_peer, :foo) end end describe ".call" do let(:uri) { URI 'http://www.google.com' } let(:options) { { foo: :bar } } it "generates an HTTParty::ConnectionAdapter instance with the given uri and options" do expect(HTTParty::ConnectionAdapter).to receive(:new).with(uri, options).and_return(double(connection: nil)) HTTParty::ConnectionAdapter.call(uri, options) end it "calls #connection on the connection adapter" do adapter = double('Adapter') connection = double('Connection') expect(adapter).to receive(:connection).and_return(connection) allow(HTTParty::ConnectionAdapter).to receive_messages(new: adapter) expect(HTTParty::ConnectionAdapter.call(uri, options)).to be connection end end describe '#connection' do let(:uri) { URI 'http://www.google.com' } let(:options) { Hash.new } let(:adapter) { HTTParty::ConnectionAdapter.new(uri, options) } describe "the resulting connection" do subject { adapter.connection } it { is_expected.to be_an_instance_of Net::HTTP } context "using port 80" do let(:uri) { URI 'http://foobar.com' } it { is_expected.not_to use_ssl } end context "when dealing with ssl" do let(:uri) { URI 'https://foobar.com' } context "uses the system cert_store, by default" do let!(:system_cert_store) do system_cert_store = double('default_cert_store') expect(system_cert_store).to receive(:set_default_paths) expect(OpenSSL::X509::Store).to receive(:new).and_return(system_cert_store) system_cert_store end it { is_expected.to use_cert_store(system_cert_store) } end context "should use the specified cert store, when one is given" do let(:custom_cert_store) { double('custom_cert_store') } let(:options) { {cert_store: custom_cert_store} } it { is_expected.to use_cert_store(custom_cert_store) } end context "using port 443 for ssl" do let(:uri) { URI 'https://api.foo.com/v1:443' } it { is_expected.to use_ssl } end context "https scheme with default port" do it { is_expected.to use_ssl } end context "https scheme with non-standard port" do let(:uri) { URI 'https://foobar.com:123456' } it { is_expected.to use_ssl } end context "when ssl version is set" do let(:options) { {ssl_version: :TLSv1} } it "sets ssl version" do expect(subject.ssl_version).to eq(:TLSv1) end end if RUBY_VERSION > '1.9' end context "when dealing with IPv6" do let(:uri) { URI 'http://[fd00::1]' } it "strips brackets from the address" do expect(subject.address).to eq('fd00::1') end end context "specifying ciphers" do let(:options) { {ciphers: 'RC4-SHA' } } it "should set the ciphers on the connection" do expect(subject.ciphers).to eq('RC4-SHA') end end if RUBY_VERSION > '1.9' context "when timeout is not set" do it "doesn't set the timeout" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false ) expect(http).not_to receive(:open_timeout=) expect(http).not_to receive(:read_timeout=) expect(http).not_to receive(:write_timeout=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end context "when setting timeout" do context "to 5 seconds" do let(:options) { {timeout: 5} } describe '#open_timeout' do subject { super().open_timeout } it { is_expected.to eq(5) } end describe '#read_timeout' do subject { super().read_timeout } it { is_expected.to eq(5) } end if RUBY_VERSION >= '2.6.0' describe '#write_timeout' do subject { super().write_timeout } it { is_expected.to eq(5) } end end end context "and timeout is a string" do let(:options) { {timeout: "five seconds"} } it "doesn't set the timeout" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false ) expect(http).not_to receive(:open_timeout=) expect(http).not_to receive(:read_timeout=) expect(http).not_to receive(:write_timeout=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end end context "when timeout is not set and read_timeout is set to 6 seconds" do let(:options) { {read_timeout: 6} } describe '#read_timeout' do subject { super().read_timeout } it { is_expected.to eq(6) } end it "should not set the open_timeout" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false, :read_timeout= => 0 ) expect(http).not_to receive(:open_timeout=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end it "should not set the write_timeout" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false, :read_timeout= => 0 ) expect(http).not_to receive(:write_timeout=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end context "when timeout is set and read_timeout is set to 6 seconds" do let(:options) { {timeout: 5, read_timeout: 6} } describe '#open_timeout' do subject { super().open_timeout } it { is_expected.to eq(5) } end if RUBY_VERSION >= '2.6.0' describe '#write_timeout' do subject { super().write_timeout } it { is_expected.to eq(5) } end end describe '#read_timeout' do subject { super().read_timeout } it { is_expected.to eq(6) } end it "should override the timeout option" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false, :read_timeout= => 0, :open_timeout= => 0, :write_timeout= => 0, ) expect(http).to receive(:open_timeout=) expect(http).to receive(:read_timeout=).twice if RUBY_VERSION >= '2.6.0' expect(http).to receive(:write_timeout=) end allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end context "when timeout is not set and open_timeout is set to 7 seconds" do let(:options) { {open_timeout: 7} } describe '#open_timeout' do subject { super().open_timeout } it { is_expected.to eq(7) } end it "should not set the read_timeout" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false, :open_timeout= => 0 ) expect(http).not_to receive(:read_timeout=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end it "should not set the write_timeout" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false, :open_timeout= => 0 ) expect(http).not_to receive(:write_timeout=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end context "when timeout is set and open_timeout is set to 7 seconds" do let(:options) { {timeout: 5, open_timeout: 7} } describe '#open_timeout' do subject { super().open_timeout } it { is_expected.to eq(7) } end if RUBY_VERSION >= '2.6.0' describe '#write_timeout' do subject { super().write_timeout } it { is_expected.to eq(5) } end end describe '#read_timeout' do subject { super().read_timeout } it { is_expected.to eq(5) } end it "should override the timeout option" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false, :read_timeout= => 0, :open_timeout= => 0, :write_timeout= => 0, ) expect(http).to receive(:open_timeout=).twice expect(http).to receive(:read_timeout=) if RUBY_VERSION >= '2.6.0' expect(http).to receive(:write_timeout=) end allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end if RUBY_VERSION >= '2.6.0' context "when timeout is not set and write_timeout is set to 8 seconds" do let(:options) { {write_timeout: 8} } describe '#write_timeout' do subject { super().write_timeout } it { is_expected.to eq(8) } end it "should not set the open timeout" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false, :read_timeout= => 0, :open_timeout= => 0, :write_timeout= => 0, ) expect(http).not_to receive(:open_timeout=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end it "should not set the read timeout" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false, :read_timeout= => 0, :open_timeout= => 0, :write_timeout= => 0, ) expect(http).not_to receive(:read_timeout=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end context "when timeout is set and write_timeout is set to 8 seconds" do let(:options) { {timeout: 2, write_timeout: 8} } describe '#write_timeout' do subject { super().write_timeout } it { is_expected.to eq(8) } end it "should override the timeout option" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false, :read_timeout= => 0, :open_timeout= => 0, :write_timeout= => 0, ) expect(http).to receive(:read_timeout=) expect(http).to receive(:open_timeout=) expect(http).to receive(:write_timeout=).twice allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end end context "when max_retries is not set" do it "doesn't set the max_retries" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false ) expect(http).not_to receive(:max_retries=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end context "when setting max_retries" do if RUBY_VERSION >= '2.5.0' context "to 5 times" do let(:options) { {max_retries: 5} } describe '#max_retries' do subject { super().max_retries } it { is_expected.to eq(5) } end end context "to 0 times" do let(:options) { {max_retries: 0} } describe '#max_retries' do subject { super().max_retries } it { is_expected.to eq(0) } end end end context "and max_retries is a string" do let(:options) { {max_retries: "five times"} } it "doesn't set the max_retries" do http = double( "http", :null_object => true, :use_ssl= => false, :use_ssl? => false ) expect(http).not_to receive(:max_retries=) allow(Net::HTTP).to receive_messages(new: http) adapter.connection end end end context "when debug_output" do let(:http) { Net::HTTP.new(uri) } before do allow(Net::HTTP).to receive_messages(new: http) end context "is set to $stderr" do let(:options) { {debug_output: $stderr} } it "has debug output set" do expect(http).to receive(:set_debug_output).with($stderr) adapter.connection end end context "is not provided" do it "does not set_debug_output" do expect(http).not_to receive(:set_debug_output) adapter.connection end end end context 'when providing proxy address and port' do let(:options) { {http_proxyaddr: '1.2.3.4', http_proxyport: 8080} } it { is_expected.to be_a_proxy } describe '#proxy_address' do subject { super().proxy_address } it { is_expected.to eq('1.2.3.4') } end describe '#proxy_port' do subject { super().proxy_port } it { is_expected.to eq(8080) } end context 'as well as proxy user and password' do let(:options) do { http_proxyaddr: '1.2.3.4', http_proxyport: 8080, http_proxyuser: 'user', http_proxypass: 'pass' } end describe '#proxy_user' do subject { super().proxy_user } it { is_expected.to eq('user') } end describe '#proxy_pass' do subject { super().proxy_pass } it { is_expected.to eq('pass') } end end end context 'when providing nil as proxy address' do let(:uri) { URI 'http://noproxytest.com' } let(:options) { {http_proxyaddr: nil} } it { is_expected.not_to be_a_proxy } it "does pass nil proxy parameters to the connection, this forces to not use a proxy" do http = Net::HTTP.new("noproxytest.com") expect(Net::HTTP).to receive(:new).once.with("noproxytest.com", 80, nil, nil, nil, nil).and_return(http) adapter.connection end end context 'when not providing a proxy address' do let(:uri) { URI 'http://proxytest.com' } it "does not pass any proxy parameters to the connection" do http = Net::HTTP.new("proxytest.com") expect(Net::HTTP).to receive(:new).once.with("proxytest.com", 80).and_return(http) adapter.connection end end context 'when providing a local bind address and port' do let(:options) { {local_host: "127.0.0.1", local_port: 12345 } } describe '#local_host' do subject { super().local_host } it { is_expected.to eq('127.0.0.1') } end describe '#local_port' do subject { super().local_port } it { is_expected.to eq(12345) } end end if RUBY_VERSION >= '2.0' context "when providing PEM certificates" do let(:pem) { :pem_contents } let(:options) { {pem: pem, pem_password: "password"} } context "when scheme is https" do let(:uri) { URI 'https://google.com' } let(:cert) { double("OpenSSL::X509::Certificate") } let(:key) { double("OpenSSL::PKey::RSA") } before do expect(OpenSSL::X509::Certificate).to receive(:new).with(pem).and_return(cert) expect(OpenSSL::PKey::RSA).to receive(:new).with(pem, "password").and_return(key) end it "uses the provided PEM certificate" do expect(subject.cert).to eq(cert) expect(subject.key).to eq(key) end it "will verify the certificate" do expect(subject.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER) end context "when options include verify=false" do let(:options) { {pem: pem, pem_password: "password", verify: false} } it "should not verify the certificate" do expect(subject.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE) end end context "when options include verify_peer=false" do let(:options) { {pem: pem, pem_password: "password", verify_peer: false} } it "should not verify the certificate" do expect(subject.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE) end end end context "when scheme is not https" do let(:uri) { URI 'http://google.com' } let(:http) { Net::HTTP.new(uri) } before do allow(Net::HTTP).to receive_messages(new: http) expect(OpenSSL::X509::Certificate).not_to receive(:new).with(pem) expect(OpenSSL::PKey::RSA).not_to receive(:new).with(pem, "password") expect(http).not_to receive(:cert=) expect(http).not_to receive(:key=) end it "has no PEM certificate " do expect(subject.cert).to be_nil expect(subject.key).to be_nil end end end context "when providing PKCS12 certificates" do let(:p12) { :p12_contents } let(:options) { {p12: p12, p12_password: "password"} } context "when scheme is https" do let(:uri) { URI 'https://google.com' } let(:pkcs12) { double("OpenSSL::PKCS12", certificate: cert, key: key) } let(:cert) { double("OpenSSL::X509::Certificate") } let(:key) { double("OpenSSL::PKey::RSA") } before do expect(OpenSSL::PKCS12).to receive(:new).with(p12, "password").and_return(pkcs12) end it "uses the provided P12 certificate " do expect(subject.cert).to eq(cert) expect(subject.key).to eq(key) end it "will verify the certificate" do expect(subject.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER) end context "when options include verify=false" do let(:options) { {p12: p12, p12_password: "password", verify: false} } it "should not verify the certificate" do expect(subject.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE) end end context "when options include verify_peer=false" do let(:options) { {p12: p12, p12_password: "password", verify_peer: false} } it "should not verify the certificate" do expect(subject.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE) end end end context "when scheme is not https" do let(:uri) { URI 'http://google.com' } let(:http) { Net::HTTP.new(uri) } before do allow(Net::HTTP).to receive_messages(new: http) expect(OpenSSL::PKCS12).not_to receive(:new).with(p12, "password") expect(http).not_to receive(:cert=) expect(http).not_to receive(:key=) end it "has no PKCS12 certificate " do expect(subject.cert).to be_nil expect(subject.key).to be_nil end end end context "when uri port is not defined" do context "falls back to 80 port on http" do let(:uri) { URI 'http://foobar.com' } before { allow(uri).to receive(:port).and_return(nil) } it { expect(subject.port).to be 80 } end context "falls back to 443 port on https" do let(:uri) { URI 'https://foobar.com' } before { allow(uri).to receive(:port).and_return(nil) } it { expect(subject.port).to be 443 } end end end end end httparty-0.18.1/spec/httparty/cookie_hash_spec.rb000066400000000000000000000067171367022441500221230ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::CookieHash do before(:each) do @cookie_hash = HTTParty::CookieHash.new end describe "#add_cookies" do describe "with a hash" do it "should add new key/value pairs to the hash" do @cookie_hash.add_cookies(foo: "bar") @cookie_hash.add_cookies(rofl: "copter") expect(@cookie_hash.length).to eql(2) end it "should overwrite any existing key" do @cookie_hash.add_cookies(foo: "bar") @cookie_hash.add_cookies(foo: "copter") expect(@cookie_hash.length).to eql(1) expect(@cookie_hash[:foo]).to eql("copter") end end describe "with a string" do it "should add new key/value pairs to the hash" do @cookie_hash.add_cookies("first=one; second=two; third") expect(@cookie_hash[:first]).to eq('one') expect(@cookie_hash[:second]).to eq('two') expect(@cookie_hash[:third]).to eq(nil) end it "should overwrite any existing key" do @cookie_hash[:foo] = 'bar' @cookie_hash.add_cookies("foo=tar") expect(@cookie_hash.length).to eql(1) expect(@cookie_hash[:foo]).to eql("tar") end it "should handle '=' within cookie value" do @cookie_hash.add_cookies("first=one=1; second=two=2==") expect(@cookie_hash.keys).to include(:first, :second) expect(@cookie_hash[:first]).to eq('one=1') expect(@cookie_hash[:second]).to eq('two=2==') end it "should handle an empty cookie parameter" do @cookie_hash.add_cookies("first=one; domain=mydomain.com; path=/; ; SameSite; Secure") expect(@cookie_hash.keys).to include(:first, :domain, :path, :SameSite, :Secure) end end describe 'with other class' do it "should error" do expect { @cookie_hash.add_cookies([]) }.to raise_error(RuntimeError) end end end # The regexen are required because Hashes aren't ordered, so a test against # a hardcoded string was randomly failing. describe "#to_cookie_string" do before(:each) do @cookie_hash.add_cookies(foo: "bar") @cookie_hash.add_cookies(rofl: "copter") @s = @cookie_hash.to_cookie_string end it "should format the key/value pairs, delimited by semi-colons" do expect(@s).to match(/foo=bar/) expect(@s).to match(/rofl=copter/) expect(@s).to match(/^\w+=\w+; \w+=\w+$/) end it "should not include client side only cookies" do @cookie_hash.add_cookies(path: "/") @s = @cookie_hash.to_cookie_string expect(@s).not_to match(/path=\//) end it "should not include SameSite attribute" do @cookie_hash.add_cookies(samesite: "Strict") @s = @cookie_hash.to_cookie_string expect(@s).not_to match(/samesite=Strict/) end it "should not include client side only cookies even when attributes use camal case" do @cookie_hash.add_cookies(Path: "/") @s = @cookie_hash.to_cookie_string expect(@s).not_to match(/Path=\//) end it "should not mutate the hash" do original_hash = { "session" => "91e25e8b-6e32-418d-c72f-2d18adf041cd", "Max-Age" => "15552000", "cart" => "91e25e8b-6e32-418d-c72f-2d18adf041cd", "httponly" => nil, "Path" => "/", "secure" => nil, } cookie_hash = HTTParty::CookieHash[original_hash] cookie_hash.to_cookie_string expect(cookie_hash).to eq(original_hash) end end end httparty-0.18.1/spec/httparty/exception_spec.rb000066400000000000000000000021211367022441500216260ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::Error do subject { described_class } describe '#ancestors' do subject { super().ancestors } it { is_expected.to include(StandardError) } end describe HTTParty::UnsupportedFormat do describe '#ancestors' do subject { super().ancestors } it { is_expected.to include(HTTParty::Error) } end end describe HTTParty::UnsupportedURIScheme do describe '#ancestors' do subject { super().ancestors } it { is_expected.to include(HTTParty::Error) } end end describe HTTParty::ResponseError do describe '#ancestors' do subject { super().ancestors } it { is_expected.to include(HTTParty::Error) } end end describe HTTParty::RedirectionTooDeep do describe '#ancestors' do subject { super().ancestors } it { is_expected.to include(HTTParty::ResponseError) } end end describe HTTParty::DuplicateLocationHeader do describe '#ancestors' do subject { super().ancestors } it { is_expected.to include(HTTParty::ResponseError) } end end end httparty-0.18.1/spec/httparty/hash_conversions_spec.rb000066400000000000000000000036311367022441500232120ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::HashConversions do describe ".to_params" do it "creates a params string from a hash" do hash = { name: "bob", address: { street: '111 ruby ave.', city: 'ruby central', phones: ['111-111-1111', '222-222-2222'] } } expect(HTTParty::HashConversions.to_params(hash)).to eq("name=bob&address%5Bstreet%5D=111%20ruby%20ave.&address%5Bcity%5D=ruby%20central&address%5Bphones%5D%5B%5D=111-111-1111&address%5Bphones%5D%5B%5D=222-222-2222") end context "nested params" do it 'creates a params string from a hash' do hash = { marketing_event: { marketed_resources: [ {type:"product", id: 57474842640 } ] } } expect(HTTParty::HashConversions.to_params(hash)).to eq("marketing_event%5Bmarketed_resources%5D%5B%5D%5Btype%5D=product&marketing_event%5Bmarketed_resources%5D%5B%5D%5Bid%5D=57474842640") end end end describe ".normalize_param" do context "value is an array" do it "creates a params string" do expect( HTTParty::HashConversions.normalize_param(:people, ["Bob Jones", "Mike Smith"]) ).to eq("people%5B%5D=Bob%20Jones&people%5B%5D=Mike%20Smith&") end end context "value is an empty array" do it "creates a params string" do expect( HTTParty::HashConversions.normalize_param(:people, []) ).to eq("people%5B%5D=&") end end context "value is hash" do it "creates a params string" do expect( HTTParty::HashConversions.normalize_param(:person, { name: "Bob Jones" }) ).to eq("person%5Bname%5D=Bob%20Jones&") end end context "value is a string" do it "creates a params string" do expect( HTTParty::HashConversions.normalize_param(:name, "Bob Jones") ).to eq("name=Bob%20Jones&") end end end end httparty-0.18.1/spec/httparty/headers_processor_spec.rb000066400000000000000000000030241367022441500233450ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::HeadersProcessor do subject(:headers) { options[:headers] } let(:options) { { headers: {} } } let(:global_headers) { {} } before { described_class.new(global_headers, options).call } context 'when headers are not set at all' do it 'returns empty hash' do expect(headers).to eq({}) end end context 'when only global headers are set' do let(:global_headers) { { accept: 'text/html' } } it 'returns stringified global headers' do expect(headers).to eq('accept' => 'text/html') end end context 'when only request specific headers are set' do let(:options) { { headers: {accept: 'text/html' } } } it 'returns stringified request specific headers' do expect(headers).to eq('accept' => 'text/html') end end context 'when global and request specific headers are set' do let(:global_headers) { { 'x-version' => '123' } } let(:options) { { headers: { accept: 'text/html' } } } it 'returns merged global and request specific headers' do expect(headers).to eq('accept' => 'text/html', 'x-version' => '123') end end context 'when headers are dynamic' do let(:global_headers) { {'x-version' => -> { 'abc'.reverse } } } let(:options) do { body: '123', headers: { sum: lambda { |options| options[:body].chars.map(&:to_i).inject(:+) } } } end it 'returns processed global and request specific headers' do expect(headers).to eq('sum' => 6, 'x-version' => 'cba') end end end httparty-0.18.1/spec/httparty/logger/000077500000000000000000000000001367022441500175545ustar00rootroot00000000000000httparty-0.18.1/spec/httparty/logger/apache_formatter_spec.rb000066400000000000000000000026611367022441500244240ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::Logger::ApacheFormatter do let(:subject) { described_class.new(logger_double, :info) } let(:logger_double) { double('Logger') } let(:request_double) { double('Request', http_method: Net::HTTP::Get, path: "http://my.domain.com/my_path") } let(:request_time) { Time.new.strftime("%Y-%m-%d %H:%M:%S %z") } before do expect(logger_double).to receive(:info).with(log_message) end describe "#format" do let(:log_message) { "[HTTParty] [#{request_time}] 302 \"GET http://my.domain.com/my_path\" - " } it "formats a response in a style that resembles apache's access log" do response_double = double( code: 302, :[] => nil ) subject.format(request_double, response_double) end context 'when there is a parsed response' do let(:log_message) { "[HTTParty] [#{request_time}] 200 \"GET http://my.domain.com/my_path\" 512 "} it "can handle the Content-Length header" do # Simulate a parsed response that is an array, where accessing a string key will raise an error. See Issue #299. response_double = double( code: 200, headers: { 'Content-Length' => 512 } ) allow(response_double).to receive(:[]).with('Content-Length').and_raise(TypeError.new('no implicit conversion of String into Integer')) subject.format(request_double, response_double) end end end end httparty-0.18.1/spec/httparty/logger/curl_formatter_spec.rb000066400000000000000000000071571367022441500241550ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::Logger::CurlFormatter do describe "#format" do let(:logger) { double('Logger') } let(:response_object) { Net::HTTPOK.new('1.1', 200, 'OK') } let(:parsed_response) { lambda { {"foo" => "bar"} } } let(:response) do HTTParty::Response.new(request, response_object, parsed_response) end let(:request) do HTTParty::Request.new(Net::HTTP::Get, 'http://foo.bar.com/') end subject { described_class.new(logger, :info) } before do allow(logger).to receive(:info) allow(request).to receive(:raw_body).and_return('content') allow(response_object).to receive_messages(body: "{foo:'bar'}") response_object['header-key'] = 'header-value' subject.format request, response end context 'when request is logged' do context "and request's option 'base_uri' is not present" do it 'logs url' do expect(logger).to have_received(:info).with(/\[HTTParty\] \[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\ [+-]\d{4}\] > GET http:\/\/foo.bar.com/) end end context "and request's option 'base_uri' is present" do let(:request) do HTTParty::Request.new(Net::HTTP::Get, '/path', base_uri: 'http://foo.bar.com') end it 'logs url' do expect(logger).to have_received(:info).with(/\[HTTParty\] \[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\ [+-]\d{4}\] > GET http:\/\/foo.bar.com\/path/) end end context 'and headers are not present' do it 'not log Headers' do expect(logger).not_to have_received(:info).with(/Headers/) end end context 'and headers are present' do let(:request) do HTTParty::Request.new(Net::HTTP::Get, '/path', base_uri: 'http://foo.bar.com', headers: { key: 'value' }) end it 'logs Headers' do expect(logger).to have_received(:info).with(/Headers/) end it 'logs headers keys' do expect(logger).to have_received(:info).with(/key: value/) end end context 'and query is not present' do it 'not logs Query' do expect(logger).not_to have_received(:info).with(/Query/) end end context 'and query is present' do let(:request) do HTTParty::Request.new(Net::HTTP::Get, '/path', query: { key: 'value' }) end it 'logs Query' do expect(logger).to have_received(:info).with(/Query/) end it 'logs query params' do expect(logger).to have_received(:info).with(/key: value/) end end context 'when request raw_body is present' do it 'not logs request body' do expect(logger).to have_received(:info).with(/content/) end end end context 'when response is logged' do it 'logs http version and response code' do expect(logger).to have_received(:info).with(/HTTP\/1.1 200/) end it 'logs headers' do expect(logger).to have_received(:info).with(/Header-key: header-value/) end it 'logs body' do expect(logger).to have_received(:info).with(/{foo:'bar'}/) end end it "formats a response in a style that resembles a -v curl" do logger_double = double expect(logger_double).to receive(:info).with( /\[HTTParty\] \[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\ [+-]\d{4}\] > GET http:\/\/localhost/) subject = described_class.new(logger_double, :info) stub_http_response_with("example.html") response = HTTParty::Request.new.perform subject.format(response.request, response) end end end httparty-0.18.1/spec/httparty/logger/logger_spec.rb000066400000000000000000000026561367022441500224030ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::Logger do describe ".build" do subject { HTTParty::Logger } it "defaults level to :info" do logger_double = double expect(subject.build(logger_double, nil, nil).level).to eq(:info) end it "defaults format to :apache" do logger_double = double expect(subject.build(logger_double, nil, nil)).to be_an_instance_of(HTTParty::Logger::ApacheFormatter) end it "builds :curl style logger" do logger_double = double expect(subject.build(logger_double, nil, :curl)).to be_an_instance_of(HTTParty::Logger::CurlFormatter) end it "builds :logstash style logger" do logger_double = double expect(subject.build(logger_double, nil, :logstash)).to be_an_instance_of(HTTParty::Logger::LogstashFormatter) end it "builds :custom style logger" do CustomFormatter = Class.new(HTTParty::Logger::CurlFormatter) HTTParty::Logger.add_formatter(:custom, CustomFormatter) logger_double = double expect(subject.build(logger_double, nil, :custom)). to be_an_instance_of(CustomFormatter) end it "raises error when formatter exists" do CustomFormatter2= Class.new(HTTParty::Logger::CurlFormatter) HTTParty::Logger.add_formatter(:custom2, CustomFormatter2) expect{ HTTParty::Logger.add_formatter(:custom2, CustomFormatter2) }. to raise_error HTTParty::Error end end end httparty-0.18.1/spec/httparty/logger/logstash_formatter_spec.rb000066400000000000000000000023771367022441500250330ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::Logger::LogstashFormatter do let(:severity) { :info } let(:http_method) { 'GET' } let(:path) { 'http://my.domain.com/my_path' } let(:logger_double) { double('Logger') } let(:request_double) { double('Request', http_method: Net::HTTP::Get, path: "#{path}") } let(:request_time) { Time.new.strftime("%Y-%m-%d %H:%M:%S %z") } let(:log_message) do { '@timestamp' => request_time, '@version' => 1, 'content_length' => content_length || '-', 'http_method' => http_method, 'message' => message, 'path' => path, 'response_code' => response_code, 'severity' => severity, 'tags' => ['HTTParty'], }.to_json end subject { described_class.new(logger_double, severity) } before do expect(logger_double).to receive(:info).with(log_message) end describe "#format" do let(:response_code) { 302 } let(:content_length) { '-' } let(:message) { "[HTTParty] #{response_code} \"#{http_method} #{path}\" #{content_length} " } it "formats a response to be compatible with Logstash" do response_double = double( code: response_code, :[] => nil ) subject.format(request_double, response_double) end end end httparty-0.18.1/spec/httparty/net_digest_auth_spec.rb000066400000000000000000000171411367022441500230060ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Net::HTTPHeader::DigestAuthenticator do def setup_digest(response) digest = Net::HTTPHeader::DigestAuthenticator.new("Mufasa", "Circle Of Life", "GET", "/dir/index.html", response) allow(digest).to receive(:random).and_return("deadbeef") allow(Digest::MD5).to receive(:hexdigest) { |str| "md5(#{str})" } digest end def authorization_header @digest.authorization_header.join(", ") end def cookie_header @digest.cookie_header end context 'Net::HTTPHeader#digest_auth' do let(:headers) { (Class.new do include Net::HTTPHeader def initialize @header = {} @path = '/' @method = 'GET' end end).new } let(:response){ (Class.new do include Net::HTTPHeader def initialize @header = {} self['WWW-Authenticate'] = 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"' end end).new } it 'should set the authorization header' do expect(headers['authorization']).to be_nil headers.digest_auth('user','pass', response) expect(headers['authorization']).to_not be_empty end end context "with a cookie value in the response header" do before do @digest = setup_digest({ 'www-authenticate' => 'Digest realm="myhost@testrealm.com"', 'Set-Cookie' => 'custom-cookie=1234567' }) end it "should set cookie header" do expect(cookie_header).to include('custom-cookie=1234567') end end context "without a cookie value in the response header" do before do @digest = setup_digest({ 'www-authenticate' => 'Digest realm="myhost@testrealm.com"' }) end it "should set empty cookie header array" do expect(cookie_header).to eql [] end end context "with an opaque value in the response header" do before do @digest = setup_digest({ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", opaque="solid"' }) end it "should set opaque" do expect(authorization_header).to include('opaque="solid"') end end context "without an opaque valid in the response header" do before do @digest = setup_digest({ 'www-authenticate' => 'Digest realm="myhost@testrealm.com"' }) end it "should not set opaque" do expect(authorization_header).not_to include("opaque=") end end context "with specified quality of protection (qop)" do before do @digest = setup_digest({ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"' }) end it "should set prefix" do expect(authorization_header).to match(/^Digest /) end it "should set username" do expect(authorization_header).to include('username="Mufasa"') end it "should set digest-uri" do expect(authorization_header).to include('uri="/dir/index.html"') end it "should set qop" do expect(authorization_header).to include('qop="auth"') end it "should set cnonce" do expect(authorization_header).to include('cnonce="md5(deadbeef)"') end it "should set nonce-count" do expect(authorization_header).to include("nc=00000001") end it "should set response" do request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))" expect(authorization_header).to include(%(response="#{request_digest}")) end end context "when quality of protection (qop) is unquoted" do before do @digest = setup_digest({ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop=auth' }) end it "should still set qop" do expect(authorization_header).to include('qop="auth"') end end context "with unspecified quality of protection (qop)" do before do @digest = setup_digest({ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE"' }) end it "should set prefix" do expect(authorization_header).to match(/^Digest /) end it "should set username" do expect(authorization_header).to include('username="Mufasa"') end it "should set digest-uri" do expect(authorization_header).to include('uri="/dir/index.html"') end it "should not set qop" do expect(authorization_header).not_to include("qop=") end it "should not set cnonce" do expect(authorization_header).not_to include("cnonce=") end it "should not set nonce-count" do expect(authorization_header).not_to include("nc=") end it "should set response" do request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(GET:/dir/index.html))" expect(authorization_header).to include(%(response="#{request_digest}")) end end context "with http basic auth response when net digest auth expected" do it "should not fail" do @digest = setup_digest({ 'www-authenticate' => 'WWW-Authenticate: Basic realm="testrealm.com""' }) expect(authorization_header).to include("Digest") end end context "with multiple authenticate headers" do before do @digest = setup_digest({ 'www-authenticate' => 'NTLM, Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"' }) end it "should set prefix" do expect(authorization_header).to match(/^Digest /) end it "should set username" do expect(authorization_header).to include('username="Mufasa"') end it "should set digest-uri" do expect(authorization_header).to include('uri="/dir/index.html"') end it "should set qop" do expect(authorization_header).to include('qop="auth"') end it "should set cnonce" do expect(authorization_header).to include('cnonce="md5(deadbeef)"') end it "should set nonce-count" do expect(authorization_header).to include("nc=00000001") end it "should set response" do request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))" expect(authorization_header).to include(%(response="#{request_digest}")) end end context "with algorithm specified" do before do @digest = setup_digest({ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth", algorithm=MD5' }) end it "should recognise algorithm was specified" do expect( @digest.send :algorithm_present? ).to be(true) end it "should set the algorithm header" do expect(authorization_header).to include('algorithm="MD5"') end end context "with md5-sess algorithm specified" do before do @digest = setup_digest({ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth", algorithm=MD5-sess' }) end it "should recognise algorithm was specified" do expect( @digest.send :algorithm_present? ).to be(true) end it "should set the algorithm header" do expect(authorization_header).to include('algorithm="MD5-sess"') end it "should set response using md5-sess algorithm" do request_digest = "md5(md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(deadbeef)):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))" expect(authorization_header).to include(%(response="#{request_digest}")) end end end httparty-0.18.1/spec/httparty/parser_spec.rb000066400000000000000000000136011367022441500211310ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::Parser do describe ".SupportedFormats" do it "returns a hash" do expect(HTTParty::Parser::SupportedFormats).to be_instance_of(Hash) end end describe ".call" do it "generates an HTTParty::Parser instance with the given body and format" do expect(HTTParty::Parser).to receive(:new).with('body', :plain).and_return(double(parse: nil)) HTTParty::Parser.call('body', :plain) end it "calls #parse on the parser" do parser = double('Parser') expect(parser).to receive(:parse) allow(HTTParty::Parser).to receive_messages(new: parser) parser = HTTParty::Parser.call('body', :plain) end end describe ".formats" do it "returns the SupportedFormats constant" do expect(HTTParty::Parser.formats).to eq(HTTParty::Parser::SupportedFormats) end it "returns the SupportedFormats constant for subclasses" do klass = Class.new(HTTParty::Parser) klass::SupportedFormats = { "application/atom+xml" => :atom } expect(klass.formats).to eq({"application/atom+xml" => :atom}) end end describe ".format_from_mimetype" do it "returns a symbol representing the format mimetype" do expect(HTTParty::Parser.format_from_mimetype("text/plain")).to eq(:plain) end it "returns nil when the mimetype is not supported" do expect(HTTParty::Parser.format_from_mimetype("application/atom+xml")).to be_nil end end describe ".supported_formats" do it "returns a unique set of supported formats represented by symbols" do expect(HTTParty::Parser.supported_formats).to eq(HTTParty::Parser::SupportedFormats.values.uniq) end end describe ".supports_format?" do it "returns true for a supported format" do allow(HTTParty::Parser).to receive_messages(supported_formats: [:json]) expect(HTTParty::Parser.supports_format?(:json)).to be_truthy end it "returns false for an unsupported format" do allow(HTTParty::Parser).to receive_messages(supported_formats: []) expect(HTTParty::Parser.supports_format?(:json)).to be_falsey end end describe "#parse" do it "attempts to parse supported formats" do parser = HTTParty::Parser.new('body', :json) allow(parser).to receive_messages(supports_format?: true) expect(parser).to receive(:parse_supported_format) parser.parse end it "returns the unparsed body when the format is unsupported" do parser = HTTParty::Parser.new('body', :json) allow(parser).to receive_messages(supports_format?: false) expect(parser.parse).to eq(parser.body) end it "returns nil for an empty body" do parser = HTTParty::Parser.new('', :json) expect(parser.parse).to be_nil end it "returns nil for a nil body" do parser = HTTParty::Parser.new(nil, :json) expect(parser.parse).to be_nil end it "returns nil for a 'null' body" do parser = HTTParty::Parser.new("null", :json) expect(parser.parse).to be_nil end it "returns nil for a body with spaces only" do parser = HTTParty::Parser.new(" ", :json) expect(parser.parse).to be_nil end it "does not raise exceptions for bodies with invalid encodings" do parser = HTTParty::Parser.new("\x80", :invalid_format) expect(parser.parse).to_not be_nil end it "ignores utf-8 bom" do parser = HTTParty::Parser.new("\xEF\xBB\xBF\{\"hi\":\"yo\"\}", :json) expect(parser.parse).to eq({"hi"=>"yo"}) end it "parses ascii 8bit encoding" do parser = HTTParty::Parser.new( "{\"currency\":\"\xE2\x82\xAC\"}".force_encoding('ASCII-8BIT'), :json ) expect(parser.parse).to eq({"currency" => "€"}) end it "parses frozen strings" do parser = HTTParty::Parser.new('{"a":1}'.freeze, :json) expect(parser.parse).to eq("a" => 1) end end describe "#supports_format?" do it "utilizes the class method to determine if the format is supported" do expect(HTTParty::Parser).to receive(:supports_format?).with(:json) parser = HTTParty::Parser.new('body', :json) parser.send(:supports_format?) end end describe "#parse_supported_format" do it "calls the parser for the given format" do parser = HTTParty::Parser.new('body', :json) expect(parser).to receive(:json) parser.send(:parse_supported_format) end context "when a parsing method does not exist for the given format" do it "raises an exception" do parser = HTTParty::Parser.new('body', :atom) expect do parser.send(:parse_supported_format) end.to raise_error(NotImplementedError, "HTTParty::Parser has not implemented a parsing method for the :atom format.") end it "raises a useful exception message for subclasses" do atom_parser = Class.new(HTTParty::Parser) do def self.name 'AtomParser' end end parser = atom_parser.new 'body', :atom expect do parser.send(:parse_supported_format) end.to raise_error(NotImplementedError, "AtomParser has not implemented a parsing method for the :atom format.") end end end context "parsers" do subject do HTTParty::Parser.new('body', nil) end it "parses xml with MultiXml" do expect(MultiXml).to receive(:parse).with('body') subject.send(:xml) end it "parses json with JSON" do expect(JSON).to receive(:parse).with('body', :quirks_mode => true, :allow_nan => true) subject.send(:json) end it "parses html by simply returning the body" do expect(subject.send(:html)).to eq('body') end it "parses plain text by simply returning the body" do expect(subject.send(:plain)).to eq('body') end it "parses csv with CSV" do expect(CSV).to receive(:parse).with('body') subject.send(:csv) end end end httparty-0.18.1/spec/httparty/request/000077500000000000000000000000001367022441500177655ustar00rootroot00000000000000httparty-0.18.1/spec/httparty/request/body_spec.rb000066400000000000000000000122651367022441500222670ustar00rootroot00000000000000require 'spec_helper' require 'tempfile' RSpec.describe HTTParty::Request::Body do describe '#call' do let(:options) { {} } subject { described_class.new(params, options).call } context 'when params is string' do let(:params) { 'name=Bob%20Jones' } it { is_expected.to eq params } end context 'when params is hash' do let(:params) { { people: ["Bob Jones", "Mike Smith"] } } let(:converted_params) { "people%5B%5D=Bob%20Jones&people%5B%5D=Mike%20Smith"} it { is_expected.to eq converted_params } context 'when params has file' do before do allow(HTTParty::Request::MultipartBoundary) .to receive(:generate).and_return("------------------------c772861a5109d5ef") end let(:file) { File.open('spec/fixtures/tiny.gif') } let(:params) do { user: { avatar: file, first_name: 'John', last_name: 'Doe', enabled: true } } end let(:expected_file_name) { 'tiny.gif' } let(:expected_file_contents) { "GIF89a\u0001\u0000\u0001\u0000\u0000\xFF\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0000;" } let(:expected_content_type) { 'image/gif' } let(:multipart_params) do "--------------------------c772861a5109d5ef\r\n" \ "Content-Disposition: form-data; name=\"user[avatar]\"; filename=\"#{expected_file_name}\"\r\n" \ "Content-Type: #{expected_content_type}\r\n" \ "\r\n" \ "#{expected_file_contents}\r\n" \ "--------------------------c772861a5109d5ef\r\n" \ "Content-Disposition: form-data; name=\"user[first_name]\"\r\n" \ "\r\n" \ "John\r\n" \ "--------------------------c772861a5109d5ef\r\n" \ "Content-Disposition: form-data; name=\"user[last_name]\"\r\n" \ "\r\n" \ "Doe\r\n" \ "--------------------------c772861a5109d5ef\r\n" \ "Content-Disposition: form-data; name=\"user[enabled]\"\r\n" \ "\r\n" \ "true\r\n" \ "--------------------------c772861a5109d5ef--\r\n" end it { is_expected.to eq multipart_params } context 'when passing multipart as an option' do let(:options) { { force_multipart: true } } let(:params) do { user: { first_name: 'John', last_name: 'Doe', enabled: true } } end let(:multipart_params) do "--------------------------c772861a5109d5ef\r\n" \ "Content-Disposition: form-data; name=\"user[first_name]\"\r\n" \ "\r\n" \ "John\r\n" \ "--------------------------c772861a5109d5ef\r\n" \ "Content-Disposition: form-data; name=\"user[last_name]\"\r\n" \ "\r\n" \ "Doe\r\n" \ "--------------------------c772861a5109d5ef\r\n" \ "Content-Disposition: form-data; name=\"user[enabled]\"\r\n" \ "\r\n" \ "true\r\n" \ "--------------------------c772861a5109d5ef--\r\n" end it { is_expected.to eq multipart_params } end context 'file object responds to original_filename' do let(:some_temp_file) { Tempfile.new(['some_temp_file','.gif']) } let(:expected_file_name) { "some_temp_file.gif" } let(:expected_file_contents) { "Hello" } let(:file) { double(:mocked_action_dispatch, path: some_temp_file.path, original_filename: 'some_temp_file.gif', read: expected_file_contents) } before { some_temp_file.write('Hello') } it { is_expected.to eq multipart_params } end end end end describe '#multipart?' do let(:force_multipart) { false } let(:file) { File.open('spec/fixtures/tiny.gif') } subject { described_class.new(params, force_multipart: force_multipart).multipart? } context 'when params does not respond to to_hash' do let(:params) { 'name=Bob%20Jones' } it { is_expected.to be false } end context 'when params responds to to_hash' do class HashLike def initialize(hash) @hash = hash end def to_hash @hash end end class ArrayLike def initialize(ary) @ary = ary end def to_ary @ary end end context 'when force_multipart is true' do let(:params) { { name: 'Bob Jones' } } let(:force_multipart) { true } it { is_expected.to be true } end context 'when it does not contain a file' do let(:hash_like_param) { HashLike.new(first: 'Bob', last: ArrayLike.new(['Jones'])) } let(:params) { { name: ArrayLike.new([hash_like_param]) } } it { is_expected.to eq false } end context 'when it contains file' do let(:hash_like_param) { HashLike.new(first: 'Bob', last: 'Jones', file: ArrayLike.new([file])) } let(:params) { { name: ArrayLike.new([hash_like_param]) } } it { is_expected.to be true } end end end end httparty-0.18.1/spec/httparty/request_spec.rb000066400000000000000000001530101367022441500213240ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::Request do before do @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', format: :xml) end describe "::NON_RAILS_QUERY_STRING_NORMALIZER" do let(:normalizer) { HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER } it "doesn't modify strings" do query_string = normalizer["foo=bar&foo=baz"] expect(CGI.unescape(query_string)).to eq("foo=bar&foo=baz") end context "when the query is an array" do it "doesn't include brackets" do query_string = normalizer[{page: 1, foo: %w(bar baz)}] expect(CGI.unescape(query_string)).to eq("foo=bar&foo=baz&page=1") end it "URI encodes array values" do query_string = normalizer[{people: ["Otis Redding", "Bob Marley", "Tim & Jon"], page: 1, xyzzy: 3}] expect(query_string).to eq("page=1&people=Otis%20Redding&people=Bob%20Marley&people=Tim%20%26%20Jon&xyzzy=3") end end context "when the query is a hash" do it "correctly handles nil values" do query_string = normalizer[{page: 1, per_page: nil}] expect(query_string).to eq("page=1&per_page") end end end describe "::JSON_API_QUERY_STRING_NORMALIZER" do let(:normalizer) { HTTParty::Request::JSON_API_QUERY_STRING_NORMALIZER } it "doesn't modify strings" do query_string = normalizer["foo=bar&foo=baz"] expect(CGI.unescape(query_string)).to eq("foo=bar&foo=baz") end context "when the query is an array" do it "doesn't include brackets" do query_string = normalizer[{page: 1, foo: %w(bar baz)}] expect(CGI.unescape(query_string)).to eq("foo=bar,baz&page=1") end it "URI encodes array values" do query_string = normalizer[{people: ["Otis Redding", "Bob Marley", "Tim & Jon"], page: 1, xyzzy: 3}] expect(query_string).to eq("page=1&people=Otis%20Redding,Bob%20Marley,Tim%20%26%20Jon&xyzzy=3") end end context "when the query is a hash" do it "correctly handles nil values" do query_string = normalizer[{page: 1, per_page: nil}] expect(query_string).to eq('page=1&per_page') end end end describe "initialization" do it "sets parser to HTTParty::Parser" do request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com') expect(request.parser).to eq(HTTParty::Parser) end it "sets parser to the optional parser" do my_parser = lambda {} request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', parser: my_parser) expect(request.parser).to eq(my_parser) end it "sets connection_adapter to HTTParty::ConnectionAdapter" do request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com') expect(request.connection_adapter).to eq(HTTParty::ConnectionAdapter) end it "sets connection_adapter to the optional connection_adapter" do my_adapter = lambda {} request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', connection_adapter: my_adapter) expect(request.connection_adapter).to eq(my_adapter) end context "when using a query string" do context "and it has an empty array" do it "sets correct query string" do request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', query: { fake_array: [] }) expect(request.uri).to eq(URI.parse("http://google.com/?fake_array%5B%5D=")) end end context "when sending an array with only one element" do it "sets correct query" do request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', query: { fake_array: [1] }) expect(request.uri).to eq(URI.parse("http://google.com/?fake_array%5B%5D=1")) end end end context "when basic authentication credentials provided in uri" do context "when basic auth options wasn't set explicitly" do it "sets basic auth from uri" do request = HTTParty::Request.new(Net::HTTP::Get, 'http://user1:pass1@example.com') expect(request.options[:basic_auth]).to eq({username: 'user1', password: 'pass1'}) end end context "when basic auth options was set explicitly" do it "uses basic auth from url anyway" do basic_auth = {username: 'user2', password: 'pass2'} request = HTTParty::Request.new(Net::HTTP::Get, 'http://user1:pass1@example.com', basic_auth: basic_auth) expect(request.options[:basic_auth]).to eq({username: 'user1', password: 'pass1'}) end end end end describe "#format" do context "request yet to be made" do it "returns format option" do request = HTTParty::Request.new 'get', '/', format: :xml expect(request.format).to eq(:xml) end it "returns nil format" do request = HTTParty::Request.new 'get', '/' expect(request.format).to be_nil end end context "request has been made" do it "returns format option" do request = HTTParty::Request.new 'get', '/', format: :xml request.last_response = double expect(request.format).to eq(:xml) end it "returns the content-type from the last response when the option is not set" do request = HTTParty::Request.new 'get', '/' response = double expect(response).to receive(:[]).with('content-type').and_return('text/json') request.last_response = response expect(request.format).to eq(:json) end end end context "options" do it "should use basic auth when configured" do @request.options[:basic_auth] = {username: 'foobar', password: 'secret'} @request.send(:setup_raw_request) expect(@request.instance_variable_get(:@raw_request)['authorization']).not_to be_nil end context 'digest_auth' do before do response_sequence = [ { status: ['401', 'Unauthorized' ], headers: { www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false', set_cookie: 'custom-cookie=1234567' } }, { status: ['200', 'OK'] } ] stub_request(:get, 'http://api.foo.com/v1').to_return(response_sequence) end it 'should not send credentials more than once' do response_sequence = [ { status: ['401', 'Unauthorized' ], headers: { www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false', set_cookie: 'custom-cookie=1234567' } }, { status: ['401', 'Unauthorized' ], headers: { www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false', set_cookie: 'custom-cookie=1234567' } }, { status: ['404', 'Not found'] } ] stub_request(:get, 'http://api.foo.com/v1').to_return(response_sequence) @request.options[:digest_auth] = {username: 'foobar', password: 'secret'} response = @request.perform { |v| } expect(response.code).to eq(401) raw_request = @request.instance_variable_get(:@raw_request) expect(raw_request['Authorization']).not_to be_nil end it 'should not be used when configured and the response is 200' do stub_request(:get, 'http://api.foo.com/v1').to_return(status: 200) @request.options[:digest_auth] = {username: 'foobar', password: 'secret'} response = @request.perform { |v| } expect(response.code).to eq(200) raw_request = @request.instance_variable_get(:@raw_request) expect(raw_request['Authorization']).to be_nil end it "should be used when configured and the response is 401" do @request.options[:digest_auth] = {username: 'foobar', password: 'secret'} response = @request.perform { |v| } expect(response.code).to eq(200) raw_request = @request.instance_variable_get(:@raw_request) expect(raw_request['Authorization']).not_to be_nil end it 'should maintain cookies returned from a 401 response' do @request.options[:digest_auth] = {username: 'foobar', password: 'secret'} response = @request.perform {|v|} expect(response.code).to eq(200) raw_request = @request.instance_variable_get(:@raw_request) expect(raw_request.get_fields('cookie')).to eql ["custom-cookie=1234567"] end it 'should merge cookies from request and a 401 response' do @request.options[:digest_auth] = {username: 'foobar', password: 'secret'} @request.options[:headers] = {'cookie' => 'request-cookie=test'} response = @request.perform {|v|} expect(response.code).to eq(200) raw_request = @request.instance_variable_get(:@raw_request) expect(raw_request.get_fields('cookie')).to eql ['request-cookie=test', 'custom-cookie=1234567'] end end it 'should use body_stream when configured' do stream = StringIO.new('foo') request = HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', body_stream: stream) request.send(:setup_raw_request) expect(request.instance_variable_get(:@raw_request).body_stream).to eq(stream) end it 'should normalize base uri when specified as request option' do stub_request(:get, 'http://foo.com/resource').to_return(body: 'Bar') response = HTTParty.get('/resource', { base_uri: 'foo.com' }) expect(response.code).to eq(200) end end describe "#uri" do context "redirects" do it "returns correct path when the server sets the location header to a filename" do @request.last_uri = URI.parse("http://example.com/foo/bar") @request.path = URI.parse("bar?foo=bar") @request.redirect = true expect(@request.uri).to eq(URI.parse("http://example.com/foo/bar?foo=bar")) end context "location header is an absolute path" do it "returns correct path when location has leading slash" do @request.last_uri = URI.parse("http://example.com/foo/bar") @request.path = URI.parse("/bar?foo=bar") @request.redirect = true expect(@request.uri).to eq(URI.parse("http://example.com/bar?foo=bar")) end it "returns the correct path when location has no leading slash" do @request.last_uri = URI.parse("http://example.com") @request.path = URI.parse("bar/") @request.redirect = true expect(@request.uri).to eq(URI.parse("http://example.com/bar/")) end end it "returns correct path when the server sets the location header to a full uri" do @request.last_uri = URI.parse("http://example.com/foo/bar") @request.path = URI.parse("http://example.com/bar?foo=bar") @request.redirect = true expect(@request.uri).to eq(URI.parse("http://example.com/bar?foo=bar")) end it "returns correct path when the server sets the location header to a network-path reference" do @request.last_uri = URI.parse("https://example.com") @request.path = URI.parse("//www.example.com") @request.redirect = true expect(@request.uri).to eq(URI.parse("https://www.example.com")) end end context "query strings" do it "does not add an empty query string when default_params are blank" do @request.options[:default_params] = {} expect(@request.uri.query).to be_nil end it "respects the query string normalization proc" do empty_proc = lambda {|qs| "I"} @request.options[:query_string_normalizer] = empty_proc @request.options[:query] = {foo: :bar} expect(CGI.unescape(@request.uri.query)).to eq("I") end it "does not append an ampersand when queries are embedded in paths" do @request.path = "/path?a=1" @request.options[:query] = {} expect(@request.uri.query).to eq("a=1") end it "does not duplicate query string parameters when uri is called twice" do @request.options[:query] = {foo: :bar} @request.uri expect(@request.uri.query).to eq("foo=bar") end context "when representing an array" do it "returns a Rails style query string" do @request.options[:query] = {foo: %w(bar baz)} expect(CGI.unescape(@request.uri.query)).to eq("foo[]=bar&foo[]=baz") expect(@request.uri.query).to eq("foo%5B%5D=bar&foo%5B%5D=baz") end end end end describe "#setup_raw_request" do context "when query_string_normalizer is set" do it "sets the body to the return value of the proc" do @request.options[:query_string_normalizer] = HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER @request.options[:body] = {page: 1, foo: %w(bar baz)} @request.send(:setup_raw_request) body = @request.instance_variable_get(:@raw_request).body expect(CGI.unescape(body)).to eq("foo=bar&foo=baz&page=1") end end context "when mulipart" do subject(:headers) do @request.send(:setup_raw_request) headers = @request.instance_variable_get(:@raw_request).each_header.to_a Hash[*headers.flatten] # Ruby 2.0 doesn't have Array#to_h end context "when body contains file" do it "sets header Content-Type: multipart/form-data; boundary=" do @request.options[:body] = {file: File.open(File::NULL, 'r')} expect(headers['content-type']).to match(%r{^multipart/form-data; boundary=---}) end context "and header Content-Type is provided" do it "overwrites the header to: multipart/form-data; boundary=" do @request.options[:body] = {file: File.open(File::NULL, 'r')} @request.options[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'} expect(headers['content-type']).to match(%r{^multipart/form-data; boundary=---}) end end end context 'when mulipart option is provided' do it "sets header Content-Type: multipart/form-data; boundary=" do @request.options[:body] = { text: 'something' } @request.options[:multipart] = true expect(headers['content-type']).to match(%r{^multipart/form-data; boundary=---}) end end end end describe 'http' do it "should get a connection from the connection_adapter" do http = Net::HTTP.new('google.com') adapter = double('adapter') request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443', connection_adapter: adapter) expect(adapter).to receive(:call).with(request.uri, request.options).and_return(http) expect(request.send(:http)).to be http end end describe '#format_from_mimetype' do it 'should handle text/xml' do ["text/xml", "text/xml; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:xml) end end it 'should handle application/xml' do ["application/xml", "application/xml; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:xml) end end it 'should handle text/json' do ["text/json", "text/json; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:json) end end it 'should handle application/vnd.api+json' do ["application/vnd.api+json", "application/vnd.api+json; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:json) end end it 'should handle application/hal+json' do ["application/hal+json", "application/hal+json; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:json) end end it 'should handle application/json' do ["application/json", "application/json; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:json) end end it 'should handle text/csv' do ["text/csv", "text/csv; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:csv) end end it 'should handle application/csv' do ["application/csv", "application/csv; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:csv) end end it 'should handle text/comma-separated-values' do ["text/comma-separated-values", "text/comma-separated-values; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:csv) end end it 'should handle text/javascript' do ["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:plain) end end it 'should handle application/javascript' do ["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct| expect(@request.send(:format_from_mimetype, ct)).to eq(:plain) end end it "returns nil for an unrecognized mimetype" do expect(@request.send(:format_from_mimetype, "application/atom+xml")).to be_nil end it "returns nil when using a default parser" do @request.options[:parser] = lambda {} expect(@request.send(:format_from_mimetype, "text/json")).to be_nil end end describe 'parsing responses' do it 'should handle xml automatically' do xml = '1234Foo Bar!' @request.options[:format] = :xml expect(@request.send(:parse_response, xml)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}) end it 'should handle utf-8 bom in xml' do xml = "\xEF\xBB\xBF1234Foo Bar!" @request.options[:format] = :xml expect(@request.send(:parse_response, xml)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}) end it 'should handle csv automatically' do csv = ['"id","Name"', '"1234","Foo Bar!"'].join("\n") @request.options[:format] = :csv expect(@request.send(:parse_response, csv)).to eq([%w(id Name), ["1234", "Foo Bar!"]]) end it 'should handle json automatically' do json = '{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}' @request.options[:format] = :json expect(@request.send(:parse_response, json)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}) end it 'should handle utf-8 bom in json' do json = "\xEF\xBB\xBF{\"books\": {\"book\": {\"name\": \"Foo Bar!\", \"id\": \"1234\"}}}" @request.options[:format] = :json expect(@request.send(:parse_response, json)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}) end it "should include any HTTP headers in the returned response" do @request.options[:format] = :html response = stub_response "Content" response.initialize_http_header("key" => "value") expect(@request.perform.headers).to eq({ "key" => ["value"] }) end if "".respond_to?(:encoding) context 'when body has ascii-8bit encoding' do let(:response) { stub_response "Content".force_encoding('ascii-8bit') } it "processes charset in content type properly" do response.initialize_http_header("Content-Type" => "text/plain;charset = utf-8") resp = @request.perform expect(resp.body.encoding).to eq(Encoding.find("UTF-8")) end it "processes charset in content type properly if it has a different case" do response.initialize_http_header("Content-Type" => "text/plain;CHARSET = utf-8") resp = @request.perform expect(resp.body.encoding).to eq(Encoding.find("UTF-8")) end it "processes quoted charset in content type properly" do response.initialize_http_header("Content-Type" => "text/plain;charset = \"utf-8\"") resp = @request.perform expect(resp.body.encoding).to eq(Encoding.find("UTF-8")) end context 'when stubed body is frozen' do let(:response) do stub_response "Content".force_encoding('ascii-8bit').freeze end it 'processes frozen body correctly' do response.initialize_http_header("Content-Type" => "text/plain;charset = utf-8") resp = @request.perform expect(resp.body.encoding).to eq(Encoding.find("UTF-8")) end end end it "should process response with a nil body" do response = stub_response nil response.initialize_http_header("Content-Type" => "text/html;charset=UTF-8") resp = @request.perform expect(resp.body).to be_nil end context 'when assume_utf16_is_big_endian is true' do before { @request.options[:assume_utf16_is_big_endian] = true } it "should process utf-16 charset with little endian bom correctly" do response = stub_response "\xFF\xFEC\x00o\x00n\x00t\x00e\x00n\x00t\x00" response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16") resp = @request.perform expect(resp.body.encoding).to eq(Encoding.find("UTF-16LE")) end it 'processes stubbed frozen body correctly' do response = stub_response "\xFF\xFEC\x00o\x00n\x00t\x00e\x00n\x00t\x00".freeze response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16") resp = @request.perform expect(resp.body.encoding).to eq(Encoding.find("UTF-16LE")) end end it "should process utf-16 charset with big endian bom correctly" do @request.options[:assume_utf16_is_big_endian] = false response = stub_response "\xFE\xFF\x00C\x00o\x00n\x00t\x00e\x00n\x00t" response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16") resp = @request.perform expect(resp.body.encoding).to eq(Encoding.find("UTF-16BE")) end it "should assume utf-16 little endian if options has been chosen" do @request.options[:assume_utf16_is_big_endian] = false response = stub_response "C\x00o\x00n\x00t\x00e\x00n\x00t\x00" response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16") resp = @request.perform expect(resp.body.encoding).to eq(Encoding.find("UTF-16LE")) end it "should perform no encoding if the charset is not available" do response = stub_response "Content" response.initialize_http_header("Content-Type" => "text/plain;charset = utf-lols") resp = @request.perform # This encoding does not exist, thus the string should not be encodd with it expect(resp.body).to eq("Content") expect(resp.body.encoding).to eq("Content".encoding) end it "should perform no encoding if the content type is specified but no charset is specified" do response = stub_response "Content" response.initialize_http_header("Content-Type" => "text/plain") resp = @request.perform expect(resp.body).to eq("Content") expect(resp.body.encoding).to eq("Content".encoding) end end describe 'with non-200 responses' do context "3xx responses" do it 'returns a valid object for 304 not modified' do stub_response '', 304 resp = @request.perform expect(resp.code).to eq(304) expect(resp.body).to eq('') expect(resp).to be_nil end it "redirects if a 300 contains a location header" do redirect = stub_response '', 300 redirect['location'] = 'http://foo.com/foo' ok = stub_response('bar', 200) allow(@http).to receive(:request).and_return(redirect, ok) response = @request.perform expect(response.request.base_uri.to_s).to eq("http://foo.com") expect(response.request.path.to_s).to eq("http://foo.com/foo") expect(response.request.uri.request_uri).to eq("/foo") expect(response.request.uri.to_s).to eq("http://foo.com/foo") expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "calls block given to perform with each redirect" do @request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml) stub_request(:get, 'http://test.com/redirect') .to_return( status: [300, 'REDIRECT'], headers: { location: 'http://api.foo.com/v2' } ) stub_request(:get, 'http://api.foo.com/v2') .to_return(body: 'bar') body = "" @request.perform { |chunk| body += chunk } expect(body.length).to eq(27) end it "redirects if a 300 contains a relative location header" do redirect = stub_response '', 300 redirect['location'] = '/foo/bar' ok = stub_response('bar', 200) allow(@http).to receive(:request).and_return(redirect, ok) response = @request.perform expect(response.request.base_uri.to_s).to eq("http://api.foo.com") expect(response.request.path.to_s).to eq("/foo/bar") expect(response.request.uri.request_uri).to eq("/foo/bar") expect(response.request.uri.to_s).to eq("http://api.foo.com/foo/bar") expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "handles multiple redirects and relative location headers on different hosts" do @request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml) stub_request(:get, 'http://test.com/redirect') .to_return( status: [300, 'REDIRECT'], headers: { location: "http://api.foo.com/v2" } ) stub_request(:get, 'http://api.foo.com/v2') .to_return( status: [300, 'REDIRECT'], headers: { location: '/v3' } ) stub_request(:get, 'http://api.foo.com/v3') .to_return(body: 'bar') response = @request.perform expect(response.request.base_uri.to_s).to eq("http://api.foo.com") expect(response.request.path.to_s).to eq("/v3") expect(response.request.uri.request_uri).to eq("/v3") expect(response.request.uri.to_s).to eq("http://api.foo.com/v3") expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "raises an error if redirect has duplicate location header" do @request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml) stub_request(:get, 'http://test.com/redirect') .to_return( status: [300, 'REDIRECT'], headers: { location: ['http://api.foo.com/v2', 'http://api.foo.com/v2'] } ) expect {@request.perform}.to raise_error(HTTParty::DuplicateLocationHeader) end it "returns the HTTParty::Response when the 300 does not contain a location header" do stub_response '', 300 expect(HTTParty::Response).to be === @request.perform end it "redirects including port" do stub_request(:get, 'http://withport.com:3000/v1') .to_return( status: [301, 'Moved Permanently'], headers: { location: 'http://withport.com:3000/v2' } ) stub_request(:get, 'http://withport.com:3000/v2') .to_return(status: 200) request = HTTParty::Request.new(Net::HTTP::Get, 'http://withport.com:3000/v1') response = request.perform expect(response.request.base_uri.to_s).to eq("http://withport.com:3000") end end it 'should return a valid object for 4xx response' do stub_response 'yes', 401 resp = @request.perform expect(resp.code).to eq(401) expect(resp.body).to eq("yes") expect(resp['foo']['bar']).to eq("yes") end it 'should return a valid object for 5xx response' do stub_response 'error', 500 resp = @request.perform expect(resp.code).to eq(500) expect(resp.body).to eq("error") expect(resp['foo']['bar']).to eq("error") end it "parses response lazily so codes can be checked prior" do stub_response 'not xml', 500 @request.options[:format] = :xml expect { response = @request.perform expect(response.code).to eq(500) expect(response.body).to eq('not xml') }.not_to raise_error end end end it "should not attempt to parse empty responses" do [204, 304].each do |code| stub_response "", code @request.options[:format] = :xml expect(@request.perform).to be_nil end end it "should not fail for missing mime type" do stub_response "Content for you" @request.options[:format] = :html expect(@request.perform.parsed_response).to eq('Content for you') end [300, 301, 302, 305].each do |code| describe "a request that #{code} redirects" do before(:each) do @redirect = stub_response("", code) @redirect['location'] = '/foo' @ok = stub_response('bar', 200) end describe "once" do before(:each) do allow(@http).to receive(:request).and_return(@redirect, @ok) end it "should be handled by GET transparently" do expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by POST transparently" do @request.http_method = Net::HTTP::Post expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by DELETE transparently" do @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by MOVE transparently" do @request.http_method = Net::HTTP::Move expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by COPY transparently" do @request.http_method = Net::HTTP::Copy expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by PATCH transparently" do @request.http_method = Net::HTTP::Patch expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by PUT transparently" do @request.http_method = Net::HTTP::Put expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by HEAD transparently" do @request.http_method = Net::HTTP::Head expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by OPTIONS transparently" do @request.http_method = Net::HTTP::Options expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by MKCOL transparently" do @request.http_method = Net::HTTP::Mkcol expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by LOCK transparently" do @request.http_method = Net::HTTP::Lock expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by UNLOCK transparently" do @request.http_method = Net::HTTP::Unlock expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should keep track of cookies between redirects" do @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly' @request.perform expect(@request.options[:headers]['Cookie']).to match(/foo=bar/) expect(@request.options[:headers]['Cookie']).to match(/name=value/) end it 'should update cookies with redirects' do @request.options[:headers] = {'Cookie' => 'foo=bar;'} @redirect['Set-Cookie'] = 'foo=tar;' @request.perform expect(@request.options[:headers]['Cookie']).to match(/foo=tar/) end it 'should keep cookies between redirects' do @request.options[:headers] = {'Cookie' => 'keep=me'} @redirect['Set-Cookie'] = 'foo=tar;' @request.perform expect(@request.options[:headers]['Cookie']).to match(/keep=me/) end it "should handle multiple Set-Cookie headers between redirects" do @redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly' @redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly' @request.perform expect(@request.options[:headers]['Cookie']).to match(/foo=bar/) expect(@request.options[:headers]['Cookie']).to match(/name=value/) expect(@request.options[:headers]['Cookie']).to match(/one=1/) expect(@request.options[:headers]['Cookie']).to match(/two=2/) end it 'should make resulting request a get request if it not already' do @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) expect(@request.http_method).to eq(Net::HTTP::Get) end it 'should not make resulting request a get request if options[:maintain_method_across_redirects] is true' do @request.options[:maintain_method_across_redirects] = true @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) expect(@request.http_method).to eq(Net::HTTP::Delete) end it 'should log the redirection' do logger_double = double expect(logger_double).to receive(:info).twice @request.options[:logger] = logger_double @request.perform end end describe "infinitely" do before(:each) do allow(@http).to receive(:request).and_return(@redirect) end it "should raise an exception" do expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep) end end end end describe "a request that 303 redirects" do before(:each) do @redirect = stub_response("", 303) @redirect['location'] = '/foo' @ok = stub_response('bar', 200) end describe "once" do before(:each) do allow(@http).to receive(:request).and_return(@redirect, @ok) end it "should be handled by GET transparently" do expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by POST transparently" do @request.http_method = Net::HTTP::Post expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by DELETE transparently" do @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by MOVE transparently" do @request.http_method = Net::HTTP::Move expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by COPY transparently" do @request.http_method = Net::HTTP::Copy expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by PATCH transparently" do @request.http_method = Net::HTTP::Patch expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by PUT transparently" do @request.http_method = Net::HTTP::Put expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by HEAD transparently" do @request.http_method = Net::HTTP::Head expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by OPTIONS transparently" do @request.http_method = Net::HTTP::Options expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by MKCOL transparently" do @request.http_method = Net::HTTP::Mkcol expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by LOCK transparently" do @request.http_method = Net::HTTP::Lock expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by UNLOCK transparently" do @request.http_method = Net::HTTP::Unlock expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should keep track of cookies between redirects" do @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly' @request.perform expect(@request.options[:headers]['Cookie']).to match(/foo=bar/) expect(@request.options[:headers]['Cookie']).to match(/name=value/) end it 'should update cookies with redirects' do @request.options[:headers] = {'Cookie' => 'foo=bar;'} @redirect['Set-Cookie'] = 'foo=tar;' @request.perform expect(@request.options[:headers]['Cookie']).to match(/foo=tar/) end it 'should keep cookies between redirects' do @request.options[:headers] = {'Cookie' => 'keep=me'} @redirect['Set-Cookie'] = 'foo=tar;' @request.perform expect(@request.options[:headers]['Cookie']).to match(/keep=me/) end it "should handle multiple Set-Cookie headers between redirects" do @redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly' @redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly' @request.perform expect(@request.options[:headers]['Cookie']).to match(/foo=bar/) expect(@request.options[:headers]['Cookie']).to match(/name=value/) expect(@request.options[:headers]['Cookie']).to match(/one=1/) expect(@request.options[:headers]['Cookie']).to match(/two=2/) end it 'should make resulting request a get request if it not already' do @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) expect(@request.http_method).to eq(Net::HTTP::Get) end it 'should make resulting request a get request if options[:maintain_method_across_redirects] is false' do @request.options[:maintain_method_across_redirects] = false @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) expect(@request.http_method).to eq(Net::HTTP::Get) end it 'should make resulting request a get request if options[:maintain_method_across_redirects] is true but options[:resend_on_redirect] is false' do @request.options[:maintain_method_across_redirects] = true @request.options[:resend_on_redirect] = false @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) expect(@request.http_method).to eq(Net::HTTP::Get) end it 'should not make resulting request a get request if options[:maintain_method_across_redirects] and options[:resend_on_redirect] is true' do @request.options[:maintain_method_across_redirects] = true @request.options[:resend_on_redirect] = true @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) expect(@request.http_method).to eq(Net::HTTP::Delete) end it 'should log the redirection' do logger_double = double expect(logger_double).to receive(:info).twice @request.options[:logger] = logger_double @request.perform end end describe "infinitely" do before(:each) do allow(@http).to receive(:request).and_return(@redirect) end it "should raise an exception" do expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep) end end end describe "a request that returns 304" do before(:each) do @redirect = stub_response("", 304) @redirect['location'] = '/foo' end before(:each) do allow(@http).to receive(:request).and_return(@redirect) end it "should report 304 with a GET request" do expect(@request.perform.code).to eq(304) end it "should report 304 with a POST request" do @request.http_method = Net::HTTP::Post expect(@request.perform.code).to eq(304) end it "should report 304 with a DELETE request" do @request.http_method = Net::HTTP::Delete expect(@request.perform.code).to eq(304) end it "should report 304 with a MOVE request" do @request.http_method = Net::HTTP::Move expect(@request.perform.code).to eq(304) end it "should report 304 with a COPY request" do @request.http_method = Net::HTTP::Copy expect(@request.perform.code).to eq(304) end it "should report 304 with a PATCH request" do @request.http_method = Net::HTTP::Patch expect(@request.perform.code).to eq(304) end it "should report 304 with a PUT request" do @request.http_method = Net::HTTP::Put expect(@request.perform.code).to eq(304) end it "should report 304 with a HEAD request" do @request.http_method = Net::HTTP::Head expect(@request.perform.code).to eq(304) end it "should report 304 with a OPTIONS request" do @request.http_method = Net::HTTP::Options expect(@request.perform.code).to eq(304) end it "should report 304 with a MKCOL request" do @request.http_method = Net::HTTP::Mkcol expect(@request.perform.code).to eq(304) end it "should be handled by LOCK transparently" do @request.http_method = Net::HTTP::Lock expect(@request.perform.code).to eq(304) end it "should be handled by UNLOCK transparently" do @request.http_method = Net::HTTP::Unlock expect(@request.perform.code).to eq(304) end it 'should not log the redirection' do logger_double = double expect(logger_double).to receive(:info).once @request.options[:logger] = logger_double @request.perform end end [307, 308].each do |code| describe "a request that #{code} redirects" do before(:each) do @redirect = stub_response("", code) @redirect['location'] = '/foo' @ok = stub_response('bar', 200) end describe "once" do before(:each) do allow(@http).to receive(:request).and_return(@redirect, @ok) end it "should be handled by GET transparently" do expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by POST transparently" do @request.http_method = Net::HTTP::Post expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by DELETE transparently" do @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by MOVE transparently" do @request.http_method = Net::HTTP::Move expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by COPY transparently" do @request.http_method = Net::HTTP::Copy expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by PATCH transparently" do @request.http_method = Net::HTTP::Patch expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by PUT transparently" do @request.http_method = Net::HTTP::Put expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by HEAD transparently" do @request.http_method = Net::HTTP::Head expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by OPTIONS transparently" do @request.http_method = Net::HTTP::Options expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by MKCOL transparently" do @request.http_method = Net::HTTP::Mkcol expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by LOCK transparently" do @request.http_method = Net::HTTP::Lock expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should be handled by UNLOCK transparently" do @request.http_method = Net::HTTP::Unlock expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) end it "should keep track of cookies between redirects" do @redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly' @request.perform expect(@request.options[:headers]['Cookie']).to match(/foo=bar/) expect(@request.options[:headers]['Cookie']).to match(/name=value/) end it 'should update cookies with redirects' do @request.options[:headers] = {'Cookie' => 'foo=bar;'} @redirect['Set-Cookie'] = 'foo=tar;' @request.perform expect(@request.options[:headers]['Cookie']).to match(/foo=tar/) end it 'should keep cookies between redirects' do @request.options[:headers] = {'Cookie' => 'keep=me'} @redirect['Set-Cookie'] = 'foo=tar;' @request.perform expect(@request.options[:headers]['Cookie']).to match(/keep=me/) end it "should handle multiple Set-Cookie headers between redirects" do @redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly' @redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly' @request.perform expect(@request.options[:headers]['Cookie']).to match(/foo=bar/) expect(@request.options[:headers]['Cookie']).to match(/name=value/) expect(@request.options[:headers]['Cookie']).to match(/one=1/) expect(@request.options[:headers]['Cookie']).to match(/two=2/) end it 'should maintain method in resulting request' do @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) expect(@request.http_method).to eq(Net::HTTP::Delete) end it 'should maintain method in resulting request if options[:maintain_method_across_redirects] is false' do @request.options[:maintain_method_across_redirects] = false @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) expect(@request.http_method).to eq(Net::HTTP::Delete) end it 'should maintain method in resulting request if options[:maintain_method_across_redirects] is true' do @request.options[:maintain_method_across_redirects] = true @request.http_method = Net::HTTP::Delete expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}}) expect(@request.http_method).to eq(Net::HTTP::Delete) end it 'should log the redirection' do logger_double = double expect(logger_double).to receive(:info).twice @request.options[:logger] = logger_double @request.perform end end describe "infinitely" do before(:each) do allow(@http).to receive(:request).and_return(@redirect) end it "should raise an exception" do expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep) end end end end describe "#send_authorization_header?" do context "basic_auth" do before do @credentials = { username: "username", password: "password" } @authorization = "Basic dXNlcm5hbWU6cGFzc3dvcmQ=" @request.options[:basic_auth] = @credentials @redirect = stub_response("", 302) @ok = stub_response('bar', 200) end before(:each) do allow(@http).to receive(:request).and_return(@redirect, @ok) end it "should not send Authorization header when redirecting to a different host" do @redirect['location'] = 'http://example.com/' @request.perform @request.send(:setup_raw_request) expect(@request.instance_variable_get(:@raw_request)['authorization']).to be_nil end it "should send Authorization header when redirecting to a relative path" do @redirect['location'] = '/v3' @request.perform @request.send(:setup_raw_request) expect(@request.instance_variable_get(:@raw_request)['authorization']).to eq(@authorization) end it "should send Authorization header when redirecting to the same host" do @redirect['location'] = 'http://api.foo.com/v2' @request.perform @request.send(:setup_raw_request) expect(@request.instance_variable_get(:@raw_request)['authorization']).to eq(@authorization) end it "should send Authorization header when redirecting to a different port on the same host" do @redirect['location'] = 'http://api.foo.com:3000/v3' @request.perform @request.send(:setup_raw_request) expect(@request.instance_variable_get(:@raw_request)['authorization']).to eq(@authorization) end end end context "with POST http method" do it "should raise argument error if query is not a hash" do expect { HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', format: :xml, query: 'astring').perform }.to raise_error(ArgumentError) end end describe "argument validation" do it "should raise argument error if basic_auth and digest_auth are both present" do expect { HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: {}, digest_auth: {}).perform }.to raise_error(ArgumentError, "only one authentication method, :basic_auth or :digest_auth may be used at a time") end it "should raise argument error if basic_auth is not a hash" do expect { HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: %w(foo bar)).perform }.to raise_error(ArgumentError, ":basic_auth must be a hash") end it "should raise argument error if digest_auth is not a hash" do expect { HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', digest_auth: %w(foo bar)).perform }.to raise_error(ArgumentError, ":digest_auth must be a hash") end it "should raise argument error if headers is not a hash" do expect { HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', headers: %w(foo bar)).perform }.to raise_error(ArgumentError, ":headers must be a hash") end it "should raise argument error if options method is not http accepted method" do expect { HTTParty::Request.new('SuperPost', 'http://api.foo.com/v1').perform }.to raise_error(ArgumentError, "only get, post, patch, put, delete, head, and options methods are supported") end it "should raise argument error if http method is post and query is not hash" do expect { HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', query: "message: hello").perform }.to raise_error(ArgumentError, ":query must be hash if using HTTP Post") end it "should raise RedirectionTooDeep error if limit is negative" do expect { HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', limit: -1).perform }.to raise_error(HTTParty::RedirectionTooDeep, 'HTTP redirects too deep') end end context 'with Accept-Encoding header' do it 'should disable content decoding if present' do request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', headers:{'Accept-Encoding' => 'custom'}) request.send(:setup_raw_request) expect(request.instance_variable_get(:@raw_request).decode_content).to eq(false) end it 'should disable content decoding if present and lowercase' do request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', headers:{'accept-encoding' => 'custom'}) request.send(:setup_raw_request) expect(request.instance_variable_get(:@raw_request).decode_content).to eq(false) end it 'should disable content decoding if present' do request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1') request.send(:setup_raw_request) expect(request.instance_variable_get(:@raw_request).decode_content).to eq(true) end end end httparty-0.18.1/spec/httparty/response_fragment_spec.rb000066400000000000000000000011021367022441500233470ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper')) RSpec.describe HTTParty::ResponseFragment do it "access to fragment" do fragment = HTTParty::ResponseFragment.new("chunk", nil, nil) expect(fragment).to eq("chunk") end it "has access to delegators" do response = double(code: '200') connection = double fragment = HTTParty::ResponseFragment.new("chunk", response, connection) expect(fragment.code).to eq(200) expect(fragment.http_response).to eq response expect(fragment.connection).to eq connection end end httparty-0.18.1/spec/httparty/response_spec.rb000066400000000000000000000336621367022441500215040ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::Response do before do @last_modified = Date.new(2010, 1, 15).to_s @content_length = '1024' @request_object = HTTParty::Request.new Net::HTTP::Get, '/' @response_object = Net::HTTPOK.new('1.1', 200, 'OK') allow(@response_object).to receive_messages(body: "{foo:'bar'}") @response_object['last-modified'] = @last_modified @response_object['content-length'] = @content_length @parsed_response = lambda { {"foo" => "bar"} } @response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) end describe ".underscore" do it "works with one capitalized word" do expect(HTTParty::Response.underscore("Accepted")).to eq("accepted") end it "works with titlecase" do expect(HTTParty::Response.underscore("BadGateway")).to eq("bad_gateway") end it "works with all caps" do expect(HTTParty::Response.underscore("OK")).to eq("ok") end end describe "initialization" do it "should set the Net::HTTP Response" do expect(@response.response).to eq(@response_object) end it "should set body" do expect(@response.body).to eq(@response_object.body) end it "should set code" do expect(@response.code).to eq(@response_object.code) end it "should set code as an Integer" do expect(@response.code).to be_a(Integer) end it "should set http_version" do unparseable_body = lambda { raise "Unparseable" } unparseable_response = HTTParty::Response.new(@request_object, @response_object, unparseable_body) expect(unparseable_response.http_version).to eq(@response_object.http_version) end context 'when raise_on is supplied' do let(:request) { HTTParty::Request.new(Net::HTTP::Get, '/', raise_on: [404]) } context "and response's status code is in range" do let(:body) { 'Not Found' } let(:response) { Net::HTTPNotFound.new('1.1', 404, body) } before do allow(response).to receive(:body).and_return(body) end subject { described_class.new(request, response, @parsed_response) } it 'throws exception' do expect{ subject }.to raise_error(HTTParty::ResponseError, "Code 404 - #{body}") end end context "and response's status code is not in range" do subject { described_class.new(request, @response_object, @parsed_response) } it 'does not throw exception' do expect{ subject }.not_to raise_error end end end end it 'does raise an error about itself when using #method' do expect { HTTParty::Response.new(@request_object, @response_object, @parsed_response).method(:qux) }.to raise_error(NameError, /HTTParty\:\:Response/) end it 'does raise an error about itself when invoking a method that does not exist' do expect { HTTParty::Response.new(@request_object, @response_object, @parsed_response).qux }.to raise_error(NoMethodError, /HTTParty\:\:Response/) end it "returns response headers" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.headers).to eq({'last-modified' => [@last_modified], 'content-length' => [@content_length]}) end it "should send missing methods to delegate" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response['foo']).to eq('bar') end it "response to request" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.respond_to?(:request)).to be_truthy end it "responds to response" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.respond_to?(:response)).to be_truthy end it "responds to body" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.respond_to?(:body)).to be_truthy end it "responds to headers" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.respond_to?(:headers)).to be_truthy end it "responds to parsed_response" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.respond_to?(:parsed_response)).to be_truthy end it "responds to predicates" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.respond_to?(:success?)).to be_truthy end it "responds to anything parsed_response responds to" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.respond_to?(:[])).to be_truthy end context 'response is array' do let(:response_value) { [{'foo' => 'bar'}, {'foo' => 'baz'}] } let(:response) { HTTParty::Response.new(@request_object, @response_object, lambda { response_value }) } it "should be able to iterate" do expect(response.size).to eq(2) expect { response.each { |item| } }.to_not raise_error end it 'should respond to array methods' do expect(response).to respond_to(:bsearch, :compact, :cycle, :delete, :each, :flatten, :flatten!, :compact, :join) end it 'should equal the string response object body' do expect(response.to_s).to eq(@response_object.body.to_s) end it 'should display the same as an array' do a = StringIO.new b = StringIO.new response_value.display(b) response.display(a) expect(a.string).to eq(b.string) end end it "allows headers to be accessed by mixed-case names in hash notation" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.headers['Content-LENGTH']).to eq(@content_length) end it "returns a comma-delimited value when multiple values exist" do @response_object.add_field 'set-cookie', 'csrf_id=12345; path=/' @response_object.add_field 'set-cookie', '_github_ses=A123CdE; path=/' response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) expect(response.headers['set-cookie']).to eq("csrf_id=12345; path=/, _github_ses=A123CdE; path=/") end # Backwards-compatibility - previously, #headers returned a Hash it "responds to hash methods" do response = HTTParty::Response.new(@request_object, @response_object, @parsed_response) hash_methods = {}.methods - response.headers.methods hash_methods.each do |method_name| expect(response.headers.respond_to?(method_name)).to be_truthy end end describe "#is_a?" do subject { HTTParty::Response.new(@request_object, @response_object, @parsed_response) } it { is_expected.to respond_to(:is_a?).with(1).arguments } it { expect(subject.is_a?(HTTParty::Response)).to be_truthy } it { expect(subject.is_a?(Object)).to be_truthy } end describe "#kind_of?" do subject { HTTParty::Response.new(@request_object, @response_object, @parsed_response) } it { is_expected.to respond_to(:kind_of?).with(1).arguments } it { expect(subject.kind_of?(HTTParty::Response)).to be_truthy } it { expect(subject.kind_of?(Object)).to be_truthy } end describe "semantic methods for response codes" do def response_mock(klass) response = klass.new('', '', '') allow(response).to receive(:body) response end context "major codes" do it "is information" do net_response = response_mock(Net::HTTPInformation) response = HTTParty::Response.new(@request_object, net_response, '') expect(response.information?).to be_truthy end it "is success" do net_response = response_mock(Net::HTTPSuccess) response = HTTParty::Response.new(@request_object, net_response, '') expect(response.success?).to be_truthy end it "is redirection" do net_response = response_mock(Net::HTTPRedirection) response = HTTParty::Response.new(@request_object, net_response, '') expect(response.redirection?).to be_truthy end it "is client error" do net_response = response_mock(Net::HTTPClientError) response = HTTParty::Response.new(@request_object, net_response, '') expect(response.client_error?).to be_truthy end it "is server error" do net_response = response_mock(Net::HTTPServerError) response = HTTParty::Response.new(@request_object, net_response, '') expect(response.server_error?).to be_truthy end end context "for specific codes" do SPECIFIC_CODES = { accepted?: Net::HTTPAccepted, bad_gateway?: Net::HTTPBadGateway, bad_request?: Net::HTTPBadRequest, conflict?: Net::HTTPConflict, continue?: Net::HTTPContinue, created?: Net::HTTPCreated, expectation_failed?: Net::HTTPExpectationFailed, forbidden?: Net::HTTPForbidden, found?: Net::HTTPFound, gateway_time_out?: Net::HTTPGatewayTimeOut, gone?: Net::HTTPGone, internal_server_error?: Net::HTTPInternalServerError, length_required?: Net::HTTPLengthRequired, method_not_allowed?: Net::HTTPMethodNotAllowed, moved_permanently?: Net::HTTPMovedPermanently, multiple_choice?: Net::HTTPMultipleChoice, no_content?: Net::HTTPNoContent, non_authoritative_information?: Net::HTTPNonAuthoritativeInformation, not_acceptable?: Net::HTTPNotAcceptable, not_found?: Net::HTTPNotFound, not_implemented?: Net::HTTPNotImplemented, not_modified?: Net::HTTPNotModified, ok?: Net::HTTPOK, partial_content?: Net::HTTPPartialContent, payment_required?: Net::HTTPPaymentRequired, precondition_failed?: Net::HTTPPreconditionFailed, proxy_authentication_required?: Net::HTTPProxyAuthenticationRequired, request_entity_too_large?: Net::HTTPRequestEntityTooLarge, request_time_out?: Net::HTTPRequestTimeOut, request_uri_too_long?: Net::HTTPRequestURITooLong, requested_range_not_satisfiable?: Net::HTTPRequestedRangeNotSatisfiable, reset_content?: Net::HTTPResetContent, see_other?: Net::HTTPSeeOther, service_unavailable?: Net::HTTPServiceUnavailable, switch_protocol?: Net::HTTPSwitchProtocol, temporary_redirect?: Net::HTTPTemporaryRedirect, unauthorized?: Net::HTTPUnauthorized, unsupported_media_type?: Net::HTTPUnsupportedMediaType, use_proxy?: Net::HTTPUseProxy, version_not_supported?: Net::HTTPVersionNotSupported } # Ruby 2.0, new name for this response. if RUBY_VERSION >= "2.0.0" && ::RUBY_PLATFORM != "java" SPECIFIC_CODES[:multiple_choices?] = Net::HTTPMultipleChoices end # Ruby 2.6, those status codes have been updated. if RUBY_VERSION >= "2.6.0" && ::RUBY_PLATFORM != "java" SPECIFIC_CODES[:gateway_timeout?] = Net::HTTPGatewayTimeout SPECIFIC_CODES[:payload_too_large?] = Net::HTTPPayloadTooLarge SPECIFIC_CODES[:request_timeout?] = Net::HTTPRequestTimeout SPECIFIC_CODES[:uri_too_long?] = Net::HTTPURITooLong SPECIFIC_CODES[:range_not_satisfiable?] = Net::HTTPRangeNotSatisfiable end SPECIFIC_CODES.each do |method, klass| it "responds to #{method}" do net_response = response_mock(klass) response = HTTParty::Response.new(@request_object, net_response, '') expect(response.__send__(method)).to be_truthy end end end end describe "headers" do let (:empty_headers) { HTTParty::Response::Headers.new } let (:some_headers_hash) do {'Cookie' => 'bob', 'Content-Encoding' => 'meow'} end let (:some_headers) do HTTParty::Response::Headers.new.tap do |h| some_headers_hash.each_pair do |k,v| h[k] = v end end end it "can initialize without headers" do expect(empty_headers).to eq({}) end it 'always equals itself' do expect(empty_headers).to eq(empty_headers) expect(some_headers).to eq(some_headers) end it 'does not equal itself when not equivalent' do expect(empty_headers).to_not eq(some_headers) end it 'does equal a hash' do expect(empty_headers).to eq({}) expect(some_headers).to eq(some_headers_hash) end end describe "#tap" do it "is possible to tap into a response" do result = @response.tap(&:code) expect(result).to eq @response end end describe "#inspect" do it "works" do inspect = @response.inspect expect(inspect).to include("HTTParty::Response:0x") expect(inspect).to include("parsed_response={\"foo\"=>\"bar\"}") expect(inspect).to include("@response=#") expect(inspect).to include("@headers={") expect(inspect).to include("last-modified") expect(inspect).to include("content-length") end end describe 'marshalling' do before { RSpec::Mocks.space.proxy_for(@response_object).remove_stub(:body) } specify do marshalled = Marshal.load(Marshal.dump(@response)) expect(marshalled.headers).to eq @response.headers expect(marshalled.body).to eq @response.body expect(marshalled.code).to eq @response.code end end end httparty-0.18.1/spec/httparty/ssl_spec.rb000066400000000000000000000062111367022441500204350ustar00rootroot00000000000000require 'spec_helper' RSpec.describe HTTParty::Request do context "SSL certificate verification" do before do WebMock.disable! end after do WebMock.enable! end it "should fail when no trusted CA list is specified, by default" do expect do ssl_verify_test(nil, nil, "selfsigned.crt") end.to raise_error OpenSSL::SSL::SSLError end it "should work when no trusted CA list is specified, when the verify option is set to false" do expect(ssl_verify_test(nil, nil, "selfsigned.crt", verify: false).parsed_response).to eq({'success' => true}) end it "should fail when no trusted CA list is specified, with a bogus hostname, by default" do expect do ssl_verify_test(nil, nil, "bogushost.crt") end.to raise_error OpenSSL::SSL::SSLError end it "should work when no trusted CA list is specified, even with a bogus hostname, when the verify option is set to true" do expect(ssl_verify_test(nil, nil, "bogushost.crt", verify: false).parsed_response).to eq({'success' => true}) end it "should work when using ssl_ca_file with a self-signed CA" do expect(ssl_verify_test(:ssl_ca_file, "selfsigned.crt", "selfsigned.crt").parsed_response).to eq({'success' => true}) end it "should work when using ssl_ca_file with a certificate authority" do expect(ssl_verify_test(:ssl_ca_file, "ca.crt", "server.crt").parsed_response).to eq({'success' => true}) end it "should work when using ssl_ca_path with a certificate authority" do http = Net::HTTP.new('www.google.com', 443) response = double(Net::HTTPResponse, :[] => '', body: '', to_hash: {}) allow(http).to receive(:request).and_return(response) expect(Net::HTTP).to receive(:new).with('www.google.com', 443).and_return(http) expect(http).to receive(:ca_path=).with('/foo/bar') HTTParty.get('https://www.google.com', ssl_ca_path: '/foo/bar') end it "should fail when using ssl_ca_file and the server uses an unrecognized certificate authority" do expect do ssl_verify_test(:ssl_ca_file, "ca.crt", "selfsigned.crt") end.to raise_error(OpenSSL::SSL::SSLError) end it "should fail when using ssl_ca_path and the server uses an unrecognized certificate authority" do expect do ssl_verify_test(:ssl_ca_path, ".", "selfsigned.crt") end.to raise_error(OpenSSL::SSL::SSLError) end it "should fail when using ssl_ca_file and the server uses a bogus hostname" do expect do ssl_verify_test(:ssl_ca_file, "ca.crt", "bogushost.crt") end.to raise_error(OpenSSL::SSL::SSLError) end it "should fail when using ssl_ca_path and the server uses a bogus hostname" do expect do ssl_verify_test(:ssl_ca_path, ".", "bogushost.crt") end.to raise_error(OpenSSL::SSL::SSLError) end it "should provide the certificate used by the server via peer_cert" do peer_cert = nil ssl_verify_test(:ssl_ca_file, "ca.crt", "server.crt") do |response| peer_cert ||= response.connection.peer_cert end expect(peer_cert).to be_a OpenSSL::X509::Certificate end end end httparty-0.18.1/spec/httparty_spec.rb000066400000000000000000000776271367022441500176570ustar00rootroot00000000000000require_relative 'spec_helper' RSpec.describe HTTParty do before(:each) do @klass = Class.new @klass.instance_eval { include HTTParty } end describe "pem" do it 'should set the pem content' do @klass.pem 'PEM-CONTENT' expect(@klass.default_options[:pem]).to eq('PEM-CONTENT') end it "should set the password to nil if it's not provided" do @klass.pem 'PEM-CONTENT' expect(@klass.default_options[:pem_password]).to be_nil end it 'should set the password' do @klass.pem 'PEM-CONTENT', 'PASSWORD' expect(@klass.default_options[:pem_password]).to eq('PASSWORD') end end describe "pkcs12" do it 'should set the p12 content' do @klass.pkcs12 'P12-CONTENT', 'PASSWORD' expect(@klass.default_options[:p12]).to eq('P12-CONTENT') end it 'should set the password' do @klass.pkcs12 'P12-CONTENT', 'PASSWORD' expect(@klass.default_options[:p12_password]).to eq('PASSWORD') end end describe 'ssl_version' do it 'should set the ssl_version content' do @klass.ssl_version :SSLv3 expect(@klass.default_options[:ssl_version]).to eq(:SSLv3) end end describe 'ciphers' do it 'should set the ciphers content' do expect(@klass.default_options[:ciphers]).to be_nil @klass.ciphers 'RC4-SHA' expect(@klass.default_options[:ciphers]).to eq('RC4-SHA') end end describe 'http_proxy' do it 'should set the address' do @klass.http_proxy 'proxy.foo.com', 80 options = @klass.default_options expect(options[:http_proxyaddr]).to eq('proxy.foo.com') expect(options[:http_proxyport]).to eq(80) end it 'should set the proxy user and pass when they are provided' do @klass.http_proxy 'proxy.foo.com', 80, 'user', 'pass' options = @klass.default_options expect(options[:http_proxyuser]).to eq('user') expect(options[:http_proxypass]).to eq('pass') end end describe "base uri" do before(:each) do @klass.base_uri('api.foo.com/v1') end it "should have reader" do expect(@klass.base_uri).to eq('http://api.foo.com/v1') end it 'should have writer' do @klass.base_uri('http://api.foobar.com') expect(@klass.base_uri).to eq('http://api.foobar.com') end it 'should not modify the parameter during assignment' do uri = 'http://api.foobar.com' @klass.base_uri(uri) expect(uri).to eq('http://api.foobar.com') end end describe ".disable_rails_query_string_format" do it "sets the query string normalizer to HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER" do @klass.disable_rails_query_string_format expect(@klass.default_options[:query_string_normalizer]).to eq(HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER) end end describe ".normalize_base_uri" do it "should add http if not present for non ssl requests" do uri = HTTParty.normalize_base_uri('api.foobar.com') expect(uri).to eq('http://api.foobar.com') end it "should add https if not present for ssl requests" do uri = HTTParty.normalize_base_uri('api.foo.com/v1:443') expect(uri).to eq('https://api.foo.com/v1:443') end it "should not remove https for ssl requests" do uri = HTTParty.normalize_base_uri('https://api.foo.com/v1:443') expect(uri).to eq('https://api.foo.com/v1:443') end it 'should not modify the parameter' do uri = 'http://api.foobar.com' HTTParty.normalize_base_uri(uri) expect(uri).to eq('http://api.foobar.com') end it "should not treat uri's with a port of 4430 as ssl" do uri = HTTParty.normalize_base_uri('http://api.foo.com:4430/v1') expect(uri).to eq('http://api.foo.com:4430/v1') end end describe "headers" do def expect_headers(header = {}) expect(HTTParty::Request).to receive(:new) \ .with(anything, anything, hash_including({ headers: header })) \ .and_return(double("mock response", perform: nil)) end it "does not modify default_options when no arguments are passed" do @klass.headers expect(@klass.default_options[:headers]).to eq(nil) end it "should default to empty hash" do expect(@klass.headers).to eq({}) end it "should be able to be updated" do init_headers = {foo: 'bar', baz: 'spax'} @klass.headers init_headers expect(@klass.headers).to eq(init_headers) end it "should be able to accept block as header value" do init_headers = {'foo' => lambda {'bar'}} @klass.headers init_headers stub_request(:get, "http://example.com/").with(headers: {'foo' => 'bar', 'baz' => 'spax'}) @klass.get('http://example.com/', headers: {'baz' => -> {'spax'}}) expect(@klass.headers).to eq(init_headers) end it "should pass options as argument to header block value" do init_headers = { 'foo' => lambda { |options| options[:body] } } @klass.headers init_headers stub_request(:get, "http://example.com/").with(body: 'bar', headers: { 'foo' => 'bar', 'baz' => 'rab' }) @klass.get('http://example.com/', body: 'bar',headers: { 'baz' => -> (options){ options[:body].reverse } }) expect(@klass.headers).to eq(init_headers) end it "uses the class headers when sending a request" do expect_headers('foo' => 'bar') @klass.headers(foo: 'bar') @klass.get('') end it "merges class headers with request headers" do expect_headers('baz' => 'spax', 'foo' => 'bar') @klass.headers(foo: 'bar') @klass.get('', headers: {baz: 'spax'}) end it 'overrides class headers with request headers' do expect_headers('baz' => 'spax', 'foo' => 'baz') @klass.headers(foo: 'bar') @klass.get('', headers: {baz: 'spax', foo: 'baz'}) end context "with cookies" do it 'utilizes the class-level cookies' do expect_headers('foo' => 'bar', 'cookie' => 'type=snickerdoodle') @klass.headers(foo: 'bar') @klass.cookies(type: 'snickerdoodle') @klass.get('') end it 'adds cookies to the headers' do expect_headers('foo' => 'bar', 'cookie' => 'type=snickerdoodle') @klass.headers(foo: 'bar') @klass.get('', cookies: {type: 'snickerdoodle'}) end it 'doesnt modify default headers' do expect(@klass.headers).to eq({}) expect_headers('cookie' => 'type=snickerdoodle') @klass.get('', cookies: {type: 'snickerdoodle'}) expect(@klass.headers).to eq({}) end it 'adds optional cookies to the optional headers' do expect_headers('baz' => 'spax', 'cookie' => 'type=snickerdoodle') @klass.get('', cookies: {type: 'snickerdoodle'}, headers: {baz: 'spax'}) end end context 'when posting file' do let(:boundary) { '------------------------c772861a5109d5ef' } let(:headers) do { 'Content-Type'=>"multipart/form-data; boundary=#{boundary}" } end before do expect(HTTParty::Request::MultipartBoundary).to receive(:generate).and_return(boundary) end it 'changes content-type headers to multipart/form-data' do stub_request(:post, "http://example.com/").with(headers: headers) @klass.post('http://example.com', body: { file: File.open('spec/fixtures/tiny.gif')}) end end context 'when headers passed as symbols' do it 'converts them to string' do expect_headers('foo' => 'application/json', 'bar' => 'example') headers = { foo: 'application/json', bar: 'example' } @klass.post('http://example.com', headers: headers) end it 'converts default headers to string' do expect_headers('foo' => 'application/json', 'bar' => 'example') @klass.headers(foo: 'application/json') @klass.post('http://example.com', headers: { bar: 'example' }) end end end describe "cookies" do def expect_cookie_header(s) expect(HTTParty::Request).to receive(:new) \ .with(anything, anything, hash_including({ headers: { "cookie" => s } })) \ .and_return(double("mock response", perform: nil)) end it "should not be in the headers by default" do allow(HTTParty::Request).to receive(:new).and_return(double(nil, perform: nil)) @klass.get("") expect(@klass.headers.keys).not_to include("cookie") end it "should raise an ArgumentError if passed a non-Hash" do expect do @klass.cookies("nonsense") end.to raise_error(ArgumentError) end it "should allow a cookie to be specified with a one-off request" do expect_cookie_header "type=snickerdoodle" @klass.get("", cookies: { type: "snickerdoodle" }) end describe "when a cookie is set at the class level" do before(:each) do @klass.cookies({ type: "snickerdoodle" }) end it "should include that cookie in the request" do expect_cookie_header "type=snickerdoodle" @klass.get("") end it "should pass the proper cookies when requested multiple times" do 2.times do expect_cookie_header "type=snickerdoodle" @klass.get("") end end it "should allow the class defaults to be overridden" do expect_cookie_header "type=chocolate_chip" @klass.get("", cookies: { type: "chocolate_chip" }) end end describe "in a class with multiple methods that use different cookies" do before(:each) do @klass.instance_eval do def first_method get("first_method", cookies: { first_method_cookie: "foo" }) end def second_method get("second_method", cookies: { second_method_cookie: "foo" }) end end end it "should not allow cookies used in one method to carry over into other methods" do expect_cookie_header "first_method_cookie=foo" @klass.first_method expect_cookie_header "second_method_cookie=foo" @klass.second_method end end end describe "default params" do it "should default to empty hash" do expect(@klass.default_params).to eq({}) end it "should be able to be updated" do new_defaults = {foo: 'bar', baz: 'spax'} @klass.default_params new_defaults expect(@klass.default_params).to eq(new_defaults) end end describe "default timeout" do it "should default to nil" do expect(@klass.default_options[:timeout]).to eq(nil) end it "should support updating" do @klass.default_timeout 10 expect(@klass.default_options[:timeout]).to eq(10) end it "should support floats" do @klass.default_timeout 0.5 expect(@klass.default_options[:timeout]).to eq(0.5) end it "should raise an exception if unsupported type provided" do expect { @klass.default_timeout "0.5" }.to raise_error ArgumentError end end describe "debug_output" do it "stores the given stream as a default_option" do @klass.debug_output $stdout expect(@klass.default_options[:debug_output]).to eq($stdout) end it "stores the $stderr stream by default" do @klass.debug_output expect(@klass.default_options[:debug_output]).to eq($stderr) end end describe "basic http authentication" do it "should work" do @klass.basic_auth 'foobar', 'secret' expect(@klass.default_options[:basic_auth]).to eq({username: 'foobar', password: 'secret'}) end end describe "digest http authentication" do it "should work" do @klass.digest_auth 'foobar', 'secret' expect(@klass.default_options[:digest_auth]).to eq({username: 'foobar', password: 'secret'}) end end describe "parser" do class CustomParser def self.parse(body) {sexy: true} end end let(:parser) do proc { |data, format| CustomParser.parse(data) } end it "should set parser options" do @klass.parser parser expect(@klass.default_options[:parser]).to eq(parser) end it "should be able parse response with custom parser" do @klass.parser parser stub_request(:get, 'http://twitter.com/statuses/public_timeline.xml') .to_return(body: 'tweets') custom_parsed_response = @klass.get('http://twitter.com/statuses/public_timeline.xml') expect(custom_parsed_response[:sexy]).to eq(true) end it "raises UnsupportedFormat when the parser cannot handle the format" do @klass.format :json parser_class = Class.new(HTTParty::Parser) parser_class::SupportedFormats = {} expect do @klass.parser parser_class end.to raise_error(HTTParty::UnsupportedFormat) end it 'does not validate format whe custom parser is a proc' do expect do @klass.format :json @klass.parser lambda {|body, format|} end.to_not raise_error end end describe "uri_adapter" do context 'with Addressable::URI' do before do @klass.uri_adapter(Addressable::URI) end it 'handles international domains' do uri = 'http://xn--i-7iqv272g.ws/' stub_request(:get, uri).to_return(body: 'stuff') response = @klass.get('http://i❤️.ws') expect(response.parsed_response).to eq('stuff') expect(response.request.uri.to_s).to eq(uri) end end context 'with custom URI Adaptor' do require 'forwardable' class CustomURIAdaptor extend Forwardable def_delegators :@uri, :userinfo, :relative?, :query, :query=, :scheme, :path, :host, :port def initialize(uri) @uri = uri end def self.parse(uri) new(URI.parse(uri)) end def normalize self end end let(:uri_adapter) { CustomURIAdaptor } it "should set the uri_adapter" do @klass.uri_adapter uri_adapter expect(@klass.default_options[:uri_adapter]).to be uri_adapter end it "should raise an ArgumentError if uri_adapter doesn't implement parse method" do expect do @klass.uri_adapter double() end.to raise_error(ArgumentError) end it "should process a request with a uri instance parsed from the uri_adapter" do uri = 'http://foo.com/bar' stub_request(:get, uri).to_return(body: 'stuff') @klass.uri_adapter uri_adapter expect(@klass.get(uri).parsed_response).to eq('stuff') end end end describe "connection_adapter" do let(:uri) { 'http://google.com/api.json' } let(:connection_adapter) { double('CustomConnectionAdapter') } it "should set the connection_adapter" do @klass.connection_adapter connection_adapter expect(@klass.default_options[:connection_adapter]).to be connection_adapter end it "should set the connection_adapter_options when provided" do options = {foo: :bar} @klass.connection_adapter connection_adapter, options expect(@klass.default_options[:connection_adapter_options]).to be options end it "should not set the connection_adapter_options when not provided" do @klass.connection_adapter connection_adapter expect(@klass.default_options[:connection_adapter_options]).to be_nil end it "should process a request with a connection from the adapter" do connection_adapter_options = {foo: :bar} expect(connection_adapter).to receive(:call) { |u, o| expect(o[:connection_adapter_options]).to eq(connection_adapter_options) HTTParty::ConnectionAdapter.call(u, o) }.with(URI.parse(uri), kind_of(Hash)) stub_request(:get, uri).to_return(body: 'stuff') @klass.connection_adapter connection_adapter, connection_adapter_options expect(@klass.get(uri).parsed_response).to eq('stuff') end end describe "format" do it "should allow xml" do @klass.format :xml expect(@klass.default_options[:format]).to eq(:xml) end it "should allow csv" do @klass.format :csv expect(@klass.default_options[:format]).to eq(:csv) end it "should allow json" do @klass.format :json expect(@klass.default_options[:format]).to eq(:json) end it "should allow plain" do @klass.format :plain expect(@klass.default_options[:format]).to eq(:plain) end it 'should not allow funky format' do expect do @klass.format :foobar end.to raise_error(HTTParty::UnsupportedFormat) end it 'should only print each format once with an exception' do expect do @klass.format :foobar end.to raise_error(HTTParty::UnsupportedFormat, "':foobar' Must be one of: csv, html, json, plain, xml") end it 'sets the default parser' do expect(@klass.default_options[:parser]).to be_nil @klass.format :json expect(@klass.default_options[:parser]).to eq(HTTParty::Parser) end it 'does not reset parser to the default parser' do my_parser = lambda {} @klass.parser my_parser @klass.format :json expect(@klass.parser).to eq(my_parser) end end describe "#no_follow" do it "sets no_follow to false by default" do @klass.no_follow expect(@klass.default_options[:no_follow]).to be_falsey end it "sets the no_follow option to true" do @klass.no_follow true expect(@klass.default_options[:no_follow]).to be_truthy end end describe "#maintain_method_across_redirects" do it "sets maintain_method_across_redirects to true by default" do @klass.maintain_method_across_redirects expect(@klass.default_options[:maintain_method_across_redirects]).to be_truthy end it "sets the maintain_method_across_redirects option to false" do @klass.maintain_method_across_redirects false expect(@klass.default_options[:maintain_method_across_redirects]).to be_falsey end end describe "#resend_on_redirect" do it "sets resend_on_redirect to true by default" do @klass.resend_on_redirect expect(@klass.default_options[:resend_on_redirect]).to be_truthy end it "sets resend_on_redirect option to false" do @klass.resend_on_redirect false expect(@klass.default_options[:resend_on_redirect]).to be_falsey end end describe ".follow_redirects" do it "sets follow redirects to true by default" do @klass.follow_redirects expect(@klass.default_options[:follow_redirects]).to be_truthy end it "sets the follow_redirects option to false" do @klass.follow_redirects false expect(@klass.default_options[:follow_redirects]).to be_falsey end end describe ".query_string_normalizer" do it "sets the query_string_normalizer option" do normalizer = proc {} @klass.query_string_normalizer normalizer expect(@klass.default_options[:query_string_normalizer]).to eq(normalizer) end end describe ".raise_on" do context 'when parameters is an array' do it 'sets raise_on option' do @klass.raise_on [500, 404] expect(@klass.default_options[:raise_on]).to contain_exactly(404, 500) end end context 'when parameters is a fixnum' do it 'sets raise_on option' do @klass.raise_on 404 expect(@klass.default_options[:raise_on]).to contain_exactly(404) end end end describe "with explicit override of automatic redirect handling" do before do @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', format: :xml, no_follow: true) @redirect = stub_response 'first redirect', 302 @redirect['location'] = 'http://foo.com/bar' allow(HTTParty::Request).to receive_messages(new: @request) end it "should fail with redirected GET" do expect do @error = @klass.get('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end it "should fail with redirected POST" do expect do @klass.post('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end it "should fail with redirected PATCH" do expect do @klass.patch('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end it "should fail with redirected DELETE" do expect do @klass.delete('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end it "should fail with redirected MOVE" do expect do @klass.move('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end it "should fail with redirected COPY" do expect do @klass.copy('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end it "should fail with redirected PUT" do expect do @klass.put('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end it "should fail with redirected HEAD" do expect do @klass.head('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end it "should fail with redirected OPTIONS" do expect do @klass.options('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end it "should fail with redirected MKCOL" do expect do @klass.mkcol('/foo', no_follow: true) end.to raise_error(HTTParty::RedirectionTooDeep) {|e| expect(e.response.body).to eq('first redirect')} end end describe "head requests should follow redirects requesting HEAD only" do before do allow(HTTParty::Request).to receive(:new). and_return(double("mock response", perform: nil)) end it "should remain HEAD request across redirects, unless specified otherwise" do expect(@klass).to receive(:ensure_method_maintained_across_redirects).with({}) @klass.head('/foo') end end describe "#ensure_method_maintained_across_redirects" do it "should set maintain_method_across_redirects option if unspecified" do options = {} @klass.send(:ensure_method_maintained_across_redirects, options) expect(options[:maintain_method_across_redirects]).to be_truthy end it "should not set maintain_method_across_redirects option if value is present" do options = { maintain_method_across_redirects: false } @klass.send(:ensure_method_maintained_across_redirects, options) expect(options[:maintain_method_across_redirects]).to be_falsey end end describe "with multiple class definitions" do before(:each) do @klass.instance_eval do base_uri "http://first.com" default_params one: 1 end @additional_klass = Class.new @additional_klass.instance_eval do include HTTParty base_uri "http://second.com" default_params two: 2 end end it "should not run over each others options" do expect(@klass.default_options).to eq({ base_uri: 'http://first.com', default_params: { one: 1 } }) expect(@additional_klass.default_options).to eq({ base_uri: 'http://second.com', default_params: { two: 2 } }) end end describe "two child classes inheriting from one parent" do before(:each) do @parent = Class.new do include HTTParty def self.name "Parent" end end @child1 = Class.new(@parent) @child2 = Class.new(@parent) end it "does not modify each others inherited attributes" do @child1.default_params joe: "alive" @child2.default_params joe: "dead" expect(@child1.default_options).to eq({ default_params: {joe: "alive"} }) expect(@child2.default_options).to eq({ default_params: {joe: "dead"} }) expect(@parent.default_options).to eq({ }) end it "inherits default_options from the superclass" do @parent.basic_auth 'user', 'password' expect(@child1.default_options).to eq({basic_auth: {username: 'user', password: 'password'}}) @child1.basic_auth 'u', 'p' # modifying child1 has no effect on child2 expect(@child2.default_options).to eq({basic_auth: {username: 'user', password: 'password'}}) end it "doesn't modify the parent's default options" do @parent.basic_auth 'user', 'password' @child1.basic_auth 'u', 'p' expect(@child1.default_options).to eq({basic_auth: {username: 'u', password: 'p'}}) @child1.basic_auth 'email', 'token' expect(@child1.default_options).to eq({basic_auth: {username: 'email', password: 'token'}}) expect(@parent.default_options).to eq({basic_auth: {username: 'user', password: 'password'}}) end it "doesn't modify hashes in the parent's default options" do @parent.headers 'Accept' => 'application/json' @child1.headers 'Accept' => 'application/xml' expect(@parent.default_options[:headers]).to eq({'Accept' => 'application/json'}) expect(@child1.default_options[:headers]).to eq({'Accept' => 'application/xml'}) end it "works with lambda values" do @child1.default_options[:imaginary_option] = lambda { "This is a new lambda "} expect(@child1.default_options[:imaginary_option]).to be_a Proc end it 'should dup the proc on the child class' do imaginary_option = lambda { 2 * 3.14 } @parent.default_options[:imaginary_option] = imaginary_option expect(@parent.default_options[:imaginary_option].call).to eq(imaginary_option.call) @child1.default_options[:imaginary_option] expect(@child1.default_options[:imaginary_option].call).to eq(imaginary_option.call) expect(@child1.default_options[:imaginary_option]).not_to be_equal imaginary_option end it "inherits default_cookies from the parent class" do @parent.cookies 'type' => 'chocolate_chip' expect(@child1.default_cookies).to eq({"type" => "chocolate_chip"}) @child1.cookies 'type' => 'snickerdoodle' expect(@child1.default_cookies).to eq({"type" => "snickerdoodle"}) expect(@child2.default_cookies).to eq({"type" => "chocolate_chip"}) end it "doesn't modify the parent's default cookies" do @parent.cookies 'type' => 'chocolate_chip' @child1.cookies 'type' => 'snickerdoodle' expect(@child1.default_cookies).to eq({"type" => "snickerdoodle"}) expect(@parent.default_cookies).to eq({"type" => "chocolate_chip"}) end end describe "grand parent with inherited callback" do before do @grand_parent = Class.new do def self.inherited(subclass) subclass.instance_variable_set(:@grand_parent, true) end end @parent = Class.new(@grand_parent) do include HTTParty end end it "continues running the #inherited on the parent" do child = Class.new(@parent) expect(child.instance_variable_get(:@grand_parent)).to be_truthy end end describe "#get" do it "should be able to get html" do stub_http_response_with('example.html') expect(HTTParty.get('http://www.example.com').parsed_response).to eq(file_fixture('example.html')) end it "should be able to get chunked html" do chunks = %w(Chunk1 Chunk2 Chunk3 Chunk4) stub_chunked_http_response_with(chunks) expect( HTTParty.get('http://www.google.com') do |fragment| expect(chunks).to include(fragment) end.parsed_response ).to eq(chunks.join) end it "should return an empty body if stream_body option is turned on" do chunks = %w(Chunk1 Chunk2 Chunk3 Chunk4) options = {stream_body: true, format: 'html'} stub_chunked_http_response_with(chunks, options) expect( HTTParty.get('http://www.google.com', options) do |fragment| expect(chunks).to include(fragment) expect(fragment.code).to eql 200 expect(fragment.http_response).to be end.parsed_response ).to eq(nil) end context 'when streaming body' do let(:chunk) { 'Content'.force_encoding('ascii-8bit') } let(:options) { { stream_body: true } } before do stub_chunked_http_response_with([chunk], options) do |response| allow(response).to receive(:[]).with('content-type').and_return('text/plain; charset = "utf-8"') end end specify do HTTParty.get('http://www.google.com', options) do |fragment| expect(fragment.encoding).to eq(Encoding.find("UTF-8")) end end end it "should be able parse response type json automatically" do stub_http_response_with('twitter.json') tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.json') expect(tweets.size).to eq(20) expect(tweets.first['user']).to eq({ "name" => "Pyk", "url" => nil, "id" => "7694602", "description" => nil, "protected" => false, "screen_name" => "Pyk", "followers_count" => 1, "location" => "Opera Plaza, California", "profile_image_url" => "http://static.twitter.com/images/default_profile_normal.png" }) end it "should be able parse response type xml automatically" do stub_http_response_with('twitter.xml') tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.xml') expect(tweets['statuses'].size).to eq(20) expect(tweets['statuses'].first['user']).to eq({ "name" => "Magic 8 Bot", "url" => nil, "id" => "17656026", "description" => "ask me a question", "protected" => "false", "screen_name" => "magic8bot", "followers_count" => "90", "profile_image_url" => "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg", "location" => nil }) end it "should be able parse response type csv automatically" do stub_http_response_with('twitter.csv') profile = HTTParty.get('http://twitter.com/statuses/profile.csv') expect(profile.size).to eq(2) expect(profile[0]).to eq(%w(name url id description protected screen_name followers_count profile_image_url location)) expect(profile[1]).to eq(["Magic 8 Bot", nil, "17656026", "ask me a question", "false", "magic8bot", "90", "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg", nil]) end it "should not get undefined method add_node for nil class for the following xml" do stub_http_response_with('undefined_method_add_node_for_nil.xml') result = HTTParty.get('http://foobar.com') expect(result.parsed_response).to eq({"Entities" => {"href" => "https://s3-sandbox.parature.com/api/v1/5578/5633/Account", "results" => "0", "total" => "0", "page_size" => "25", "page" => "1"}}) end it "should parse empty response fine" do stub_http_response_with('empty.xml') result = HTTParty.get('http://foobar.com') expect(result).to be_nil end it "should accept http URIs" do stub_http_response_with('example.html') expect do HTTParty.get('http://example.com') end.not_to raise_error end it "should accept https URIs" do stub_http_response_with('example.html') expect do HTTParty.get('https://example.com') end.not_to raise_error end it "should accept webcal URIs" do uri = 'http://google.com/' stub_request(:get, uri).to_return(body: 'stuff') uri = 'webcal://google.com/' expect do HTTParty.get(uri) end.not_to raise_error end it "should raise an InvalidURIError on URIs that can't be parsed at all" do expect do HTTParty.get("It's the one that says 'Bad URI'") end.to raise_error(URI::InvalidURIError) end end end httparty-0.18.1/spec/spec_helper.rb000066400000000000000000000023701367022441500172360ustar00rootroot00000000000000require "simplecov" SimpleCov.start require 'pry' require "httparty" require 'webmock/rspec' def file_fixture(filename) open(File.join(File.dirname(__FILE__), 'fixtures', "#{filename}")).read end Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '**', '*.rb'))].each {|f| require f} RSpec.configure do |config| config.include HTTParty::StubResponse config.include HTTParty::SSLTestHelper config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = false end config.filter_run :focus config.run_all_when_everything_filtered = true config.disable_monkey_patching! config.warnings = true if config.files_to_run.one? config.default_formatter = 'doc' end config.profile_examples = 10 config.order = :random config.before(:each) do # Reset default_cert_store cache HTTParty::ConnectionAdapter.instance_variable_set(:@default_cert_store, nil) end Kernel.srand config.seed end RSpec::Matchers.define :use_ssl do match(&:use_ssl?) end RSpec::Matchers.define :use_cert_store do |cert_store| match do |connection| connection.cert_store == cert_store end end httparty-0.18.1/spec/support/000077500000000000000000000000001367022441500161325ustar00rootroot00000000000000httparty-0.18.1/spec/support/ssl_test_helper.rb000066400000000000000000000030271367022441500216600ustar00rootroot00000000000000require 'pathname' module HTTParty module SSLTestHelper def ssl_verify_test(mode, ca_basename, server_cert_filename, options = {}, &block) options = { format: :json, timeout: 30 }.merge(options) if mode ca_path = File.expand_path("../../fixtures/ssl/generated/#{ca_basename}", __FILE__) raise ArgumentError.new("#{ca_path} does not exist") unless File.exist?(ca_path) options[mode] = ca_path end begin test_server = SSLTestServer.new( rsa_key: File.read(File.expand_path("../../fixtures/ssl/generated/server.key", __FILE__)), cert: File.read(File.expand_path("../../fixtures/ssl/generated/#{server_cert_filename}", __FILE__))) test_server.start if mode ca_path = File.expand_path("../../fixtures/ssl/generated/#{ca_basename}", __FILE__) raise ArgumentError.new("#{ca_path} does not exist") unless File.exist?(ca_path) return HTTParty.get("https://localhost:#{test_server.port}/", options, &block) else return HTTParty.get("https://localhost:#{test_server.port}/", options, &block) end ensure test_server.stop if test_server end test_server = SSLTestServer.new({ rsa_key: path.join('server.key').read, cert: path.join(server_cert_filename).read }) test_server.start HTTParty.get("https://localhost:#{test_server.port}/", options, &block) ensure test_server.stop if test_server end end end httparty-0.18.1/spec/support/ssl_test_server.rb000066400000000000000000000035611367022441500217120ustar00rootroot00000000000000require 'openssl' require 'socket' require 'thread' # NOTE: This code is garbage. It probably has deadlocks, it might leak # threads, and otherwise cause problems in a real system. It's really only # intended for testing HTTParty. class SSLTestServer attr_accessor :ctx # SSLContext object attr_reader :port def initialize(options = {}) @ctx = OpenSSL::SSL::SSLContext.new @ctx.cert = OpenSSL::X509::Certificate.new(options[:cert]) @ctx.key = OpenSSL::PKey::RSA.new(options[:rsa_key]) @ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # Don't verify client certificate @port = options[:port] || 0 @thread = nil @stopping_mutex = Mutex.new @stopping = false end def start @raw_server = TCPServer.new(@port) if @port == 0 @port = Socket.getnameinfo(@raw_server.getsockname, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV)[1].to_i end @ssl_server = OpenSSL::SSL::SSLServer.new(@raw_server, @ctx) @stopping_mutex.synchronize { return if @stopping @thread = Thread.new { thread_main } } nil end def stop @stopping_mutex.synchronize { return if @stopping @stopping = true } @thread.join end private def thread_main until @stopping_mutex.synchronize { @stopping } (rr, _, _) = select([@ssl_server.to_io], nil, nil, 0.1) next unless rr && rr.include?(@ssl_server.to_io) socket = @ssl_server.accept Thread.new { header = [] until (line = socket.readline).rstrip.empty? header << line end response = < HTTParty by John Nunemaker

Install

$ sudo gem install httparty

Some Quick Examples

The following is a simple example of wrapping Twitter's API for posting updates.

class Twitter
  include HTTParty
  base_uri 'twitter.com'
  basic_auth 'username', 'password'
end

Twitter.post('/statuses/update.json', query: {status: "It's an HTTParty and everyone is invited!"})

That is really it! The object returned is a ruby hash that is decoded from Twitter's json response. JSON parsing is used because of the .json extension in the path of the request. You can also explicitly set a format (see the examples).

That works and all but what if you don't want to embed your username and password in the class? Below is an example to fix that:

class Twitter
  include HTTParty
  base_uri 'twitter.com'

  def initialize(u, p)
    @auth = {username: u, password: p}
  end

  def post(text)
    options = { query: {status: text}, basic_auth: @auth }
    self.class.post('/statuses/update.json', options)
  end
end

Twitter.new('username', 'password').post("It's an HTTParty and everyone is invited!")

More Examples: There are several examples in the gem itself.

Support

Conversations welcome in the google group and bugs/features over at Github.