├── .gitignore ├── .mailmap ├── .travis.yml ├── CONTRIBUTING.mkdn ├── Changes ├── Makefile.PL ├── README.mkdn ├── corpus ├── auth-01.txt ├── auth-02.txt ├── auth-03.txt ├── auth-04.txt ├── auth-05.txt ├── cookies-01.txt ├── cookies-02.txt ├── cookies-03.txt ├── cookies-04.txt ├── cookies-05.txt ├── cookies-06.txt ├── cookies-07.txt ├── delete-01.txt ├── form-01.txt ├── form-02.txt ├── form-03.txt ├── form-04.txt ├── form-05.txt ├── get-01.txt ├── get-02.txt ├── get-03.txt ├── get-04.txt ├── get-05.txt ├── get-06.txt ├── get-07.txt ├── get-08.txt ├── get-09.txt ├── get-10.txt ├── get-11.txt ├── get-12.txt ├── get-13.txt ├── get-14.txt ├── get-15.txt ├── get-16.txt ├── get-17.txt ├── get-18.txt ├── get-19.txt ├── get-20.txt ├── get-21.txt ├── get-22.txt ├── get-23.txt ├── head-01.txt ├── keepalive-01.txt ├── keepalive-02.txt ├── keepalive-03.txt ├── keepalive-04.txt ├── keepalive-05.txt ├── mirror-01.txt ├── mirror-02.txt ├── mirror-03.txt ├── mirror-04.txt ├── mirror-05.txt ├── post-01.txt ├── post-02.txt ├── proxy-auth-01.txt ├── put-01.txt ├── put-02.txt ├── put-03.txt ├── put-04.txt ├── put-05.txt ├── put-06.txt ├── put-07.txt ├── put-08.txt ├── redirect-01.txt ├── redirect-02.txt ├── redirect-03.txt ├── redirect-04.txt ├── redirect-05.txt ├── redirect-06.txt ├── redirect-07.txt ├── redirect-08.txt ├── redirect-09.txt ├── redirect-10.txt └── snake-oil.crt ├── dist.ini ├── eg ├── get.pl ├── mirror.pl └── post.pl ├── lib └── HTTP │ └── Tiny.pm ├── perlcritic.rc └── t ├── 000_load.t ├── 001_api.t ├── 002_croakage.t ├── 003_agent.t ├── 004_timeout.t ├── 010_url.t ├── 020_headers.t ├── 030_response.t ├── 040_content.t ├── 050_chunked_body.t ├── 060_http_date.t ├── 070_cookie_jar.t ├── 100_get.t ├── 101_head.t ├── 102_put.t ├── 103_delete.t ├── 104_post.t ├── 110_mirror.t ├── 130_redirect.t ├── 140_proxy.t ├── 141_no_proxy.t ├── 150_post_form.t ├── 160_cookies.t ├── 161_basic_auth.t ├── 162_proxy_auth.t ├── 170_keepalive.t ├── 180_verify_SSL.t ├── 200_live.t ├── 200_live_local_ip.t ├── 210_live_ssl.t ├── BrokenCookieJar.pm ├── SimpleCookieJar.pm └── Util.pm /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.old 3 | *.tmp 4 | *.tar.gz 5 | *.rej 6 | *.orig 7 | *~ 8 | /.build 9 | /Build 10 | /Build.bat 11 | /Makefile 12 | /_build 13 | /blib 14 | /cover_db 15 | /pm_to_blib 16 | /PM_to_blib 17 | /MYMETA.yml 18 | /*.tar.gz 19 | /HTTP-Tiny-* 20 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Brad Gilbert 2 | Christian Hansen 3 | Lukas Eklund 4 | Mike Doherty 5 | Mike Doherty 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | arch: 2 | - amd64 3 | - ppc64le 4 | language: perl 5 | script: prove -lr t 6 | install: 7 | - cpanm -n -q --skip-satisfied --installdeps . 8 | perl: 9 | - "5.26" 10 | - "5.24" 11 | - "5.22" 12 | - "5.20" 13 | - "5.18" 14 | - "5.16" 15 | - "5.14" 16 | - "5.12" 17 | - "5.10" 18 | - "5.8" 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.mkdn: -------------------------------------------------------------------------------- 1 | ## HOW TO CONTRIBUTE 2 | 3 | Thank you for considering contributing to this distribution. This file 4 | contains instructions that will help you work with the source code. 5 | 6 | The distribution is managed with Dist::Zilla. This means than many of the 7 | usual files you might expect are not in the repository, but are generated at 8 | release time, as is much of the documentation. Some generated files are 9 | kept in the repository as a convenience (e.g. Makefile.PL or cpanfile). 10 | 11 | Generally, **you do not need Dist::Zilla to contribute patches**. You do need 12 | Dist::Zilla to create a tarball. See below for guidance. 13 | 14 | ### Getting dependencies 15 | 16 | If you have App::cpanminus 1.6 or later installed, you can use `cpanm` to 17 | satisfy dependencies like this: 18 | 19 | $ cpanm --installdeps . 20 | 21 | Otherwise, look for either a `Makefile.PL` or `cpanfile` file for 22 | a list of dependencies to satisfy. 23 | 24 | ### Running tests 25 | 26 | You can run tests directly using the `prove` tool: 27 | 28 | $ prove -l 29 | $ prove -lv t/some_test_file.t 30 | 31 | For most of my distributions, `prove` is entirely sufficient for you to test any 32 | patches you have. I use `prove` for 99% of my testing during development. 33 | 34 | ### Code style and tidying 35 | 36 | Please try to match any existing coding style. If there is a `.perltidyrc` 37 | file, please install Perl::Tidy and use perltidy before submitting patches. 38 | 39 | If there is a `tidyall.ini` file, you can also install Code::TidyAll and run 40 | `tidyall` on a file or `tidyall -a` to tidy all files. 41 | 42 | ### Patching documentation 43 | 44 | Much of the documentation Pod is generated at release time. Some is 45 | generated boilerplate; other documentation is built from pseudo-POD 46 | directives in the source like C<=method> or C<=func>. 47 | 48 | If you would like to submit a documentation edit, please limit yourself to 49 | the documentation you see. 50 | 51 | If you see typos or documentation issues in the generated docs, please 52 | email or open a bug ticket instead of patching. 53 | 54 | ### Installing and using Dist::Zilla 55 | 56 | Dist::Zilla is a very powerful authoring tool, optimized for maintaining a 57 | large number of distributions with a high degree of automation, but it has a 58 | large dependency chain, a bit of a learning curve and requires a number of 59 | author-specific plugins. 60 | 61 | To install it from CPAN, I recommend one of the following approaches for 62 | the quickest installation: 63 | 64 | # using CPAN.pm, but bypassing non-functional pod tests 65 | $ cpan TAP::Harness::Restricted 66 | $ PERL_MM_USE_DEFAULT=1 HARNESS_CLASS=TAP::Harness::Restricted cpan Dist::Zilla 67 | 68 | # using cpanm, bypassing *all* tests 69 | $ cpanm -n Dist::Zilla 70 | 71 | In either case, it's probably going to take about 10 minutes. Go for a walk, 72 | go get a cup of your favorite beverage, take a bathroom break, or whatever. 73 | When you get back, Dist::Zilla should be ready for you. 74 | 75 | Then you need to install any plugins specific to this distribution: 76 | 77 | $ cpan `dzil authordeps` 78 | $ dzil authordeps | cpanm 79 | 80 | Once installed, here are some dzil commands you might try: 81 | 82 | $ dzil build 83 | $ dzil test 84 | $ dzil xtest 85 | 86 | You can learn more about Dist::Zilla at http://dzil.org/ 87 | 88 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | Release notes for HTTP-Tiny 2 | 3 | {{$NEXT}} 4 | 5 | 0.086 2023-06-22 10:06:37-04:00 America/New_York 6 | 7 | [FIXED] 8 | 9 | - Fix code to use `$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}` as documented. 10 | 11 | 0.084 2023-06-14 06:35:01-04:00 America/New_York 12 | 13 | - No changes from 0.083-TRIAL. 14 | 15 | 0.083 2023-06-11 07:05:45-04:00 America/New_York (TRIAL RELEASE) 16 | 17 | [!!! SECURITY !!!] 18 | 19 | - Changes the `verify_SSL` default parameter from `0` to `1`. 20 | Fixes CVE-2023-31486. 21 | 22 | - `$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}` can be used to restore the 23 | old default if required. 24 | 25 | 0.082 2022-07-25 09:45:34-04:00 America/New_York 26 | 27 | - No changes from 0.081-TRIAL. 28 | 29 | 0.081 2022-07-17 09:01:51-04:00 America/New_York (TRIAL RELEASE) 30 | 31 | [FIXED] 32 | 33 | - No longer deletes the 'headers' key from post_form arguments hashref. 34 | 35 | [DOCS] 36 | 37 | - Noted that request/response content are handled as raw bytes. 38 | 39 | 0.080 2021-11-05 08:15:46-04:00 America/New_York 40 | 41 | - No changes from 0.079-TRIAL. 42 | 43 | 0.079 2021-11-04 12:33:43-04:00 America/New_York (TRIAL RELEASE) 44 | 45 | [FIXED] 46 | 47 | - Fixed uninitialized value warnings on older Perls when the REQUEST_METHOD 48 | environment variable is set and CGI_HTTP_PROXY is not. 49 | 50 | 0.078 2021-08-02 09:24:03-04:00 America/New_York 51 | 52 | - No changes from 0.077-TRIAL. 53 | 54 | 0.077 2021-07-22 13:07:14-04:00 America/New_York (TRIAL RELEASE) 55 | 56 | [ADDED] 57 | 58 | - Added a `patch` helper method for the HTTP `PATCH` verb. 59 | 60 | - If the REQUEST_METHOD environment variable is set, then CGI_HTTP_PROXY 61 | replaces HTTP_PROXY. 62 | 63 | [FIXED] 64 | 65 | - Unsupported scheme errors early without giving an uninitialized value 66 | warning first. 67 | 68 | - Sends Content-Length: 0 on empty body PUT/POST. This is not in the spec, 69 | but some servers require this. 70 | 71 | - Allows optional status line reason, as clarified in RFC 7230. 72 | 73 | - Ignore SIGPIPE on reads as well as writes, as IO::Socket::SSL says that 74 | SSL reads can also send writes as a side effect. 75 | 76 | - Check if a server has closed a connection before preserving it for reuse. 77 | 78 | [DOCS] 79 | 80 | - Clarified that exceptions/errors result in 599 status codes. 81 | 82 | [PREREQS] 83 | 84 | - Optional IO::Socket::IP prereq must be at least version 0.32 to be used. 85 | This ensures correct timeout support. 86 | 87 | 0.076 2018-08-05 21:07:38-04:00 America/New_York 88 | 89 | - No changes from 0.075-TRIAL. 90 | 91 | 0.075 2018-08-01 07:03:36-04:00 America/New_York (TRIAL RELEASE) 92 | 93 | [CHANGED] 94 | 95 | - The 'peer' option now also can take a code reference 96 | 97 | 0.074 2018-07-30 15:35:44-04:00 America/New_York 98 | 99 | - No changes from 0.073-TRIAL. 100 | 101 | 0.073 2018-07-24 11:33:53-04:00 America/New_York (TRIAL RELEASE) 102 | 103 | 0.071 never made it to CPAN; skipping to 0.073 104 | 105 | [DOCS] 106 | 107 | - Documented 'protocol' field in response hash. 108 | 109 | 0.071 2018-04-22 14:45:43+02:00 Europe/Oslo (TRIAL RELEASE) 110 | 111 | [DOCS] 112 | 113 | - Documented that method argument to request() is case-sensitive. 114 | 115 | [INTERNAL] 116 | 117 | - Minor regex cleanup 118 | 119 | - Updated .travis.yml for recent Perls 120 | 121 | 0.070 2016-10-09 23:23:28-04:00 America/New_York 122 | 123 | - No changes from 0.069-TRIAL. 124 | 125 | 0.069 2016-10-05 11:35:58-04:00 America/New_York (TRIAL RELEASE) 126 | 127 | [INTERNAL] 128 | 129 | - Lazy load Carp only if needed. 130 | 131 | 0.068 2016-09-23 16:10:03-04:00 America/New_York 132 | 133 | - No changes from 0.067-TRIAL. 134 | 135 | 0.067 2016-09-14 11:43:14-04:00 America/New_York (TRIAL RELEASE) 136 | 137 | [FIXED] 138 | 139 | - Includes redirect history when issuing a 599 internal error. 140 | 141 | 0.065 2016-09-09 22:42:43-04:00 America/New_York (TRIAL RELEASE) 142 | 143 | [TESTS] 144 | 145 | - Try harder to clean up environment in t/140_proxy.t (needed for VMS) 146 | 147 | 0.064 2016-08-16 21:37:51-04:00 America/New_York 148 | 149 | - No changes from 0.063-TRIAL 150 | 151 | 0.063 2016-08-08 12:18:03-04:00 America/New_York (TRIAL RELEASE) 152 | 153 | [FIXED] 154 | 155 | - Fixed chunked transfer encoding, which previously omitted a trailing 156 | CRLF. 157 | 158 | 0.061 2016-08-05 12:10:19-04:00 America/New_York (TRIAL RELEASE) 159 | 160 | [FIXED] 161 | 162 | - Avoid overwriting 'If-Modified-Since' header in mirror() if 163 | the header already exists in something other than lower-case. 164 | 165 | [TESTS] 166 | 167 | - Normalize CRLF when reading test data files in t\150-post_form.t 168 | on Win32 169 | 170 | 0.059 2016-07-29 16:10:32-04:00 America/New_York (TRIAL RELEASE) 171 | 172 | [FIXED] 173 | 174 | - Timeout can now be set as a constructor argument again. 175 | 176 | - CVE-2016-1238: avoid loading optional modules from 177 | @INC path with `.` at the end. 178 | 179 | [TESTS] 180 | 181 | - Updated tests for a future perl which may omit `.` from 182 | the list of directories in @INC by default. 183 | 184 | 0.058 2016-05-03 11:29:57-04:00 America/New_York 185 | 186 | - No changes from 0.057 187 | 188 | 0.057 2016-04-18 10:17:00-04:00 America/New_York (TRIAL RELEASE) 189 | 190 | [ADDED] 191 | 192 | - Added support for the SSL_CERT_FILE environment variable. 193 | 194 | - Added 'peer' attribute to force a connection to a particular 195 | server. 196 | 197 | - Added 'connected' method to allow introspection of persistent 198 | connections. 199 | 200 | - An array reference of redirection result hash references is included 201 | in the final response hash reference (but only if redirects occur). 202 | 203 | [CHANGED] 204 | 205 | - Because folded headers are obsoleted in the revised RFCs, if CRLF is 206 | found in header values followed by one or more spaces, they are all 207 | replaced by a single space. 208 | 209 | [FIXED] 210 | 211 | - Per the RFC, control headers are now sent first before other headers 212 | (which are sent in arbitrary order). 213 | 214 | - Only well-known headers have their case canonicalized; all other 215 | headers are sent in the case provided by the user. 216 | 217 | - The 'keep_alive' option now also sets the SO_KEEPALIVE option 218 | on the underlying socket to help with long-lived, idle connections. 219 | 220 | - Request header field values are now validated against the RFC rules 221 | (i.e. empty or else space-or-tab separated tokens of printable 222 | characters). 223 | 224 | 0.056 2015-05-19 06:00:40-04:00 America/New_York 225 | 226 | - No changes from 0.055 227 | 228 | 0.055 2015-05-07 18:13:41-04:00 America/New_York (TRIAL RELEASE) 229 | 230 | [ADDED] 231 | 232 | - Added 'can_ssl' method to detect SSL support before trying and 233 | failing with a fatal exception. 234 | 235 | - Added support for 308 redirects 236 | 237 | [FIXED] 238 | 239 | - When specifying a custom CA file, if that file is missing or 240 | unreadable, HTTP::Tiny will no longer fall back to a default CA 241 | 242 | [DOCUMENTED] 243 | 244 | - Noted units are bytes for max_size 245 | 246 | 0.054 2015-01-27 07:18:19-05:00 America/New_York 247 | 248 | [ADDED] 249 | 250 | - Added more fallback paths to find CA files (thanks golang) 251 | 252 | [DOCUMENTED] 253 | 254 | - Fixed a typo 255 | 256 | 0.053 2014-12-11 23:42:17-05:00 America/New_York 257 | 258 | [FIXED] 259 | 260 | - Defended tests against HTTP_PROXY set in the environment 261 | 262 | 0.052 2014-12-11 15:23:54-05:00 America/New_York 263 | 264 | [CHANGED] 265 | 266 | - Proxy allowed from environment variable HTTP_PROXY (uppercase) unless 267 | REQUEST_METHOD is also set. 268 | 269 | 0.051 2014-11-17 22:58:44-05:00 America/New_York 270 | 271 | [FIXED] 272 | 273 | - Checks for threads without loading threads.pm 274 | 275 | 0.050 2014-09-23 15:30:18-04:00 America/New_York 276 | 277 | [FIXED] 278 | 279 | - Fixed CONNECT requests for some proxies 280 | 281 | 0.049 2014-09-02 11:20:07-04:00 America/New_York 282 | 283 | [FIXED] 284 | 285 | - 'keep_alive' is now fork-safe and thread-safe 286 | 287 | 0.048 2014-08-21 13:19:51-04:00 America/New_York 288 | 289 | [FIXED] 290 | 291 | - Protected proxy tests from ALL_PROXY in the environment 292 | 293 | 0.047 2014-07-29 14:09:05-04:00 America/New_York 294 | 295 | [CHANGED] 296 | 297 | - Updated Mozilla::CA module recommendation version to 20130114 298 | 299 | [FIXED] 300 | 301 | - Fixed t/00-report-prereqs.t when CPAN::Meta is not installed 302 | 303 | 0.046 2014-07-21 10:32:32-04:00 America/New_York 304 | 305 | [FIXED] 306 | 307 | - Empty header fields are now allowed; headers with the 'undef' value 308 | will be rendered as an empty header. 309 | 310 | [DOCUMENTED] 311 | 312 | - Updated HTTP/1.1 spec description from RFC 2616 to RFC 7230-7235 313 | 314 | 0.045 2014-07-19 23:17:28-04:00 America/New_York (TRIAL RELEASE) 315 | 316 | [FIXED] 317 | 318 | - Fixed t/002_croakage.t for various operating systems. 319 | 320 | 0.044 2014-07-16 23:46:09-04:00 America/New_York 321 | 322 | [CHANGED] 323 | 324 | - Providing a custom 'Host' header is now a fatal exception. Previously, it 325 | was silently ignored, as the RFC mandates that Host be set from the 326 | URL, but ignoring it could lead to unexpected, confusing errors. 327 | 328 | - optimized URL splitting 329 | 330 | - Passing 'undef' for any proxy attribute will prevent HTTP::Tiny from 331 | setting the proxy from the environment. 332 | 333 | 0.043 2014-02-20 20:40:23-05:00 America/New_York 334 | 335 | [FIXED] 336 | 337 | - Does not send absolute request URI when tunneling SSL via proxy 338 | 339 | - Fixes regression in setting host name to verify SSL 340 | 341 | - Protects tests from https_proxy and all_proxy when doing mock testing 342 | 343 | 0.042 2014-02-18 11:23:17EST-0500 America/New_York 344 | 345 | [ADDED] 346 | 347 | - If IO::Socket::IP 0.25+ is installed, HTTP::Tiny will use it for 348 | transparent IPv4 or IPv6 support. 349 | 350 | 0.041 2014-02-17 13:07:54-05:00 America/New_York 351 | 352 | [no code change, only an amended Changes file] 353 | 354 | [INCOMPATIBLE CHANGES (from 0.039)] 355 | 356 | - The 'proxy' attribute no longer takes precedence over the 357 | 'http_proxy' environment variable. With the addition of http_proxy 358 | and https_proxy attributes (and corresponding environment variable 359 | defaults), the legacy 'proxy' attribute now maps to the 360 | all_proxy/ALL_PROXY environment variable and only takes effect when 361 | other proxy attributes are not defined. 362 | 363 | [ADDED (since 0.039)] 364 | 365 | - Added 'keep_alive' attribute for single-server persistent connections 366 | (Clinton Gormley) 367 | 368 | - Added support for Basic authorization with proxies 369 | 370 | - Added support for https proxies via CONNECT 371 | 372 | [FIXED (since 0.039)] 373 | 374 | - Requests are made with one less write for lower latency (Martin 375 | Evans) 376 | 377 | 0.040 2014-02-17 13:02:47-05:00 America/New_York 378 | 379 | [INCOMPATIBLE CHANGES] 380 | 381 | - The 'proxy' attribute no longer takes precedence over the 382 | 'http_proxy' environment variable. With the addition of http_proxy 383 | and https_proxy attributes (and corresponding environment variable 384 | defaults), the legacy 'proxy' attribute now maps to the 385 | all_proxy/ALL_PROXY environment variable and only takes effect when 386 | other proxy attributes are not defined. 387 | 388 | [ADDED] 389 | 390 | - Added support for Basic authorization with proxies 391 | 392 | - Added support for https proxies via CONNECT 393 | 394 | 0.039 2013-11-27 19:48:29 America/New_York 395 | 396 | [FIXED] 397 | 398 | - Temporary file creating during mirror() is now opened with O_EXCL 399 | for added security 400 | 401 | 0.038 2013-11-18 12:56:26 America/New_York 402 | 403 | [FIXED] 404 | 405 | - Fixed a bug where authentication parameters in the URL would override 406 | an existing Authorization header 407 | 408 | 0.037 2013-10-28 13:26:21 America/New_York 409 | 410 | [FIXED] 411 | 412 | - Basic authentication in the URL is now unescaped before being encoded 413 | into the authentication header 414 | 415 | [DOCUMENTED] 416 | 417 | - Added HTTP::Tiny::UA to SEE ALSO and suggested it as the appropriate 418 | place for new features 419 | 420 | 0.036 2013-09-25 12:10:06 America/New_York 421 | 422 | [FIXED] 423 | 424 | - Compile test could hang on Windows 425 | 426 | [PREREQS] 427 | 428 | - Dropped configure_requires for ExtUtils::MakeMaker to 6.17 429 | 430 | [META] 431 | 432 | - Updated support files 433 | 434 | 0.035 2013-09-10 12:29:28 America/New_York 435 | 436 | [CHANGED] 437 | 438 | - Encoded from data from 'post_form' preserves term order if data is 439 | provided as an array reference. (They are still sorted for consistency 440 | if provided as a hash reference.) 441 | 442 | 0.034 2013-06-26 19:02:25 America/New_York 443 | 444 | [ADDED] 445 | 446 | - Added support for 'Basic' authorization from 447 | user:password parameters in the URL 448 | 449 | 0.033 2013-06-21 06:26:51 America/New_York 450 | 451 | [FIXED] 452 | 453 | - Modifying the 'agent' attribute with the accessor will append the 454 | default agent string, just like setting it during construction 455 | 456 | 0.032 2013-06-20 11:41:24 America/New_York 457 | 458 | [ADDED] 459 | 460 | - Added 'no_proxy' attribute, defaulting to $ENV{no_proxy} 461 | 462 | 0.031 2013-06-16 23:18:18 America/New_York 463 | 464 | [FIXED] 465 | 466 | - Fixed bug receiving 0-length content bodies 467 | 468 | 0.030 2013-06-13 11:46:15 America/New_York 469 | 470 | [FIXED] 471 | 472 | - Requests with the empty string as body content no longer generate 473 | 'content-type' and 'content-length' headers. 474 | 475 | 0.029 2013-04-17 13:49:07 America/New_York 476 | 477 | [FIXED] 478 | 479 | - Checks for new enough OpenSSL library before using SNI (otherwise 480 | IO::Socket::SSL throws warnings) 481 | 482 | 0.028 2013-03-05 14:11:57 America/New_York 483 | 484 | [SUPPORT] 485 | 486 | - Fix repository/issue links to reflect proper repo name 487 | 488 | 0.027 2013-03-05 12:02:58 America/New_York 489 | 490 | [SUPPORT] 491 | 492 | - Changed metadata to point to the chansen github repository 493 | for code and issues 494 | 495 | [DOCUMENTATION] 496 | 497 | - Added hyperlink for HTTP::CookieJar 498 | 499 | 0.026 2013-03-04 22:53:39 America/New_York 500 | 501 | [ADDED] 502 | 503 | - Added cookie support if an HTTP::CookieJar object is provided in the 504 | 'cookie_jar' attribute [Edward Zborowski] 505 | 506 | 0.025 2012-12-26 12:09:43 America/New_York 507 | 508 | [ADDED] 509 | 510 | - Agent string appends default if it ends in a space, just like LWP 511 | [Chris Weyl] 512 | 513 | 0.024 2012-10-09 20:44:53 America/New_York 514 | 515 | [ADDED] 516 | 517 | - SSL connections now auto-retry I/O after SSL renegotiation [Alan 518 | Gardner] 519 | 520 | [FIXED] 521 | 522 | - User-specified CA bundles take precedence over Mozilla::CA [Alan 523 | Gardner] 524 | 525 | [PREREQS] 526 | 527 | - SSL support now requires Net::SSLeay 1.49 or greater to support 528 | auto-retry [Mike Doherty] 529 | 530 | - Downgraded IO::Socket::SSL and related prereqs to 'suggests' again 531 | 532 | 0.023 2012-09-19 09:55:46 America/New_York 533 | 534 | [PREREQS] 535 | 536 | - IO::Socket::SSL and related prereqs changed to 'required' for dev 537 | release to get better failure diagnostics from CPAN Testers 538 | 539 | [TESTING] 540 | 541 | - Skip live SSL testing unless IO::Socket::SSL 1.56+ installed 542 | 543 | 0.022 2012-06-01 23:31:40 America/New_York 544 | 545 | [ADDED] 546 | 547 | - Supports local_address option to set local socket interface 548 | [Chris Nehren, David Golden] 549 | 550 | 0.021 2012-05-15 22:38:57 America/New_York 551 | 552 | [TESTING] 553 | 554 | - Skip live SSL testing if $ENV{http_proxy} is set 555 | 556 | 0.020 2012-05-14 15:24:37 America/New_York 557 | 558 | [TESTING] 559 | 560 | - Capture prerequisite versions under AUTOMATED_TESTING to help 561 | chase down some failures from CPAN Testers 562 | 563 | 0.019 2012-05-14 07:14:00 America/New_York 564 | 565 | [ADDED] 566 | 567 | - Require IO::Socket::SSL 1.56 (which added SSL_hostname support) when 568 | doing HTTPS. [Mike Doherty] 569 | 570 | [TESTING] 571 | 572 | - Provide better diagnostic output in t/210_live_ssl.t [Mike 573 | Doherty] 574 | 575 | 0.018 2012-04-18 09:39:50 America/New_York 576 | 577 | [ADDED] 578 | 579 | - Add verify_SSL option to do more secure SSL operations, incl. 580 | attempting to validate against a CA bundle (Mozilla::CA 581 | recommended, but will attempt to find some OS bundles). Also 582 | add SSL_opts, which passes through IO::Socket::SSL's SSL_* 583 | options to control SSL verification. (GH #6, #9) [Mike Doherty] 584 | 585 | 586 | - Reponse hashref includes final URL (including any redirections) 587 | [Lukas Eklund] 588 | 589 | 0.017 2012-02-22 21:57:37 EST5EDT 590 | 591 | [DOCUMENTATION] 592 | 593 | - Clarified how max_size exceptions work [rt.cpan.org #75142] 594 | 595 | - Clarify that 2XX is success for most methods (except mirror 596 | where 304 is also success) [rt.cpan.org #75141] 597 | 598 | 0.016 2011-10-26 23:05:50 America/New_York 599 | 600 | [BUG FIXES] 601 | 602 | - Fixed Perl 5.6 compatibility by emulating utf8::encode [David Golden] 603 | 604 | 0.015 2011-10-26 16:42:26 America/New_York 605 | 606 | [BUG FIXES] 607 | 608 | - Make sure PERL_UNICODE doesn't affect PUT test data [Tony Cook] 609 | 610 | [DOCUMENTATION] 611 | 612 | - Fixed typo 613 | 614 | 0.014 2011-10-20 13:54:13 America/New_York 615 | 616 | [NEW FEATURES] 617 | 618 | - Adds additional shorthand methods for all common HTTP verbs 619 | (HEAD, PUT, POST, DELETE) [David Golden] 620 | 621 | - post_form() method for POST-ing x-www-form-urlencoded data 622 | [David Golden] 623 | 624 | - www_form_urlencode() utility method [David Golden] 625 | 626 | 0.013 2011-07-17 23:14:22 America/New_York 627 | 628 | [NEW FEATURES] 629 | 630 | - $ENV{http_proxy} support added [Claes Jakobsson] 631 | 632 | [OTHER] 633 | 634 | - Internal/private errors converted from "croak" to "die" as internal 635 | errors are caught by "eval" 636 | 637 | 0.012 2011-03-31 15:48:02 America/New_York 638 | 639 | [BUG FIXES] 640 | 641 | - mirror() now uses binmode during output (RT #67118) [Serguei Trouchelle] 642 | 643 | [DOCUMENTATION] 644 | 645 | - noted that SSL certificates are not verified against CA's 646 | (RT #66907) 647 | 648 | 0.011 2011-03-19 20:48:39 America/New_York 649 | 650 | [BUG FIXES] 651 | 652 | - Made t/000_load.t less verbose under harness (RT#65507) [Dave Mitchell] 653 | 654 | - Removed 'Errno' as an explicit prefix (it is a core module, but not 655 | indexed by PAUSE, which might confuse some installers) 656 | 657 | 0.010 2011-02-04 02:45:31 EST5EDT 658 | 659 | [BUG FIXES] 660 | 661 | - Fixed test errors on VMS (RT#65430) [Craig Berry] 662 | 663 | 0.009 2011-01-17 16:29:22 EST5EDT 664 | 665 | - Added workaround for IO::Socket::SSL certificate verification bug 666 | 667 | - Minor documentation improvements 668 | 669 | - POST example added to the eg/ directory in the distribution tarball 670 | 671 | 0.008 2011-01-14 06:34:55 EST5EDT 672 | 673 | - Added support for direct 'https' connections if IO::Socket::SSL 674 | is installed 675 | 676 | - Added support for a callback to provide trailing headers for 677 | chunked transfer encoding 678 | 679 | - Data callbacks receive the response hashref as a second argument 680 | for greater flexibility 681 | 682 | - Additional limitations documented 683 | 684 | 0.007 2011-01-12 04:56:16 EST5EDT 685 | 686 | - Added support for redirecting 303 and 307 response codes 687 | 688 | - Retry (once) a request that fails due to a closed socket 689 | (per RFC2616 8.1.4) 690 | 691 | - Automatically sets request Content-Type to 'application/octet-stream' 692 | if there is content the user has not defined its type 693 | 694 | - Trailing headers from chunked transfer encoding are now merged 695 | into the response headers instead of ignored 696 | 697 | - Improved handling of malformed or unsupported HTTP protocols 698 | 699 | - Expanded http:///.../ as http://localhost/.../ and set Host header 700 | 701 | - Documented that URL's must be escaped/encoded 702 | 703 | - Documented that the headers hash option may contain an array reference 704 | to output multiple values of the same header field 705 | 706 | - Improved documentation of limitations 707 | 708 | - Added numerous new tests to ensure compliance with the HTTP/1.1 spec 709 | 710 | 0.006 2011-01-10 07:28:11 EST5EDT 711 | 712 | - Transfer-Encodings are case insensitive 713 | 714 | - Add additional test for proper behavior when both Content-Length 715 | and Transfer-Encoding headers are received 716 | 717 | 0.005 2011-01-08 06:32:05 EST5EDT 718 | 719 | - Fixed bug getting content for servers which do not sent Content-Length 720 | 721 | - Add test coverage for get(), mirror() and request() 722 | 723 | - Add test coverage for requests with static and generated content 724 | 725 | 0.004 2010-12-15 22:53:59 EST5EDT 726 | 727 | - Renamed 'ok' response field to 'success' 728 | 729 | - Handle all required HTTP/1.1 date formats 730 | 731 | - Documented how callbacks are supposed to work 732 | 733 | 0.003 2010-12-15 12:30:42 EST5EDT 734 | 735 | - Added 'ok' response field to simplify checking success 736 | 737 | - Added a 'mirror' method mirror content to a file, but shortcut 738 | if not modified 739 | 740 | 0.002 2010-12-13 21:59:39 EST5EDT 741 | 742 | - Added some initial documentation 743 | 744 | - Skips utf8::* code on Perls older than 5.8 745 | 746 | 0.001 2010-12-11 07:59:16 EST5EDT 747 | 748 | - Initial CPAN release 749 | 750 | # vim: ts=2 sts=2 sw=2 et: 751 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use 5.006; 5 | 6 | use ExtUtils::MakeMaker 6.17; 7 | 8 | my %WriteMakefileArgs = ( 9 | "ABSTRACT" => "A small, simple, correct HTTP/1.1 client", 10 | "AUTHOR" => "Christian Hansen , David Golden ", 11 | "CONFIGURE_REQUIRES" => { 12 | "ExtUtils::MakeMaker" => "6.17" 13 | }, 14 | "DISTNAME" => "HTTP-Tiny", 15 | "LICENSE" => "perl", 16 | "MIN_PERL_VERSION" => "5.006", 17 | "NAME" => "HTTP::Tiny", 18 | "PREREQ_PM" => { 19 | "Carp" => 0, 20 | "Fcntl" => 0, 21 | "IO::Socket" => 0, 22 | "MIME::Base64" => 0, 23 | "Socket" => 0, 24 | "Time::Local" => 0, 25 | "bytes" => 0, 26 | "strict" => 0, 27 | "warnings" => 0 28 | }, 29 | "TEST_REQUIRES" => { 30 | "Data::Dumper" => 0, 31 | "Exporter" => 0, 32 | "ExtUtils::MakeMaker" => 0, 33 | "File::Basename" => 0, 34 | "File::Spec" => 0, 35 | "File::Temp" => 0, 36 | "IO::Dir" => 0, 37 | "IO::File" => 0, 38 | "IO::Socket::INET" => 0, 39 | "IPC::Cmd" => 0, 40 | "Test::More" => "0.96", 41 | "lib" => 0, 42 | "open" => 0 43 | }, 44 | "VERSION" => "0.087", 45 | "test" => { 46 | "TESTS" => "t/*.t" 47 | } 48 | ); 49 | 50 | 51 | my %FallbackPrereqs = ( 52 | "Carp" => 0, 53 | "Data::Dumper" => 0, 54 | "Exporter" => 0, 55 | "ExtUtils::MakeMaker" => 0, 56 | "Fcntl" => 0, 57 | "File::Basename" => 0, 58 | "File::Spec" => 0, 59 | "File::Temp" => 0, 60 | "IO::Dir" => 0, 61 | "IO::File" => 0, 62 | "IO::Socket" => 0, 63 | "IO::Socket::INET" => 0, 64 | "IPC::Cmd" => 0, 65 | "MIME::Base64" => 0, 66 | "Socket" => 0, 67 | "Test::More" => "0.96", 68 | "Time::Local" => 0, 69 | "bytes" => 0, 70 | "lib" => 0, 71 | "open" => 0, 72 | "strict" => 0, 73 | "warnings" => 0 74 | ); 75 | 76 | 77 | unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { 78 | delete $WriteMakefileArgs{TEST_REQUIRES}; 79 | delete $WriteMakefileArgs{BUILD_REQUIRES}; 80 | $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; 81 | } 82 | 83 | delete $WriteMakefileArgs{CONFIGURE_REQUIRES} 84 | unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; 85 | 86 | WriteMakefile(%WriteMakefileArgs); 87 | -------------------------------------------------------------------------------- /README.mkdn: -------------------------------------------------------------------------------- 1 | CONTRIBUTING.mkdn -------------------------------------------------------------------------------- /corpus/auth-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://foo:bar@example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | Authorization: Basic Zm9vOmJhcg== 11 | 12 | ---------- 13 | HTTP/1.1 200 OK 14 | Date: Thu, 03 Feb 1994 00:00:00 GMT 15 | Content-Type: text/plain 16 | Content-Length: 42 17 | 18 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 19 | -------------------------------------------------------------------------------- /corpus/auth-02.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://Aladdin:open sesame@example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== 11 | 12 | ---------- 13 | HTTP/1.1 200 OK 14 | Date: Thu, 03 Feb 1994 00:00:00 GMT 15 | Content-Type: text/plain 16 | Content-Length: 42 17 | 18 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 19 | -------------------------------------------------------------------------------- /corpus/auth-03.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://foo:bar@example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | expected_url 6 | http://example.com/index2.html 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | Authorization: Basic Zm9vOmJhcg== 13 | 14 | ---------- 15 | HTTP/1.1 302 Found 16 | Date: Thu, 03 Feb 1994 00:00:00 GMT 17 | Content-Type: text/html 18 | Content-Length: 53 19 | Location: http://example.com/index2.html 20 | 21 | redirect 22 | 23 | ---------- 24 | GET /index2.html HTTP/1.1 25 | Host: example.com 26 | Connection: close 27 | User-Agent: HTTP-Tiny/VERSION 28 | 29 | ---------- 30 | HTTP/1.1 200 OK 31 | Date: Thu, 03 Feb 1994 00:00:00 GMT 32 | Content-Type: text/plain 33 | Content-Length: 42 34 | 35 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 36 | 37 | -------------------------------------------------------------------------------- /corpus/auth-04.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://rjbs%40cpan.org:password@example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | Authorization: Basic cmpic0BjcGFuLm9yZzpwYXNzd29yZA== 11 | 12 | ---------- 13 | HTTP/1.1 200 OK 14 | Date: Thu, 03 Feb 1994 00:00:00 GMT 15 | Content-Type: text/plain 16 | Content-Length: 42 17 | 18 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 19 | -------------------------------------------------------------------------------- /corpus/auth-05.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://foo:bar@example.com/index.html 3 | headers 4 | authorization: Whatever 5 | expected 6 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | Authorization: Whatever 13 | 14 | ---------- 15 | HTTP/1.1 200 OK 16 | Date: Thu, 03 Feb 1994 00:00:00 GMT 17 | Content-Type: text/plain 18 | Content-Length: 42 19 | 20 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 21 | -------------------------------------------------------------------------------- /corpus/cookies-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | ---------- 4 | GET /index.html HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | User-Agent: HTTP-Tiny/VERSION 8 | 9 | ---------- 10 | HTTP/1.1 200 OK 11 | Date: Sat, 02 Mar 2013 00:00:00 GMT 12 | Set-Cookie: cname=cvalue; domain=example.com; path=/ 13 | Content-Type: text/plain 14 | Content-Length: 44 15 | 16 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 17 | ---------- 18 | url 19 | http://example.com/index.html 20 | ---------- 21 | GET /index.html HTTP/1.1 22 | Host: example.com 23 | Connection: close 24 | User-Agent: HTTP-Tiny/VERSION 25 | Cookie: cname=cvalue 26 | 27 | ---------- 28 | HTTP/1.1 200 OK 29 | Date: Sat, 02 Mar 2013 00:00:00 GMT 30 | Set-Cookie: cname=cvalue; domain=example.com; path=/ 31 | Content-Type: text/plain 32 | Content-Length: 44 33 | 34 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 35 | -------------------------------------------------------------------------------- /corpus/cookies-02.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | ---------- 4 | GET /index.html HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | User-Agent: HTTP-Tiny/VERSION 8 | 9 | ---------- 10 | HTTP/1.1 200 OK 11 | Date: Sat, 02 Mar 2013 00:00:00 GMT 12 | Set-Cookie: cname=cvalue01; domain=example.com; path=/ 13 | Content-Type: text/plain 14 | Content-Length: 44 15 | 16 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 17 | ---------- 18 | url 19 | http://example.com/index.html 20 | ---------- 21 | GET /index.html HTTP/1.1 22 | Host: example.com 23 | Connection: close 24 | User-Agent: HTTP-Tiny/VERSION 25 | Cookie: cname=cvalue01 26 | 27 | ---------- 28 | HTTP/1.1 200 OK 29 | Date: Sat, 02 Mar 2013 00:00:00 GMT 30 | Set-Cookie: cname=cvalue02; domain=example.com; path=/ 31 | Content-Type: text/plain 32 | Content-Length: 44 33 | 34 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 35 | ---------- 36 | url 37 | http://example.com/index.html 38 | ---------- 39 | GET /index.html HTTP/1.1 40 | Host: example.com 41 | Connection: close 42 | User-Agent: HTTP-Tiny/VERSION 43 | Cookie: cname=cvalue02 44 | 45 | ---------- 46 | HTTP/1.1 200 OK 47 | Date: Sat, 02 Mar 2013 00:00:00 GMT 48 | Set-Cookie: cname=cvalue02; domain=example.com; path=/ 49 | Content-Type: text/plain 50 | Content-Length: 44 51 | 52 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 53 | -------------------------------------------------------------------------------- /corpus/cookies-03.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | ---------- 4 | GET /index.html HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | User-Agent: HTTP-Tiny/VERSION 8 | 9 | ---------- 10 | HTTP/1.1 200 OK 11 | Date: Sat, 02 Mar 2013 00:00:00 GMT 12 | Set-Cookie: cname=cvalue01; domain=example.com; path=/ 13 | Content-Type: text/plain 14 | Content-Length: 44 15 | 16 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 17 | ---------- 18 | url 19 | http://example.com/index.html 20 | ---------- 21 | GET /index.html HTTP/1.1 22 | Host: example.com 23 | Connection: close 24 | User-Agent: HTTP-Tiny/VERSION 25 | Cookie: cname=cvalue01 26 | 27 | ---------- 28 | HTTP/1.1 200 OK 29 | Date: Sat, 02 Mar 2013 00:00:00 GMT 30 | Set-Cookie: cname=cvalue02; domain=example.com; path=/ 31 | Content-Type: text/plain 32 | Content-Length: 44 33 | 34 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 35 | ---------- 36 | url 37 | http://example.com/index02.html 38 | ---------- 39 | GET /index02.html HTTP/1.1 40 | Host: example.com 41 | Connection: close 42 | User-Agent: HTTP-Tiny/VERSION 43 | 44 | ---------- 45 | HTTP/1.1 200 OK 46 | Date: Sat, 02 Mar 2013 00:00:00 GMT 47 | Set-Cookie: cname=cvalue03; domain=example.com; path=/ 48 | Content-Type: text/plain 49 | Content-Length: 44 50 | 51 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 52 | ---------- 53 | url 54 | http://example.com/index.html 55 | ---------- 56 | GET /index.html HTTP/1.1 57 | Host: example.com 58 | Connection: close 59 | User-Agent: HTTP-Tiny/VERSION 60 | Cookie: cname=cvalue02 61 | 62 | ---------- 63 | HTTP/1.1 200 OK 64 | Date: Sat, 02 Mar 2013 00:00:00 GMT 65 | Set-Cookie: cname=cvalue02; domain=example.com; path=/ 66 | Content-Type: text/plain 67 | Content-Length: 44 68 | 69 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 70 | ---------- 71 | url 72 | http://example.com/index02.html 73 | ---------- 74 | GET /index02.html HTTP/1.1 75 | Host: example.com 76 | Connection: close 77 | User-Agent: HTTP-Tiny/VERSION 78 | Cookie: cname=cvalue03 79 | 80 | ---------- 81 | HTTP/1.1 200 OK 82 | Date: Sat, 02 Mar 2013 00:00:00 GMT 83 | Set-Cookie: cname=cvalue03; domain=example.com; path=/ 84 | Content-Type: text/plain 85 | Content-Length: 44 86 | 87 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 88 | -------------------------------------------------------------------------------- /corpus/cookies-04.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | headers 4 | Cookie: cname=cvalue05; domain=example.com; path=/ 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | Cookie: cname=cvalue05 11 | 12 | ---------- 13 | HTTP/1.1 200 OK 14 | Date: Sat, 02 Mar 2013 00:00:00 GMT 15 | Set-Cookie: cname=cvalue06; domain=example.com; path=/ 16 | Content-Type: text/plain 17 | Content-Length: 44 18 | 19 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 20 | ---------- 21 | url 22 | http://example.com/index.html 23 | ---------- 24 | GET /index.html HTTP/1.1 25 | Host: example.com 26 | Connection: close 27 | User-Agent: HTTP-Tiny/VERSION 28 | Cookie: cname=cvalue06 29 | 30 | ---------- 31 | HTTP/1.1 200 OK 32 | Date: Sat, 02 Mar 2013 00:00:00 GMT 33 | Set-Cookie: cname=cvalue06; domain=example.com; path=/ 34 | Content-Type: text/plain 35 | Content-Length: 44 36 | 37 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 38 | -------------------------------------------------------------------------------- /corpus/cookies-05.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | headers 4 | No-Cookie-Jar: 1 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Sat, 02 Mar 2013 00:00:00 GMT 14 | Set-Cookie: cname=cvalue06; domain=example.com; path=/ 15 | Content-Type: text/plain 16 | Content-Length: 44 17 | 18 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 19 | ---------- 20 | url 21 | http://example.com/index.html 22 | ---------- 23 | GET /index.html HTTP/1.1 24 | Host: example.com 25 | Connection: close 26 | User-Agent: HTTP-Tiny/VERSION 27 | 28 | ---------- 29 | HTTP/1.1 200 OK 30 | Date: Sat, 02 Mar 2013 00:00:00 GMT 31 | Set-Cookie: cname=cvalue06; domain=example.com; path=/ 32 | Content-Type: text/plain 33 | Content-Length: 44 34 | 35 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 36 | -------------------------------------------------------------------------------- /corpus/cookies-06.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | ---------- 4 | GET /index.html HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | User-Agent: HTTP-Tiny/VERSION 8 | 9 | ---------- 10 | HTTP/1.1 200 OK 11 | Date: Sat, 02 Mar 2013 00:00:00 GMT 12 | Set-Cookie: cname1=cvalue01; domain=example.com; path=/ 13 | Content-Type: text/plain 14 | Content-Length: 44 15 | 16 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 17 | ---------- 18 | url 19 | http://example.com/index.html 20 | ---------- 21 | GET /index.html HTTP/1.1 22 | Host: example.com 23 | Connection: close 24 | User-Agent: HTTP-Tiny/VERSION 25 | Cookie: cname1=cvalue01 26 | 27 | ---------- 28 | HTTP/1.1 200 OK 29 | Date: Sat, 02 Mar 2013 00:00:00 GMT 30 | Set-Cookie: cname2=cvalue02; domain=example.com; path=/ 31 | Content-Type: text/plain 32 | Content-Length: 44 33 | 34 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 35 | ---------- 36 | url 37 | http://example.com/index.html 38 | ---------- 39 | GET /index.html HTTP/1.1 40 | Host: example.com 41 | Connection: close 42 | User-Agent: HTTP-Tiny/VERSION 43 | Cookie: cname1=cvalue01; cname2=cvalue02 44 | 45 | ---------- 46 | HTTP/1.1 200 OK 47 | Date: Sat, 02 Mar 2013 00:00:00 GMT 48 | Content-Type: text/plain 49 | Content-Length: 44 50 | 51 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 52 | -------------------------------------------------------------------------------- /corpus/cookies-07.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | ---------- 4 | GET /index.html HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | User-Agent: HTTP-Tiny/VERSION 8 | 9 | ---------- 10 | HTTP/1.1 200 OK 11 | Date: Sat, 02 Mar 2013 00:00:00 GMT 12 | Set-Cookie: cname1=cvalue01; domain=example.com; path=/ 13 | Set-Cookie: cname2=cvalue02; domain=example.com; path=/ 14 | Content-Type: text/plain 15 | Content-Length: 44 16 | 17 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 18 | ---------- 19 | url 20 | http://example.com/index.html 21 | ---------- 22 | GET /index.html HTTP/1.1 23 | Host: example.com 24 | Connection: close 25 | User-Agent: HTTP-Tiny/VERSION 26 | Cookie: cname1=cvalue01; cname2=cvalue02 27 | 28 | ---------- 29 | HTTP/1.1 200 OK 30 | Date: Sat, 02 Mar 2013 00:00:00 GMT 31 | Content-Type: text/plain 32 | Content-Length: 44 33 | 34 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 35 | -------------------------------------------------------------------------------- /corpus/delete-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | DELETE /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Length: 0 15 | 16 | -------------------------------------------------------------------------------- /corpus/form-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/new 3 | headers 4 | Content-Type: text/plain 5 | content 6 | key|value|name|John Doe|undef||noise|!@#$%^&*() 7 | datatype 8 | ARRAY 9 | ---------- 10 | POST /new HTTP/1.1 11 | Host: example.com 12 | Connection: close 13 | User-Agent: HTTP-Tiny/VERSION 14 | Content-Type: application/x-www-form-urlencoded 15 | Content-Length: 67 16 | 17 | key=value&name=John+Doe&undef=&noise=%21%40%23%24%25%5E%26%2A%28%29 18 | ---------- 19 | HTTP/1.1 201 Created 20 | Date: Thu, 03 Feb 1994 00:00:00 GMT 21 | Location: http://example.com/new/01.txt 22 | Content-Length: 0 23 | 24 | -------------------------------------------------------------------------------- /corpus/form-02.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/new 3 | content 4 | key|value|name|John Doe|undef||noise|!@#$%^&*() 5 | datatype 6 | HASH 7 | ---------- 8 | POST /new HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | Content-Type: application/x-www-form-urlencoded 13 | Content-Length: 67 14 | 15 | key=value&name=John+Doe&noise=%21%40%23%24%25%5E%26%2A%28%29&undef= 16 | ---------- 17 | HTTP/1.1 201 Created 18 | Date: Thu, 03 Feb 1994 00:00:00 GMT 19 | Location: http://example.com/new/01.txt 20 | Content-Length: 0 21 | 22 | -------------------------------------------------------------------------------- /corpus/form-03.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/new 3 | content 4 | bar|baz|ack|foo 5 | datatype 6 | HASH 7 | ---------- 8 | POST /new HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | Content-Type: application/x-www-form-urlencoded 13 | Content-Length: 15 14 | 15 | ack=foo&bar=baz 16 | ---------- 17 | HTTP/1.1 201 Created 18 | Date: Thu, 03 Feb 1994 00:00:00 GMT 19 | Location: http://example.com/new/01.txt 20 | Content-Length: 0 21 | 22 | -------------------------------------------------------------------------------- /corpus/form-04.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/new 3 | content 4 | utf8|☺ 5 | datatype 6 | ARRAY 7 | ---------- 8 | POST /new HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | Content-Type: application/x-www-form-urlencoded 13 | Content-Length: 23 14 | 15 | utf8=%C3%A2%C2%98%C2%BA 16 | ---------- 17 | HTTP/1.1 201 Created 18 | Date: Thu, 03 Feb 1994 00:00:00 GMT 19 | Location: http://example.com/new/01.txt 20 | Content-Length: 0 21 | 22 | -------------------------------------------------------------------------------- /corpus/form-05.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/new 3 | content 4 | bar|baz|ack|foo 5 | datatype 6 | ARRAY 7 | ---------- 8 | POST /new HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | Content-Type: application/x-www-form-urlencoded 13 | Content-Length: 15 14 | 15 | bar=baz&ack=foo 16 | ---------- 17 | HTTP/1.1 201 Created 18 | Date: Thu, 03 Feb 1994 00:00:00 GMT 19 | Location: http://example.com/new/01.txt 20 | Content-Length: 0 21 | 22 | -------------------------------------------------------------------------------- /corpus/get-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Content-Length: 44 16 | 17 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 18 | -------------------------------------------------------------------------------- /corpus/get-02.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | headers 6 | Accept: */* 7 | X-Custom: This is a custom header 8 | x-lower: This is a lower-case custom header 9 | authorization: fake auth data 10 | ---------- 11 | GET /index.html HTTP/1.1 12 | Host: example.com 13 | Accept: */* 14 | Authorization: fake auth data 15 | Connection: close 16 | User-Agent: HTTP-Tiny/VERSION 17 | X-Custom: This is a custom header 18 | x-lower: This is a lower-case custom header 19 | 20 | ---------- 21 | HTTP/1.1 200 OK 22 | Date: Thu, 03 Feb 1994 00:00:00 GMT 23 | Content-Type: text/plain 24 | Content-Length: 44 25 | 26 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 27 | -------------------------------------------------------------------------------- /corpus/get-03.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/missing.html 3 | ---------- 4 | GET /missing.html HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | User-Agent: HTTP-Tiny/VERSION 8 | 9 | ---------- 10 | HTTP/1.1 404 Not Found 11 | Date: Thu, 03 Feb 1994 00:00:00 GMT 12 | Content-Length: 0 13 | 14 | -------------------------------------------------------------------------------- /corpus/get-04.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com:9000/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com:9000 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Content-Length: 44 16 | 17 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 18 | -------------------------------------------------------------------------------- /corpus/get-05.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/chunked.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /chunked.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Transfer-Encoding: chunked 16 | 17 | 2C 18 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 19 | 20 | 0 21 | 22 | -------------------------------------------------------------------------------- /corpus/get-06.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/cb.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | data_cb 6 | sub { $main::data .= $_[0] } 7 | ---------- 8 | GET /cb.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.1 200 OK 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/plain 17 | Content-Length: 44 18 | 19 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 20 | -------------------------------------------------------------------------------- /corpus/get-07.txt: -------------------------------------------------------------------------------- 1 | new_args 2 | proxy: http://proxy.example.com:8080/ 3 | url 4 | http://example.com/index.html 5 | expected 6 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 7 | ---------- 8 | GET http://example.com/index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.1 200 OK 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/plain 17 | Content-Length: 44 18 | 19 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 20 | -------------------------------------------------------------------------------- /corpus/get-08.txt: -------------------------------------------------------------------------------- 1 | new_args 2 | max_size: 26 3 | url 4 | http://example.com/index.html 5 | expected_rc 6 | 599 7 | expected_like 8 | Size of response body exceeds the maximum allowed of 26 9 | ---------- 10 | GET /index.html HTTP/1.1 11 | Host: example.com 12 | Connection: close 13 | User-Agent: HTTP-Tiny/VERSION 14 | 15 | ---------- 16 | HTTP/1.1 200 OK 17 | Date: Thu, 03 Feb 1994 00:00:00 GMT 18 | Content-Type: text/plain 19 | Content-Length: 44 20 | 21 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 22 | -------------------------------------------------------------------------------- /corpus/get-09.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | 16 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 17 | -------------------------------------------------------------------------------- /corpus/get-10.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/chunked.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /chunked.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Transfer-Encoding: CHUNKED 16 | 17 | 2C 18 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 19 | 20 | 0 21 | 22 | -------------------------------------------------------------------------------- /corpus/get-11.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/chunked.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /chunked.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Content-Length: 1024 16 | Transfer-Encoding: chunked 17 | 18 | 2C 19 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 20 | 21 | 0 22 | 23 | -------------------------------------------------------------------------------- /corpus/get-12.txt: -------------------------------------------------------------------------------- 1 | url 2 | http:///index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: localhost 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Content-Length: 44 16 | 17 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 18 | -------------------------------------------------------------------------------- /corpus/get-13.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/chunked.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /chunked.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Transfer-Encoding: chunked 16 | 17 | 2C; this_extension=foo 18 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 19 | 20 | 0 21 | 22 | -------------------------------------------------------------------------------- /corpus/get-14.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | headers 6 | X-Foo: one 7 | X-Foo: two 8 | ---------- 9 | GET /index.html HTTP/1.1 10 | Host: example.com 11 | Connection: close 12 | User-Agent: HTTP-Tiny/VERSION 13 | X-Foo: one 14 | X-Foo: two 15 | 16 | ---------- 17 | HTTP/1.1 200 OK 18 | Date: Thu, 03 Feb 1994 00:00:00 GMT 19 | Content-Type: text/plain 20 | Content-Length: 44 21 | 22 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 23 | -------------------------------------------------------------------------------- /corpus/get-15.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/01.01 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Content-Length: 44 16 | 17 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 18 | -------------------------------------------------------------------------------- /corpus/get-16.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected_rc 4 | 599 5 | expected_like 6 | Malformed Status-Line 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/0 200 OK 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/plain 17 | Content-Length: 44 18 | 19 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 20 | -------------------------------------------------------------------------------- /corpus/get-17.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected_rc 4 | 599 5 | expected_like 6 | Unsupported HTTP protocol 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.2 200 OK 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/plain 17 | Content-Length: 44 18 | 19 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 20 | -------------------------------------------------------------------------------- /corpus/get-18.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/chunked.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | expected_headers 6 | transfer-encoding: chunked 7 | x-a: Foo 8 | x-a: Bar 9 | x-a: Baz 10 | x-b: Foo 11 | x-b: Bar 12 | x-b: Baz 13 | x-c: Foo 14 | 15 | ---------- 16 | GET /chunked.html HTTP/1.1 17 | Host: example.com 18 | Connection: close 19 | User-Agent: HTTP-Tiny/VERSION 20 | 21 | ---------- 22 | HTTP/1.1 200 OK 23 | Transfer-Encoding: chunked 24 | X-A: Foo 25 | X-B: Foo 26 | X-B: Bar 27 | 28 | 2C 29 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 30 | 31 | 0 32 | X-A: Bar 33 | X-A: Baz 34 | X-B: Baz 35 | X-C: Foo 36 | 37 | -------------------------------------------------------------------------------- /corpus/get-19.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/chunked.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /chunked.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Transfer-Encoding: wacky 16 | Transfer-Encoding: chunked 17 | 18 | 2C 19 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 20 | 21 | 0 22 | 23 | -------------------------------------------------------------------------------- /corpus/get-20.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | headers 4 | connection: X-Foo 5 | X-Foo: bar 6 | expected 7 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 8 | ---------- 9 | GET /index.html HTTP/1.1 10 | Host: example.com 11 | Connection: close 12 | X-Foo: bar 13 | User-Agent: HTTP-Tiny/VERSION 14 | 15 | ---------- 16 | HTTP/1.1 200 OK 17 | Date: Thu, 03 Feb 1994 00:00:00 GMT 18 | Content-Type: text/plain 19 | Content-Length: 44 20 | 21 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 22 | -------------------------------------------------------------------------------- /corpus/get-21.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | expected_rc 6 | 200 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.1 100 Continue 15 | 16 | HTTP/1.1 110 Arbitrary 1XX status code 17 | 18 | HTTP/1.1 200 OK 19 | Date: Thu, 03 Feb 1994 00:00:00 GMT 20 | Content-Type: text/plain 21 | Content-Length: 44 22 | 23 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 24 | -------------------------------------------------------------------------------- /corpus/get-22.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected_rc 4 | 599 5 | expected_like 6 | Invalid HTTP header field 7 | headers 8 | trailing-space : foo 9 | ---------- 10 | ---------- 11 | -------------------------------------------------------------------------------- /corpus/get-23.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | GET /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Content-Length: 44 16 | 17 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 18 | -------------------------------------------------------------------------------- /corpus/head-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | HEAD /index.html HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | 11 | ---------- 12 | HTTP/1.1 200 OK 13 | Date: Thu, 03 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Content-Length: 44 16 | 17 | -------------------------------------------------------------------------------- /corpus/keepalive-01.txt: -------------------------------------------------------------------------------- 1 | No content length 2 | ---------- 3 | 0 4 | ---------- 5 | HTTP/1.1 200 OK 6 | Date: Thu, 03 Feb 1994 00:00:00 GMT 7 | Content-Type: text/html 8 | 9 | 0123456789 10 | -------------------------------------------------------------------------------- /corpus/keepalive-02.txt: -------------------------------------------------------------------------------- 1 | Wrong content length 2 | ---------- 3 | 0 4 | ---------- 5 | HTTP/1.1 200 OK 6 | Date: Thu, 03 Feb 1994 00:00:00 GMT 7 | Content-Type: text/html 8 | Content-Length: 10 9 | 10 | 01234567890123456789 11 | -------------------------------------------------------------------------------- /corpus/keepalive-03.txt: -------------------------------------------------------------------------------- 1 | Connection close 2 | ---------- 3 | 0 4 | ---------- 5 | HTTP/1.1 200 OK 6 | Date: Thu, 03 Feb 1994 00:00:00 GMT 7 | Content-Type: text/html 8 | Content-Length: 10 9 | Connection: close 10 | 11 | 0123456789 12 | -------------------------------------------------------------------------------- /corpus/keepalive-04.txt: -------------------------------------------------------------------------------- 1 | Not HTTP/1.1 2 | ---------- 3 | 0 4 | ---------- 5 | HTTP/1.0 200 OK 6 | Date: Thu, 03 Feb 1994 00:00:00 GMT 7 | Content-Type: text/html 8 | Content-Length: 10 9 | 10 | 0123456789 11 | -------------------------------------------------------------------------------- /corpus/keepalive-05.txt: -------------------------------------------------------------------------------- 1 | Not HTTP/1.1 with keep-alive 2 | ---------- 3 | 0 4 | ---------- 5 | HTTP/1.0 200 OK 6 | Date: Thu, 03 Feb 1994 00:00:00 GMT 7 | Content-Type: text/html 8 | Content-Length: 10 9 | Connection: keep-alive 10 | 11 | 0123456789 12 | -------------------------------------------------------------------------------- /corpus/mirror-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/modified.txt 3 | ---------- 4 | GET /modified.txt HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | If-Modified-Since: Tue, 01 Feb 1994 00:00:00 GMT 8 | User-Agent: HTTP-Tiny/VERSION 9 | 10 | ---------- 11 | HTTP/1.1 200 OK 12 | Date: Thu, 03 Feb 1994 00:00:00 GMT 13 | Last-Modified: Wed, 02 Feb 1994 00:00:00 GMT 14 | Content-Type: text/plain 15 | Content-Length: 42 16 | 17 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 18 | 19 | -------------------------------------------------------------------------------- /corpus/mirror-02.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/not-modified.txt 3 | ---------- 4 | GET /not-modified.txt HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | If-Modified-Since: Tue, 01 Feb 1994 00:00:00 GMT 8 | User-Agent: HTTP-Tiny/VERSION 9 | 10 | ---------- 11 | HTTP/1.1 304 Not Modified 12 | Date: Thu, 03 Feb 1994 00:00:00 GMT 13 | Last-Modified: Tue, 01 Feb 1994 00:00:00 GMT 14 | 15 | -------------------------------------------------------------------------------- /corpus/mirror-03.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/new.txt 3 | ---------- 4 | GET /new.txt HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | User-Agent: HTTP-Tiny/VERSION 8 | 9 | ---------- 10 | HTTP/1.1 200 OK 11 | Date: Thu, 03 Feb 1994 00:00:00 GMT 12 | Last-Modified: Tue, 01 Feb 1994 00:00:00 GMT 13 | Content-Type: text/plain 14 | Content-Length: 42 15 | 16 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 17 | 18 | -------------------------------------------------------------------------------- /corpus/mirror-04.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/missing.txt 3 | ---------- 4 | GET /missing.txt HTTP/1.1 5 | Host: example.com 6 | Connection: close 7 | User-Agent: HTTP-Tiny/VERSION 8 | 9 | ---------- 10 | HTTP/1.1 404 Not Found 11 | Date: Thu, 03 Feb 1994 00:00:00 GMT 12 | Content-Length: 0 13 | 14 | -------------------------------------------------------------------------------- /corpus/mirror-05.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/modified.txt 3 | headers 4 | if-modified-since: Tue, 01 Feb 1994 12:00:00 GMT 5 | ---------- 6 | GET /modified.txt HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | If-Modified-Since: Tue, 01 Feb 1994 12:00:00 GMT 10 | User-Agent: HTTP-Tiny/VERSION 11 | 12 | ---------- 13 | HTTP/1.1 200 OK 14 | Date: Thu, 03 Feb 1994 00:00:00 GMT 15 | Last-Modified: Wed, 02 Feb 1994 00:00:00 GMT 16 | Content-Type: text/plain 17 | Content-Length: 42 18 | 19 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 20 | 21 | -------------------------------------------------------------------------------- /corpus/post-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | headers 4 | Content-Type: text/plain 5 | Content-Length: 42 6 | content 7 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 8 | ---------- 9 | POST /index.html HTTP/1.1 10 | Host: example.com 11 | Connection: close 12 | User-Agent: HTTP-Tiny/VERSION 13 | Content-Type: text/plain 14 | Content-Length: 42 15 | 16 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 17 | 18 | ---------- 19 | HTTP/1.1 200 OK 20 | Date: Thu, 03 Feb 1994 00:00:00 GMT 21 | Content-Type: text/plain 22 | Content-Length: 42 23 | 24 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 25 | -------------------------------------------------------------------------------- /corpus/post-02.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | headers 4 | Content-Type: text/plain 5 | content 6 | 7 | ---------- 8 | POST /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | Content-Type: text/plain 13 | Content-Length: 0 14 | 15 | ---------- 16 | HTTP/1.1 200 OK 17 | Date: Thu, 03 Feb 1994 00:00:00 GMT 18 | Content-Type: text/plain 19 | Content-Length: 0 20 | 21 | -------------------------------------------------------------------------------- /corpus/proxy-auth-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | new_args 6 | proxy: http://foo:bar@proxy.example.com/ 7 | 8 | ---------- 9 | GET http://example.com/index.html HTTP/1.1 10 | Host: example.com 11 | Connection: close 12 | User-Agent: HTTP-Tiny/VERSION 13 | Proxy-Authorization: Basic Zm9vOmJhcg== 14 | 15 | ---------- 16 | HTTP/1.1 200 OK 17 | Date: Thu, 03 Feb 1994 00:00:00 GMT 18 | Content-Type: text/plain 19 | Content-Length: 42 20 | 21 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 22 | -------------------------------------------------------------------------------- /corpus/put-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/new.txt 3 | headers 4 | Content-Type: text/plain 5 | content 6 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 7 | ---------- 8 | PUT /new.txt HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | Content-Type: text/plain 13 | Content-Length: 42 14 | 15 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 16 | 17 | ---------- 18 | HTTP/1.1 201 Created 19 | Date: Thu, 03 Feb 1994 00:00:00 GMT 20 | Location: http://example.com/new.txt 21 | Content-Length: 0 22 | 23 | -------------------------------------------------------------------------------- /corpus/put-02.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/callback.txt 3 | headers 4 | Content-Type: text/plain 5 | Content-Length: 42 6 | content_cb 7 | my @content = qq{abcdefghijklmnopqrstuvwxyz1234567890abcdef}; 8 | sub { shift @content } 9 | ---------- 10 | PUT /callback.txt HTTP/1.1 11 | Host: example.com 12 | Connection: close 13 | User-Agent: HTTP-Tiny/VERSION 14 | Content-Type: text/plain 15 | Content-Length: 42 16 | 17 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 18 | 19 | ---------- 20 | HTTP/1.1 201 Created 21 | Date: Thu, 03 Feb 1994 00:00:00 GMT 22 | Location: http://example.com/callback.txt 23 | Content-Length: 0 24 | 25 | -------------------------------------------------------------------------------- /corpus/put-03.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/chunked.txt 3 | headers 4 | Content-Type: text/plain 5 | content_cb 6 | my @content = qq{abcdefghijklmnopqrstuvwxyz1234567890abcdef}; 7 | sub { shift @content } 8 | ---------- 9 | PUT /chunked.txt HTTP/1.1 10 | Host: example.com 11 | Connection: close 12 | User-Agent: HTTP-Tiny/VERSION 13 | Content-Type: text/plain 14 | Transfer-Encoding: chunked 15 | 16 | 2A 17 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 18 | 0 19 | 20 | ---------- 21 | HTTP/1.1 201 Created 22 | Date: Thu, 03 Feb 1994 00:00:00 GMT 23 | Location: http://example.com/chunked.txt 24 | Content-Length: 0 25 | 26 | -------------------------------------------------------------------------------- /corpus/put-04.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/new.txt 3 | content 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | ---------- 6 | PUT /new.txt HTTP/1.1 7 | Host: example.com 8 | Connection: close 9 | User-Agent: HTTP-Tiny/VERSION 10 | Content-Type: application/octet-stream 11 | Content-Length: 42 12 | 13 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 14 | ---------- 15 | HTTP/1.1 201 Created 16 | Date: Thu, 03 Feb 1994 00:00:00 GMT 17 | Location: http://example.com/new.txt 18 | Content-Length: 0 19 | 20 | -------------------------------------------------------------------------------- /corpus/put-05.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/chunked.txt 3 | headers 4 | Content-Type: text/plain 5 | content_cb 6 | my @content = qq{abcdefghijklmnopqrstuvwxyz1234567890abcdef}; 7 | sub { shift @content } 8 | trailer_cb 9 | sub { return { 'x-foo' => 'bar' } } 10 | ---------- 11 | PUT /chunked.txt HTTP/1.1 12 | Host: example.com 13 | Connection: close 14 | User-Agent: HTTP-Tiny/VERSION 15 | Content-Type: text/plain 16 | Transfer-Encoding: chunked 17 | 18 | 2A 19 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 20 | 0 21 | X-Foo: bar 22 | ---------- 23 | HTTP/1.1 201 Created 24 | Date: Thu, 03 Feb 1994 00:00:00 GMT 25 | Location: http://example.com/chunked.txt 26 | Content-Length: 0 27 | 28 | -------------------------------------------------------------------------------- /corpus/put-06.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/new.txt 3 | headers 4 | Content-Type: text/plain 5 | content 6 | 7 | ---------- 8 | PUT /new.txt HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | Content-Type: text/plain 13 | Content-Length: 0 14 | 15 | ---------- 16 | HTTP/1.1 201 Created 17 | Date: Thu, 03 Feb 1994 00:00:00 GMT 18 | Location: http://example.com/new.txt 19 | Content-Length: 0 20 | 21 | -------------------------------------------------------------------------------- /corpus/put-07.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/callback.txt 3 | headers 4 | Content-Type: text/plain 5 | Content-Length: 0 6 | content_cb 7 | my @content = qq{}; 8 | sub { shift @content } 9 | ---------- 10 | PUT /callback.txt HTTP/1.1 11 | Host: example.com 12 | Connection: close 13 | User-Agent: HTTP-Tiny/VERSION 14 | Content-Type: text/plain 15 | Content-Length: 0 16 | 17 | ---------- 18 | HTTP/1.1 201 Created 19 | Date: Thu, 03 Feb 1994 00:00:00 GMT 20 | Location: http://example.com/callback.txt 21 | Content-Length: 0 22 | 23 | -------------------------------------------------------------------------------- /corpus/put-08.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/callback.txt 3 | headers 4 | Content-Type: text/plain 5 | content_cb 6 | my @content = qq{}; 7 | sub { shift @content } 8 | ---------- 9 | PUT /callback.txt HTTP/1.1 10 | Host: example.com 11 | Connection: close 12 | User-Agent: HTTP-Tiny/VERSION 13 | Transfer-Encoding: chunked 14 | Content-Type: text/plain 15 | 16 | 0 17 | 18 | ---------- 19 | HTTP/1.1 201 Created 20 | Date: Thu, 03 Feb 1994 00:00:00 GMT 21 | Location: http://example.com/callback.txt 22 | Content-Length: 0 23 | 24 | -------------------------------------------------------------------------------- /corpus/redirect-01.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | expected_url 6 | http://example.com/index2.html 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.1 302 Found 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/html 17 | Content-Length: 53 18 | Location: http://example.com/index2.html 19 | 20 | redirect 21 | 22 | ---------- 23 | GET /index2.html HTTP/1.1 24 | Host: example.com 25 | Connection: close 26 | User-Agent: HTTP-Tiny/VERSION 27 | 28 | ---------- 29 | HTTP/1.1 200 OK 30 | Date: Thu, 03 Feb 1994 00:00:00 GMT 31 | Content-Type: text/plain 32 | Content-Length: 42 33 | 34 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 35 | 36 | -------------------------------------------------------------------------------- /corpus/redirect-02.txt: -------------------------------------------------------------------------------- 1 | new_args 2 | max_redirect: 0 3 | url 4 | http://example.com/index.html 5 | expected 6 | redirect 7 | expected_url 8 | http://example.com/index.html 9 | ---------- 10 | GET /index.html HTTP/1.1 11 | Host: example.com 12 | Connection: close 13 | User-Agent: HTTP-Tiny/VERSION 14 | 15 | ---------- 16 | HTTP/1.1 302 Found 17 | Date: Thu, 03 Feb 1994 00:00:00 GMT 18 | Content-Type: text/html 19 | Content-Length: 53 20 | Location: http://example.com/index2.html 21 | 22 | redirect 23 | 24 | ---------- 25 | GET /index2.html HTTP/1.1 26 | Host: example.com 27 | Connection: close 28 | User-Agent: HTTP-Tiny/VERSION 29 | 30 | ---------- 31 | HTTP/1.1 302 Found 32 | Date: Thu, 03 Feb 1994 00:00:00 GMT 33 | Content-Type: text/html 34 | Content-Length: 53 35 | Location: http://example.com/index3.html 36 | 37 | redirect 38 | 39 | ---------- 40 | GET /index3.html HTTP/1.1 41 | Host: example.com 42 | Connection: close 43 | User-Agent: HTTP-Tiny/VERSION 44 | 45 | ---------- 46 | HTTP/1.1 200 OK 47 | Date: Thu, 03 Feb 1994 00:00:00 GMT 48 | Content-Type: text/plain 49 | Content-Length: 42 50 | 51 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 52 | 53 | -------------------------------------------------------------------------------- /corpus/redirect-03.txt: -------------------------------------------------------------------------------- 1 | new_args 2 | max_redirect: 1 3 | url 4 | http://example.com/index.html 5 | expected 6 | redirect 7 | expected_url 8 | http://example.com/index2.html 9 | ---------- 10 | GET /index.html HTTP/1.1 11 | Host: example.com 12 | Connection: close 13 | User-Agent: HTTP-Tiny/VERSION 14 | 15 | ---------- 16 | HTTP/1.1 302 Found 17 | Date: Thu, 03 Feb 1994 00:00:00 GMT 18 | Content-Type: text/html 19 | Content-Length: 53 20 | Location: http://example.com/index2.html 21 | 22 | redirect 23 | 24 | ---------- 25 | GET /index2.html HTTP/1.1 26 | Host: example.com 27 | Connection: close 28 | User-Agent: HTTP-Tiny/VERSION 29 | 30 | ---------- 31 | HTTP/1.1 302 Found 32 | Date: Thu, 03 Feb 1994 00:00:00 GMT 33 | Content-Type: text/html 34 | Content-Length: 53 35 | Location: http://example.com/index3.html 36 | 37 | redirect 38 | 39 | ---------- 40 | GET /index3.html HTTP/1.1 41 | Host: example.com 42 | Connection: close 43 | User-Agent: HTTP-Tiny/VERSION 44 | 45 | ---------- 46 | HTTP/1.1 200 OK 47 | Date: Thu, 03 Feb 1994 00:00:00 GMT 48 | Content-Type: text/plain 49 | Content-Length: 42 50 | 51 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 52 | 53 | -------------------------------------------------------------------------------- /corpus/redirect-04.txt: -------------------------------------------------------------------------------- 1 | new_args 2 | max_redirect: 2 3 | url 4 | http://example.com/index.html 5 | expected 6 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 7 | expected_url 8 | http://example.com/index3.html 9 | ---------- 10 | GET /index.html HTTP/1.1 11 | Host: example.com 12 | Connection: close 13 | User-Agent: HTTP-Tiny/VERSION 14 | 15 | ---------- 16 | HTTP/1.1 302 Found 17 | Date: Thu, 03 Feb 1994 00:00:00 GMT 18 | Content-Type: text/html 19 | Content-Length: 53 20 | Location: http://example.com/index2.html 21 | 22 | redirect 23 | 24 | ---------- 25 | GET /index2.html HTTP/1.1 26 | Host: example.com 27 | Connection: close 28 | User-Agent: HTTP-Tiny/VERSION 29 | 30 | ---------- 31 | HTTP/1.1 302 Found 32 | Date: Thu, 03 Feb 1994 00:00:00 GMT 33 | Content-Type: text/html 34 | Content-Length: 53 35 | Location: http://example.com/index3.html 36 | 37 | redirect 38 | 39 | ---------- 40 | GET /index3.html HTTP/1.1 41 | Host: example.com 42 | Connection: close 43 | User-Agent: HTTP-Tiny/VERSION 44 | 45 | ---------- 46 | HTTP/1.1 200 OK 47 | Date: Thu, 03 Feb 1994 00:00:00 GMT 48 | Content-Type: text/plain 49 | Content-Length: 42 50 | 51 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 52 | 53 | -------------------------------------------------------------------------------- /corpus/redirect-05.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | expected_url 6 | http://example.com/index3.html 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.1 302 Found 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/html 17 | Content-Length: 53 18 | Location: http://example.com/index2.html 19 | 20 | redirect 21 | 22 | ---------- 23 | GET /index2.html HTTP/1.1 24 | Host: example.com 25 | Connection: close 26 | User-Agent: HTTP-Tiny/VERSION 27 | 28 | ---------- 29 | HTTP/1.1 301 Found 30 | Date: Thu, 03 Feb 1994 00:00:00 GMT 31 | Content-Type: text/html 32 | Content-Length: 53 33 | Location: /index3.html 34 | 35 | redirect 36 | 37 | ---------- 38 | GET /index3.html HTTP/1.1 39 | Host: example.com 40 | Connection: close 41 | User-Agent: HTTP-Tiny/VERSION 42 | 43 | ---------- 44 | HTTP/1.1 200 OK 45 | Date: Thu, 03 Feb 1994 00:00:00 GMT 46 | Content-Type: text/plain 47 | Content-Length: 42 48 | 49 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 50 | 51 | -------------------------------------------------------------------------------- /corpus/redirect-06.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | expected_url 6 | http://example.com/index2.html 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.1 303 See Other 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/html 17 | Content-Length: 53 18 | Location: http://example.com/index2.html 19 | 20 | redirect 21 | 22 | ---------- 23 | GET /index2.html HTTP/1.1 24 | Host: example.com 25 | Connection: close 26 | User-Agent: HTTP-Tiny/VERSION 27 | 28 | ---------- 29 | HTTP/1.1 200 OK 30 | Date: Thu, 03 Feb 1994 00:00:00 GMT 31 | Content-Type: text/plain 32 | Content-Length: 42 33 | 34 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 35 | 36 | -------------------------------------------------------------------------------- /corpus/redirect-07.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | expected_url 6 | http://example.com/index2.html 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.1 307 Temporary Redirect 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/html 17 | Content-Length: 53 18 | Location: http://example.com/index2.html 19 | 20 | redirect 21 | 22 | ---------- 23 | GET /index2.html HTTP/1.1 24 | Host: example.com 25 | Connection: close 26 | User-Agent: HTTP-Tiny/VERSION 27 | 28 | ---------- 29 | HTTP/1.1 200 OK 30 | Date: Thu, 03 Feb 1994 00:00:00 GMT 31 | Content-Type: text/plain 32 | Content-Length: 42 33 | 34 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 35 | 36 | -------------------------------------------------------------------------------- /corpus/redirect-08.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | redirect 5 | expected_url 6 | http://example.com/index.html 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.1 305 Use Proxy 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/html 17 | Content-Length: 53 18 | Location: http://example.com/index2.html 19 | 20 | redirect 21 | 22 | -------------------------------------------------------------------------------- /corpus/redirect-09.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | method 4 | POST 5 | expected 6 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 7 | expected_url 8 | http://example.com/index2.html 9 | ---------- 10 | POST /index.html HTTP/1.1 11 | Host: example.com 12 | Connection: close 13 | Content-Length: 0 14 | User-Agent: HTTP-Tiny/VERSION 15 | 16 | ---------- 17 | HTTP/1.1 303 See Other 18 | Date: Thu, 03 Feb 1994 00:00:00 GMT 19 | Content-Type: text/html 20 | Content-Length: 53 21 | Location: http://example.com/index2.html 22 | 23 | redirect 24 | 25 | ---------- 26 | GET /index2.html HTTP/1.1 27 | Host: example.com 28 | Connection: close 29 | User-Agent: HTTP-Tiny/VERSION 30 | 31 | ---------- 32 | HTTP/1.1 200 OK 33 | Date: Thu, 03 Feb 1994 00:00:00 GMT 34 | Content-Type: text/plain 35 | Content-Length: 42 36 | 37 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 38 | 39 | -------------------------------------------------------------------------------- /corpus/redirect-10.txt: -------------------------------------------------------------------------------- 1 | url 2 | http://example.com/index.html 3 | expected 4 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 5 | expected_url 6 | http://example.com/index2.html 7 | ---------- 8 | GET /index.html HTTP/1.1 9 | Host: example.com 10 | Connection: close 11 | User-Agent: HTTP-Tiny/VERSION 12 | 13 | ---------- 14 | HTTP/1.1 308 Permanent Redirect 15 | Date: Thu, 03 Feb 1994 00:00:00 GMT 16 | Content-Type: text/html 17 | Content-Length: 53 18 | Location: http://example.com/index2.html 19 | 20 | redirect 21 | 22 | ---------- 23 | GET /index2.html HTTP/1.1 24 | Host: example.com 25 | Connection: close 26 | User-Agent: HTTP-Tiny/VERSION 27 | 28 | ---------- 29 | HTTP/1.1 200 OK 30 | Date: Thu, 03 Feb 1994 00:00:00 GMT 31 | Content-Type: text/plain 32 | Content-Length: 42 33 | 34 | abcdefghijklmnopqrstuvwxyz1234567890abcdef 35 | 36 | -------------------------------------------------------------------------------- /corpus/snake-oil.crt: -------------------------------------------------------------------------------- 1 | Generated with: 2 | 3 | openssl req -new -newkey rsa:4096 -x509 -new -nodes -sha256 -days 7300 -keyout /dev/null -out snake-oil.crt -subj '/CN=snake.oil/' 4 | 5 | -----BEGIN CERTIFICATE----- 6 | MIIFCTCCAvGgAwIBAgIUUUWe96AgoaW3pyYxlJfMxUMA6bgwDQYJKoZIhvcNAQEL 7 | BQAwFDESMBAGA1UEAwwJc25ha2Uub2lsMB4XDTIzMDUyMTE1NDkxMVoXDTQzMDUx 8 | NjE1NDkxMVowFDESMBAGA1UEAwwJc25ha2Uub2lsMIICIjANBgkqhkiG9w0BAQEF 9 | AAOCAg8AMIICCgKCAgEAnScXg4MGa6CmCFOYzr8ggzqsDAR0CoVdOaqQ6XtRoRcP 10 | PzptoqHDFtr4NqWwmeWAGIcey6CKFZXsm9LvPly/VUDDjctYZig3UoLaoQpygwae 11 | 2BgslsfuhwomxXuinatF6bo1vz+EaRpASJyHOBOp3Yvh2cLSXmD+YuTU8rci1IG/ 12 | FFmjsrftPsxKFZiI9meAtsGayQGdUIBsEvawhs5y7TDcblPfbBM21sg3touTrfzZ 13 | Yk9dXd7hX3uq5ZX4H9BWcqeGux3speYC2STClnGMl8DqGdAV4XssbFCVqIhvmzrW 14 | L6Ce9vt0x/gxQQB4EYJlvECSqm7IiwO85I8XJ04EzmVU4e2+c1B7WS/swhGLr8JJ 15 | 4yk/gbCe98ErU3ccnXPzZznNQXTt2iAQLqa5zNDmxjzyZXhDA1nijg2cJb1RnQVu 16 | m5YrUXOXt9b5664nLCVUf0s/yMqPbcIUA3puAPS6BgDEExnYL48rmTT1gazMO6S5 17 | ZwpycEVkwYUFj364vIHJvQO0xB54dqNul9kMLUwPLmP9H6nBIsGgJhZCAp+WDEzp 18 | Y4eqp0drTlJlpfjd/QOaOsKZgwrqiD0yh35bj43zcVHKaFYGLcS8M1+XlbYNYx90 19 | w7+GpbY+MebCYF//dXAFXzORxdA1XZ30I7CAxAVK5l5cokrMIHJ01kkzYEGA1Y0C 20 | AwEAAaNTMFEwHQYDVR0OBBYEFAyj5N91aOt4TxNEOJ18JUPEBsOyMB8GA1UdIwQY 21 | MBaAFAyj5N91aOt4TxNEOJ18JUPEBsOyMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI 22 | hvcNAQELBQADggIBAHGiT2h0SU2tlFmUQ8CzgJ7JRTg91ltRwEvs/SQ/YN7DdDwt 23 | dQy/0nMIeFstdDIkdQyjcAUO92msLzd8AniiwAjynxamiQqdA57+rdhp7oEbFgw+ 24 | nF56bG4tiBG1gAIxtY9f2sG/Sqf6UuimxOX43nybG8RdRu8hOh/TQXcj8raa2PMT 25 | pTdphjMJUKSplHtFpbLFuf5FxklpeAYxYAReMzQhVgTzi7fcz3QhT/l6eqK6G05v 26 | gi+QsgesMiGdHKiTtx8N70JFZ+8BzJ0CJDI8PR2XZTLbpKxNfk426hTjJBkRULT5 27 | s7IWuuEO4Bb1p27K2WgHGh0mxFk4POPFmotxupVqzl8g2umcfWLDq0UR3BcRyR3B 28 | GWZNCcDTVLaAsarbSJoY1L/6j4O0RQdgpOWiENLbEcelGprGLBVe4s/NDA6aUYA+ 29 | 2Dll+0tHe6oKI+RCRoDhhiAH7UVIGQdORzcbY3Fxbf1OlFdpOyXLI751b1DjSYRu 30 | 9cVFXZIBRTTiEvGbUfoDEXDmKxpWHkGRel2864FBodcwGv7yW6mC3o6vpOqQFcW7 31 | MjJsFhtVj8PdPmue+ye766PeH45ydDD01nr1I92w6E1C0pEEqRNEpoOGgORyNgit 32 | EZag4DlWFs5MFdlj32haztRgi2dhVuJxlzx4lAmvOoqvGVQVIicN1JSlikBk 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name = HTTP-Tiny 2 | author = Christian Hansen 3 | author = David Golden 4 | license = Perl_5 5 | copyright_holder = Christian Hansen 6 | 7 | [@DAGOLDEN] 8 | :version = 0.072 9 | stopwords = CA 10 | stopwords = CGI 11 | stopwords = TLS 12 | stopwords = UA 13 | stopwords = proxying 14 | 15 | [ReleaseStatus::FromVersion] 16 | testing = third_decimal_odd 17 | 18 | [RemovePrereqs] 19 | remove = Errno 20 | remove = HTTP::CookieJar 21 | remove = IO::Socket::IP 22 | remove = IO::Socket::SSL 23 | remove = Mozilla::CA 24 | remove = Net::SSLeay 25 | remove = threads 26 | 27 | [Prereqs / Recommends] 28 | HTTP::CookieJar = 0.001 29 | IO::Socket::IP = 0.32 30 | IO::Socket::SSL = 1.42 31 | Mozilla::CA = 20160104 32 | Net::SSLeay = 1.49 33 | 34 | [Prereqs / Suggests] 35 | IO::Socket::SSL = 1.56 36 | 37 | [Prereqs / DevelopRequires] 38 | ; Non core Perl::Critic policy used in perlcritic.rc 39 | Perl::Critic::Policy::Lax::ProhibitStringyEval::ExceptForRequire = 0 40 | -------------------------------------------------------------------------------- /eg/get.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use HTTP::Tiny; 7 | 8 | my $url = shift(@ARGV) || 'http://example.com'; 9 | 10 | my $response = HTTP::Tiny->new->get($url); 11 | 12 | print "$response->{status} $response->{reason}\n"; 13 | 14 | while (my ($k, $v) = each %{$response->{headers}}) { 15 | for (ref $v eq 'ARRAY' ? @$v : $v) { 16 | print "$k: $_\n"; 17 | } 18 | } 19 | 20 | print $response->{content} if length $response->{content}; 21 | 22 | -------------------------------------------------------------------------------- /eg/mirror.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use HTTP::Tiny; 7 | 8 | my $url = shift(@ARGV) || 'http://example.com'; 9 | my $file = shift(@ARGV) || 'example.output'; 10 | 11 | my $response = HTTP::Tiny->new->mirror($url, $file); 12 | 13 | print "$response->{status} $response->{reason}\n"; 14 | 15 | while (my ($k, $v) = each %{$response->{headers}}) { 16 | for (ref $v eq 'ARRAY' ? @$v : $v) { 17 | print "$k: $_\n"; 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /eg/post.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use HTTP::Tiny; 7 | use URI::Escape qw/uri_escape_utf8/; 8 | 9 | my $url = 'http://search.cpan.org/search'; 10 | 11 | my %form_data = ( 12 | query => 'DAGOLDEN', 13 | mode => 'author', 14 | ); 15 | 16 | my @params; 17 | while( my @pair = each %form_data ) { 18 | push @params, join("=", map { uri_escape_utf8($_) } @pair); 19 | } 20 | 21 | my $response = HTTP::Tiny->new->request('POST', $url, { 22 | content => join("&", @params), 23 | headers => { 'content-type' => 'application/x-www-form-urlencoded' } 24 | }); 25 | 26 | print "$response->{status} $response->{reason}\n"; 27 | 28 | print $response->{content}; 29 | 30 | -------------------------------------------------------------------------------- /perlcritic.rc: -------------------------------------------------------------------------------- 1 | severity = 5 2 | verbose = 8 3 | 4 | [Variables::ProhibitPunctuationVars] 5 | allow = $@ $! 6 | 7 | [TestingAndDebugging::ProhibitNoStrict] 8 | allow = refs 9 | 10 | [Variables::ProhibitEvilVariables] 11 | variables = $DB::single 12 | 13 | # Turn these off 14 | [-BuiltinFunctions::ProhibitStringyEval] 15 | [-ControlStructures::ProhibitPostfixControls] 16 | [-ControlStructures::ProhibitUnlessBlocks] 17 | [-Documentation::RequirePodSections] 18 | [-InputOutput::ProhibitInteractiveTest] 19 | [-References::ProhibitDoubleSigils] 20 | [-RegularExpressions::RequireExtendedFormatting] 21 | [-InputOutput::ProhibitTwoArgOpen] 22 | [-Modules::ProhibitEvilModules] 23 | 24 | # Turn this on 25 | [Lax::ProhibitStringyEval::ExceptForRequire] 26 | 27 | -------------------------------------------------------------------------------- /t/000_load.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More 0.88 tests => 1; 7 | 8 | require_ok('HTTP::Tiny'); 9 | 10 | local $HTTP::Tiny::VERSION = $HTTP::Tiny::VERSION || 'from repo'; 11 | note("HTTP::Tiny $HTTP::Tiny::VERSION, Perl $], $^X"); 12 | 13 | -------------------------------------------------------------------------------- /t/001_api.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 2; 7 | use HTTP::Tiny; 8 | 9 | my @accessors = qw( 10 | agent default_headers http_proxy https_proxy keep_alive local_address 11 | max_redirect max_size proxy no_proxy timeout SSL_options verify_SSL cookie_jar 12 | ); 13 | my @methods = qw( 14 | new get head put post patch delete post_form request mirror www_form_urlencode can_ssl 15 | connected 16 | ); 17 | 18 | my %api; 19 | @api{@accessors} = (1) x @accessors; 20 | @api{@methods} = (1) x @methods; 21 | 22 | can_ok('HTTP::Tiny', @methods, @accessors); 23 | 24 | my @extra = 25 | grep {! $api{$_} } 26 | grep { $_ !~ /\A_/ } 27 | grep {; no strict 'refs'; *{"HTTP::Tiny::$_"}{CODE} } 28 | sort keys %HTTP::Tiny::; 29 | 30 | ok( ! scalar @extra, "No unexpected subroutines defined" ) 31 | or diag "Found: @extra"; 32 | -------------------------------------------------------------------------------- /t/002_croakage.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | use lib 't'; 8 | use Util qw[tmpfile monkey_patch set_socket_source clear_socket_source]; 9 | 10 | use HTTP::Tiny; 11 | 12 | BEGIN { monkey_patch() } 13 | 14 | my %usage = ( 15 | 'get' => q/Usage: $http->get(URL, [HASHREF])/, 16 | 'mirror' => q/Usage: $http->mirror(URL, FILE, [HASHREF])/, 17 | 'request' => q/Usage: $http->request(METHOD, URL, [HASHREF])/, 18 | ); 19 | 20 | my @cases = ( 21 | ['get'], 22 | ['get','http://www.example.com/','extra'], 23 | ['get','http://www.example.com/','extra', 'extra'], 24 | ['mirror'], 25 | ['mirror','http://www.example.com/',], 26 | ['mirror','http://www.example.com/','extra', 'extra'], 27 | ['mirror','http://www.example.com/','extra', 'extra', 'extra'], 28 | ['request'], 29 | ['request','GET'], 30 | ['request','GET','http://www.example.com/','extra'], 31 | ['request','GET','http://www.example.com/','extra', 'extra'], 32 | ); 33 | 34 | my $res_fh = tmpfile(); 35 | my $req_fh = tmpfile(); 36 | 37 | my $http = HTTP::Tiny->new; 38 | clear_socket_source(); 39 | set_socket_source($req_fh, $res_fh); 40 | 41 | for my $c ( @cases ) { 42 | my ($method, @args) = @$c; 43 | eval {$http->$method(@args)}; 44 | my $err = $@; 45 | like ($err, qr/\Q$usage{$method}\E/, join("|",@$c) ); 46 | } 47 | 48 | my $res = eval{ $http->get("http://www.example.com/", { headers => { host => "www.example2.com" } } ) }; 49 | is( $res->{status}, 599, "Providing a Host header errors with 599" ); 50 | like( $res->{content}, qr/'Host' header/, "Providing a Host header gives right error message" ); 51 | 52 | $res = eval { $http->head("hxxp://www.example.com/") }; 53 | is( $res->{status}, 599, "Error on unsupported scheme" ); 54 | like( 55 | $res->{content}, 56 | qr/Unsupported URL scheme 'hxxp'/, 57 | "Error for unsupported scheme" 58 | ); 59 | 60 | $res = eval { $http->post_form("http://www.example.com/", [undef, "123"]) }; 61 | my $err = $@; 62 | like( 63 | $err, 64 | qr/form data keys must not be undef/, 65 | "Error for undef key in form" 66 | ); 67 | 68 | done_testing; 69 | 70 | -------------------------------------------------------------------------------- /t/003_agent.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 8; 7 | use HTTP::Tiny; 8 | 9 | # a couple tests to ensure that we get the default agent expected, the correct 10 | # agent when specified, and the correct agent when specified with a space at 11 | # the end of the string (as LWP::UserAgent does) 12 | 13 | 14 | my $default = 'HTTP-Tiny/' . (HTTP::Tiny->VERSION || 0); 15 | 16 | { 17 | my $ua = HTTP::Tiny->new(); 18 | is $ua->agent, $default, 'default agent string is as expected'; 19 | } 20 | 21 | { 22 | my $ua = HTTP::Tiny->new(agent => 'something else'); 23 | is $ua->agent, 'something else', 'agent string is as expected'; 24 | } 25 | 26 | { 27 | my $ua = HTTP::Tiny->new(agent => 'something else '); 28 | is 29 | $ua->agent, 30 | "something else $default", 31 | 'agent string is as properly appended to', 32 | ; 33 | } 34 | 35 | { 36 | my $ua = HTTP::Tiny->new(); 37 | 38 | is( HTTP::Tiny->_agent(), $default, 'check _agent on class' ); 39 | is $ua->_agent(), $default, 'check _agent on object'; 40 | 41 | $ua->agent(undef); 42 | is $ua->agent, undef, 'agent string is empty'; 43 | 44 | $ua->agent('something else'); 45 | is $ua->agent, 'something else', 'agent string is as expected'; 46 | 47 | $ua->agent('something else '); 48 | is 49 | $ua->agent, 50 | "something else $default", 51 | 'agent string is as properly appended to', 52 | ; 53 | } 54 | -------------------------------------------------------------------------------- /t/004_timeout.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 5; 7 | use HTTP::Tiny; 8 | 9 | # Just make sure timeout is handled correctly as a constructor param, 10 | # and that it works as expected as an "attribute". 11 | 12 | my $default = 60; 13 | 14 | { 15 | my $ua = HTTP::Tiny->new(); 16 | is $ua->timeout, $default, 'default timeout is as expected'; 17 | } 18 | 19 | { 20 | my $ua = HTTP::Tiny->new(timeout => 10); 21 | is $ua->timeout, 10, 'timeout is handled as a constructor param'; 22 | } 23 | 24 | { 25 | my $ua = HTTP::Tiny->new(timeout => 0); 26 | is $ua->timeout, 0, 'constructor arg of timeout=0 is passed through'; 27 | } 28 | 29 | { 30 | my $ua = HTTP::Tiny->new(timeout => undef); 31 | is $ua->timeout, $default, 'constructor arg of timeout=undef is ignored'; 32 | } 33 | 34 | { 35 | my $ua = HTTP::Tiny->new(); 36 | $ua->timeout(15); 37 | is $ua->timeout, 15, 'timeout works as expected as a r/w attribute'; 38 | } 39 | -------------------------------------------------------------------------------- /t/010_url.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More 0.86; 7 | use HTTP::Tiny; 8 | 9 | my @tests = ( 10 | [ 'HtTp://Example.COM/', 'http', 'example.com', 80, '/', '', ], 11 | [ 'HtTp://Example.com:1024/', 'http', 'example.com', 1024, '/', '', ], 12 | [ 'http://example.com', 'http', 'example.com', 80, '/', '', ], 13 | [ 'http://example.com:', 'http', 'example.com', 80, '/', '', ], 14 | [ 'http://foo@example.com:', 'http', 'example.com', 80, '/', 'foo', ], 15 | [ 'http://foo:pass@example.com:', 'http', 'example.com', 80, '/', 'foo:pass', ], 16 | [ 'http://@example.com:', 'http', 'example.com', 80, '/', '', ], 17 | [ 'http://example.com?foo=bar', 'http', 'example.com', 80, '/?foo=bar', '', ], 18 | [ 'http://example.com?foo=bar#fragment', 'http', 'example.com', 80, '/?foo=bar', '', ], 19 | [ 'http://example.com/path?foo=bar', 'http', 'example.com', 80, '/path?foo=bar', '', ], 20 | [ 'http:///path?foo=bar', 'http', 'localhost', 80, '/path?foo=bar', '', ], 21 | [ 'HTTPS://example.com/', 'https', 'example.com', 443, '/', '', ], 22 | [ 'http://[::]:1024', 'http', '[::]', 1024, '/', '', ], 23 | [ 'xxx://foo/', 'xxx', 'foo', undef, '/', '', ], 24 | ); 25 | 26 | plan tests => scalar @tests; 27 | 28 | for my $test (@tests) { 29 | my $url = shift(@$test); 30 | my $got = [ HTTP::Tiny->_split_url($url) ]; 31 | my $exp = $test; 32 | is_deeply($got, $exp, "->split_url('$url')") or diag explain $got; 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /t/020_headers.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More qw[no_plan]; 7 | use lib 't'; 8 | use Util qw[tmpfile rewind $CRLF $LF]; 9 | use HTTP::Tiny; 10 | 11 | { 12 | no warnings 'redefine'; 13 | sub HTTP::Tiny::Handle::can_read { 1 }; 14 | sub HTTP::Tiny::Handle::can_write { 1 }; 15 | } 16 | 17 | { 18 | my $header = join $CRLF, 'Foo: Foo', 'Foo: Baz', 'Bar: Bar', '', ''; 19 | my $fh = tmpfile($header); 20 | my $exp = { foo => ['Foo', 'Baz'], bar => 'Bar' }; 21 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 22 | my $got = $handle->read_header_lines; 23 | is_deeply($got, $exp, "->read_header_lines() CRLF"); 24 | } 25 | 26 | { 27 | my $header = join $LF, 'Foo: Foo', 'Foo: Baz', 'Bar: Bar', '', ''; 28 | my $fh = tmpfile($header); 29 | my $exp = { foo => ['Foo', 'Baz'], bar => 'Bar' }; 30 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 31 | my $got = $handle->read_header_lines; 32 | is_deeply($got, $exp, "->read_header_lines() LF"); 33 | } 34 | 35 | { 36 | my $header = "Foo: $CRLF\x09Bar$CRLF\x09$CRLF\x09Baz$CRLF$CRLF"; 37 | my $fh = tmpfile($header); 38 | my $exp = { foo => 'Bar Baz' }; 39 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 40 | my $got = $handle->read_header_lines; 41 | is_deeply($got, $exp, "->read_header_lines() insane continuations"); 42 | } 43 | 44 | { 45 | my $fh = tmpfile(); 46 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 47 | my $headers = { foo => ['Foo', 'Baz'], bar => 'Bar' }; 48 | $handle->write_header_lines($headers); 49 | rewind($fh); 50 | is_deeply($handle->read_header_lines, $headers, "roundtrip header lines"); 51 | } 52 | 53 | { 54 | my $fh = tmpfile(); 55 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 56 | my $headers = { foo => ['Foo', 'Baz'], bar => 'Bar', baz => '' }; 57 | $handle->write_header_lines($headers); 58 | rewind($fh); 59 | is_deeply($handle->read_header_lines, $headers, "roundtrip header lines"); 60 | } 61 | 62 | -------------------------------------------------------------------------------- /t/030_response.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More qw[no_plan]; 7 | use lib 't'; 8 | use Util qw[tmpfile rewind $CRLF $LF]; 9 | use HTTP::Tiny; 10 | 11 | sub _header { 12 | return [ @{$_[0]}{qw/status reason headers protocol/} ] 13 | } 14 | 15 | { 16 | no warnings 'redefine'; 17 | sub HTTP::Tiny::Handle::can_read { 1 }; 18 | sub HTTP::Tiny::Handle::can_write { 1 }; 19 | } 20 | 21 | { 22 | my $response = join $CRLF, 'HTTP/1.1 200 OK', 'Foo: Foo', 'Bar: Bar', '', ''; 23 | my $fh = tmpfile($response); 24 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 25 | my $exp = [ 200, 'OK', { foo => 'Foo', bar => 'Bar' }, 'HTTP/1.1' ]; 26 | is_deeply(_header($handle->read_response_header), $exp, "->read_response_header CRLF"); 27 | } 28 | 29 | { 30 | my $response = join $LF, 'HTTP/1.1 200 OK', 'Foo: Foo', 'Bar: Bar', '', ''; 31 | my $fh = tmpfile($response); 32 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 33 | my $exp = [ 200, 'OK', { foo => 'Foo', bar => 'Bar' }, 'HTTP/1.1' ]; 34 | is_deeply(_header($handle->read_response_header), $exp, "->read_response_header LF"); 35 | } 36 | 37 | { 38 | # broken status-line 39 | my $response = join $LF, "HTTP/08.15 66x Foo\nbar", 'Foo: Foo', 'Bar: Bar', '', ''; 40 | my $fh = tmpfile($response); 41 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 42 | my $res = eval{ $handle->read_response_header }; 43 | my $err = $@; 44 | like $err, qr/Malformed Status-Line: /, '->read_response_header diagnoses malformed status line'; 45 | } 46 | 47 | { 48 | my $response = join $LF, "HTTP/2.0 200 Okish", 'Foo: Foo', 'Bar: Bar', '', ''; 49 | my $fh = tmpfile($response); 50 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 51 | my $res = eval{ $handle->read_response_header }; 52 | my $err = $@; 53 | like $err, qr/Unsupported HTTP protocol: /, '->read_response_header unsupported HTTP protocol'; 54 | } 55 | 56 | { 57 | # strict RFC7230#3.1.2 compliance, require space after code 58 | my $response = join $LF, 'HTTP/1.1 200 ', 'Foo: Foo', 'Bar: Bar', '', ''; 59 | my $fh = tmpfile($response); 60 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 61 | my $exp = [ 200, '', { foo => 'Foo', bar => 'Bar' }, 'HTTP/1.1' ]; 62 | is_deeply(_header($handle->read_response_header), $exp, "->read_response_header empty phrase preceded by SP"); 63 | } 64 | 65 | { 66 | # practical RFC7230#3.1.2 interpretation, require space after code 67 | # only if there is a reason-phrase 68 | my $response = join $LF, 'HTTP/1.1 200', 'Foo: Foo', 'Bar: Bar', '', ''; 69 | my $fh = tmpfile($response); 70 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 71 | my $exp = [ 200, '', { foo => 'Foo', bar => 'Bar' }, 'HTTP/1.1' ]; 72 | is_deeply(_header($handle->read_response_header), $exp, "->read_response_header empty phrase without preceding SP"); 73 | } 74 | -------------------------------------------------------------------------------- /t/040_content.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More qw[no_plan]; 7 | use lib 't'; 8 | use Util qw[tmpfile rewind $CRLF $LF]; 9 | use HTTP::Tiny; 10 | 11 | { 12 | no warnings 'redefine'; 13 | sub HTTP::Tiny::Handle::can_read { 1 }; 14 | sub HTTP::Tiny::Handle::can_write { 1 }; 15 | } 16 | 17 | { 18 | my $chunk = join('', '0' .. '9', 'A' .. 'Z', 'a' .. 'z', '_', $LF) x 16; # 1024 19 | my $fh = tmpfile(); 20 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 21 | my $nchunks = 128; 22 | my $length = $nchunks * length $chunk; 23 | 24 | { 25 | my $request = { 26 | cb => sub { $nchunks-- ? $chunk : undef }, 27 | headers => { 'content-length' => $length } 28 | }; 29 | my $got = $handle->write_content_body($request); 30 | is($got, $length, "written $length octets"); 31 | } 32 | 33 | rewind($fh); 34 | 35 | { 36 | my $got = 0; 37 | $handle->read_content_body(sub { $got += length $_[0] }, {}, $length); 38 | is($got, $length, "read $length octets"); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /t/050_chunked_body.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More qw[no_plan]; 7 | use lib 't'; 8 | use Util qw[tmpfile rewind $CRLF]; 9 | use HTTP::Tiny; 10 | 11 | { 12 | no warnings 'redefine'; 13 | sub HTTP::Tiny::Handle::can_read { 1 }; 14 | sub HTTP::Tiny::Handle::can_write { 1 }; 15 | } 16 | 17 | { 18 | my $fh = tmpfile(); 19 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 20 | 21 | my $exp = ['A'..'Z']; 22 | my $got = []; 23 | 24 | { 25 | my @chunks = @$exp; 26 | my $request = { 27 | cb => sub { shift @chunks }, 28 | }; 29 | $handle->write_chunked_body($request); 30 | } 31 | 32 | rewind($fh); 33 | 34 | { 35 | my $cb = sub { push @$got, $_[0] }; 36 | my $response = { headers => {} }; 37 | $handle->read_chunked_body($cb, $response); 38 | } 39 | 40 | is_deeply($got, $exp, "roundtrip chunked chunks w/o trailers"); 41 | } 42 | 43 | { 44 | my $fh = tmpfile(); 45 | my $handle = HTTP::Tiny::Handle->new(fh => $fh); 46 | 47 | my $exp = ['A'..'Z']; 48 | my $trailers = { foo => 'Bar', bar => 'Baz' }; 49 | my $got = []; 50 | 51 | { 52 | my @chunks = @$exp; 53 | my $request = { 54 | cb => sub { shift @chunks }, 55 | trailer_cb => sub { $trailers }, 56 | }; 57 | $handle->write_chunked_body($request); 58 | } 59 | 60 | rewind($fh); 61 | 62 | { 63 | my $cb = sub { push @$got, $_[0] }; 64 | my $response = { headers => {} }; 65 | $handle->read_chunked_body($cb, $response); 66 | is_deeply($response->{headers}, $trailers, 'roundtrip chunked trailers'); 67 | } 68 | 69 | is_deeply($got, $exp, "roundtrip chunked chunks (with trailers)"); 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /t/060_http_date.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | use HTTP::Tiny; 8 | 9 | # test cases adapted from HTTP::Date 10 | my $epoch = 760233600; 11 | 12 | my @cases = ( 13 | ['Thu, 03 Feb 1994 00:00:00 GMT', 'RFC822+RFC1123'], 14 | ['Thu, 3 Feb 1994 00:00:00 GMT', 'broken RFC822+RFC1123'], 15 | ['Thursday, 03-Feb-94 00:00:00 GMT', 'old rfc850 HTTP format'], 16 | ['Thursday, 03-Feb-1994 00:00:00 GMT', 'broken rfc850 HTTP format'], 17 | ['Thu Feb 3 00:00:00 GMT 1994', 'ctime format'], 18 | ['Thu Feb 3 00:00:00 1994', 'same as ctime, except no TZ'], 19 | ); 20 | 21 | plan tests => 1 + @cases; 22 | 23 | is(HTTP::Tiny->_http_date($epoch), $cases[0][0], "epoch -> RFC822/RFC1123"); 24 | 25 | for my $c ( @cases ) { 26 | is( HTTP::Tiny->_parse_http_date($c->[0]), $epoch, $c->[1] . " -> epoch"); 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /t/070_cookie_jar.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 4; 7 | use lib 't'; 8 | use SimpleCookieJar; 9 | use BrokenCookieJar; 10 | use HTTP::Tiny; 11 | 12 | ### a couple tests to ensure that: 13 | ### * by default there is no cookie jar defined 14 | ### * the correct cookie jar is returned when specified 15 | ### * error when cookie jar does not support the add and cookie_header methods 16 | 17 | 18 | my $default = undef; 19 | my $jar = SimpleCookieJar->new(); 20 | my $mug = BrokenCookieJar->new(); 21 | my $dog = BrokenCookieJar2->new(); 22 | 23 | { 24 | my $ua = HTTP::Tiny->new(); 25 | is $ua->cookie_jar, $default, 'default cookie jar is as expected'; 26 | } 27 | 28 | { 29 | my $ua = HTTP::Tiny->new(cookie_jar => $jar); 30 | is $ua->cookie_jar, $jar, 'cookie_jar is as expected'; 31 | } 32 | 33 | { 34 | my $ua = eval { HTTP::Tiny->new(cookie_jar => $mug) }; 35 | my $err = $@; 36 | like( $err, qr/must provide .* 'add' method/ 37 | => 'invalid jar does not support add method' ); 38 | 39 | $ua = eval { HTTP::Tiny->new(cookie_jar => $dog) }; 40 | $err = $@; 41 | like( $err, qr/must provide .* 'cookie_header' method/ 42 | => 'invalid jar does not support cookie_header method' ); 43 | } 44 | -------------------------------------------------------------------------------- /t/100_get.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | hashify connect_args clear_socket_source set_socket_source sort_headers 11 | $CRLF $LF]; 12 | 13 | use HTTP::Tiny; 14 | BEGIN { monkey_patch() } 15 | 16 | for my $file ( dir_list("corpus", qr/^get/ ) ) { 17 | my $label = basename($file); 18 | my $data = do { local (@ARGV,$/) = $file; <> }; 19 | my ($params, $expect_req, $give_res) = split /--+\n/, $data; 20 | my $case = parse_case($params); 21 | 22 | my $url = $case->{url}[0]; 23 | my %headers = hashify( $case->{headers} ); 24 | my %new_args = hashify( $case->{new_args} ); 25 | 26 | my %options; 27 | $options{headers} = \%headers if %headers; 28 | if ( $case->{data_cb} ) { 29 | $main::data = ''; 30 | $options{data_callback} = eval join "\n", @{$case->{data_cb}}; 31 | die unless ref( $options{data_callback} ) eq 'CODE'; 32 | } 33 | 34 | my $version = HTTP::Tiny->VERSION || 0; 35 | my $agent = $new_args{agent} || "HTTP-Tiny/$version"; 36 | 37 | # cleanup source data 38 | $expect_req =~ s{HTTP-Tiny/VERSION}{$agent}; 39 | s{\n}{$CRLF}g for ($expect_req, $give_res); 40 | 41 | # setup mocking and test 42 | my $res_fh = tmpfile($give_res); 43 | my $req_fh = tmpfile(); 44 | 45 | my $http = HTTP::Tiny->new(keep_alive => 0, %new_args); 46 | clear_socket_source(); 47 | set_socket_source($req_fh, $res_fh); 48 | 49 | (my $url_basename = $url) =~ s{.*/}{}; 50 | 51 | my @call_args = %options ? ($url, \%options) : ($url); 52 | my $response = $http->get(@call_args); 53 | 54 | my ($got_host, $got_port) = connect_args(); 55 | my ($exp_host, $exp_port) = ( 56 | ($new_args{proxy} || $url ) =~ m{^http://([^:/]+?):?(\d*)/}g 57 | ); 58 | $exp_host ||= 'localhost'; 59 | $exp_port ||= 80; 60 | 61 | my $got_req = slurp($req_fh); 62 | 63 | is ($got_host, $exp_host, "$label host $exp_host"); 64 | is ($got_port, $exp_port, "$label port $exp_port"); 65 | is( sort_headers($got_req), sort_headers($expect_req), "$label request data"); 66 | 67 | my ($rc) = $give_res =~ m{\S+\s+(\d+)}g; 68 | # maybe override 69 | $rc = $case->{expected_rc}[0] if defined $case->{expected_rc}; 70 | 71 | is( $response->{status}, $rc, "$label response code $rc" ) 72 | or diag $response->{content}; 73 | 74 | if ( substr($rc,0,1) eq '2' ) { 75 | ok( $response->{success}, "$label success flag true" ); 76 | } 77 | else { 78 | ok( ! $response->{success}, "$label success flag false" ); 79 | } 80 | 81 | is ( $response->{url}, $url, "$label response URL" ); 82 | 83 | if (defined $case->{expected_headers}) { 84 | my %expected = hashify( $case->{expected_headers} ); 85 | is_deeply($response->{headers}, \%expected, "$label expected headers"); 86 | } 87 | 88 | my $check_expected = $case->{expected_like} 89 | ? sub { 90 | my ($text, $msg) = @_; 91 | like( $text, "/".$case->{expected_like}[0]."/", $msg ); 92 | } 93 | : sub { 94 | my ($text, $msg) = @_; 95 | my $exp_content = 96 | $case->{expected} ? join("$CRLF", @{$case->{expected}}, '') : ''; 97 | is ( $text, $exp_content, $msg ); 98 | } 99 | ; 100 | 101 | 102 | 103 | if ( $options{data_callback} ) { 104 | $check_expected->( $main::data, "$label cb got content" ); 105 | is ( $response->{content}, '', "$label resp content empty" ); 106 | } 107 | else { 108 | $check_expected->( $response->{content}, "$label content" ); 109 | } 110 | 111 | ok ( ! exists $response->{redirects}, "$label redirects array doesn't exist") 112 | or diag explain $response->{redirects}; 113 | } 114 | 115 | done_testing; 116 | -------------------------------------------------------------------------------- /t/101_head.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | clear_socket_source set_socket_source sort_headers $CRLF $LF]; 11 | use HTTP::Tiny; 12 | BEGIN { monkey_patch() } 13 | 14 | for my $file ( dir_list("corpus", qr/^head/ ) ) { 15 | my $data = do { local (@ARGV,$/) = $file; <> }; 16 | my ($params, $expect_req, $give_res) = split /--+\n/, $data; 17 | # cleanup source data 18 | my $version = HTTP::Tiny->VERSION || 0; 19 | $expect_req =~ s{VERSION}{$version}; 20 | s{\n}{$CRLF}g for ($expect_req, $give_res); 21 | 22 | # figure out what request to make 23 | my $case = parse_case($params); 24 | my $url = $case->{url}[0]; 25 | my %options; 26 | 27 | my %headers; 28 | for my $line ( @{ $case->{headers} } ) { 29 | my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g); 30 | $headers{$k} = $v; 31 | } 32 | $options{headers} = \%headers if %headers; 33 | 34 | if ( $case->{content} ) { 35 | $options{content} = $case->{content}[0]; 36 | } 37 | elsif ( $case->{content_cb} ) { 38 | $options{content} = eval join "\n", @{$case->{content_cb}}; 39 | } 40 | 41 | if ( $case->{trailer_cb} ) { 42 | $options{trailer_callback} = eval join "\n", @{$case->{trailer_cb}}; 43 | } 44 | 45 | # setup mocking and test 46 | my $res_fh = tmpfile($give_res); 47 | my $req_fh = tmpfile(); 48 | 49 | my $http = HTTP::Tiny->new( keep_alive => 0 ); 50 | clear_socket_source(); 51 | set_socket_source($req_fh, $res_fh); 52 | 53 | (my $url_basename = $url) =~ s{.*/}{}; 54 | 55 | my @call_args = %options ? ($url, \%options) : ($url); 56 | my $response = $http->head(@call_args); 57 | 58 | my $got_req = slurp($req_fh); 59 | 60 | my $label = basename($file); 61 | 62 | is( sort_headers($got_req), sort_headers($expect_req), "$label request" ); 63 | 64 | my ($rc) = $give_res =~ m{\S+\s+(\d+)}g; 65 | is( $response->{status}, $rc, "$label response code $rc" ) 66 | or diag $response->{content}; 67 | 68 | if ( substr($rc,0,1) eq '2' ) { 69 | ok( $response->{success}, "$label success flag true" ); 70 | } 71 | else { 72 | ok( ! $response->{success}, "$label success flag false" ); 73 | } 74 | } 75 | 76 | done_testing; 77 | -------------------------------------------------------------------------------- /t/102_put.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | clear_socket_source set_socket_source sort_headers $CRLF $LF]; 11 | use HTTP::Tiny; 12 | BEGIN { monkey_patch() } 13 | 14 | for my $file ( dir_list("corpus", qr/^put/ ) ) { 15 | my $data = do { local (@ARGV,$/) = $file; <> }; 16 | my ($params, $expect_req, $give_res) = split /--+\n/, $data; 17 | # cleanup source data 18 | my $version = HTTP::Tiny->VERSION || 0; 19 | $expect_req =~ s{VERSION}{$version}; 20 | s{\n}{$CRLF}g for ($expect_req, $give_res); 21 | 22 | # figure out what request to make 23 | my $case = parse_case($params); 24 | my $url = $case->{url}[0]; 25 | my %options; 26 | 27 | my %headers; 28 | for my $line ( @{ $case->{headers} } ) { 29 | my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g); 30 | $headers{$k} = $v; 31 | } 32 | $options{headers} = \%headers if %headers; 33 | 34 | if ( $case->{content} ) { 35 | $options{content} = $case->{content}[0]; 36 | } 37 | elsif ( exists $case->{content} ) { 38 | $options{content} = ""; 39 | } 40 | elsif ( $case->{content_cb} ) { 41 | $options{content} = eval join "\n", @{$case->{content_cb}}; 42 | } 43 | 44 | if ( $case->{trailer_cb} ) { 45 | $options{trailer_callback} = eval join "\n", @{$case->{trailer_cb}}; 46 | } 47 | 48 | # setup mocking and test 49 | my $res_fh = tmpfile($give_res); 50 | my $req_fh = tmpfile(); 51 | 52 | my $http = HTTP::Tiny->new( keep_alive => 0 ); 53 | clear_socket_source(); 54 | set_socket_source($req_fh, $res_fh); 55 | 56 | (my $url_basename = $url) =~ s{.*/}{}; 57 | 58 | my @call_args = %options ? ($url, \%options) : ($url); 59 | my $response = $http->put(@call_args); 60 | 61 | my $got_req = slurp($req_fh); 62 | 63 | my $label = basename($file); 64 | 65 | is( sort_headers($got_req), sort_headers($expect_req), "$label request" ); 66 | 67 | my ($rc) = $give_res =~ m{\S+\s+(\d+)}g; 68 | is( $response->{status}, $rc, "$label response code $rc" ) 69 | or diag $response->{content}; 70 | 71 | if ( substr($rc,0,1) eq '2' ) { 72 | ok( $response->{success}, "$label success flag true" ); 73 | } 74 | else { 75 | ok( ! $response->{success}, "$label success flag false" ); 76 | } 77 | } 78 | 79 | done_testing; 80 | -------------------------------------------------------------------------------- /t/103_delete.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | clear_socket_source set_socket_source sort_headers $CRLF $LF]; 11 | use HTTP::Tiny; 12 | BEGIN { monkey_patch() } 13 | 14 | for my $file ( dir_list("corpus", qr/^delete/ ) ) { 15 | my $data = do { local (@ARGV,$/) = $file; <> }; 16 | my ($params, $expect_req, $give_res) = split /--+\n/, $data; 17 | # cleanup source data 18 | my $version = HTTP::Tiny->VERSION || 0; 19 | $expect_req =~ s{VERSION}{$version}; 20 | s{\n}{$CRLF}g for ($expect_req, $give_res); 21 | 22 | # figure out what request to make 23 | my $case = parse_case($params); 24 | my $url = $case->{url}[0]; 25 | my %options; 26 | 27 | my %headers; 28 | for my $line ( @{ $case->{headers} } ) { 29 | my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g); 30 | $headers{$k} = $v; 31 | } 32 | $options{headers} = \%headers if %headers; 33 | 34 | if ( $case->{content} ) { 35 | $options{content} = $case->{content}[0]; 36 | } 37 | elsif ( $case->{content_cb} ) { 38 | $options{content} = eval join "\n", @{$case->{content_cb}}; 39 | } 40 | 41 | if ( $case->{trailer_cb} ) { 42 | $options{trailer_callback} = eval join "\n", @{$case->{trailer_cb}}; 43 | } 44 | 45 | # setup mocking and test 46 | my $res_fh = tmpfile($give_res); 47 | my $req_fh = tmpfile(); 48 | 49 | my $http = HTTP::Tiny->new( keep_alive => 0 ); 50 | clear_socket_source(); 51 | set_socket_source($req_fh, $res_fh); 52 | 53 | (my $url_basename = $url) =~ s{.*/}{}; 54 | 55 | my @call_args = %options ? ($url, \%options) : ($url); 56 | my $response = $http->delete(@call_args); 57 | 58 | my $got_req = slurp($req_fh); 59 | 60 | my $label = basename($file); 61 | 62 | is( sort_headers($got_req), sort_headers($expect_req), "$label request" ); 63 | 64 | my ($rc) = $give_res =~ m{\S+\s+(\d+)}g; 65 | is( $response->{status}, $rc, "$label response code $rc" ) 66 | or diag $response->{content}; 67 | 68 | if ( substr($rc,0,1) eq '2' ) { 69 | ok( $response->{success}, "$label success flag true" ); 70 | } 71 | else { 72 | ok( ! $response->{success}, "$label success flag false" ); 73 | } 74 | } 75 | 76 | done_testing; 77 | -------------------------------------------------------------------------------- /t/104_post.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | clear_socket_source set_socket_source sort_headers $CRLF $LF]; 11 | use HTTP::Tiny; 12 | BEGIN { monkey_patch() } 13 | 14 | for my $file ( dir_list("corpus", qr/^post/ ) ) { 15 | my $data = do { local (@ARGV,$/) = $file; <> }; 16 | my ($params, $expect_req, $give_res) = split /--+\n/, $data; 17 | # cleanup source data 18 | my $version = HTTP::Tiny->VERSION || 0; 19 | $expect_req =~ s{VERSION}{$version}; 20 | s{\n}{$CRLF}g for ($expect_req, $give_res); 21 | 22 | # figure out what request to make 23 | my $case = parse_case($params); 24 | my $url = $case->{url}[0]; 25 | my %options; 26 | 27 | my %headers; 28 | for my $line ( @{ $case->{headers} } ) { 29 | my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g); 30 | $headers{$k} = $v; 31 | } 32 | $options{headers} = \%headers if %headers; 33 | 34 | if ( $case->{content} ) { 35 | $options{content} = $case->{content}[0]; 36 | } 37 | elsif ( $case->{content_cb} ) { 38 | $options{content} = eval join "\n", @{$case->{content_cb}}; 39 | } 40 | 41 | if ( $case->{trailer_cb} ) { 42 | $options{trailer_callback} = eval join "\n", @{$case->{trailer_cb}}; 43 | } 44 | 45 | # setup mocking and test 46 | my $res_fh = tmpfile($give_res); 47 | my $req_fh = tmpfile(); 48 | 49 | my $http = HTTP::Tiny->new( keep_alive => 0 ); 50 | clear_socket_source(); 51 | set_socket_source($req_fh, $res_fh); 52 | 53 | (my $url_basename = $url) =~ s{.*/}{}; 54 | 55 | my @call_args = %options ? ($url, \%options) : ($url); 56 | my $response = $http->post(@call_args); 57 | 58 | my $got_req = slurp($req_fh); 59 | 60 | my $label = basename($file); 61 | 62 | is( sort_headers($got_req), sort_headers($expect_req), "$label request" ); 63 | 64 | my ($rc) = $give_res =~ m{\S+\s+(\d+)}g; 65 | is( $response->{status}, $rc, "$label response code $rc" ) 66 | or diag $response->{content}; 67 | 68 | if ( substr($rc,0,1) eq '2' ) { 69 | ok( $response->{success}, "$label success flag true" ); 70 | } 71 | else { 72 | ok( ! $response->{success}, "$label success flag false" ); 73 | } 74 | } 75 | 76 | done_testing; 77 | -------------------------------------------------------------------------------- /t/110_mirror.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | clear_socket_source set_socket_source sort_headers $CRLF $LF]; 11 | use HTTP::Tiny; 12 | use File::Temp qw/tempdir/; 13 | use File::Spec; 14 | 15 | BEGIN { monkey_patch() } 16 | 17 | my $tempdir = tempdir( TMPDIR => 1, CLEANUP => 1 ); 18 | my $tempfile = File::Spec->catfile( $tempdir, "tempfile.txt" ); 19 | 20 | my $known_epoch = 760233600; 21 | my $day = 24*3600; 22 | 23 | my %timestamp = ( 24 | 'modified.txt' => $known_epoch - 2 * $day, 25 | 'not-modified.txt' => $known_epoch - 2 * $day, 26 | ); 27 | 28 | for my $file ( dir_list("corpus", qr/^mirror/ ) ) { 29 | 1 while unlink $tempfile; 30 | my $data = do { local (@ARGV,$/) = $file; <> }; 31 | my ($params, $expect_req, $give_res) = split /--+\n/, $data; 32 | # cleanup source data 33 | my $version = HTTP::Tiny->VERSION || 0; 34 | $expect_req =~ s{VERSION}{$version}; 35 | s{\n}{$CRLF}g for ($expect_req, $give_res); 36 | 37 | # figure out what request to make 38 | my $case = parse_case($params); 39 | my $url = $case->{url}->[0]; 40 | my %options; 41 | 42 | my %headers; 43 | for my $line ( @{ $case->{headers} } ) { 44 | my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g); 45 | $headers{$k} = $v; 46 | } 47 | $options{headers} = \%headers if %headers; 48 | 49 | # maybe create a file 50 | (my $url_basename = $url) =~ s{.*/}{}; 51 | if ( my $mtime = $timestamp{$url_basename} ) { 52 | open my $fh, ">", $tempfile; 53 | close $fh; 54 | utime $mtime, $mtime, $tempfile; 55 | if ($^O eq 'MSWin32') { 56 | # Deal with stat and daylight savings issues on Windows 57 | # by reading back mtime 58 | $timestamp{$url_basename} = (stat $tempfile)[9]; 59 | } 60 | } 61 | 62 | # setup mocking and test 63 | my $res_fh = tmpfile($give_res); 64 | my $req_fh = tmpfile(); 65 | 66 | my $http = HTTP::Tiny->new( keep_alive => 0 ); 67 | clear_socket_source(); 68 | set_socket_source($req_fh, $res_fh); 69 | 70 | my @call_args = %options ? ($url, $tempfile, \%options) : ($url, $tempfile); 71 | my $response = $http->mirror(@call_args); 72 | 73 | my $got_req = slurp($req_fh); 74 | 75 | my $label = basename($file); 76 | 77 | is( sort_headers($got_req), sort_headers($expect_req), "$label request" ); 78 | 79 | my ($rc) = $give_res =~ m{\S+\s+(\d+)}g; 80 | is( $response->{status}, $rc, "$label response code $rc" ) 81 | or diag $response->{content}; 82 | 83 | if ( substr($rc,0,1) eq '2' ) { 84 | ok( $response->{success}, "$label success flag true" ); 85 | ok( -e $tempfile, "$label file created" ); 86 | } 87 | elsif ( $rc eq '304' ) { 88 | ok( $response->{success}, "$label success flag true" ); 89 | is( (stat($tempfile))[9], $timestamp{$url_basename}, 90 | "$label file not overwritten" ); 91 | } 92 | else { 93 | ok( ! $response->{success}, "$label success flag false" ); 94 | ok( ! -e $tempfile, "$label file not created" ); 95 | } 96 | } 97 | 98 | done_testing; 99 | -------------------------------------------------------------------------------- /t/130_redirect.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | hashify connect_args clear_socket_source set_socket_source sort_headers 11 | $CRLF $LF]; 12 | 13 | use HTTP::Tiny; 14 | BEGIN { monkey_patch() } 15 | 16 | for my $file ( dir_list("corpus", qr/^redirect/ ) ) { 17 | my $label = basename($file); 18 | my $data = do { local (@ARGV,$/) = $file; <> }; 19 | my ($params, @case_pairs) = split /--+\n/, $data; 20 | my $case = parse_case($params); 21 | my $number_of_requests = @case_pairs / 2; 22 | 23 | my $url = $case->{url}[0]; 24 | my $method = $case->{method}[0] || 'GET'; 25 | my %headers = hashify( $case->{headers} ); 26 | my %new_args = hashify( $case->{new_args} ); 27 | 28 | my %options; 29 | $options{headers} = \%headers if %headers; 30 | my $call_args = %options ? [$method, $url, \%options] : [$method, $url]; 31 | 32 | my $version = HTTP::Tiny->VERSION || 0; 33 | my $agent = $new_args{agent} || "HTTP-Tiny/$version"; 34 | 35 | my (@socket_pairs); 36 | while ( @case_pairs ) { 37 | my ($expect_req, $give_res) = splice( @case_pairs, 0, 2 ); 38 | # cleanup source data 39 | $expect_req =~ s{HTTP-Tiny/VERSION}{$agent}; 40 | s{\n}{$CRLF}g for ($expect_req, $give_res); 41 | 42 | # setup mocking and test 43 | my $req_fh = tmpfile(); 44 | my $res_fh = tmpfile($give_res); 45 | 46 | push @socket_pairs, [$req_fh, $res_fh, $expect_req]; 47 | } 48 | 49 | clear_socket_source(); 50 | set_socket_source(@$_) for @socket_pairs; 51 | 52 | my $http = HTTP::Tiny->new(keep_alive => 0, %new_args); 53 | my $response = $http->request(@$call_args); 54 | my $max_redirects = defined($new_args{max_redirect}) ? $new_args{max_redirect} : 5; 55 | my $calls = 0 + $max_redirects; 56 | 57 | for my $i ( 0 .. $calls ) { 58 | last unless @socket_pairs; 59 | my ($req_fh, $res_fh, $expect_req) = @{ shift @socket_pairs }; 60 | my $got_req = slurp($req_fh); 61 | is( sort_headers($got_req), sort_headers($expect_req), "$label request ($i)"); 62 | $i++; 63 | } 64 | 65 | my $exp_content = $case->{expected} 66 | ? join("$CRLF", @{$case->{expected}}) : ''; 67 | 68 | is ( $response->{content}, $exp_content, "$label content" ); 69 | 70 | my $number_of_redirects = 71 | $max_redirects < $number_of_requests ? $max_redirects : $number_of_requests - 1; 72 | is ( @{ $response->{redirects} || [] }, $number_of_redirects, 73 | "$label redirects array size" 74 | ) or diag explain $response->{redirects}; 75 | 76 | if ( $case->{expected_url} ) { 77 | is ( $response->{url}, $case->{expected_url}[0], "$label response URL" ); 78 | } 79 | 80 | } 81 | 82 | done_testing; 83 | -------------------------------------------------------------------------------- /t/140_proxy.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | 9 | use lib 't'; 10 | use Util qw[ monkey_patch ]; 11 | use HTTP::Tiny; 12 | 13 | BEGIN { 14 | monkey_patch(); 15 | } 16 | 17 | 18 | # Require a true value 19 | for my $proxy (undef, "", 0){ 20 | no warnings 'uninitialized'; 21 | local $ENV{all_proxy}; 22 | local $ENV{ALL_PROXY}; 23 | local $ENV{http_proxy} = $proxy; 24 | my $c = HTTP::Tiny->new(); 25 | ok(!defined $c->http_proxy); 26 | } 27 | 28 | # trailing / is optional 29 | for my $proxy ("http://localhost:8080/", "http://localhost:8080"){ 30 | local $ENV{http_proxy} = $proxy; 31 | my $c = HTTP::Tiny->new(); 32 | is($c->http_proxy, $proxy); 33 | } 34 | 35 | # http_proxy must be http://: format 36 | { 37 | local $ENV{http_proxy} = "localhost:8080"; 38 | eval { 39 | my $c = HTTP::Tiny->new(); 40 | }; 41 | like($@, qr{http_proxy URL must be in format http\[s\]://\[auth\@\]:/}); 42 | } 43 | 44 | # Explicitly disable proxy 45 | { 46 | local $ENV{all_proxy} = "http://localhost:8080"; 47 | local $ENV{http_proxy} = "http://localhost:8080"; 48 | local $ENV{https_proxy} = "http://localhost:8080"; 49 | my $c = HTTP::Tiny->new( 50 | proxy => undef, 51 | http_proxy => undef, 52 | https_proxy => undef, 53 | ); 54 | ok(!defined $c->proxy, "proxy => undef disables ENV proxy"); 55 | ok(!defined $c->http_proxy, "http_proxy => undef disables ENV proxy"); 56 | ok(!defined $c->https_proxy, "https_proxy => undef disables ENV proxy"); 57 | } 58 | 59 | # case variations 60 | for my $var ( qw/http_proxy https_proxy all_proxy/ ) { 61 | my $proxy = "http://localhost:8080"; 62 | for my $s ( uc($var), lc($var) ) { 63 | local $ENV{$s} = $proxy; 64 | my $c = HTTP::Tiny->new(); 65 | my $m = ($s =~ /all/i) ? 'proxy' : lc($s); 66 | is( $c->$m, $proxy, "set $m from $s" ); 67 | } 68 | } 69 | 70 | # ignore HTTP_PROXY with REQUEST_METHOD 71 | { 72 | # in case previous clean-up failed for some reason 73 | delete local @ENV{'http_proxy', 'https_proxy', 'all_proxy', 74 | 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY'}; 75 | 76 | local $ENV{HTTP_PROXY} = "http://localhost:8080"; 77 | local $ENV{REQUEST_METHOD} = 'GET'; 78 | my $c = HTTP::Tiny->new(); 79 | ok(!defined $c->http_proxy, 80 | "http_proxy not set from HTTP_PROXY if REQUEST_METHOD set"); 81 | 82 | } 83 | 84 | # allow CGI_HTTP_PROXY with REQUEST_METHOD 85 | { 86 | local $ENV{HTTP_PROXY} = "http://localhost:8080"; 87 | local $ENV{CGI_HTTP_PROXY} = "http://localhost:9090"; 88 | local $ENV{REQUEST_METHOD} = 'GET'; 89 | my $c = HTTP::Tiny->new(); 90 | is($c->http_proxy, "http://localhost:9090", 91 | "http_proxy set from CGI_HTTP_PROXY if REQUEST_METHOD set"); 92 | } 93 | 94 | done_testing(); 95 | -------------------------------------------------------------------------------- /t/141_no_proxy.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | 5 | use Test::More 0.88; 6 | 7 | use HTTP::Tiny; 8 | 9 | # blank slate for testing 10 | delete $ENV{no_proxy}; 11 | 12 | { 13 | my $c = HTTP::Tiny->new(); 14 | is_deeply( $c->no_proxy, [], "no no_proxy given" ); 15 | } 16 | 17 | my @cases = ( 18 | #<<< No perltidy 19 | { 20 | no_proxy => [ 21 | undef, 22 | [], 23 | ], 24 | expect => [], 25 | }, 26 | { 27 | no_proxy => [ 28 | "localhost", 29 | ["localhost"], 30 | ], 31 | expect => ["localhost"], 32 | }, 33 | { 34 | no_proxy => [ 35 | "localhost,example.com", 36 | "localhost, example.com", 37 | [qw/localhost example.com/] 38 | ], 39 | expect => [ "localhost", "example.com" ], 40 | }, 41 | #>>> 42 | ); 43 | 44 | for my $c (@cases) { 45 | for my $no_proxy ( @{ $c->{no_proxy} } ) { 46 | my $label = 47 | !defined $no_proxy ? 'undef' 48 | : ref $no_proxy ? "[qw/@$no_proxy/]" 49 | : "'$no_proxy'"; 50 | 51 | # Can't test environment with array ref (ENV stringifies in new perls) 52 | if ( ref $no_proxy ) { 53 | my $ht = HTTP::Tiny->new( no_proxy => $no_proxy ); 54 | is_deeply( $ht->no_proxy, $c->{expect}, "new(no_proxy => $label)" ); 55 | } 56 | else { 57 | { 58 | no warnings 'uninitialized'; 59 | local $ENV{no_proxy} = $no_proxy; 60 | my $ht = HTTP::Tiny->new(); 61 | is_deeply( $ht->no_proxy, $c->{expect}, "\$ENV{no_proxy} = $label" ); 62 | } 63 | { 64 | local $ENV{no_proxy} = "Shouldnt,see,this"; 65 | my $ht = HTTP::Tiny->new( no_proxy => $no_proxy ); 66 | is_deeply( $ht->no_proxy, $c->{expect}, 67 | "new(no_proxy => $label) versus other \$ENV{no_proxy}" ); 68 | } 69 | } 70 | } 71 | } 72 | 73 | done_testing(); 74 | -------------------------------------------------------------------------------- /t/150_post_form.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use open IN => ':raw'; 6 | 7 | use File::Basename; 8 | use Test::More 0.88; 9 | use lib 't'; 10 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 11 | clear_socket_source set_socket_source sort_headers $CRLF $LF]; 12 | use HTTP::Tiny; 13 | BEGIN { monkey_patch() } 14 | 15 | for my $file ( dir_list("corpus", qr/^form/ ) ) { 16 | my $data = do { local (@ARGV,$/) = $file; <> }; 17 | $data =~ s/$CRLF/$LF/gm if $^O eq 'MSWin32'; 18 | my ($params, $expect_req, $give_res) = split /--+\n/, $data; 19 | # cleanup source data 20 | my $version = HTTP::Tiny->VERSION || 0; 21 | $expect_req =~ s{VERSION}{$version}; 22 | s{\n}{$CRLF}g for ($expect_req, $give_res); 23 | 24 | # figure out what request to make 25 | my $case = parse_case($params); 26 | my $url = $case->{url}[0]; 27 | my %options; 28 | 29 | my %headers; 30 | for my $line ( @{ $case->{headers} } ) { 31 | my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g); 32 | $headers{$k} = $v; 33 | } 34 | $options{headers} = \%headers if %headers; 35 | 36 | my @params = split "\\|", $case->{content}[0]; 37 | my $formdata; 38 | if ( $case->{datatype}[0] eq 'HASH' ) { 39 | while ( @params ) { 40 | my ($key, $value) = splice( @params, 0, 2 ); 41 | if ($value eq "") { 42 | $value = undef; 43 | } 44 | if ( ref $formdata->{$key} ) { 45 | push @{$formdata->{$key}}, $value; 46 | } 47 | elsif ( exists $formdata->{$key} ) { 48 | $formdata->{$key} = [ $formdata->{$key}, $value ]; 49 | } 50 | else { 51 | $formdata->{$key} = $value; 52 | } 53 | } 54 | } 55 | else { 56 | $formdata = [ map { $_ eq "" ? undef : $_ } @params ]; 57 | } 58 | 59 | # setup mocking and test 60 | my $res_fh = tmpfile($give_res); 61 | my $req_fh = tmpfile(); 62 | 63 | my $http = HTTP::Tiny->new( keep_alive => 0 ); 64 | clear_socket_source(); 65 | set_socket_source($req_fh, $res_fh); 66 | 67 | (my $url_basename = $url) =~ s{.*/}{}; 68 | 69 | my $response = $http->post_form( $url, $formdata, %options ? (\%options) : ()); 70 | 71 | my $got_req = slurp($req_fh); 72 | 73 | my $label = basename($file); 74 | 75 | 76 | is( sort_headers($got_req), sort_headers($expect_req), "$label request" ); 77 | 78 | my ($rc) = $give_res =~ m{\S+\s+(\d+)}g; 79 | is( $response->{status}, $rc, "$label response code $rc" ) 80 | or diag $response->{content}; 81 | 82 | if ( substr($rc,0,1) eq '2' ) { 83 | ok( $response->{success}, "$label success flag true" ); 84 | } 85 | else { 86 | ok( ! $response->{success}, "$label success flag false" ); 87 | } 88 | } 89 | 90 | done_testing; 91 | -------------------------------------------------------------------------------- /t/160_cookies.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.96; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | hashify connect_args clear_socket_source set_socket_source sort_headers 11 | $CRLF $LF]; 12 | 13 | use HTTP::Tiny; 14 | BEGIN { monkey_patch() } 15 | 16 | SKIP: for my $class ( qw/SimpleCookieJar HTTP::CookieJar/ ) { 17 | 18 | subtest $class => sub { 19 | eval "require $class; 1" 20 | or plan skip_all => "Needs $class"; 21 | 22 | for my $file ( dir_list("corpus", qr/^cookies/ ) ) { 23 | my $label = basename($file); 24 | my $data = do { local (@ARGV,$/) = $file; <> }; 25 | my @cases = split /--+\n/, $data; 26 | 27 | my $jar = SimpleCookieJar->new(); 28 | my $http = undef; 29 | while (@cases) { 30 | my ($params, $expect_req, $give_res) = splice( @cases, 0, 3 ); 31 | 32 | my $case = parse_case($params); 33 | 34 | my $url = $case->{url}[0]; 35 | my $method = $case->{method}[0] || 'GET'; 36 | my %headers = hashify( $case->{headers} ); 37 | my %new_args = hashify( $case->{new_args} ); 38 | 39 | if( exists $headers{Cookie} ) { 40 | my $cookies = delete $headers{Cookie}; 41 | $jar->add( $url, $cookies ); 42 | } 43 | 44 | if( exists $headers{'No-Cookie-Jar'} ) { 45 | delete $headers{'No-Cookie-Jar'}; 46 | $jar = undef; 47 | } 48 | 49 | my %options; 50 | $options{headers} = \%headers if %headers; 51 | 52 | my $version = HTTP::Tiny->VERSION || 0; 53 | my $agent = $new_args{agent} || "HTTP-Tiny/$version"; 54 | 55 | $new_args{cookie_jar} = $jar; 56 | 57 | # cleanup source data 58 | $expect_req =~ s{HTTP-Tiny/VERSION}{$agent}; 59 | s{\n}{$CRLF}g for ($expect_req, $give_res); 60 | 61 | # setup mocking and test 62 | my $res_fh = tmpfile($give_res); 63 | my $req_fh = tmpfile(); 64 | 65 | $http = HTTP::Tiny->new(keep_alive => 0, %new_args) if !defined $http; 66 | clear_socket_source(); 67 | set_socket_source($req_fh, $res_fh); 68 | 69 | my @call_args = %options ? ($url, \%options) : ($url); 70 | my $response = $http->get(@call_args); 71 | 72 | my $got_req = slurp($req_fh); 73 | is( sort_headers($got_req), sort_headers($expect_req), "$label request data"); 74 | } 75 | } 76 | }; 77 | } 78 | 79 | done_testing; 80 | -------------------------------------------------------------------------------- /t/161_basic_auth.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | hashify connect_args clear_socket_source set_socket_source sort_headers 11 | $CRLF $LF]; 12 | 13 | use HTTP::Tiny; 14 | BEGIN { monkey_patch() } 15 | 16 | for my $file ( dir_list("corpus", qr/^auth/ ) ) { 17 | my $label = basename($file); 18 | my $data = do { local (@ARGV,$/) = $file; <> }; 19 | my ($params, @case_pairs) = split /--+\n/, $data; 20 | my $case = parse_case($params); 21 | 22 | my $url = $case->{url}[0]; 23 | my $method = $case->{method}[0] || 'GET'; 24 | my %headers = hashify( $case->{headers} ); 25 | my %new_args = hashify( $case->{new_args} ); 26 | 27 | my %options; 28 | $options{headers} = \%headers if %headers; 29 | my $call_args = %options ? [$method, $url, \%options] : [$method, $url]; 30 | 31 | my $version = HTTP::Tiny->VERSION || 0; 32 | my $agent = $new_args{agent} || "HTTP-Tiny/$version"; 33 | 34 | my (@socket_pairs); 35 | while ( @case_pairs ) { 36 | my ($expect_req, $give_res) = splice( @case_pairs, 0, 2 ); 37 | # cleanup source data 38 | $expect_req =~ s{HTTP-Tiny/VERSION}{$agent}; 39 | s{\n}{$CRLF}g for ($expect_req, $give_res); 40 | 41 | # setup mocking and test 42 | my $req_fh = tmpfile(); 43 | my $res_fh = tmpfile($give_res); 44 | 45 | push @socket_pairs, [$req_fh, $res_fh, $expect_req]; 46 | } 47 | 48 | clear_socket_source(); 49 | set_socket_source(@$_) for @socket_pairs; 50 | 51 | my $http = HTTP::Tiny->new(keep_alive => 0, %new_args); 52 | my $response = $http->request(@$call_args); 53 | 54 | my $calls = 0 55 | + (defined($new_args{max_redirect}) ? $new_args{max_redirect} : 5); 56 | 57 | for my $i ( 0 .. $calls ) { 58 | last unless @socket_pairs; 59 | my ($req_fh, $res_fh, $expect_req) = @{ shift @socket_pairs }; 60 | my $got_req = slurp($req_fh); 61 | is( sort_headers($got_req), sort_headers($expect_req), "$label request ($i)"); 62 | $i++; 63 | } 64 | 65 | my $exp_content = $case->{expected} 66 | ? join("$CRLF", @{$case->{expected}}) : ''; 67 | 68 | is ( $response->{content}, $exp_content, "$label content" ); 69 | 70 | if ( $case->{expected_url} ) { 71 | is ( $response->{url}, $case->{expected_url}[0], "$label response URL" ); 72 | } 73 | 74 | } 75 | 76 | done_testing; 77 | -------------------------------------------------------------------------------- /t/162_proxy_auth.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Basename; 7 | use Test::More 0.88; 8 | use lib 't'; 9 | use Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case 10 | hashify connect_args clear_socket_source set_socket_source sort_headers 11 | $CRLF $LF]; 12 | 13 | use HTTP::Tiny; 14 | BEGIN { monkey_patch() } 15 | 16 | for my $file ( dir_list("corpus", qr/^proxy-auth/ ) ) { 17 | my $label = basename($file); 18 | my $data = do { local (@ARGV,$/) = $file; <> }; 19 | my ($params, @case_pairs) = split /--+\n/, $data; 20 | my $case = parse_case($params); 21 | 22 | my $url = $case->{url}[0]; 23 | my $method = $case->{method}[0] || 'GET'; 24 | my %headers = hashify( $case->{headers} ); 25 | my %new_args = hashify( $case->{new_args} ); 26 | 27 | my %options; 28 | $options{headers} = \%headers if %headers; 29 | my $call_args = %options ? [$method, $url, \%options] : [$method, $url]; 30 | 31 | my $version = HTTP::Tiny->VERSION || 0; 32 | my $agent = $new_args{agent} || "HTTP-Tiny/$version"; 33 | 34 | my (@socket_pairs); 35 | while ( @case_pairs ) { 36 | my ($expect_req, $give_res) = splice( @case_pairs, 0, 2 ); 37 | # cleanup source data 38 | $expect_req =~ s{HTTP-Tiny/VERSION}{$agent}; 39 | s{\n}{$CRLF}g for ($expect_req, $give_res); 40 | 41 | # setup mocking and test 42 | my $req_fh = tmpfile(); 43 | my $res_fh = tmpfile($give_res); 44 | 45 | push @socket_pairs, [$req_fh, $res_fh, $expect_req]; 46 | } 47 | 48 | clear_socket_source(); 49 | set_socket_source(@$_) for @socket_pairs; 50 | 51 | my $http = HTTP::Tiny->new(keep_alive => 0, %new_args); 52 | my $response = $http->request(@$call_args); 53 | 54 | my $calls = 0 55 | + (defined($new_args{max_redirect}) ? $new_args{max_redirect} : 5); 56 | 57 | for my $i ( 0 .. $calls ) { 58 | last unless @socket_pairs; 59 | my ($req_fh, $res_fh, $expect_req) = @{ shift @socket_pairs }; 60 | my $got_req = slurp($req_fh); 61 | is( sort_headers($got_req), sort_headers($expect_req), "$label request ($i)"); 62 | $i++; 63 | } 64 | 65 | my $exp_content = $case->{expected} 66 | ? join("$CRLF", @{$case->{expected}}) : ''; 67 | 68 | is ( $response->{content}, $exp_content, "$label content" ); 69 | 70 | if ( $case->{expected_url} ) { 71 | is ( $response->{url}, $case->{expected_url}[0], "$label response URL" ); 72 | } 73 | 74 | } 75 | 76 | done_testing; 77 | -------------------------------------------------------------------------------- /t/170_keepalive.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use File::Basename; 6 | use Test::More 0.88; 7 | use lib 't'; 8 | use Util qw[ 9 | tmpfile monkey_patch dir_list clear_socket_source set_socket_source 10 | $CRLF 11 | ]; 12 | use HTTP::Tiny; 13 | our $can_read; 14 | 15 | BEGIN { 16 | no warnings qw/redefine once/; 17 | monkey_patch(); 18 | *HTTP::Tiny::Handle::can_read = sub { $can_read++ }; 19 | } 20 | 21 | my $response = <<'RESPONSE'; 22 | HTTP/1.1 200 OK 23 | Date: Thu, 03 Feb 1994 00:00:00 GMT 24 | Content-Type: text/html 25 | Content-Length: 10 26 | 27 | 0123456789 28 | 29 | RESPONSE 30 | 31 | trim($response); 32 | 33 | my $h; 34 | 35 | new_ht(); 36 | test_ht( "Keep-alive", 1, 'http://foo.com' ); 37 | 38 | new_ht(); 39 | test_ht( "Different scheme", 0, 'https://foo.com' ); 40 | 41 | new_ht(); 42 | test_ht( "Different host", 0, 'http://bar.com' ); 43 | 44 | new_ht(); 45 | test_ht( "Different port", 0, 'http://foo.com:8000' ); 46 | 47 | new_ht(); 48 | $h->timeout(30); 49 | test_ht( "Different timeout", 0, 'http://foo.com' ); 50 | 51 | new_ht(); 52 | $h->timeout(60); 53 | test_ht( "Same timeout", 1, 'http://foo.com' ); 54 | 55 | new_ht(); 56 | $h->default_headers({ 'X-Foo' => 'Bar' }); 57 | test_ht( "Default headers change", 1, 'http://foo.com' ); 58 | 59 | new_ht(); 60 | $h->{handle}->close; 61 | test_ht( "Socket closed", 0, 'http://foo.com' ); 62 | 63 | for my $file ( dir_list( "corpus", qr/^keepalive/ ) ) { 64 | my $label = basename($file); 65 | my $data = do { local ( @ARGV, $/ ) = $file; <> }; 66 | my ( $title, $ok, $response ) = map { trim($_) } split /--+/, $data; 67 | new_ht(); 68 | clear_socket_source(); 69 | set_socket_source( tmpfile(), tmpfile($response) ); 70 | $h->request( 'POST', 'http://foo.com', { content => 'xx' } ); 71 | is !!$h->{handle}, !!$ok, "$label - $title"; 72 | } 73 | 74 | sub test_ht { 75 | my $title = shift; 76 | my $result = !!shift(); 77 | my $url = shift; 78 | 79 | clear_socket_source(); 80 | set_socket_source( tmpfile(), tmpfile($response) ); 81 | $can_read = 0 if $result; 82 | my $old = $h->{handle} || 'old'; 83 | $h->request( 'POST', $url, { content => 'xx' } ); 84 | my $new = $h->{handle} || 'new'; 85 | is $old eq $new, $result, $title; 86 | } 87 | 88 | sub new_ht { 89 | $h = HTTP::Tiny->new( keep_alive => 1, @_ ); 90 | $can_read = 1; 91 | clear_socket_source(); 92 | set_socket_source( tmpfile(), tmpfile($response) ); 93 | $h->request( 'POST', 'http://foo.com' ); 94 | } 95 | 96 | sub trim { $_[0] =~ s/^\s+//; $_[0] =~ s/\s+$//; return $_ } 97 | 98 | done_testing; 99 | 100 | -------------------------------------------------------------------------------- /t/180_verify_SSL.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More 0.88; 6 | use lib 't'; 7 | 8 | use HTTP::Tiny; 9 | 10 | delete $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}; 11 | 12 | { 13 | my $ht = HTTP::Tiny->new(); 14 | is($ht->verify_SSL, 1, "verify_SSL is 1 by default"); 15 | } 16 | 17 | { 18 | my $ht = HTTP::Tiny->new( 19 | verify_SSL => 0 20 | ); 21 | is($ht->verify_SSL, 0, "verify_SSL=>0 sets 0"); 22 | } 23 | 24 | { 25 | my $ht = HTTP::Tiny->new( 26 | verify_ssl => 0 27 | ); 28 | is($ht->verify_SSL, 0, "verify_ssl=>0 sets 0"); 29 | } 30 | 31 | { 32 | my $ht = HTTP::Tiny->new( 33 | verify_SSL => 1, 34 | verify_ssl => 0 35 | ); 36 | is($ht->verify_SSL, 1, "verify_SSL=>1 and verify_ssl=>0 sets 1"); 37 | } 38 | 39 | { 40 | my $ht = HTTP::Tiny->new( 41 | verify_SSL => 0, 42 | verify_ssl => 1 43 | ); 44 | is($ht->verify_SSL, 1, "verify_SSL=>0 and verify_ssl=>1 sets 1"); 45 | } 46 | 47 | { 48 | my $ht = HTTP::Tiny->new( 49 | verify_SSL => 0, 50 | verify_ssl => 0 51 | ); 52 | is($ht->verify_SSL, 0, "verify_SSL=>0 and verify_ssl=>0 sets 0"); 53 | } 54 | 55 | { 56 | local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = "1"; 57 | my $ht = HTTP::Tiny->new(); 58 | is($ht->verify_SSL, 0, "PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=1 changes verify_SSL default to 0"); 59 | } 60 | 61 | { 62 | local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = "0"; 63 | my $ht = HTTP::Tiny->new(); 64 | is($ht->verify_SSL, 1, "PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=0 keeps verify_SSL default at 1"); 65 | } 66 | 67 | { 68 | local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = "False"; 69 | my $ht = HTTP::Tiny->new(); 70 | is($ht->verify_SSL, 1, "Unsupported PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=False keeps verify_SSL default at 1"); 71 | } 72 | 73 | { 74 | local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = "1"; 75 | my $ht = HTTP::Tiny->new(verify_SSL=>1); 76 | is($ht->verify_SSL, 1, "PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=1 does not override verify_SSL attribute set to 1"); 77 | } 78 | 79 | { 80 | local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = "1"; 81 | my $ht = HTTP::Tiny->new( 82 | verify_SSL => 1, 83 | verify_ssl => 1 84 | ); 85 | is($ht->verify_SSL, 1, "PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=1, verify_SSL=>1 and verify_ssl=>1 sets 1"); 86 | } 87 | 88 | { 89 | local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = "1"; 90 | my $ht = HTTP::Tiny->new( 91 | verify_SSL => 1, 92 | verify_ssl => 0 93 | ); 94 | is($ht->verify_SSL, 1, "PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=1, verify_SSL=>1 and verify_ssl=>0 sets 1"); 95 | } 96 | 97 | { 98 | local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = "1"; 99 | my $ht = HTTP::Tiny->new( 100 | verify_SSL => 0, 101 | verify_ssl => 0 102 | ); 103 | is($ht->verify_SSL, 0, "PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=1, verify_SSL=>0 and verify_ssl=>0 sets 0"); 104 | } 105 | 106 | 107 | 108 | done_testing; 109 | 110 | -------------------------------------------------------------------------------- /t/200_live.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use IO::Socket::INET; 7 | use Test::More 0.88; 8 | use HTTP::Tiny; 9 | 10 | my $test_host = "google.com"; 11 | my $test_url = "http://www.google.com/"; 12 | my $test_re = qr/google/; 13 | 14 | plan 'skip_all' => "Only run for \$ENV{AUTOMATED_TESTING}" 15 | unless $ENV{AUTOMATED_TESTING}; 16 | 17 | plan 'skip_all' => "Internet connection timed out" 18 | unless IO::Socket::INET->new( 19 | PeerHost => $test_host, 20 | PeerPort => 80, 21 | Proto => 'tcp', 22 | Timeout => 10, 23 | ); 24 | 25 | my $ua = HTTP::Tiny->new; 26 | my $response = $ua->get($test_url); 27 | if ( $ua->{handle} ) { 28 | my $socket_class = ref $ua->{handle}{fh}; 29 | diag "HTTP::Tiny using $socket_class version " . $socket_class->VERSION; 30 | } 31 | 32 | ok( $response->{status} ne '599', "Request to $test_url completed" ) 33 | or dump_hash($response); 34 | ok( $response->{content}, "Got content" ); 35 | 36 | sub dump_hash { 37 | my $hash = shift; 38 | $hash->{content} = substr($hash->{content},0,160) . "..."; 39 | require Data::Dumper; 40 | my $dumped = Data::Dumper::Dumper($hash); 41 | $dumped =~ s{^}{# }; 42 | print $dumped; 43 | } 44 | 45 | done_testing; 46 | -------------------------------------------------------------------------------- /t/200_live_local_ip.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use IO::Socket::INET; 7 | use Test::More 0.88; 8 | use HTTP::Tiny; 9 | 10 | my $test_host = "www.google.com"; 11 | my $test_url = "http://www.google.com/"; 12 | 13 | plan 'skip_all' => "Only run for \$ENV{AUTOMATED_TESTING}" 14 | unless $ENV{AUTOMATED_TESTING}; 15 | 16 | plan 'skip_all' => "Internet connection timed out" 17 | unless IO::Socket::INET->new( 18 | PeerHost => $test_host, 19 | PeerPort => 80, 20 | Proto => 'tcp', 21 | Timeout => 10, 22 | ); 23 | 24 | my ($tiny, $response); 25 | 26 | # default local address should work; try three times since the test url 27 | # can have intermittent failures 28 | $tiny = HTTP::Tiny->new; 29 | for (1 .. 3) { 30 | $response = $tiny->get($test_url); 31 | last if $response->{success}; 32 | sleep 2; 33 | } 34 | isnt( $response->{status}, '599', "Request to $test_url completed (default local address)" ); 35 | 36 | # bad local IP should fail 37 | $tiny = HTTP::Tiny->new(local_address => '999.999.999.999'); # bad IP is error 38 | $response = $tiny->get($test_url); 39 | is( $response->{status}, '599', "Request to $test_url failed (invalid local address)" ) 40 | or diag explain $response; 41 | 42 | done_testing; 43 | -------------------------------------------------------------------------------- /t/210_live_ssl.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More 0.96; 7 | use IO::Socket::INET; 8 | BEGIN { 9 | eval { require IO::Socket::SSL; IO::Socket::SSL->VERSION(1.56); 1 }; 10 | plan skip_all => 'IO::Socket::SSL 1.56 required for SSL tests' if $@; 11 | # $IO::Socket::SSL::DEBUG = 3; 12 | 13 | eval { require Net::SSLeay; Net::SSLeay->VERSION(1.49); 1}; 14 | plan skip_all => 'Net::SSLeay 1.49 required for SSL tests' if $@; 15 | 16 | eval { require Mozilla::CA; 1 }; 17 | plan skip_all => 'Mozilla::CA required for SSL tests' if $@; 18 | } 19 | use HTTP::Tiny; 20 | 21 | delete $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}; 22 | 23 | plan skip_all => 'Only run for $ENV{AUTOMATED_TESTING}' 24 | unless $ENV{AUTOMATED_TESTING}; 25 | 26 | use IPC::Cmd qw/can_run/; 27 | 28 | if ( can_run('openssl') ) { 29 | diag "\nNote: running test with ", qx/openssl version/; 30 | } 31 | 32 | test_ssl('https://cpan.org/' => { 33 | host => 'cpan.org', 34 | pass => { verify_SSL => 1 }, 35 | fail => { verify_SSL => 1, SSL_options => { SSL_ca_file => "corpus/snake-oil.crt" } }, 36 | default_verify_should_return => !!1, 37 | }); 38 | 39 | test_ssl('https://github.com/' => { 40 | host => 'github.com', 41 | pass => { verify_SSL => 1 }, 42 | fail => { verify_SSL => 1, SSL_options => { SSL_ca_file => "corpus/snake-oil.crt" } }, 43 | default_verify_should_return => !!1, 44 | }); 45 | 46 | test_ssl('https://wrong.host.badssl.com/' => { 47 | host => 'wrong.host.badssl.com', 48 | pass => { SSL_options => { SSL_verifycn_scheme => 'none', SSL_verifycn_name => 'wrong.host.badssl.com', SSL_verify_mode => 0x00 } }, 49 | fail => { SSL_options => { SSL_verifycn_scheme => 'http', SSL_verifycn_name => 'wrong.host.badssl.com', SSL_verify_mode => 0x01, SSL_ca_file => Mozilla::CA::SSL_ca_file() } }, 50 | default_verify_should_return => !!0, 51 | }); 52 | 53 | test_ssl('https://untrusted-root.badssl.com/' => { 54 | host => 'untrusted-root.badssl.com', 55 | pass => { verify_SSL => 0 }, 56 | fail => { verify_SSL => 1 }, 57 | default_verify_should_return => !!0, 58 | }); 59 | 60 | test_ssl('https://mozilla-modern.badssl.com/' => { 61 | host => 'mozilla-modern.badssl.com', 62 | pass => { verify_SSL => 1 }, 63 | fail => { verify_SSL => 1, SSL_options => { SSL_ca_file => "corpus/snake-oil.crt" } }, 64 | default_verify_should_return => !!1, 65 | }); 66 | 67 | { 68 | local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = 1; 69 | test_ssl('https://wrong.host.badssl.com/' => { 70 | host => 'wrong.host.badssl.com', 71 | pass => { verify_SSL => 0 }, 72 | fail => { verify_SSL => 1 }, 73 | default_verify_should_return => !!1, 74 | }); 75 | test_ssl('https://expired.badssl.com/' => { 76 | host => 'expired.badssl.com', 77 | pass => { verify_SSL => 0 }, 78 | fail => { verify_SSL => 1 }, 79 | default_verify_should_return => !!1, 80 | }); 81 | 82 | } 83 | 84 | test_ssl('https://wrong.host.badssl.com/' => { 85 | host => 'wrong.host.badssl.com', 86 | pass => { verify_SSL => 0 }, 87 | fail => { verify_SSL => 1 }, 88 | default_verify_should_return => !!0, 89 | }); 90 | 91 | test_ssl('https://expired.badssl.com/' => { 92 | host => 'expired.badssl.com', 93 | pass => { verify_SSL => 0 }, 94 | fail => { verify_SSL => 1 }, 95 | default_verify_should_return => !!0, 96 | }); 97 | 98 | 99 | 100 | subtest "can_ssl" => sub { 101 | ok( HTTP::Tiny->can_ssl, "class method" ); 102 | ok( HTTP::Tiny->new->can_ssl, "object method, default params" ); 103 | ok( HTTP::Tiny->new(verify_SSL => 1)->can_ssl, "object method, verify_SSL" ); 104 | 105 | my $ht = HTTP::Tiny->new( 106 | verify_SSL => 1, 107 | SSL_options => { SSL_ca_file => 'adlfadkfadlfad' }, 108 | ); 109 | my ($ok, $why) = $ht->can_ssl; 110 | ok( ! $ok, "object methods, verify_SSL, bogus CA file (FAILS)" ); 111 | like( $why, qr/not found or not readable/, "failure reason" ); 112 | }; 113 | 114 | done_testing(); 115 | 116 | sub test_ssl { 117 | my ($url, $data) = @_; 118 | subtest $url => sub { 119 | plan 'skip_all' => 'Internet connection timed out' 120 | unless IO::Socket::INET->new( 121 | PeerHost => $data->{host}, 122 | PeerPort => 443, 123 | Proto => 'tcp', 124 | Timeout => 10, 125 | ); 126 | 127 | # the default verification 128 | my $response = HTTP::Tiny->new()->get($url); 129 | is $response->{success}, $data->{default_verify_should_return}, "Request to $url passed/failed using default as expected" 130 | or do { 131 | # $response->{content} = substr $response->{content}, 0, 50; 132 | $response->{content} =~ s{\n.*}{}s; 133 | diag explain [IO::Socket::SSL::errstr(), $response] 134 | }; 135 | 136 | # force validation to succeed 137 | if ($data->{pass}) { 138 | my $pass = HTTP::Tiny->new( %{$data->{pass}} )->get($url); 139 | isnt $pass->{status}, '599', "Request to $url completed (forced pass)" 140 | or do { 141 | $pass->{content} =~ s{\n.*}{}s; 142 | diag explain $pass 143 | }; 144 | ok $pass->{content}, 'Got some content'; 145 | } 146 | 147 | # force validation to fail 148 | if ($data->{fail}) { 149 | my $fail = HTTP::Tiny->new( %{$data->{fail}} )->get($url); 150 | is $fail->{status}, '599', "Request to $url failed (forced fail)" 151 | or do { 152 | $fail->{content} =~ s{\n.*}{}s; 153 | diag explain [IO::Socket::SSL::errstr(), $fail] 154 | }; 155 | ok $fail->{content}, 'Got some content'; 156 | } 157 | }; 158 | } 159 | -------------------------------------------------------------------------------- /t/BrokenCookieJar.pm: -------------------------------------------------------------------------------- 1 | package BrokenCookieJar; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | sub new { 7 | my $class = shift; 8 | return bless {} => $class; 9 | } 10 | 11 | package BrokenCookieJar2; 12 | 13 | use strict; 14 | use warnings; 15 | 16 | sub new { 17 | my $class = shift; 18 | return bless {} => $class; 19 | } 20 | 21 | sub add { 22 | } 23 | 24 | 1; 25 | -------------------------------------------------------------------------------- /t/SimpleCookieJar.pm: -------------------------------------------------------------------------------- 1 | package SimpleCookieJar; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | sub new { 7 | my $class = shift; 8 | return bless {} => $class; 9 | } 10 | 11 | sub add { 12 | my ($self, $url, $cookie) = @_; 13 | 14 | my ($kv) = split qr/;/, $cookie; 15 | my ($k, $v) = split qr/\s*=\s*/, $kv, 2; 16 | 17 | $self->{$url}{$k} = $v; 18 | } 19 | 20 | sub cookie_header { 21 | my ($self, $url) = @_; 22 | 23 | my $cookies = $self->{$url} 24 | or return ''; 25 | 26 | return join( "; ", map{ "$_=$cookies->{$_}" } sort keys %$cookies ); 27 | } 28 | 29 | 1; 30 | -------------------------------------------------------------------------------- /t/Util.pm: -------------------------------------------------------------------------------- 1 | package Util; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use IO::File qw(SEEK_SET SEEK_END); 7 | use IO::Dir; 8 | 9 | BEGIN { 10 | our @EXPORT_OK = qw( 11 | rewind 12 | tmpfile 13 | dir_list 14 | slurp 15 | parse_case 16 | hashify 17 | sort_headers 18 | connect_args 19 | clear_socket_source 20 | set_socket_source 21 | monkey_patch 22 | $CRLF 23 | $LF 24 | ); 25 | 26 | require Exporter; 27 | *import = \&Exporter::import; 28 | } 29 | 30 | our $CRLF = "\x0D\x0A"; 31 | our $LF = "\x0A"; 32 | 33 | sub rewind(*) { 34 | seek($_[0], 0, SEEK_SET) 35 | || die(qq/Couldn't rewind file handle: '$!'/); 36 | } 37 | 38 | sub tmpfile { 39 | my $fh = IO::File->new_tmpfile 40 | || die(qq/Couldn't create a new temporary file: '$!'/); 41 | 42 | binmode($fh) 43 | || die(qq/Couldn't binmode temporary file handle: '$!'/); 44 | 45 | if (@_) { 46 | print({$fh} @_) 47 | || die(qq/Couldn't write to temporary file handle: '$!'/); 48 | 49 | seek($fh, 0, SEEK_SET) 50 | || die(qq/Couldn't rewind temporary file handle: '$!'/); 51 | } 52 | 53 | return $fh; 54 | } 55 | 56 | sub dir_list { 57 | my ($dir, $filter) = @_; 58 | $filter ||= qr/./; 59 | my $d = IO::Dir->new($dir) 60 | or return; 61 | return map { "$dir/$_" } sort grep { /$filter/ } grep { /^[^.]/ } $d->read; 62 | } 63 | 64 | sub slurp (*) { 65 | my ($fh) = @_; 66 | 67 | seek($fh, 0, SEEK_END) 68 | || die(qq/Couldn't navigate to EOF on file handle: '$!'/); 69 | 70 | my $exp = tell($fh); 71 | 72 | rewind($fh); 73 | 74 | binmode($fh) 75 | || die(qq/Couldn't binmode file handle: '$!'/); 76 | 77 | my $buf = do { local $/; <$fh> }; 78 | my $got = length $buf; 79 | 80 | ($exp == $got) 81 | || die(qq[I/O read mismatch (expexted: $exp got: $got)]); 82 | 83 | return $buf; 84 | } 85 | 86 | sub parse_case { 87 | my ($case) = @_; 88 | my %args; 89 | my $key = ''; 90 | my %seen; 91 | for my $line ( split "\n", $case ) { 92 | chomp $line; 93 | if ( substr($line,0,1) eq q{ } ) { 94 | $line =~ s/^\s+//; 95 | push @{$args{$key}}, $line; 96 | } 97 | else { 98 | $key = $line; 99 | $seen{$key}++; 100 | } 101 | } 102 | for my $k (keys %seen) { 103 | $args{$k}=undef unless exists $args{$k}; 104 | } 105 | return \%args; 106 | } 107 | 108 | sub hashify { 109 | my ($lines) = @_; 110 | return unless $lines; 111 | my %hash; 112 | for my $line ( @$lines ) { 113 | my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g); 114 | $hash{$k} = [ $hash{$k} ] if exists $hash{$k} && ref $hash{$k} ne 'ARRAY'; 115 | if ( ref($hash{$k}) eq 'ARRAY' ) { 116 | push @{$hash{$k}}, $v; 117 | } 118 | else { 119 | $hash{$k} = $v; 120 | } 121 | } 122 | return %hash; 123 | } 124 | 125 | sub sort_headers { 126 | my ($text) = shift; 127 | my @lines = split /$CRLF/, $text; 128 | my $request = shift(@lines) || ''; 129 | my @headers; 130 | while (my $line = shift @lines) { 131 | last unless length $line; 132 | push @headers, $line; 133 | } 134 | @headers = sort @headers; 135 | return join($CRLF, $request, @headers, '', @lines); 136 | } 137 | 138 | { 139 | my (@req_fh, @res_fh, $monkey_host, $monkey_port); 140 | 141 | sub clear_socket_source { 142 | @req_fh = (); 143 | @res_fh = (); 144 | } 145 | 146 | sub set_socket_source { 147 | my ($req_fh, $res_fh) = @_; 148 | push @req_fh, $req_fh; 149 | push @res_fh, $res_fh; 150 | } 151 | 152 | sub connect_args { return ($monkey_host, $monkey_port) } 153 | 154 | sub monkey_patch { 155 | no warnings qw/redefine once/; 156 | *HTTP::Tiny::Handle::can_read = sub {1}; 157 | *HTTP::Tiny::Handle::can_write = sub {1}; 158 | *HTTP::Tiny::Handle::connect = sub { 159 | my ($self, $scheme, $host, $port, $peer) = @_; 160 | $self->{host} = $monkey_host = $host; 161 | $self->{port} = $monkey_port = $port; 162 | $self->{peer} = $peer; 163 | $self->{scheme} = $scheme; 164 | $self->{fh} = shift @req_fh; 165 | $self->{pid} = $$; 166 | $self->{tid} = HTTP::Tiny::Handle::_get_tid(); 167 | return $self; 168 | }; 169 | my $original_write_request = \&HTTP::Tiny::Handle::write_request; 170 | *HTTP::Tiny::Handle::write_request = sub { 171 | my ($self, $request) = @_; 172 | $original_write_request->($self, $request); 173 | $self->{fh} = shift @res_fh; 174 | }; 175 | *HTTP::Tiny::Handle::close = sub { 1 }; # don't close our temps 176 | *HTTP::Tiny::Handle::connected = sub { 1 }; 177 | 178 | # don't try to proxy in mock-mode 179 | delete $ENV{$_} for map { $_, uc($_) } qw/http_proxy https_proxy all_proxy/; 180 | } 181 | } 182 | 183 | 1; 184 | 185 | 186 | # vim: et ts=4 sts=4 sw=4: 187 | --------------------------------------------------------------------------------