├── .gitignore ├── lib ├── src │ ├── site │ │ └── resources │ │ │ ├── javascripts │ │ │ └── main.js │ │ │ ├── images │ │ │ ├── bg_hr.png │ │ │ ├── blacktocat.png │ │ │ ├── icon_download.png │ │ │ └── sprite_download.png │ │ │ ├── stylesheets │ │ │ ├── pygment_trac.css │ │ │ └── stylesheet.css │ │ │ ├── params.json │ │ │ └── index.html │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── kevinsawicki │ │ │ └── http │ │ │ ├── EncodeTest.java │ │ │ └── ServerTestCase.java │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── kevinsawicki │ │ └── http │ │ └── HttpRequest.java ├── .classpath ├── .project ├── .settings │ ├── org.eclipse.jdt.ui.prefs │ └── org.eclipse.jdt.core.prefs └── pom.xml ├── .travis.yml ├── pom.xml ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | target 3 | *~ 4 | 5 | # IntelliJ IDEA 6 | .idea 7 | *.iml 8 | -------------------------------------------------------------------------------- /lib/src/site/resources/javascripts/main.js: -------------------------------------------------------------------------------- 1 | console.log('This would be the main JS file.'); 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | notifications: 4 | email: false 5 | 6 | git: 7 | depth: 10 8 | -------------------------------------------------------------------------------- /lib/src/site/resources/images/bg_hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinsawicki/http-request/HEAD/lib/src/site/resources/images/bg_hr.png -------------------------------------------------------------------------------- /lib/src/site/resources/images/blacktocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinsawicki/http-request/HEAD/lib/src/site/resources/images/blacktocat.png -------------------------------------------------------------------------------- /lib/src/site/resources/images/icon_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinsawicki/http-request/HEAD/lib/src/site/resources/images/icon_download.png -------------------------------------------------------------------------------- /lib/src/site/resources/images/sprite_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinsawicki/http-request/HEAD/lib/src/site/resources/images/sprite_download.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.github.kevinsawicki 5 | http-request-parent 6 | 1.0 7 | pom 8 | 9 | lib 10 | 11 | 12 | -------------------------------------------------------------------------------- /lib/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | http-request 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Kevin Sawicki 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true 3 | formatter_profile=_two-spaces 4 | formatter_settings_version=12 5 | sp_cleanup.add_default_serial_version_id=true 6 | sp_cleanup.add_generated_serial_version_id=false 7 | sp_cleanup.add_missing_annotations=false 8 | sp_cleanup.add_missing_deprecated_annotations=true 9 | sp_cleanup.add_missing_methods=false 10 | sp_cleanup.add_missing_nls_tags=false 11 | sp_cleanup.add_missing_override_annotations=true 12 | sp_cleanup.add_missing_override_annotations_interface_methods=false 13 | sp_cleanup.add_serial_version_id=false 14 | sp_cleanup.always_use_blocks=true 15 | sp_cleanup.always_use_parentheses_in_expressions=false 16 | sp_cleanup.always_use_this_for_non_static_field_access=false 17 | sp_cleanup.always_use_this_for_non_static_method_access=false 18 | sp_cleanup.convert_to_enhanced_for_loop=false 19 | sp_cleanup.correct_indentation=false 20 | sp_cleanup.format_source_code=false 21 | sp_cleanup.format_source_code_changes_only=false 22 | sp_cleanup.make_local_variable_final=false 23 | sp_cleanup.make_parameters_final=false 24 | sp_cleanup.make_private_fields_final=true 25 | sp_cleanup.make_type_abstract_if_missing_method=false 26 | sp_cleanup.make_variable_declarations_final=false 27 | sp_cleanup.never_use_blocks=false 28 | sp_cleanup.never_use_parentheses_in_expressions=true 29 | sp_cleanup.on_save_use_additional_actions=true 30 | sp_cleanup.organize_imports=false 31 | sp_cleanup.qualify_static_field_accesses_with_declaring_class=false 32 | sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true 33 | sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true 34 | sp_cleanup.qualify_static_member_accesses_with_declaring_class=false 35 | sp_cleanup.qualify_static_method_accesses_with_declaring_class=false 36 | sp_cleanup.remove_private_constructors=true 37 | sp_cleanup.remove_trailing_whitespaces=true 38 | sp_cleanup.remove_trailing_whitespaces_all=true 39 | sp_cleanup.remove_trailing_whitespaces_ignore_empty=false 40 | sp_cleanup.remove_unnecessary_casts=false 41 | sp_cleanup.remove_unnecessary_nls_tags=false 42 | sp_cleanup.remove_unused_imports=false 43 | sp_cleanup.remove_unused_local_variables=false 44 | sp_cleanup.remove_unused_private_fields=true 45 | sp_cleanup.remove_unused_private_members=false 46 | sp_cleanup.remove_unused_private_methods=true 47 | sp_cleanup.remove_unused_private_types=true 48 | sp_cleanup.sort_members=false 49 | sp_cleanup.sort_members_all=false 50 | sp_cleanup.use_blocks=false 51 | sp_cleanup.use_blocks_only_for_return_and_throw=false 52 | sp_cleanup.use_parentheses_in_expressions=false 53 | sp_cleanup.use_this_for_non_static_field_access=false 54 | sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true 55 | sp_cleanup.use_this_for_non_static_method_access=false 56 | sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true 57 | -------------------------------------------------------------------------------- /lib/src/test/java/com/github/kevinsawicki/http/EncodeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Kevin Sawicki 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | package com.github.kevinsawicki.http; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | 26 | import com.github.kevinsawicki.http.HttpRequest.HttpRequestException; 27 | 28 | import org.junit.Test; 29 | 30 | /** 31 | * Unit tests of URL encoding done by {@link HttpRequest} 32 | */ 33 | public class EncodeTest { 34 | 35 | /** 36 | * Verify encoding of URLs 37 | */ 38 | @Test 39 | public void encode() { 40 | assertEquals("http://google.com", HttpRequest.encode("http://google.com")); 41 | 42 | assertEquals("https://google.com", HttpRequest.encode("https://google.com")); 43 | 44 | assertEquals("http://google.com/a", 45 | HttpRequest.encode("http://google.com/a")); 46 | 47 | assertEquals("http://google.com/a/", 48 | HttpRequest.encode("http://google.com/a/")); 49 | 50 | assertEquals("http://google.com/a/b", 51 | HttpRequest.encode("http://google.com/a/b")); 52 | 53 | assertEquals("http://google.com/a?", 54 | HttpRequest.encode("http://google.com/a?")); 55 | 56 | assertEquals("http://google.com/a?b=c", 57 | HttpRequest.encode("http://google.com/a?b=c")); 58 | 59 | assertEquals("http://google.com/a?b=c%20d", 60 | HttpRequest.encode("http://google.com/a?b=c d")); 61 | 62 | assertEquals("http://google.com/a%20b", 63 | HttpRequest.encode("http://google.com/a b")); 64 | 65 | assertEquals("http://google.com/a.b", 66 | HttpRequest.encode("http://google.com/a.b")); 67 | 68 | assertEquals("http://google.com/%E2%9C%93?a=b", 69 | HttpRequest.encode("http://google.com/\u2713?a=b")); 70 | 71 | assertEquals("http://google.com/a%5Eb", 72 | HttpRequest.encode("http://google.com/a^b")); 73 | 74 | assertEquals("http://google.com/%25", 75 | HttpRequest.encode("http://google.com/%")); 76 | 77 | assertEquals("http://google.com/a.b?c=d.e", 78 | HttpRequest.encode("http://google.com/a.b?c=d.e")); 79 | 80 | assertEquals("http://google.com/a.b?c=d/e", 81 | HttpRequest.encode("http://google.com/a.b?c=d/e")); 82 | 83 | assertEquals("http://google.com/a?%E2%98%91", 84 | HttpRequest.encode("http://google.com/a?\u2611")); 85 | 86 | assertEquals("http://google.com/a?b=%E2%98%90", 87 | HttpRequest.encode("http://google.com/a?b=\u2610")); 88 | 89 | assertEquals("http://google.com/a?b=c%2Bd&e=f%2Bg", 90 | HttpRequest.encode("http://google.com/a?b=c+d&e=f+g")); 91 | 92 | assertEquals("http://google.com/+", 93 | HttpRequest.encode("http://google.com/+")); 94 | 95 | assertEquals("http://google.com/+?a=b%2Bc", 96 | HttpRequest.encode("http://google.com/+?a=b+c")); 97 | } 98 | 99 | /** 100 | * Encoding malformed URI 101 | */ 102 | @Test(expected = HttpRequestException.class) 103 | public void encodeMalformedUri() { 104 | HttpRequest.encode("\\m/"); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/src/site/resources/stylesheets/pygment_trac.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #f0f3f3; } 3 | .highlight .c { color: #0099FF; font-style: italic } /* Comment */ 4 | .highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */ 5 | .highlight .k { color: #006699; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #555555 } /* Operator */ 7 | .highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #009999 } /* Comment.Preproc */ 9 | .highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */ 11 | .highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 14 | .highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */ 15 | .highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ 16 | .highlight .go { color: #AAAAAA } /* Generic.Output */ 17 | .highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */ 20 | .highlight .gt { color: #99CC66 } /* Generic.Traceback */ 21 | .highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { color: #006699 } /* Keyword.Pseudo */ 25 | .highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */ 27 | .highlight .m { color: #FF6600 } /* Literal.Number */ 28 | .highlight .s { color: #CC3300 } /* Literal.String */ 29 | .highlight .na { color: #330099 } /* Name.Attribute */ 30 | .highlight .nb { color: #336666 } /* Name.Builtin */ 31 | .highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #336600 } /* Name.Constant */ 33 | .highlight .nd { color: #9999FF } /* Name.Decorator */ 34 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 35 | .highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */ 36 | .highlight .nf { color: #CC00FF } /* Name.Function */ 37 | .highlight .nl { color: #9999FF } /* Name.Label */ 38 | .highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */ 39 | .highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */ 40 | .highlight .nv { color: #003333 } /* Name.Variable */ 41 | .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ 42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .highlight .mf { color: #FF6600 } /* Literal.Number.Float */ 44 | .highlight .mh { color: #FF6600 } /* Literal.Number.Hex */ 45 | .highlight .mi { color: #FF6600 } /* Literal.Number.Integer */ 46 | .highlight .mo { color: #FF6600 } /* Literal.Number.Oct */ 47 | .highlight .sb { color: #CC3300 } /* Literal.String.Backtick */ 48 | .highlight .sc { color: #CC3300 } /* Literal.String.Char */ 49 | .highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ 50 | .highlight .s2 { color: #CC3300 } /* Literal.String.Double */ 51 | .highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */ 52 | .highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */ 53 | .highlight .si { color: #AA0000 } /* Literal.String.Interpol */ 54 | .highlight .sx { color: #CC3300 } /* Literal.String.Other */ 55 | .highlight .sr { color: #33AAAA } /* Literal.String.Regex */ 56 | .highlight .s1 { color: #CC3300 } /* Literal.String.Single */ 57 | .highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */ 58 | .highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */ 59 | .highlight .vc { color: #003333 } /* Name.Variable.Class */ 60 | .highlight .vg { color: #003333 } /* Name.Variable.Global */ 61 | .highlight .vi { color: #003333 } /* Name.Variable.Instance */ 62 | .highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */ 63 | 64 | .type-csharp .highlight .k { color: #0000FF } 65 | .type-csharp .highlight .kt { color: #0000FF } 66 | .type-csharp .highlight .nf { color: #000000; font-weight: normal } 67 | .type-csharp .highlight .nc { color: #2B91AF } 68 | .type-csharp .highlight .nn { color: #000000 } 69 | .type-csharp .highlight .s { color: #A31515 } 70 | .type-csharp .highlight .sc { color: #A31515 } 71 | -------------------------------------------------------------------------------- /lib/src/site/resources/params.json: -------------------------------------------------------------------------------- 1 | {"google":"","body":"# Http Request\r\n\r\nA simple convenience library for using a [HttpURLConnection](http://download.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html)\r\nto make requests and access the response. \r\n\r\n## Usage\r\n\r\nThe http-request library is available from [Maven Central](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.kevinsawicki%22%20AND%20a%3A%22http-request%22).\r\n\r\n```xml\r\n\r\n com.github.kevinsawicki\r\n http-request\r\n 2.2\r\n \r\n```\r\n\r\nJavadocs are available [here](http://kevinsawicki.github.com/http-request/apidocs/index.html).\r\n\r\n## FAQ\r\n\r\n### Why was this written?\r\n\r\nThis library was written to make HTTP requests simple and easy when using a `HttpURLConnection`.\r\n\r\nLibraries like [Apache HttpComponents](http://hc.apache.org) are great but sometimes\r\nfor either simplicity, or perhaps for the environment you are deploying to (Android),\r\nyou just want to use a good old-fashioned `HttpURLConnection`. This library seeks\r\nto add convenience and common patterns to the act of making HTTP requests such as\r\na fluid-interface for building requests and support for features such as multipart\r\nrequests.\r\n\r\n**Bottom line:** The single goal of this library is to improve the usability of the\r\n`HttpURLConnection` class.\r\n\r\n### What are the dependencies?\r\n\r\nNone. The goal of this library is to be a single class class with some inner static\r\nclasses. The test project does require [Jetty](http://eclipse.org/jetty/) in order\r\nto test requests against an actual HTTP server implementation.\r\n\r\n### How are exceptions managed?\r\n\r\nThe `HttpRequest` class does not throw any checked exceptions, instead all low-level\r\nexceptions are wrapped up in a `HttpRequestException` which extends `RuntimeException`.\r\nYou can access the underlying exception by catching `HttpRequestException` and calling\r\n`getCause()` which will always return the original `IOException`.\r\n\r\n## Examples\r\n\r\n### Perform a GET request and get the status of the response\r\n\r\n```java\r\nint response = HttpRequest.get(\"http://google.com\").code();\r\n```\r\n\r\n### Perform a GET request and get the body of the response\r\n\r\n```java\r\nString response = HttpRequest.get(\"http://google.com\").body();\r\nSystem.out.println(\"Response was: \" + response);\r\n```\r\n\r\n### Print the response of a GET request to standard out\r\n\r\n```java\r\nHttpRequest.get(\"http://google.com\").receive(System.out);\r\n```\r\n\r\n### Working with request/response headers\r\n\r\n```java\r\nString contentType = HttpRequest.get(\"http://google.com\")\r\n .accept(\"application/json\") //Sets request header\r\n .contentType(); //Gets response header\r\nSystem.out.println(\"Response content type was \" + contentType);\r\n``` \r\n\r\n### Perform a POST request with some data and get the status of the response\r\n\r\n```java\r\nint response = HttpRequest.post(\"http://google.com\").send(\"name=kevin\").code();\r\n```\r\n\r\n### Authenticate using Basic authentication\r\n\r\n```java\r\nint response = HttpRequest.get(\"http://google.com\").basic(\"username\", \"p4ssw0rd\").code();\r\n```\r\n\r\n### Perform a multipart POST request\r\n\r\n```java\r\nHttpRequest request = HttpRequest.post(\"http://google.com\");\r\nrequest.part(\"status[body]\", \"Making a multipart request\");\r\nrequest.part(\"status[image]\", new File(\"/home/kevin/Pictures/ide.png\"));\r\nif (request.ok())\r\n System.out.println(\"Status was updated\");\r\n```\r\n\r\n### Perform a POST request with form data\r\n\r\n```java\r\nMap data = new HashMap();\r\ndata.put(\"user\", \"A User\");\r\ndata.put(\"state\", \"CA\");\r\nif (HttpRequest.post(\"http://google.com\").form(data).created())\r\n System.out.println(\"User was created\");\r\n```\r\n\r\n### Copy body of response to a file\r\n\r\n```java\r\nFile output = new File(\"/output/request.out\");\r\nHttpRequest.get(\"http://google.com\").receive(output);\r\n```\r\n### Post contents of a file\r\n\r\n```java\r\nFile input = new File(\"/input/data.txt\");\r\nint response = HttpRequest.post(\"http://google.com\").send(input).code();\r\n```\r\n\r\n### Using entity tags for caching\r\n\r\n```java\r\nFile latest = new File(\"/data/cache.json\");\r\nHttpRequest request = HttpRequest.get(\"http://google.com\");\r\n//Copy response to file\r\nrequest.receive(latest);\r\n//Store eTag of response\r\nString eTag = request.eTag();\r\n//Later on check if changes exist\r\nboolean unchanged = HttpRequest.get(\"http://google.com\")\r\n .ifNoneMatch(eTag)\r\n .notModified();\r\n```\r\n\r\n### Using gzip compression\r\n\r\n```java\r\nHttpRequest request = HttpRequest.get(\"http://google.com\");\r\n//Tell server to gzip response and automatically uncompress\r\nrequest.acceptGzipEncoding().uncompress(true);\r\nString uncompressed = request.body();\r\nSystem.out.println(\"Uncompressed response is: \" + uncompressed);\r\n```\r\n\r\n### Ignoring security when using HTTPS\r\n\r\n```java\r\nHttpRequest request = HttpRequest.get(\"https://google.com\");\r\n//Accept all certificates\r\nrequest.trustAllCerts();\r\n//Accept all hostnames\r\nrequest.trustAllHosts();\r\n```\r\n\r\n## Contributors\r\n\r\n* [Kevin Sawicki](https://github.com/kevinsawicki) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=kevinsawicki)\r\n* [Eddie Ringle](https://github.com/eddieringle) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=eddieringle)\r\n* [Sean Jensen-Grey](https://github.com/seanjensengrey) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=seanjensengrey)\r\n* [Levi Notik](https://github.com/levinotik) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=levinotik)\r\n","name":"http-request","tagline":"Java HTTP Request Library","note":"Don't delete this file! It's used internally to help with page regeneration."} -------------------------------------------------------------------------------- /lib/src/test/java/com/github/kevinsawicki/http/ServerTestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Kevin Sawicki 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | package com.github.kevinsawicki.http; 23 | 24 | import org.eclipse.jetty.util.B64Code; 25 | 26 | import org.junit.Before; 27 | import org.eclipse.jetty.server.handler.HandlerList; 28 | import org.eclipse.jetty.servlet.ServletHolder; 29 | import org.eclipse.jetty.servlet.ServletHandler; 30 | 31 | import java.io.ByteArrayOutputStream; 32 | import java.io.IOException; 33 | import java.io.InputStream; 34 | import java.io.UnsupportedEncodingException; 35 | import java.util.concurrent.atomic.AtomicInteger; 36 | import java.util.concurrent.atomic.AtomicReference; 37 | 38 | import static com.github.kevinsawicki.http.HttpRequest.CHARSET_UTF8; 39 | 40 | import javax.servlet.ServletException; 41 | import javax.servlet.http.HttpServletRequest; 42 | import javax.servlet.http.HttpServletResponse; 43 | 44 | import org.eclipse.jetty.server.Connector; 45 | import org.eclipse.jetty.server.Handler; 46 | import org.eclipse.jetty.server.Request; 47 | import org.eclipse.jetty.server.Server; 48 | import org.eclipse.jetty.server.handler.AbstractHandler; 49 | import org.eclipse.jetty.server.nio.SelectChannelConnector; 50 | import org.junit.AfterClass; 51 | 52 | /** 53 | * Base test case that provides a running HTTP server 54 | */ 55 | public class ServerTestCase { 56 | 57 | /** 58 | * Simplified handler 59 | */ 60 | protected static abstract class RequestHandler extends AbstractHandler { 61 | 62 | private Request request; 63 | 64 | private HttpServletResponse response; 65 | 66 | /** 67 | * Handle request 68 | * 69 | * @param request 70 | * @param response 71 | */ 72 | public abstract void handle(Request request, HttpServletResponse response); 73 | 74 | /** 75 | * Read content 76 | * 77 | * @return content 78 | */ 79 | protected byte[] read() { 80 | ByteArrayOutputStream content = new ByteArrayOutputStream(); 81 | final byte[] buffer = new byte[8196]; 82 | int read; 83 | try { 84 | InputStream input = request.getInputStream(); 85 | while ((read = input.read(buffer)) != -1) 86 | content.write(buffer, 0, read); 87 | } catch (IOException e) { 88 | throw new RuntimeException(e); 89 | } 90 | return content.toByteArray(); 91 | } 92 | 93 | /** 94 | * Write value 95 | * 96 | * @param value 97 | */ 98 | protected void write(String value) { 99 | try { 100 | response.getWriter().print(value); 101 | } catch (IOException e) { 102 | throw new RuntimeException(e); 103 | } 104 | } 105 | 106 | /** 107 | * Write line 108 | * 109 | * @param value 110 | */ 111 | protected void writeln(String value) { 112 | try { 113 | response.getWriter().println(value); 114 | } catch (IOException e) { 115 | throw new RuntimeException(e); 116 | } 117 | } 118 | 119 | public void handle(String target, Request baseRequest, 120 | HttpServletRequest request, HttpServletResponse response) 121 | throws IOException, ServletException { 122 | this.request = (Request) request; 123 | this.response = response; 124 | this.request.setHandled(true); 125 | handle(this.request, response); 126 | } 127 | 128 | } 129 | 130 | /** 131 | * Server 132 | */ 133 | protected static Server server; 134 | protected static Server proxy; 135 | protected static int proxyPort; 136 | protected static final AtomicInteger proxyHitCount = new AtomicInteger(0); 137 | protected static final AtomicReference proxyUser = new AtomicReference(); 138 | protected static final AtomicReference proxyPassword = new AtomicReference(); 139 | 140 | /** 141 | * Set up server with handler 142 | * 143 | * @param handler 144 | * @return port 145 | * @throws Exception 146 | */ 147 | public static String setUp(final Handler handler) throws Exception { 148 | server = new Server(); 149 | if (handler != null) 150 | server.setHandler(handler); 151 | Connector connector = new SelectChannelConnector(); 152 | connector.setPort(0); 153 | server.setConnectors(new Connector[] { connector }); 154 | server.start(); 155 | 156 | proxy = new Server(); 157 | Connector proxyConnector = new SelectChannelConnector(); 158 | proxyConnector.setPort(0); 159 | proxy.setConnectors(new Connector[] { proxyConnector }); 160 | 161 | ServletHandler proxyHandler = new ServletHandler(); 162 | 163 | RequestHandler proxyCountingHandler = new RequestHandler() { 164 | 165 | @Override 166 | public void handle(Request request, HttpServletResponse response) { 167 | proxyHitCount.incrementAndGet(); 168 | String auth = request.getHeader("Proxy-Authorization"); 169 | auth = auth.substring(auth.indexOf(' ') + 1); 170 | try { 171 | auth = B64Code.decode(auth, CHARSET_UTF8); 172 | } catch (UnsupportedEncodingException e) { 173 | throw new RuntimeException(e); 174 | } 175 | int colon = auth.indexOf(':'); 176 | proxyUser.set(auth.substring(0, colon)); 177 | proxyPassword.set(auth.substring(colon + 1)); 178 | request.setHandled(false); 179 | } 180 | }; 181 | 182 | HandlerList handlerList = new HandlerList(); 183 | handlerList.addHandler(proxyCountingHandler); 184 | handlerList.addHandler(proxyHandler); 185 | proxy.setHandler(handlerList); 186 | 187 | ServletHolder proxyHolder = proxyHandler.addServletWithMapping("org.eclipse.jetty.servlets.ProxyServlet", "/"); 188 | proxyHolder.setAsyncSupported(true); 189 | 190 | proxy.start(); 191 | 192 | proxyPort = proxyConnector.getLocalPort(); 193 | 194 | return "http://localhost:" + connector.getLocalPort(); 195 | } 196 | 197 | @Before 198 | public void clearProxyHitCount() { 199 | proxyHitCount.set(0); 200 | } 201 | 202 | /** 203 | * Tear down server if created 204 | * 205 | * @throws Exception 206 | */ 207 | @AfterClass 208 | public static void tearDown() throws Exception { 209 | if (server != null) 210 | server.stop(); 211 | if (proxy != null) 212 | proxy.stop(); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /lib/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.sonatype.oss 7 | oss-parent 8 | 7 9 | 10 | 11 | com.github.kevinsawicki 12 | http-request 13 | 6.1-SNAPSHOT 14 | https://github.com/kevinsawicki/http-request 15 | Library for making HTTP requests 16 | 2011 17 | 18 | 19 | https://github.com/kevinsawicki/http-request/issues 20 | GitHub Issues 21 | 22 | 23 | 24 | UTF-8 25 | 8.1.9.v20130131 26 | 27 | 28 | 29 | 30 | MIT License 31 | http://www.opensource.org/licenses/mit-license.php 32 | repo 33 | 34 | 35 | 36 | 37 | https://github.com/kevinsawicki/http-request 38 | scm:git:git://github.com/kevinsawicki/http-request.git 39 | scm:git:git@github.com:kevinsawicki/http-request.git 40 | 41 | 42 | 43 | 44 | kevinsawicki@gmail.com 45 | Kevin Sawicki 46 | https://github.com/kevinsawicki 47 | kevinsawicki 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 2.3.2 57 | 58 | 1.5 59 | 1.5 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-jar-plugin 65 | 2.3.2 66 | 67 | 68 | ${project.build.outputDirectory}/META-INF/MANIFEST.MF 69 | 70 | 71 | 72 | 73 | org.apache.felix 74 | maven-bundle-plugin 75 | 2.3.7 76 | true 77 | 78 | 79 | 80 | !.,com.github.kevinsawicki.http 81 | J2SE-1.5 82 | 83 | 84 | 85 | 86 | bundle-manifest 87 | process-classes 88 | 89 | manifest 90 | 91 | 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-javadoc-plugin 97 | 2.8 98 | 99 | 100 | 101 | jar 102 | 103 | 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-source-plugin 109 | 2.1.2 110 | 111 | 112 | 113 | jar 114 | 115 | 116 | 117 | 118 | 119 | com.github.github 120 | site-maven-plugin 121 | 0.8 122 | 123 | Generated site for ${project.name} ${project.version} 124 | true 125 | 126 | 127 | 128 | 129 | site 130 | 131 | site 132 | 133 | 134 | 135 | 136 | org.apache.maven.plugins 137 | maven-site-plugin 138 | 3.0 139 | 140 | 141 | 142 | org.apache.maven.plugins 143 | maven-project-info-reports-plugin 144 | 2.2 145 | 146 | true 147 | true 148 | 149 | 150 | 151 | org.apache.maven.plugins 152 | maven-javadoc-plugin 153 | 2.8 154 | 155 | 156 | org.apache.maven.plugins 157 | maven-surefire-report-plugin 158 | 2.6 159 | 160 | 161 | org.apache.maven.plugins 162 | maven-changelog-plugin 163 | 2.2 164 | 165 | 166 | org.codehaus.mojo 167 | emma-maven-plugin 168 | 1.0-alpha-3 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | sign 179 | 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-gpg-plugin 184 | 1.4 185 | 186 | 187 | sign-artifacts 188 | verify 189 | 190 | sign 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | junit 203 | junit 204 | 4.10 205 | test 206 | 207 | 208 | org.eclipse.jetty 209 | jetty-server 210 | ${jetty.version} 211 | test 212 | 213 | 214 | 215 | org.eclipse.jetty 216 | jetty-servlet 217 | ${jetty.version} 218 | test 219 | 220 | 221 | org.eclipse.jetty 222 | jetty-servlets 223 | ${jetty.version} 224 | test 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /lib/src/site/resources/stylesheets/stylesheet.css: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Slate Theme for Github Pages 3 | by Jason Costello, @jsncostello 4 | *******************************************************************************/ 5 | 6 | @import url(pygment_trac.css); 7 | 8 | /******************************************************************************* 9 | MeyerWeb Reset 10 | *******************************************************************************/ 11 | 12 | html, body, div, span, applet, object, iframe, 13 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 14 | a, abbr, acronym, address, big, cite, code, 15 | del, dfn, em, img, ins, kbd, q, s, samp, 16 | small, strike, strong, sub, sup, tt, var, 17 | b, u, i, center, 18 | dl, dt, dd, ol, ul, li, 19 | fieldset, form, label, legend, 20 | table, caption, tbody, tfoot, thead, tr, th, td, 21 | article, aside, canvas, details, embed, 22 | figure, figcaption, footer, header, hgroup, 23 | menu, nav, output, ruby, section, summary, 24 | time, mark, audio, video { 25 | margin: 0; 26 | padding: 0; 27 | border: 0; 28 | font: inherit; 29 | vertical-align: baseline; 30 | } 31 | 32 | /* HTML5 display-role reset for older browsers */ 33 | article, aside, details, figcaption, figure, 34 | footer, header, hgroup, menu, nav, section { 35 | display: block; 36 | } 37 | 38 | ol, ul { 39 | list-style: none; 40 | } 41 | 42 | blockquote, q { 43 | } 44 | 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | 50 | a:focus { 51 | outline: none; 52 | } 53 | 54 | /******************************************************************************* 55 | Theme Styles 56 | *******************************************************************************/ 57 | 58 | body { 59 | box-sizing: border-box; 60 | color:#373737; 61 | background: #212121; 62 | font-size: 16px; 63 | font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; 64 | line-height: 1.5; 65 | -webkit-font-smoothing: antialiased; 66 | } 67 | 68 | h1, h2, h3, h4, h5, h6 { 69 | margin: 10px 0; 70 | font-weight: 700; 71 | color:#222222; 72 | font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif; 73 | letter-spacing: -1px; 74 | } 75 | 76 | h1 { 77 | font-size: 36px; 78 | font-weight: 700; 79 | } 80 | 81 | h2 { 82 | padding-bottom: 10px; 83 | font-size: 32px; 84 | background: url('../images/bg_hr.png') repeat-x bottom; 85 | } 86 | 87 | h3 { 88 | font-size: 24px; 89 | } 90 | 91 | h4 { 92 | font-size: 21px; 93 | } 94 | 95 | h5 { 96 | font-size: 18px; 97 | } 98 | 99 | h6 { 100 | font-size: 16px; 101 | } 102 | 103 | p { 104 | margin: 10px 0 15px 0; 105 | } 106 | 107 | footer p { 108 | color: #f2f2f2; 109 | } 110 | 111 | a { 112 | text-decoration: none; 113 | color: #007edf; 114 | text-shadow: none; 115 | 116 | transition: color 0.5s ease; 117 | transition: text-shadow 0.5s ease; 118 | -webkit-transition: color 0.5s ease; 119 | -webkit-transition: text-shadow 0.5s ease; 120 | -moz-transition: color 0.5s ease; 121 | -moz-transition: text-shadow 0.5s ease; 122 | -o-transition: color 0.5s ease; 123 | -o-transition: text-shadow 0.5s ease; 124 | -ms-transition: color 0.5s ease; 125 | -ms-transition: text-shadow 0.5s ease; 126 | } 127 | 128 | #main_content a:hover { 129 | color: #0069ba; 130 | text-shadow: #0090ff 0px 0px 2px; 131 | } 132 | 133 | footer a:hover { 134 | color: #43adff; 135 | text-shadow: #0090ff 0px 0px 2px; 136 | } 137 | 138 | em { 139 | font-style: italic; 140 | } 141 | 142 | strong { 143 | font-weight: bold; 144 | } 145 | 146 | img { 147 | position: relative; 148 | margin: 0 auto; 149 | max-width: 739px; 150 | padding: 5px; 151 | margin: 10px 0 10px 0; 152 | border: 1px solid #ebebeb; 153 | 154 | box-shadow: 0 0 5px #ebebeb; 155 | -webkit-box-shadow: 0 0 5px #ebebeb; 156 | -moz-box-shadow: 0 0 5px #ebebeb; 157 | -o-box-shadow: 0 0 5px #ebebeb; 158 | -ms-box-shadow: 0 0 5px #ebebeb; 159 | } 160 | 161 | pre, code { 162 | width: 100%; 163 | color: #222; 164 | background-color: #fff; 165 | 166 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; 167 | font-size: 14px; 168 | 169 | border-radius: 2px; 170 | -moz-border-radius: 2px; 171 | -webkit-border-radius: 2px; 172 | 173 | 174 | 175 | } 176 | 177 | pre { 178 | width: 100%; 179 | padding: 10px; 180 | box-shadow: 0 0 10px rgba(0,0,0,.1); 181 | overflow: auto; 182 | } 183 | 184 | code { 185 | padding: 3px; 186 | margin: 0 3px; 187 | box-shadow: 0 0 10px rgba(0,0,0,.1); 188 | } 189 | 190 | pre code { 191 | display: block; 192 | box-shadow: none; 193 | } 194 | 195 | blockquote { 196 | color: #666; 197 | margin-bottom: 20px; 198 | padding: 0 0 0 20px; 199 | border-left: 3px solid #bbb; 200 | } 201 | 202 | ul, ol, dl { 203 | margin-bottom: 15px 204 | } 205 | 206 | ul li { 207 | list-style: inside; 208 | padding-left: 20px; 209 | } 210 | 211 | ol li { 212 | list-style: decimal inside; 213 | padding-left: 20px; 214 | } 215 | 216 | dl dt { 217 | font-weight: bold; 218 | } 219 | 220 | dl dd { 221 | padding-left: 20px; 222 | font-style: italic; 223 | } 224 | 225 | dl p { 226 | padding-left: 20px; 227 | font-style: italic; 228 | } 229 | 230 | hr { 231 | height: 1px; 232 | margin-bottom: 5px; 233 | border: none; 234 | background: url('../images/bg_hr.png') repeat-x center; 235 | } 236 | 237 | table { 238 | border: 1px solid #373737; 239 | margin-bottom: 20px; 240 | text-align: left; 241 | } 242 | 243 | th { 244 | font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; 245 | padding: 10px; 246 | background: #373737; 247 | color: #fff; 248 | } 249 | 250 | td { 251 | padding: 10px; 252 | border: 1px solid #373737; 253 | } 254 | 255 | form { 256 | background: #f2f2f2; 257 | padding: 20px; 258 | } 259 | 260 | img { 261 | width: 100%; 262 | max-width: 100%; 263 | } 264 | 265 | /******************************************************************************* 266 | Full-Width Styles 267 | *******************************************************************************/ 268 | 269 | .outer { 270 | width: 100%; 271 | } 272 | 273 | .inner { 274 | position: relative; 275 | max-width: 640px; 276 | padding: 20px 10px; 277 | margin: 0 auto; 278 | } 279 | 280 | #forkme_banner { 281 | display: block; 282 | position: absolute; 283 | top:0; 284 | right: 10px; 285 | z-index: 10; 286 | padding: 10px 50px 10px 10px; 287 | color: #fff; 288 | background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%; 289 | font-weight: 700; 290 | box-shadow: 0 0 10px rgba(0,0,0,.5); 291 | border-bottom-left-radius: 2px; 292 | border-bottom-right-radius: 2px; 293 | } 294 | 295 | #header_wrap { 296 | background: #212121; 297 | background: -moz-linear-gradient(top, #373737, #212121); 298 | background: -webkit-linear-gradient(top, #373737, #212121); 299 | background: -ms-linear-gradient(top, #373737, #212121); 300 | background: -o-linear-gradient(top, #373737, #212121); 301 | background: linear-gradient(top, #373737, #212121); 302 | } 303 | 304 | #header_wrap .inner { 305 | padding: 50px 10px 30px 10px; 306 | } 307 | 308 | #project_title { 309 | margin: 0; 310 | color: #fff; 311 | font-size: 42px; 312 | font-weight: 700; 313 | text-shadow: #111 0px 0px 10px; 314 | } 315 | 316 | #project_tagline { 317 | color: #fff; 318 | font-size: 24px; 319 | font-weight: 300; 320 | background: none; 321 | text-shadow: #111 0px 0px 10px; 322 | } 323 | 324 | #downloads { 325 | position: absolute; 326 | width: 210px; 327 | z-index: 10; 328 | bottom: -40px; 329 | right: 0; 330 | height: 70px; 331 | background: url('../images/icon_download.png') no-repeat 0% 90%; 332 | } 333 | 334 | .zip_download_link { 335 | display: block; 336 | float: right; 337 | width: 90px; 338 | height:70px; 339 | text-indent: -5000px; 340 | overflow: hidden; 341 | background: url(../images/sprite_download.png) no-repeat bottom left; 342 | } 343 | 344 | .tar_download_link { 345 | display: block; 346 | float: right; 347 | width: 90px; 348 | height:70px; 349 | text-indent: -5000px; 350 | overflow: hidden; 351 | background: url(../images/sprite_download.png) no-repeat bottom right; 352 | margin-left: 10px; 353 | } 354 | 355 | .zip_download_link:hover { 356 | background: url(../images/sprite_download.png) no-repeat top left; 357 | } 358 | 359 | .tar_download_link:hover { 360 | background: url(../images/sprite_download.png) no-repeat top right; 361 | } 362 | 363 | #main_content_wrap { 364 | background: #f2f2f2; 365 | border-top: 1px solid #111; 366 | border-bottom: 1px solid #111; 367 | } 368 | 369 | #main_content { 370 | padding-top: 40px; 371 | } 372 | 373 | #footer_wrap { 374 | background: #212121; 375 | } 376 | 377 | 378 | 379 | /******************************************************************************* 380 | Small Device Styles 381 | *******************************************************************************/ 382 | 383 | @media screen and (max-width: 480px) { 384 | body { 385 | font-size:14px; 386 | } 387 | 388 | #downloads { 389 | display: none; 390 | } 391 | 392 | .inner { 393 | min-width: 320px; 394 | max-width: 480px; 395 | } 396 | 397 | #project_title { 398 | font-size: 32px; 399 | } 400 | 401 | h1 { 402 | font-size: 28px; 403 | } 404 | 405 | h2 { 406 | font-size: 24px; 407 | } 408 | 409 | h3 { 410 | font-size: 21px; 411 | } 412 | 413 | h4 { 414 | font-size: 18px; 415 | } 416 | 417 | h5 { 418 | font-size: 14px; 419 | } 420 | 421 | h6 { 422 | font-size: 12px; 423 | } 424 | 425 | code, pre { 426 | min-width: 320px; 427 | max-width: 480px; 428 | font-size: 11px; 429 | } 430 | 431 | } 432 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Http Request [![Build Status](https://travis-ci.org/kevinsawicki/http-request.svg)](https://travis-ci.org/kevinsawicki/http-request) 2 | 3 | A simple convenience library for using a [HttpURLConnection](http://download.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html) 4 | to make requests and access the response. 5 | 6 | This library is available under the [MIT License](http://www.opensource.org/licenses/mit-license.php). 7 | 8 | ## Usage 9 | 10 | The http-request library is available from [Maven Central](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.kevinsawicki%22%20AND%20a%3A%22http-request%22). 11 | 12 | ```xml 13 | 14 | com.github.kevinsawicki 15 | http-request 16 | 6.0 17 | 18 | ``` 19 | 20 | Not using [Maven](http://maven.apache.org/)? Simply copy the [HttpRequest](https://raw.githubusercontent.com/kevinsawicki/http-request/master/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java) 21 | class into your project, update the package declaration, and you are good to go. 22 | 23 | Javadocs are available [here](http://kevinsawicki.github.com/http-request/apidocs/index.html). 24 | 25 | ## FAQ 26 | 27 | ### Who uses this? 28 | 29 | See [here](https://github.com/kevinsawicki/http-request/wiki/Used-By) for a 30 | list of known projects using this library. 31 | 32 | ### Why was this written? 33 | 34 | This library was written to make HTTP requests simple and easy when using a `HttpURLConnection`. 35 | 36 | Libraries like [Apache HttpComponents](http://hc.apache.org) are great but sometimes 37 | for either simplicity, or perhaps for the environment you are deploying to (Android), 38 | you just want to use a good old-fashioned `HttpURLConnection`. This library seeks 39 | to add convenience and common patterns to the act of making HTTP requests such as 40 | a fluid-interface for building requests and support for features such as multipart 41 | requests. 42 | 43 | **Bottom line:** The single goal of this library is to improve the usability of the 44 | `HttpURLConnection` class. 45 | 46 | ### What are the dependencies? 47 | 48 | **None**. The goal of this library is to be a single class class with some inner static 49 | classes. The test project does require [Jetty](http://eclipse.org/jetty/) in order 50 | to test requests against an actual HTTP server implementation. 51 | 52 | ### How are exceptions managed? 53 | 54 | The `HttpRequest` class does not throw any checked exceptions, instead all low-level 55 | exceptions are wrapped up in a `HttpRequestException` which extends `RuntimeException`. 56 | You can access the underlying exception by catching `HttpRequestException` and calling 57 | `getCause()` which will always return the original `IOException`. 58 | 59 | ### Are requests asynchronous? 60 | 61 | **No**. The underlying `HttpUrlConnection` object that each `HttpRequest` 62 | object wraps has a synchronous API and therefore all methods on `HttpRequest` 63 | are also synchronous. 64 | 65 | Therefore it is important to not use an `HttpRequest` object on the main thread 66 | of your application. 67 | 68 | Here is a simple Android example of using it from an 69 | [AsyncTask](http://developer.android.com/reference/android/os/AsyncTask.html): 70 | 71 | ```java 72 | private class DownloadTask extends AsyncTask { 73 | protected File doInBackground(String... urls) { 74 | try { 75 | HttpRequest request = HttpRequest.get(urls[0]); 76 | File file = null; 77 | if (request.ok()) { 78 | file = File.createTempFile("download", ".tmp"); 79 | request.receive(file); 80 | publishProgress(file.length()); 81 | } 82 | return file; 83 | } catch (HttpRequestException exception) { 84 | return null; 85 | } 86 | } 87 | 88 | protected void onProgressUpdate(Long... progress) { 89 | Log.d("MyApp", "Downloaded bytes: " + progress[0]); 90 | } 91 | 92 | protected void onPostExecute(File file) { 93 | if (file != null) 94 | Log.d("MyApp", "Downloaded file to: " + file.getAbsolutePath()); 95 | else 96 | Log.d("MyApp", "Download failed"); 97 | } 98 | } 99 | 100 | new DownloadTask().execute("http://google.com"); 101 | ``` 102 | 103 | ## Examples 104 | 105 | ### Perform a GET request and get the status of the response 106 | 107 | ```java 108 | int response = HttpRequest.get("http://google.com").code(); 109 | ``` 110 | 111 | ### Perform a GET request and get the body of the response 112 | 113 | ```java 114 | String response = HttpRequest.get("http://google.com").body(); 115 | System.out.println("Response was: " + response); 116 | ``` 117 | 118 | ### Print the response of a GET request to standard out 119 | 120 | ```java 121 | HttpRequest.get("http://google.com").receive(System.out); 122 | ``` 123 | 124 | ### Adding query parameters 125 | 126 | ```java 127 | HttpRequest request = HttpRequest.get("http://google.com", true, 'q', "baseball gloves", "size", 100); 128 | System.out.println(request.toString()); // GET http://google.com?q=baseball%20gloves&size=100 129 | ``` 130 | 131 | ### Using arrays as query parameters 132 | 133 | ```java 134 | int[] ids = new int[] { 22, 23 }; 135 | HttpRequest request = HttpRequest.get("http://google.com", true, "id", ids); 136 | System.out.println(request.toString()); // GET http://google.com?id[]=22&id[]=23 137 | ``` 138 | 139 | ### Working with request/response headers 140 | 141 | ```java 142 | String contentType = HttpRequest.get("http://google.com") 143 | .accept("application/json") //Sets request header 144 | .contentType(); //Gets response header 145 | System.out.println("Response content type was " + contentType); 146 | ``` 147 | 148 | ### Perform a POST request with some data and get the status of the response 149 | 150 | ```java 151 | int response = HttpRequest.post("http://google.com").send("name=kevin").code(); 152 | ``` 153 | 154 | ### Authenticate using Basic authentication 155 | 156 | ```java 157 | int response = HttpRequest.get("http://google.com").basic("username", "p4ssw0rd").code(); 158 | ``` 159 | 160 | ### Perform a multipart POST request 161 | 162 | ```java 163 | HttpRequest request = HttpRequest.post("http://google.com"); 164 | request.part("status[body]", "Making a multipart request"); 165 | request.part("status[image]", new File("/home/kevin/Pictures/ide.png")); 166 | if (request.ok()) 167 | System.out.println("Status was updated"); 168 | ``` 169 | 170 | ### Perform a POST request with form data 171 | 172 | ```java 173 | Map data = new HashMap(); 174 | data.put("user", "A User"); 175 | data.put("state", "CA"); 176 | if (HttpRequest.post("http://google.com").form(data).created()) 177 | System.out.println("User was created"); 178 | ``` 179 | 180 | ### Copy body of response to a file 181 | 182 | ```java 183 | File output = new File("/output/request.out"); 184 | HttpRequest.get("http://google.com").receive(output); 185 | ``` 186 | ### Post contents of a file 187 | 188 | ```java 189 | File input = new File("/input/data.txt"); 190 | int response = HttpRequest.post("http://google.com").send(input).code(); 191 | ``` 192 | 193 | ### Using entity tags for caching 194 | 195 | ```java 196 | File latest = new File("/data/cache.json"); 197 | HttpRequest request = HttpRequest.get("http://google.com"); 198 | //Copy response to file 199 | request.receive(latest); 200 | //Store eTag of response 201 | String eTag = request.eTag(); 202 | //Later on check if changes exist 203 | boolean unchanged = HttpRequest.get("http://google.com") 204 | .ifNoneMatch(eTag) 205 | .notModified(); 206 | ``` 207 | 208 | ### Using gzip compression 209 | 210 | ```java 211 | HttpRequest request = HttpRequest.get("http://google.com"); 212 | //Tell server to gzip response and automatically uncompress 213 | request.acceptGzipEncoding().uncompress(true); 214 | String uncompressed = request.body(); 215 | System.out.println("Uncompressed response is: " + uncompressed); 216 | ``` 217 | 218 | ### Ignoring security when using HTTPS 219 | 220 | ```java 221 | HttpRequest request = HttpRequest.get("https://google.com"); 222 | //Accept all certificates 223 | request.trustAllCerts(); 224 | //Accept all hostnames 225 | request.trustAllHosts(); 226 | ``` 227 | 228 | ### Configuring an HTTP proxy 229 | 230 | ```java 231 | HttpRequest request = HttpRequest.get("https://google.com"); 232 | //Configure proxy 233 | request.useProxy("localhost", 8080); 234 | //Optional proxy basic authentication 235 | request.proxyBasic("username", "p4ssw0rd"); 236 | ``` 237 | 238 | ### Following redirects 239 | 240 | ```java 241 | int code = HttpRequest.get("http://google.com").followRedirects(true).code(); 242 | ``` 243 | 244 | ### Custom connection factory 245 | 246 | Looking to use this library with [OkHttp](https://github.com/square/okhttp)? 247 | Read [here](https://gist.github.com/JakeWharton/5797571). 248 | 249 | ```java 250 | HttpRequest.setConnectionFactory(new ConnectionFactory() { 251 | 252 | public HttpURLConnection create(URL url) throws IOException { 253 | if (!"https".equals(url.getProtocol())) 254 | throw new IOException("Only secure requests are allowed"); 255 | return (HttpURLConnection) url.openConnection(); 256 | } 257 | 258 | public HttpURLConnection create(URL url, Proxy proxy) throws IOException { 259 | if (!"https".equals(url.getProtocol())) 260 | throw new IOException("Only secure requests are allowed"); 261 | return (HttpURLConnection) url.openConnection(proxy); 262 | } 263 | }); 264 | ``` 265 | 266 | ## Contributors 267 | 268 | * [Kevin Sawicki](https://github.com/kevinsawicki) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=kevinsawicki) 269 | * [Eddie Ringle](https://github.com/eddieringle) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=eddieringle) 270 | * [Sean Jensen-Grey](https://github.com/seanjensengrey) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=seanjensengrey) 271 | * [Levi Notik](https://github.com/levinotik) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=levinotik) 272 | * [Michael Wang](https://github.com/michael-wang) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=michael-wang) 273 | * [Julien HENRY](https://github.com/henryju) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=henryju) 274 | * [Benoit Lubek](https://github.com/BoD) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=BoD) 275 | * [Jake Wharton](https://github.com/JakeWharton) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=JakeWharton) 276 | * [Oskar Hagberg](https://github.com/oskarhagberg) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=oskarhagberg) 277 | * [David Pate](https://github.com/DavidTPate) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=DavidTPate) 278 | * [Anton Rieder](https://github.com/aried3r) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=aried3r) 279 | * [Jean-Baptiste Lièvremont](https://github.com/jblievremont) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=jblievremont) 280 | * [Roman Petrenko](https://github.com/romanzes) :: [contributions](https://github.com/kevinsawicki/http-request/commits?author=romanzes) 281 | -------------------------------------------------------------------------------- /lib/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.5 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.5 12 | org.eclipse.jdt.core.formatter.align_type_members_on_columns=false 13 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 14 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 15 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 16 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 17 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 18 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 19 | org.eclipse.jdt.core.formatter.alignment_for_assignment=0 20 | org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 21 | org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 22 | org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 23 | org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 24 | org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 25 | org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 26 | org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 27 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 28 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 29 | org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 30 | org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 31 | org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 32 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 33 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 34 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 35 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 36 | org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 37 | org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 38 | org.eclipse.jdt.core.formatter.blank_lines_after_package=1 39 | org.eclipse.jdt.core.formatter.blank_lines_before_field=0 40 | org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 41 | org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 42 | org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 43 | org.eclipse.jdt.core.formatter.blank_lines_before_method=1 44 | org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 45 | org.eclipse.jdt.core.formatter.blank_lines_before_package=0 46 | org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 47 | org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 48 | org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line 49 | org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line 50 | org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line 51 | org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line 52 | org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line 53 | org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line 54 | org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line 55 | org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line 56 | org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line 57 | org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line 58 | org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line 59 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false 60 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false 61 | org.eclipse.jdt.core.formatter.comment.format_block_comments=true 62 | org.eclipse.jdt.core.formatter.comment.format_header=false 63 | org.eclipse.jdt.core.formatter.comment.format_html=true 64 | org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true 65 | org.eclipse.jdt.core.formatter.comment.format_line_comments=true 66 | org.eclipse.jdt.core.formatter.comment.format_source_code=true 67 | org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true 68 | org.eclipse.jdt.core.formatter.comment.indent_root_tags=true 69 | org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert 70 | org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert 71 | org.eclipse.jdt.core.formatter.comment.line_length=80 72 | org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true 73 | org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true 74 | org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false 75 | org.eclipse.jdt.core.formatter.compact_else_if=true 76 | org.eclipse.jdt.core.formatter.continuation_indentation=2 77 | org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 78 | org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off 79 | org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on 80 | org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false 81 | org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true 82 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true 83 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true 84 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true 85 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true 86 | org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true 87 | org.eclipse.jdt.core.formatter.indent_empty_lines=false 88 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true 89 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true 90 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true 91 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false 92 | org.eclipse.jdt.core.formatter.indentation.size=2 93 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert 94 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert 95 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert 96 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert 97 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert 98 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert 99 | org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert 100 | org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert 101 | org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert 102 | org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert 103 | org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert 104 | org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert 105 | org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert 106 | org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert 107 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert 108 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert 109 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert 110 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert 111 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert 112 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert 113 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert 114 | org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert 115 | org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert 116 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert 117 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert 118 | org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert 119 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert 120 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert 121 | org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert 122 | org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert 123 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert 124 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert 125 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert 126 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert 127 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert 128 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert 129 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert 130 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert 131 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert 132 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert 133 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert 134 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert 135 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert 136 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert 137 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert 138 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert 139 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert 140 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert 141 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert 142 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert 143 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert 144 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert 145 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert 146 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert 147 | org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert 148 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert 149 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert 150 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert 151 | org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert 152 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert 153 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert 154 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert 155 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert 156 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert 157 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert 158 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert 159 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert 160 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert 161 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert 162 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert 163 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert 164 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert 165 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert 166 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert 167 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert 168 | org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert 169 | org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert 170 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert 171 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert 172 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert 173 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert 174 | org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert 175 | org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert 176 | org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert 177 | org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert 178 | org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert 179 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert 180 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert 181 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert 182 | org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert 183 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert 184 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert 185 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert 186 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert 187 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert 188 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert 189 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert 190 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert 191 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert 192 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert 193 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert 194 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert 195 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert 196 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert 197 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert 198 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert 199 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert 200 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert 201 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert 202 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert 203 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert 204 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert 205 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert 206 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert 207 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert 208 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert 209 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert 210 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert 211 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert 212 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert 213 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert 214 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert 215 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert 216 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert 217 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert 218 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert 219 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert 220 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert 221 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert 222 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert 223 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert 224 | org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert 225 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert 226 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert 227 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert 228 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert 229 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert 230 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert 231 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert 232 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert 233 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert 234 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert 235 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert 236 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert 237 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert 238 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert 239 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert 240 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert 241 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert 242 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert 243 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert 244 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert 245 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert 246 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert 247 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert 248 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert 249 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert 250 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert 251 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert 252 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert 253 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert 254 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert 255 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert 256 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert 257 | org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert 258 | org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert 259 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert 260 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert 261 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert 262 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert 263 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert 264 | org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert 265 | org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert 266 | org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert 267 | org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert 268 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert 269 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert 270 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert 271 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert 272 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert 273 | org.eclipse.jdt.core.formatter.join_lines_in_comments=true 274 | org.eclipse.jdt.core.formatter.join_wrapped_lines=true 275 | org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false 276 | org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false 277 | org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false 278 | org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false 279 | org.eclipse.jdt.core.formatter.lineSplit=80 280 | org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false 281 | org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false 282 | org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 283 | org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 284 | org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true 285 | org.eclipse.jdt.core.formatter.tabulation.char=space 286 | org.eclipse.jdt.core.formatter.tabulation.size=2 287 | org.eclipse.jdt.core.formatter.use_on_off_tags=false 288 | org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false 289 | org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true 290 | org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true 291 | org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true 292 | -------------------------------------------------------------------------------- /lib/src/site/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | http-request 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | View on GitHub 20 | 21 |

http-request

22 |

Java HTTP Request Library

23 | 24 |
25 | Download this project as a .zip file 26 | Download this project as a tar.gz file 27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |

35 | Http Request

36 | 37 |

A simple convenience library for using a HttpURLConnection 38 | to make requests and access the response.

39 | 40 |

41 | Usage

42 | 43 |

The http-request library is available from Maven Central.

44 | 45 |
<dependency>
 46 |   <groupId>com.github.kevinsawicki</groupId>
 47 |   <artifactId>http-request</artifactId>
 48 |   <version>5.4</version>
 49 | </dependency>
 50 | 
51 | 52 |

Javadocs are available here.

53 | 54 |

55 | FAQ

56 | 57 |

58 | Who uses this?

59 | 60 |

See here for a 61 | list of known projects using this library.

62 | 63 |

64 | Why was this written?

65 | 66 |

This library was written to make HTTP requests simple and easy when using a HttpURLConnection.

67 | 68 |

Libraries like Apache HttpComponents are great but sometimes 69 | for either simplicity, or perhaps for the environment you are deploying to (Android), 70 | you just want to use a good old-fashioned HttpURLConnection. This library seeks 71 | to add convenience and common patterns to the act of making HTTP requests such as 72 | a fluid-interface for building requests and support for features such as multipart 73 | requests.

74 | 75 |

Bottom line: The single goal of this library is to improve the usability of the 76 | HttpURLConnection class.

77 | 78 |

79 | What are the dependencies?

80 | 81 |

None. The goal of this library is to be a single class class with some inner static 82 | classes. The test project does require Jetty in order 83 | to test requests against an actual HTTP server implementation.

84 | 85 |

86 | How are exceptions managed?

87 | 88 |

The HttpRequest class does not throw any checked exceptions, instead all low-level 89 | exceptions are wrapped up in a HttpRequestException which extends RuntimeException. 90 | You can access the underlying exception by catching HttpRequestException and calling 91 | getCause() which will always return the original IOException.

92 | 93 |

94 | Examples

95 | 96 |

97 | Perform a GET request and get the status of the response

98 | 99 |
int response = HttpRequest.get("http://google.com").code();
100 | 
101 | 102 |

103 | Perform a GET request and get the body of the response

104 | 105 |
String response = HttpRequest.get("http://google.com").body();
106 | System.out.println("Response was: " + response);
107 | 
108 | 109 |

110 | Print the response of a GET request to standard out

111 | 112 |
HttpRequest.get("http://google.com").receive(System.out);
113 | 
114 | 115 |

116 | Adding query parameters

117 | 118 |
HttpRequest request = HttpRequest.get("http://google.com", true, 'q', "baseball gloves", "size", 100);
119 | System.out.println(request.toString()); // GET http://google.com?q=baseball%20gloves&size=100
120 | 
121 | 122 |

123 | Working with request/response headers

124 | 125 |
String contentType = HttpRequest.get("http://google.com")
126 |                                 .accept("application/json") //Sets request header
127 |                                 .contentType(); //Gets response header
128 | System.out.println("Response content type was " + contentType);
129 | 
130 | 131 |

132 | Perform a POST request with some data and get the status of the response

133 | 134 |
int response = HttpRequest.post("http://google.com").send("name=kevin").code();
135 | 
136 | 137 |

138 | Authenticate using Basic authentication

139 | 140 |
int response = HttpRequest.get("http://google.com").basic("username", "p4ssw0rd").code();
141 | 
142 | 143 |

144 | Perform a multipart POST request

145 | 146 |
HttpRequest request = HttpRequest.post("http://google.com");
147 | request.part("status[body]", "Making a multipart request");
148 | request.part("status[image]", new File("/home/kevin/Pictures/ide.png"));
149 | if (request.ok())
150 |   System.out.println("Status was updated");
151 | 
152 | 153 |

154 | Perform a POST request with form data

155 | 156 |
Map<String, String> data = new HashMap<String, String>();
157 | data.put("user", "A User");
158 | data.put("state", "CA");
159 | if (HttpRequest.post("http://google.com").form(data).created())
160 |   System.out.println("User was created");
161 | 
162 | 163 |

164 | Copy body of response to a file

165 | 166 |
File output = new File("/output/request.out");
167 | HttpRequest.get("http://google.com").receive(output);
168 | 
169 | 170 |

171 | Post contents of a file

172 | 173 |
File input = new File("/input/data.txt");
174 | int response = HttpRequest.post("http://google.com").send(input).code();
175 | 
176 | 177 |

178 | Using entity tags for caching

179 | 180 |
File latest = new File("/data/cache.json");
181 | HttpRequest request = HttpRequest.get("http://google.com");
182 | //Copy response to file
183 | request.receive(latest);
184 | //Store eTag of response
185 | String eTag = request.eTag();
186 | //Later on check if changes exist
187 | boolean unchanged = HttpRequest.get("http://google.com")
188 |                                .ifNoneMatch(eTag)
189 |                                .notModified();
190 | 
191 | 192 |

193 | Using gzip compression

194 | 195 |
HttpRequest request = HttpRequest.get("http://google.com");
196 | //Tell server to gzip response and automatically uncompress
197 | request.acceptGzipEncoding().uncompress(true);
198 | String uncompressed = request.body();
199 | System.out.println("Uncompressed response is: " + uncompressed);
200 | 
201 | 202 |

203 | Ignoring security when using HTTPS

204 | 205 |
HttpRequest request = HttpRequest.get("https://google.com");
206 | //Accept all certificates
207 | request.trustAllCerts();
208 | //Accept all hostnames
209 | request.trustAllHosts();
210 | 
211 | 212 |

213 | Configure HTTP proxy

214 | 215 |
HttpRequest request = HttpRequest.get("https://google.com");
216 | //Configure proxy
217 | request.useProxy("localhost", 8080);
218 | //Optional proxy basic uthentication
219 | request.proxyBasic("username", "p4ssw0rd");
220 | 
221 | 222 |

223 | Follow redirects

224 | 225 |
int code = HttpRequest.get("http://google.com").followRedirects(true).code();
226 | 
227 | 228 |

229 | Custom connection factory

230 | 231 |
HttpRequest.setConnectionFactory(new ConnectionFactory() {
232 | 
233 |   public HttpURLConnection create(URL url) throws IOException {
234 |     if (!"https".equals(url.getProtocol())) {
235 |       throw new IOException("Only secure requests are allowed");
236 |     }
237 |     return (HttpURLConnection) url.openConnection();
238 |   }
239 | 
240 |   public HttpURLConnection create(URL url, Proxy proxy) throws IOException {
241 |     if (!"https".equals(url.getProtocol())) {
242 |       throw new IOException("Only secure requests are allowed");
243 |     }
244 |     return (HttpURLConnection) url.openConnection(proxy);
245 |   }
246 | });
247 | 
248 | 249 |

250 | Contributors

251 | 252 | 278 |
279 |
280 | 281 | 282 | 288 | 289 | 290 | 291 | 292 | 293 | -------------------------------------------------------------------------------- /lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Kevin Sawicki 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | package com.github.kevinsawicki.http; 23 | 24 | import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; 25 | import static java.net.HttpURLConnection.HTTP_CREATED; 26 | import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; 27 | import static java.net.HttpURLConnection.HTTP_NO_CONTENT; 28 | import static java.net.HttpURLConnection.HTTP_NOT_FOUND; 29 | import static java.net.HttpURLConnection.HTTP_NOT_MODIFIED; 30 | import static java.net.HttpURLConnection.HTTP_OK; 31 | import static java.net.Proxy.Type.HTTP; 32 | 33 | import java.io.BufferedInputStream; 34 | import java.io.BufferedOutputStream; 35 | import java.io.BufferedReader; 36 | import java.io.ByteArrayInputStream; 37 | import java.io.ByteArrayOutputStream; 38 | import java.io.Closeable; 39 | import java.io.File; 40 | import java.io.FileInputStream; 41 | import java.io.FileNotFoundException; 42 | import java.io.FileOutputStream; 43 | import java.io.Flushable; 44 | import java.io.IOException; 45 | import java.io.InputStream; 46 | import java.io.InputStreamReader; 47 | import java.io.OutputStream; 48 | import java.io.OutputStreamWriter; 49 | import java.io.PrintStream; 50 | import java.io.Reader; 51 | import java.io.UnsupportedEncodingException; 52 | import java.io.Writer; 53 | import java.net.HttpURLConnection; 54 | import java.net.InetSocketAddress; 55 | import java.net.MalformedURLException; 56 | import java.net.Proxy; 57 | import java.net.URI; 58 | import java.net.URISyntaxException; 59 | import java.net.URL; 60 | import java.net.URLEncoder; 61 | import java.nio.ByteBuffer; 62 | import java.nio.CharBuffer; 63 | import java.nio.charset.Charset; 64 | import java.nio.charset.CharsetEncoder; 65 | import java.security.AccessController; 66 | import java.security.GeneralSecurityException; 67 | import java.security.PrivilegedAction; 68 | import java.security.SecureRandom; 69 | import java.security.cert.X509Certificate; 70 | import java.util.ArrayList; 71 | import java.util.Arrays; 72 | import java.util.Collections; 73 | import java.util.Iterator; 74 | import java.util.LinkedHashMap; 75 | import java.util.List; 76 | import java.util.Map; 77 | import java.util.Map.Entry; 78 | import java.util.concurrent.Callable; 79 | import java.util.concurrent.atomic.AtomicInteger; 80 | import java.util.concurrent.atomic.AtomicReference; 81 | import java.util.zip.GZIPInputStream; 82 | 83 | import javax.net.ssl.HostnameVerifier; 84 | import javax.net.ssl.HttpsURLConnection; 85 | import javax.net.ssl.SSLContext; 86 | import javax.net.ssl.SSLSession; 87 | import javax.net.ssl.SSLSocketFactory; 88 | import javax.net.ssl.TrustManager; 89 | import javax.net.ssl.X509TrustManager; 90 | 91 | /** 92 | * A fluid interface for making HTTP requests using an underlying 93 | * {@link HttpURLConnection} (or sub-class). 94 | *

95 | * Each instance supports making a single request and cannot be reused for 96 | * further requests. 97 | */ 98 | public class HttpRequest { 99 | 100 | /** 101 | * 'UTF-8' charset name 102 | */ 103 | public static final String CHARSET_UTF8 = "UTF-8"; 104 | 105 | /** 106 | * 'application/x-www-form-urlencoded' content type header value 107 | */ 108 | public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded"; 109 | 110 | /** 111 | * 'application/json' content type header value 112 | */ 113 | public static final String CONTENT_TYPE_JSON = "application/json"; 114 | 115 | /** 116 | * 'gzip' encoding header value 117 | */ 118 | public static final String ENCODING_GZIP = "gzip"; 119 | 120 | /** 121 | * 'Accept' header name 122 | */ 123 | public static final String HEADER_ACCEPT = "Accept"; 124 | 125 | /** 126 | * 'Accept-Charset' header name 127 | */ 128 | public static final String HEADER_ACCEPT_CHARSET = "Accept-Charset"; 129 | 130 | /** 131 | * 'Accept-Encoding' header name 132 | */ 133 | public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; 134 | 135 | /** 136 | * 'Authorization' header name 137 | */ 138 | public static final String HEADER_AUTHORIZATION = "Authorization"; 139 | 140 | /** 141 | * 'Cache-Control' header name 142 | */ 143 | public static final String HEADER_CACHE_CONTROL = "Cache-Control"; 144 | 145 | /** 146 | * 'Content-Encoding' header name 147 | */ 148 | public static final String HEADER_CONTENT_ENCODING = "Content-Encoding"; 149 | 150 | /** 151 | * 'Content-Length' header name 152 | */ 153 | public static final String HEADER_CONTENT_LENGTH = "Content-Length"; 154 | 155 | /** 156 | * 'Content-Type' header name 157 | */ 158 | public static final String HEADER_CONTENT_TYPE = "Content-Type"; 159 | 160 | /** 161 | * 'Date' header name 162 | */ 163 | public static final String HEADER_DATE = "Date"; 164 | 165 | /** 166 | * 'ETag' header name 167 | */ 168 | public static final String HEADER_ETAG = "ETag"; 169 | 170 | /** 171 | * 'Expires' header name 172 | */ 173 | public static final String HEADER_EXPIRES = "Expires"; 174 | 175 | /** 176 | * 'If-None-Match' header name 177 | */ 178 | public static final String HEADER_IF_NONE_MATCH = "If-None-Match"; 179 | 180 | /** 181 | * 'Last-Modified' header name 182 | */ 183 | public static final String HEADER_LAST_MODIFIED = "Last-Modified"; 184 | 185 | /** 186 | * 'Location' header name 187 | */ 188 | public static final String HEADER_LOCATION = "Location"; 189 | 190 | /** 191 | * 'Proxy-Authorization' header name 192 | */ 193 | public static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization"; 194 | 195 | /** 196 | * 'Referer' header name 197 | */ 198 | public static final String HEADER_REFERER = "Referer"; 199 | 200 | /** 201 | * 'Server' header name 202 | */ 203 | public static final String HEADER_SERVER = "Server"; 204 | 205 | /** 206 | * 'User-Agent' header name 207 | */ 208 | public static final String HEADER_USER_AGENT = "User-Agent"; 209 | 210 | /** 211 | * 'DELETE' request method 212 | */ 213 | public static final String METHOD_DELETE = "DELETE"; 214 | 215 | /** 216 | * 'GET' request method 217 | */ 218 | public static final String METHOD_GET = "GET"; 219 | 220 | /** 221 | * 'HEAD' request method 222 | */ 223 | public static final String METHOD_HEAD = "HEAD"; 224 | 225 | /** 226 | * 'OPTIONS' options method 227 | */ 228 | public static final String METHOD_OPTIONS = "OPTIONS"; 229 | 230 | /** 231 | * 'POST' request method 232 | */ 233 | public static final String METHOD_POST = "POST"; 234 | 235 | /** 236 | * 'PUT' request method 237 | */ 238 | public static final String METHOD_PUT = "PUT"; 239 | 240 | /** 241 | * 'TRACE' request method 242 | */ 243 | public static final String METHOD_TRACE = "TRACE"; 244 | 245 | /** 246 | * 'charset' header value parameter 247 | */ 248 | public static final String PARAM_CHARSET = "charset"; 249 | 250 | private static final String BOUNDARY = "00content0boundary00"; 251 | 252 | private static final String CONTENT_TYPE_MULTIPART = "multipart/form-data; boundary=" 253 | + BOUNDARY; 254 | 255 | private static final String CRLF = "\r\n"; 256 | 257 | private static final String[] EMPTY_STRINGS = new String[0]; 258 | 259 | private static SSLSocketFactory TRUSTED_FACTORY; 260 | 261 | private static HostnameVerifier TRUSTED_VERIFIER; 262 | 263 | private static String getValidCharset(final String charset) { 264 | if (charset != null && charset.length() > 0) 265 | return charset; 266 | else 267 | return CHARSET_UTF8; 268 | } 269 | 270 | private static SSLSocketFactory getTrustedFactory() 271 | throws HttpRequestException { 272 | if (TRUSTED_FACTORY == null) { 273 | final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { 274 | 275 | public X509Certificate[] getAcceptedIssuers() { 276 | return new X509Certificate[0]; 277 | } 278 | 279 | public void checkClientTrusted(X509Certificate[] chain, String authType) { 280 | // Intentionally left blank 281 | } 282 | 283 | public void checkServerTrusted(X509Certificate[] chain, String authType) { 284 | // Intentionally left blank 285 | } 286 | } }; 287 | try { 288 | SSLContext context = SSLContext.getInstance("TLS"); 289 | context.init(null, trustAllCerts, new SecureRandom()); 290 | TRUSTED_FACTORY = context.getSocketFactory(); 291 | } catch (GeneralSecurityException e) { 292 | IOException ioException = new IOException( 293 | "Security exception configuring SSL context"); 294 | ioException.initCause(e); 295 | throw new HttpRequestException(ioException); 296 | } 297 | } 298 | 299 | return TRUSTED_FACTORY; 300 | } 301 | 302 | private static HostnameVerifier getTrustedVerifier() { 303 | if (TRUSTED_VERIFIER == null) 304 | TRUSTED_VERIFIER = new HostnameVerifier() { 305 | 306 | public boolean verify(String hostname, SSLSession session) { 307 | return true; 308 | } 309 | }; 310 | 311 | return TRUSTED_VERIFIER; 312 | } 313 | 314 | private static StringBuilder addPathSeparator(final String baseUrl, 315 | final StringBuilder result) { 316 | // Add trailing slash if the base URL doesn't have any path segments. 317 | // 318 | // The following test is checking for the last slash not being part of 319 | // the protocol to host separator: '://'. 320 | if (baseUrl.indexOf(':') + 2 == baseUrl.lastIndexOf('/')) 321 | result.append('/'); 322 | return result; 323 | } 324 | 325 | private static StringBuilder addParamPrefix(final String baseUrl, 326 | final StringBuilder result) { 327 | // Add '?' if missing and add '&' if params already exist in base url 328 | final int queryStart = baseUrl.indexOf('?'); 329 | final int lastChar = result.length() - 1; 330 | if (queryStart == -1) 331 | result.append('?'); 332 | else if (queryStart < lastChar && baseUrl.charAt(lastChar) != '&') 333 | result.append('&'); 334 | return result; 335 | } 336 | 337 | private static StringBuilder addParam(final Object key, Object value, 338 | final StringBuilder result) { 339 | if (value != null && value.getClass().isArray()) 340 | value = arrayToList(value); 341 | 342 | if (value instanceof Iterable) { 343 | Iterator iterator = ((Iterable) value).iterator(); 344 | while (iterator.hasNext()) { 345 | result.append(key); 346 | result.append("[]="); 347 | Object element = iterator.next(); 348 | if (element != null) 349 | result.append(element); 350 | if (iterator.hasNext()) 351 | result.append("&"); 352 | } 353 | } else { 354 | result.append(key); 355 | result.append("="); 356 | if (value != null) 357 | result.append(value); 358 | } 359 | 360 | return result; 361 | } 362 | 363 | /** 364 | * Creates {@link HttpURLConnection HTTP connections} for 365 | * {@link URL urls}. 366 | */ 367 | public interface ConnectionFactory { 368 | /** 369 | * Open an {@link HttpURLConnection} for the specified {@link URL}. 370 | * 371 | * @throws IOException 372 | */ 373 | HttpURLConnection create(URL url) throws IOException; 374 | 375 | /** 376 | * Open an {@link HttpURLConnection} for the specified {@link URL} 377 | * and {@link Proxy}. 378 | * 379 | * @throws IOException 380 | */ 381 | HttpURLConnection create(URL url, Proxy proxy) throws IOException; 382 | 383 | /** 384 | * A {@link ConnectionFactory} which uses the built-in 385 | * {@link URL#openConnection()} 386 | */ 387 | ConnectionFactory DEFAULT = new ConnectionFactory() { 388 | public HttpURLConnection create(URL url) throws IOException { 389 | return (HttpURLConnection) url.openConnection(); 390 | } 391 | 392 | public HttpURLConnection create(URL url, Proxy proxy) throws IOException { 393 | return (HttpURLConnection) url.openConnection(proxy); 394 | } 395 | }; 396 | } 397 | 398 | private static ConnectionFactory CONNECTION_FACTORY = ConnectionFactory.DEFAULT; 399 | 400 | /** 401 | * Specify the {@link ConnectionFactory} used to create new requests. 402 | */ 403 | public static void setConnectionFactory(final ConnectionFactory connectionFactory) { 404 | if (connectionFactory == null) 405 | CONNECTION_FACTORY = ConnectionFactory.DEFAULT; 406 | else 407 | CONNECTION_FACTORY = connectionFactory; 408 | } 409 | 410 | /** 411 | * Callback interface for reporting upload progress for a request. 412 | */ 413 | public interface UploadProgress { 414 | /** 415 | * Callback invoked as data is uploaded by the request. 416 | * 417 | * @param uploaded The number of bytes already uploaded 418 | * @param total The total number of bytes that will be uploaded or -1 if 419 | * the length is unknown. 420 | */ 421 | void onUpload(long uploaded, long total); 422 | 423 | UploadProgress DEFAULT = new UploadProgress() { 424 | public void onUpload(long uploaded, long total) { 425 | } 426 | }; 427 | } 428 | 429 | /** 430 | *

431 | * Encodes and decodes to and from Base64 notation. 432 | *

433 | *

434 | * I am placing this code in the Public Domain. Do with it as you will. This 435 | * software comes with no guarantees or warranties but with plenty of 436 | * well-wishing instead! Please visit http://iharder.net/base64 periodically 438 | * to check for updates or to contribute improvements. 439 | *

440 | * 441 | * @author Robert Harder 442 | * @author rob@iharder.net 443 | * @version 2.3.7 444 | */ 445 | public static class Base64 { 446 | 447 | /** The equals sign (=) as a byte. */ 448 | private final static byte EQUALS_SIGN = (byte) '='; 449 | 450 | /** Preferred encoding. */ 451 | private final static String PREFERRED_ENCODING = "US-ASCII"; 452 | 453 | /** The 64 valid Base64 values. */ 454 | private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B', 455 | (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', 456 | (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', 457 | (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', 458 | (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', 459 | (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', 460 | (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', 461 | (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', 462 | (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', 463 | (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', 464 | (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', 465 | (byte) '+', (byte) '/' }; 466 | 467 | /** Defeats instantiation. */ 468 | private Base64() { 469 | } 470 | 471 | /** 472 | *

473 | * Encodes up to three bytes of the array source and writes the 474 | * resulting four Base64 bytes to destination. The source and 475 | * destination arrays can be manipulated anywhere along their length by 476 | * specifying srcOffset and destOffset. This method 477 | * does not check to make sure your arrays are large enough to accomodate 478 | * srcOffset + 3 for the source array or 479 | * destOffset + 4 for the destination array. The 480 | * actual number of significant bytes in your array is given by 481 | * numSigBytes. 482 | *

483 | *

484 | * This is the lowest level of the encoding methods with all possible 485 | * parameters. 486 | *

487 | * 488 | * @param source 489 | * the array to convert 490 | * @param srcOffset 491 | * the index where conversion begins 492 | * @param numSigBytes 493 | * the number of significant bytes in your array 494 | * @param destination 495 | * the array to hold the conversion 496 | * @param destOffset 497 | * the index where output will be put 498 | * @return the destination array 499 | * @since 1.3 500 | */ 501 | private static byte[] encode3to4(byte[] source, int srcOffset, 502 | int numSigBytes, byte[] destination, int destOffset) { 503 | 504 | byte[] ALPHABET = _STANDARD_ALPHABET; 505 | 506 | int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0) 507 | | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0) 508 | | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0); 509 | 510 | switch (numSigBytes) { 511 | case 3: 512 | destination[destOffset] = ALPHABET[(inBuff >>> 18)]; 513 | destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; 514 | destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; 515 | destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f]; 516 | return destination; 517 | 518 | case 2: 519 | destination[destOffset] = ALPHABET[(inBuff >>> 18)]; 520 | destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; 521 | destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; 522 | destination[destOffset + 3] = EQUALS_SIGN; 523 | return destination; 524 | 525 | case 1: 526 | destination[destOffset] = ALPHABET[(inBuff >>> 18)]; 527 | destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; 528 | destination[destOffset + 2] = EQUALS_SIGN; 529 | destination[destOffset + 3] = EQUALS_SIGN; 530 | return destination; 531 | 532 | default: 533 | return destination; 534 | } 535 | } 536 | 537 | /** 538 | * Encode string as a byte array in Base64 annotation. 539 | * 540 | * @param string 541 | * @return The Base64-encoded data as a string 542 | */ 543 | public static String encode(String string) { 544 | byte[] bytes; 545 | try { 546 | bytes = string.getBytes(PREFERRED_ENCODING); 547 | } catch (UnsupportedEncodingException e) { 548 | bytes = string.getBytes(); 549 | } 550 | return encodeBytes(bytes); 551 | } 552 | 553 | /** 554 | * Encodes a byte array into Base64 notation. 555 | * 556 | * @param source 557 | * The data to convert 558 | * @return The Base64-encoded data as a String 559 | * @throws NullPointerException 560 | * if source array is null 561 | * @throws IllegalArgumentException 562 | * if source array, offset, or length are invalid 563 | * @since 2.0 564 | */ 565 | public static String encodeBytes(byte[] source) { 566 | return encodeBytes(source, 0, source.length); 567 | } 568 | 569 | /** 570 | * Encodes a byte array into Base64 notation. 571 | * 572 | * @param source 573 | * The data to convert 574 | * @param off 575 | * Offset in array where conversion should begin 576 | * @param len 577 | * Length of data to convert 578 | * @return The Base64-encoded data as a String 579 | * @throws NullPointerException 580 | * if source array is null 581 | * @throws IllegalArgumentException 582 | * if source array, offset, or length are invalid 583 | * @since 2.0 584 | */ 585 | public static String encodeBytes(byte[] source, int off, int len) { 586 | byte[] encoded = encodeBytesToBytes(source, off, len); 587 | try { 588 | return new String(encoded, PREFERRED_ENCODING); 589 | } catch (UnsupportedEncodingException uue) { 590 | return new String(encoded); 591 | } 592 | } 593 | 594 | /** 595 | * Similar to {@link #encodeBytes(byte[], int, int)} but returns a byte 596 | * array instead of instantiating a String. This is more efficient if you're 597 | * working with I/O streams and have large data sets to encode. 598 | * 599 | * 600 | * @param source 601 | * The data to convert 602 | * @param off 603 | * Offset in array where conversion should begin 604 | * @param len 605 | * Length of data to convert 606 | * @return The Base64-encoded data as a String if there is an error 607 | * @throws NullPointerException 608 | * if source array is null 609 | * @throws IllegalArgumentException 610 | * if source array, offset, or length are invalid 611 | * @since 2.3.1 612 | */ 613 | public static byte[] encodeBytesToBytes(byte[] source, int off, int len) { 614 | 615 | if (source == null) 616 | throw new NullPointerException("Cannot serialize a null array."); 617 | 618 | if (off < 0) 619 | throw new IllegalArgumentException("Cannot have negative offset: " 620 | + off); 621 | 622 | if (len < 0) 623 | throw new IllegalArgumentException("Cannot have length offset: " + len); 624 | 625 | if (off + len > source.length) 626 | throw new IllegalArgumentException( 627 | String 628 | .format( 629 | "Cannot have offset of %d and length of %d with array of length %d", 630 | off, len, source.length)); 631 | 632 | // Bytes needed for actual encoding 633 | int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); 634 | 635 | byte[] outBuff = new byte[encLen]; 636 | 637 | int d = 0; 638 | int e = 0; 639 | int len2 = len - 2; 640 | for (; d < len2; d += 3, e += 4) 641 | encode3to4(source, d + off, 3, outBuff, e); 642 | 643 | if (d < len) { 644 | encode3to4(source, d + off, len - d, outBuff, e); 645 | e += 4; 646 | } 647 | 648 | if (e <= outBuff.length - 1) { 649 | byte[] finalOut = new byte[e]; 650 | System.arraycopy(outBuff, 0, finalOut, 0, e); 651 | return finalOut; 652 | } else 653 | return outBuff; 654 | } 655 | } 656 | 657 | /** 658 | * HTTP request exception whose cause is always an {@link IOException} 659 | */ 660 | public static class HttpRequestException extends RuntimeException { 661 | 662 | private static final long serialVersionUID = -1170466989781746231L; 663 | 664 | /** 665 | * Create a new HttpRequestException with the given cause 666 | * 667 | * @param cause 668 | */ 669 | public HttpRequestException(final IOException cause) { 670 | super(cause); 671 | } 672 | 673 | /** 674 | * Get {@link IOException} that triggered this request exception 675 | * 676 | * @return {@link IOException} cause 677 | */ 678 | @Override 679 | public IOException getCause() { 680 | return (IOException) super.getCause(); 681 | } 682 | } 683 | 684 | /** 685 | * Operation that handles executing a callback once complete and handling 686 | * nested exceptions 687 | * 688 | * @param 689 | */ 690 | protected static abstract class Operation implements Callable { 691 | 692 | /** 693 | * Run operation 694 | * 695 | * @return result 696 | * @throws HttpRequestException 697 | * @throws IOException 698 | */ 699 | protected abstract V run() throws HttpRequestException, IOException; 700 | 701 | /** 702 | * Operation complete callback 703 | * 704 | * @throws IOException 705 | */ 706 | protected abstract void done() throws IOException; 707 | 708 | public V call() throws HttpRequestException { 709 | boolean thrown = false; 710 | try { 711 | return run(); 712 | } catch (HttpRequestException e) { 713 | thrown = true; 714 | throw e; 715 | } catch (IOException e) { 716 | thrown = true; 717 | throw new HttpRequestException(e); 718 | } finally { 719 | try { 720 | done(); 721 | } catch (IOException e) { 722 | if (!thrown) 723 | throw new HttpRequestException(e); 724 | } 725 | } 726 | } 727 | } 728 | 729 | /** 730 | * Class that ensures a {@link Closeable} gets closed with proper exception 731 | * handling. 732 | * 733 | * @param 734 | */ 735 | protected static abstract class CloseOperation extends Operation { 736 | 737 | private final Closeable closeable; 738 | 739 | private final boolean ignoreCloseExceptions; 740 | 741 | /** 742 | * Create closer for operation 743 | * 744 | * @param closeable 745 | * @param ignoreCloseExceptions 746 | */ 747 | protected CloseOperation(final Closeable closeable, 748 | final boolean ignoreCloseExceptions) { 749 | this.closeable = closeable; 750 | this.ignoreCloseExceptions = ignoreCloseExceptions; 751 | } 752 | 753 | @Override 754 | protected void done() throws IOException { 755 | if (closeable instanceof Flushable) 756 | ((Flushable) closeable).flush(); 757 | if (ignoreCloseExceptions) 758 | try { 759 | closeable.close(); 760 | } catch (IOException e) { 761 | // Ignored 762 | } 763 | else 764 | closeable.close(); 765 | } 766 | } 767 | 768 | /** 769 | * Class that and ensures a {@link Flushable} gets flushed with proper 770 | * exception handling. 771 | * 772 | * @param 773 | */ 774 | protected static abstract class FlushOperation extends Operation { 775 | 776 | private final Flushable flushable; 777 | 778 | /** 779 | * Create flush operation 780 | * 781 | * @param flushable 782 | */ 783 | protected FlushOperation(final Flushable flushable) { 784 | this.flushable = flushable; 785 | } 786 | 787 | @Override 788 | protected void done() throws IOException { 789 | flushable.flush(); 790 | } 791 | } 792 | 793 | /** 794 | * Request output stream 795 | */ 796 | public static class RequestOutputStream extends BufferedOutputStream { 797 | 798 | private final CharsetEncoder encoder; 799 | 800 | /** 801 | * Create request output stream 802 | * 803 | * @param stream 804 | * @param charset 805 | * @param bufferSize 806 | */ 807 | public RequestOutputStream(final OutputStream stream, final String charset, 808 | final int bufferSize) { 809 | super(stream, bufferSize); 810 | 811 | encoder = Charset.forName(getValidCharset(charset)).newEncoder(); 812 | } 813 | 814 | /** 815 | * Write string to stream 816 | * 817 | * @param value 818 | * @return this stream 819 | * @throws IOException 820 | */ 821 | public RequestOutputStream write(final String value) throws IOException { 822 | final ByteBuffer bytes = encoder.encode(CharBuffer.wrap(value)); 823 | 824 | super.write(bytes.array(), 0, bytes.limit()); 825 | 826 | return this; 827 | } 828 | } 829 | 830 | /** 831 | * Represents array of any type as list of objects so we can easily iterate over it 832 | * @param array of elements 833 | * @return list with the same elements 834 | */ 835 | private static List arrayToList(final Object array) { 836 | if (array instanceof Object[]) 837 | return Arrays.asList((Object[]) array); 838 | 839 | List result = new ArrayList(); 840 | // Arrays of the primitive types can't be cast to array of Object, so this: 841 | if (array instanceof int[]) 842 | for (int value : (int[]) array) result.add(value); 843 | else if (array instanceof boolean[]) 844 | for (boolean value : (boolean[]) array) result.add(value); 845 | else if (array instanceof long[]) 846 | for (long value : (long[]) array) result.add(value); 847 | else if (array instanceof float[]) 848 | for (float value : (float[]) array) result.add(value); 849 | else if (array instanceof double[]) 850 | for (double value : (double[]) array) result.add(value); 851 | else if (array instanceof short[]) 852 | for (short value : (short[]) array) result.add(value); 853 | else if (array instanceof byte[]) 854 | for (byte value : (byte[]) array) result.add(value); 855 | else if (array instanceof char[]) 856 | for (char value : (char[]) array) result.add(value); 857 | return result; 858 | } 859 | 860 | /** 861 | * Encode the given URL as an ASCII {@link String} 862 | *

863 | * This method ensures the path and query segments of the URL are properly 864 | * encoded such as ' ' characters being encoded to '%20' or any UTF-8 865 | * characters that are non-ASCII. No encoding of URLs is done by default by 866 | * the {@link HttpRequest} constructors and so if URL encoding is needed this 867 | * method should be called before calling the {@link HttpRequest} constructor. 868 | * 869 | * @param url 870 | * @return encoded URL 871 | * @throws HttpRequestException 872 | */ 873 | public static String encode(final CharSequence url) 874 | throws HttpRequestException { 875 | URL parsed; 876 | try { 877 | parsed = new URL(url.toString()); 878 | } catch (IOException e) { 879 | throw new HttpRequestException(e); 880 | } 881 | 882 | String host = parsed.getHost(); 883 | int port = parsed.getPort(); 884 | if (port != -1) 885 | host = host + ':' + Integer.toString(port); 886 | 887 | try { 888 | String encoded = new URI(parsed.getProtocol(), host, parsed.getPath(), 889 | parsed.getQuery(), null).toASCIIString(); 890 | int paramsStart = encoded.indexOf('?'); 891 | if (paramsStart > 0 && paramsStart + 1 < encoded.length()) 892 | encoded = encoded.substring(0, paramsStart + 1) 893 | + encoded.substring(paramsStart + 1).replace("+", "%2B"); 894 | return encoded; 895 | } catch (URISyntaxException e) { 896 | IOException io = new IOException("Parsing URI failed"); 897 | io.initCause(e); 898 | throw new HttpRequestException(io); 899 | } 900 | } 901 | 902 | /** 903 | * Append given map as query parameters to the base URL 904 | *

905 | * Each map entry's key will be a parameter name and the value's 906 | * {@link Object#toString()} will be the parameter value. 907 | * 908 | * @param url 909 | * @param params 910 | * @return URL with appended query params 911 | */ 912 | public static String append(final CharSequence url, final Map params) { 913 | final String baseUrl = url.toString(); 914 | if (params == null || params.isEmpty()) 915 | return baseUrl; 916 | 917 | final StringBuilder result = new StringBuilder(baseUrl); 918 | 919 | addPathSeparator(baseUrl, result); 920 | addParamPrefix(baseUrl, result); 921 | 922 | Entry entry; 923 | Iterator iterator = params.entrySet().iterator(); 924 | entry = (Entry) iterator.next(); 925 | addParam(entry.getKey().toString(), entry.getValue(), result); 926 | 927 | while (iterator.hasNext()) { 928 | result.append('&'); 929 | entry = (Entry) iterator.next(); 930 | addParam(entry.getKey().toString(), entry.getValue(), result); 931 | } 932 | 933 | return result.toString(); 934 | } 935 | 936 | /** 937 | * Append given name/value pairs as query parameters to the base URL 938 | *

939 | * The params argument is interpreted as a sequence of name/value pairs so the 940 | * given number of params must be divisible by 2. 941 | * 942 | * @param url 943 | * @param params 944 | * name/value pairs 945 | * @return URL with appended query params 946 | */ 947 | public static String append(final CharSequence url, final Object... params) { 948 | final String baseUrl = url.toString(); 949 | if (params == null || params.length == 0) 950 | return baseUrl; 951 | 952 | if (params.length % 2 != 0) 953 | throw new IllegalArgumentException( 954 | "Must specify an even number of parameter names/values"); 955 | 956 | final StringBuilder result = new StringBuilder(baseUrl); 957 | 958 | addPathSeparator(baseUrl, result); 959 | addParamPrefix(baseUrl, result); 960 | 961 | addParam(params[0], params[1], result); 962 | 963 | for (int i = 2; i < params.length; i += 2) { 964 | result.append('&'); 965 | addParam(params[i], params[i + 1], result); 966 | } 967 | 968 | return result.toString(); 969 | } 970 | 971 | /** 972 | * Start a 'GET' request to the given URL 973 | * 974 | * @param url 975 | * @return request 976 | * @throws HttpRequestException 977 | */ 978 | public static HttpRequest get(final CharSequence url) 979 | throws HttpRequestException { 980 | return new HttpRequest(url, METHOD_GET); 981 | } 982 | 983 | /** 984 | * Start a 'GET' request to the given URL 985 | * 986 | * @param url 987 | * @return request 988 | * @throws HttpRequestException 989 | */ 990 | public static HttpRequest get(final URL url) throws HttpRequestException { 991 | return new HttpRequest(url, METHOD_GET); 992 | } 993 | 994 | /** 995 | * Start a 'GET' request to the given URL along with the query params 996 | * 997 | * @param baseUrl 998 | * @param params 999 | * The query parameters to include as part of the baseUrl 1000 | * @param encode 1001 | * true to encode the full URL 1002 | * 1003 | * @see #append(CharSequence, Map) 1004 | * @see #encode(CharSequence) 1005 | * 1006 | * @return request 1007 | */ 1008 | public static HttpRequest get(final CharSequence baseUrl, 1009 | final Map params, final boolean encode) { 1010 | String url = append(baseUrl, params); 1011 | return get(encode ? encode(url) : url); 1012 | } 1013 | 1014 | /** 1015 | * Start a 'GET' request to the given URL along with the query params 1016 | * 1017 | * @param baseUrl 1018 | * @param encode 1019 | * true to encode the full URL 1020 | * @param params 1021 | * the name/value query parameter pairs to include as part of the 1022 | * baseUrl 1023 | * 1024 | * @see #append(CharSequence, Object...) 1025 | * @see #encode(CharSequence) 1026 | * 1027 | * @return request 1028 | */ 1029 | public static HttpRequest get(final CharSequence baseUrl, 1030 | final boolean encode, final Object... params) { 1031 | String url = append(baseUrl, params); 1032 | return get(encode ? encode(url) : url); 1033 | } 1034 | 1035 | /** 1036 | * Start a 'POST' request to the given URL 1037 | * 1038 | * @param url 1039 | * @return request 1040 | * @throws HttpRequestException 1041 | */ 1042 | public static HttpRequest post(final CharSequence url) 1043 | throws HttpRequestException { 1044 | return new HttpRequest(url, METHOD_POST); 1045 | } 1046 | 1047 | /** 1048 | * Start a 'POST' request to the given URL 1049 | * 1050 | * @param url 1051 | * @return request 1052 | * @throws HttpRequestException 1053 | */ 1054 | public static HttpRequest post(final URL url) throws HttpRequestException { 1055 | return new HttpRequest(url, METHOD_POST); 1056 | } 1057 | 1058 | /** 1059 | * Start a 'POST' request to the given URL along with the query params 1060 | * 1061 | * @param baseUrl 1062 | * @param params 1063 | * the query parameters to include as part of the baseUrl 1064 | * @param encode 1065 | * true to encode the full URL 1066 | * 1067 | * @see #append(CharSequence, Map) 1068 | * @see #encode(CharSequence) 1069 | * 1070 | * @return request 1071 | */ 1072 | public static HttpRequest post(final CharSequence baseUrl, 1073 | final Map params, final boolean encode) { 1074 | String url = append(baseUrl, params); 1075 | return post(encode ? encode(url) : url); 1076 | } 1077 | 1078 | /** 1079 | * Start a 'POST' request to the given URL along with the query params 1080 | * 1081 | * @param baseUrl 1082 | * @param encode 1083 | * true to encode the full URL 1084 | * @param params 1085 | * the name/value query parameter pairs to include as part of the 1086 | * baseUrl 1087 | * 1088 | * @see #append(CharSequence, Object...) 1089 | * @see #encode(CharSequence) 1090 | * 1091 | * @return request 1092 | */ 1093 | public static HttpRequest post(final CharSequence baseUrl, 1094 | final boolean encode, final Object... params) { 1095 | String url = append(baseUrl, params); 1096 | return post(encode ? encode(url) : url); 1097 | } 1098 | 1099 | /** 1100 | * Start a 'PUT' request to the given URL 1101 | * 1102 | * @param url 1103 | * @return request 1104 | * @throws HttpRequestException 1105 | */ 1106 | public static HttpRequest put(final CharSequence url) 1107 | throws HttpRequestException { 1108 | return new HttpRequest(url, METHOD_PUT); 1109 | } 1110 | 1111 | /** 1112 | * Start a 'PUT' request to the given URL 1113 | * 1114 | * @param url 1115 | * @return request 1116 | * @throws HttpRequestException 1117 | */ 1118 | public static HttpRequest put(final URL url) throws HttpRequestException { 1119 | return new HttpRequest(url, METHOD_PUT); 1120 | } 1121 | 1122 | /** 1123 | * Start a 'PUT' request to the given URL along with the query params 1124 | * 1125 | * @param baseUrl 1126 | * @param params 1127 | * the query parameters to include as part of the baseUrl 1128 | * @param encode 1129 | * true to encode the full URL 1130 | * 1131 | * @see #append(CharSequence, Map) 1132 | * @see #encode(CharSequence) 1133 | * 1134 | * @return request 1135 | */ 1136 | public static HttpRequest put(final CharSequence baseUrl, 1137 | final Map params, final boolean encode) { 1138 | String url = append(baseUrl, params); 1139 | return put(encode ? encode(url) : url); 1140 | } 1141 | 1142 | /** 1143 | * Start a 'PUT' request to the given URL along with the query params 1144 | * 1145 | * @param baseUrl 1146 | * @param encode 1147 | * true to encode the full URL 1148 | * @param params 1149 | * the name/value query parameter pairs to include as part of the 1150 | * baseUrl 1151 | * 1152 | * @see #append(CharSequence, Object...) 1153 | * @see #encode(CharSequence) 1154 | * 1155 | * @return request 1156 | */ 1157 | public static HttpRequest put(final CharSequence baseUrl, 1158 | final boolean encode, final Object... params) { 1159 | String url = append(baseUrl, params); 1160 | return put(encode ? encode(url) : url); 1161 | } 1162 | 1163 | /** 1164 | * Start a 'DELETE' request to the given URL 1165 | * 1166 | * @param url 1167 | * @return request 1168 | * @throws HttpRequestException 1169 | */ 1170 | public static HttpRequest delete(final CharSequence url) 1171 | throws HttpRequestException { 1172 | return new HttpRequest(url, METHOD_DELETE); 1173 | } 1174 | 1175 | /** 1176 | * Start a 'DELETE' request to the given URL 1177 | * 1178 | * @param url 1179 | * @return request 1180 | * @throws HttpRequestException 1181 | */ 1182 | public static HttpRequest delete(final URL url) throws HttpRequestException { 1183 | return new HttpRequest(url, METHOD_DELETE); 1184 | } 1185 | 1186 | /** 1187 | * Start a 'DELETE' request to the given URL along with the query params 1188 | * 1189 | * @param baseUrl 1190 | * @param params 1191 | * The query parameters to include as part of the baseUrl 1192 | * @param encode 1193 | * true to encode the full URL 1194 | * 1195 | * @see #append(CharSequence, Map) 1196 | * @see #encode(CharSequence) 1197 | * 1198 | * @return request 1199 | */ 1200 | public static HttpRequest delete(final CharSequence baseUrl, 1201 | final Map params, final boolean encode) { 1202 | String url = append(baseUrl, params); 1203 | return delete(encode ? encode(url) : url); 1204 | } 1205 | 1206 | /** 1207 | * Start a 'DELETE' request to the given URL along with the query params 1208 | * 1209 | * @param baseUrl 1210 | * @param encode 1211 | * true to encode the full URL 1212 | * @param params 1213 | * the name/value query parameter pairs to include as part of the 1214 | * baseUrl 1215 | * 1216 | * @see #append(CharSequence, Object...) 1217 | * @see #encode(CharSequence) 1218 | * 1219 | * @return request 1220 | */ 1221 | public static HttpRequest delete(final CharSequence baseUrl, 1222 | final boolean encode, final Object... params) { 1223 | String url = append(baseUrl, params); 1224 | return delete(encode ? encode(url) : url); 1225 | } 1226 | 1227 | /** 1228 | * Start a 'HEAD' request to the given URL 1229 | * 1230 | * @param url 1231 | * @return request 1232 | * @throws HttpRequestException 1233 | */ 1234 | public static HttpRequest head(final CharSequence url) 1235 | throws HttpRequestException { 1236 | return new HttpRequest(url, METHOD_HEAD); 1237 | } 1238 | 1239 | /** 1240 | * Start a 'HEAD' request to the given URL 1241 | * 1242 | * @param url 1243 | * @return request 1244 | * @throws HttpRequestException 1245 | */ 1246 | public static HttpRequest head(final URL url) throws HttpRequestException { 1247 | return new HttpRequest(url, METHOD_HEAD); 1248 | } 1249 | 1250 | /** 1251 | * Start a 'HEAD' request to the given URL along with the query params 1252 | * 1253 | * @param baseUrl 1254 | * @param params 1255 | * The query parameters to include as part of the baseUrl 1256 | * @param encode 1257 | * true to encode the full URL 1258 | * 1259 | * @see #append(CharSequence, Map) 1260 | * @see #encode(CharSequence) 1261 | * 1262 | * @return request 1263 | */ 1264 | public static HttpRequest head(final CharSequence baseUrl, 1265 | final Map params, final boolean encode) { 1266 | String url = append(baseUrl, params); 1267 | return head(encode ? encode(url) : url); 1268 | } 1269 | 1270 | /** 1271 | * Start a 'GET' request to the given URL along with the query params 1272 | * 1273 | * @param baseUrl 1274 | * @param encode 1275 | * true to encode the full URL 1276 | * @param params 1277 | * the name/value query parameter pairs to include as part of the 1278 | * baseUrl 1279 | * 1280 | * @see #append(CharSequence, Object...) 1281 | * @see #encode(CharSequence) 1282 | * 1283 | * @return request 1284 | */ 1285 | public static HttpRequest head(final CharSequence baseUrl, 1286 | final boolean encode, final Object... params) { 1287 | String url = append(baseUrl, params); 1288 | return head(encode ? encode(url) : url); 1289 | } 1290 | 1291 | /** 1292 | * Start an 'OPTIONS' request to the given URL 1293 | * 1294 | * @param url 1295 | * @return request 1296 | * @throws HttpRequestException 1297 | */ 1298 | public static HttpRequest options(final CharSequence url) 1299 | throws HttpRequestException { 1300 | return new HttpRequest(url, METHOD_OPTIONS); 1301 | } 1302 | 1303 | /** 1304 | * Start an 'OPTIONS' request to the given URL 1305 | * 1306 | * @param url 1307 | * @return request 1308 | * @throws HttpRequestException 1309 | */ 1310 | public static HttpRequest options(final URL url) throws HttpRequestException { 1311 | return new HttpRequest(url, METHOD_OPTIONS); 1312 | } 1313 | 1314 | /** 1315 | * Start a 'TRACE' request to the given URL 1316 | * 1317 | * @param url 1318 | * @return request 1319 | * @throws HttpRequestException 1320 | */ 1321 | public static HttpRequest trace(final CharSequence url) 1322 | throws HttpRequestException { 1323 | return new HttpRequest(url, METHOD_TRACE); 1324 | } 1325 | 1326 | /** 1327 | * Start a 'TRACE' request to the given URL 1328 | * 1329 | * @param url 1330 | * @return request 1331 | * @throws HttpRequestException 1332 | */ 1333 | public static HttpRequest trace(final URL url) throws HttpRequestException { 1334 | return new HttpRequest(url, METHOD_TRACE); 1335 | } 1336 | 1337 | /** 1338 | * Set the 'http.keepAlive' property to the given value. 1339 | *

1340 | * This setting will apply to all requests. 1341 | * 1342 | * @param keepAlive 1343 | */ 1344 | public static void keepAlive(final boolean keepAlive) { 1345 | setProperty("http.keepAlive", Boolean.toString(keepAlive)); 1346 | } 1347 | 1348 | /** 1349 | * Set the 'http.maxConnections' property to the given value. 1350 | *

1351 | * This setting will apply to all requests. 1352 | * 1353 | * @param maxConnections 1354 | */ 1355 | public static void maxConnections(final int maxConnections) { 1356 | setProperty("http.maxConnections", Integer.toString(maxConnections)); 1357 | } 1358 | 1359 | /** 1360 | * Set the 'http.proxyHost' and 'https.proxyHost' properties to the given host 1361 | * value. 1362 | *

1363 | * This setting will apply to all requests. 1364 | * 1365 | * @param host 1366 | */ 1367 | public static void proxyHost(final String host) { 1368 | setProperty("http.proxyHost", host); 1369 | setProperty("https.proxyHost", host); 1370 | } 1371 | 1372 | /** 1373 | * Set the 'http.proxyPort' and 'https.proxyPort' properties to the given port 1374 | * number. 1375 | *

1376 | * This setting will apply to all requests. 1377 | * 1378 | * @param port 1379 | */ 1380 | public static void proxyPort(final int port) { 1381 | final String portValue = Integer.toString(port); 1382 | setProperty("http.proxyPort", portValue); 1383 | setProperty("https.proxyPort", portValue); 1384 | } 1385 | 1386 | /** 1387 | * Set the 'http.nonProxyHosts' property to the given host values. 1388 | *

1389 | * Hosts will be separated by a '|' character. 1390 | *

1391 | * This setting will apply to all requests. 1392 | * 1393 | * @param hosts 1394 | */ 1395 | public static void nonProxyHosts(final String... hosts) { 1396 | if (hosts != null && hosts.length > 0) { 1397 | StringBuilder separated = new StringBuilder(); 1398 | int last = hosts.length - 1; 1399 | for (int i = 0; i < last; i++) 1400 | separated.append(hosts[i]).append('|'); 1401 | separated.append(hosts[last]); 1402 | setProperty("http.nonProxyHosts", separated.toString()); 1403 | } else 1404 | setProperty("http.nonProxyHosts", null); 1405 | } 1406 | 1407 | /** 1408 | * Set property to given value. 1409 | *

1410 | * Specifying a null value will cause the property to be cleared 1411 | * 1412 | * @param name 1413 | * @param value 1414 | * @return previous value 1415 | */ 1416 | private static String setProperty(final String name, final String value) { 1417 | final PrivilegedAction action; 1418 | if (value != null) 1419 | action = new PrivilegedAction() { 1420 | 1421 | public String run() { 1422 | return System.setProperty(name, value); 1423 | } 1424 | }; 1425 | else 1426 | action = new PrivilegedAction() { 1427 | 1428 | public String run() { 1429 | return System.clearProperty(name); 1430 | } 1431 | }; 1432 | return AccessController.doPrivileged(action); 1433 | } 1434 | 1435 | private HttpURLConnection connection = null; 1436 | 1437 | private final URL url; 1438 | 1439 | private final String requestMethod; 1440 | 1441 | private RequestOutputStream output; 1442 | 1443 | private boolean multipart; 1444 | 1445 | private boolean form; 1446 | 1447 | private boolean ignoreCloseExceptions = true; 1448 | 1449 | private boolean uncompress = false; 1450 | 1451 | private int bufferSize = 8192; 1452 | 1453 | private long totalSize = -1; 1454 | 1455 | private long totalWritten = 0; 1456 | 1457 | private String httpProxyHost; 1458 | 1459 | private int httpProxyPort; 1460 | 1461 | private UploadProgress progress = UploadProgress.DEFAULT; 1462 | 1463 | /** 1464 | * Create HTTP connection wrapper 1465 | * 1466 | * @param url Remote resource URL. 1467 | * @param method HTTP request method (e.g., "GET", "POST"). 1468 | * @throws HttpRequestException 1469 | */ 1470 | public HttpRequest(final CharSequence url, final String method) 1471 | throws HttpRequestException { 1472 | try { 1473 | this.url = new URL(url.toString()); 1474 | } catch (MalformedURLException e) { 1475 | throw new HttpRequestException(e); 1476 | } 1477 | this.requestMethod = method; 1478 | } 1479 | 1480 | /** 1481 | * Create HTTP connection wrapper 1482 | * 1483 | * @param url Remote resource URL. 1484 | * @param method HTTP request method (e.g., "GET", "POST"). 1485 | * @throws HttpRequestException 1486 | */ 1487 | public HttpRequest(final URL url, final String method) 1488 | throws HttpRequestException { 1489 | this.url = url; 1490 | this.requestMethod = method; 1491 | } 1492 | 1493 | private Proxy createProxy() { 1494 | return new Proxy(HTTP, new InetSocketAddress(httpProxyHost, httpProxyPort)); 1495 | } 1496 | 1497 | private HttpURLConnection createConnection() { 1498 | try { 1499 | final HttpURLConnection connection; 1500 | if (httpProxyHost != null) 1501 | connection = CONNECTION_FACTORY.create(url, createProxy()); 1502 | else 1503 | connection = CONNECTION_FACTORY.create(url); 1504 | connection.setRequestMethod(requestMethod); 1505 | return connection; 1506 | } catch (IOException e) { 1507 | throw new HttpRequestException(e); 1508 | } 1509 | } 1510 | 1511 | @Override 1512 | public String toString() { 1513 | return method() + ' ' + url(); 1514 | } 1515 | 1516 | /** 1517 | * Get underlying connection 1518 | * 1519 | * @return connection 1520 | */ 1521 | public HttpURLConnection getConnection() { 1522 | if (connection == null) 1523 | connection = createConnection(); 1524 | return connection; 1525 | } 1526 | 1527 | /** 1528 | * Set whether or not to ignore exceptions that occur from calling 1529 | * {@link Closeable#close()} 1530 | *

1531 | * The default value of this setting is true 1532 | * 1533 | * @param ignore 1534 | * @return this request 1535 | */ 1536 | public HttpRequest ignoreCloseExceptions(final boolean ignore) { 1537 | ignoreCloseExceptions = ignore; 1538 | return this; 1539 | } 1540 | 1541 | /** 1542 | * Get whether or not exceptions thrown by {@link Closeable#close()} are 1543 | * ignored 1544 | * 1545 | * @return true if ignoring, false if throwing 1546 | */ 1547 | public boolean ignoreCloseExceptions() { 1548 | return ignoreCloseExceptions; 1549 | } 1550 | 1551 | /** 1552 | * Get the status code of the response 1553 | * 1554 | * @return the response code 1555 | * @throws HttpRequestException 1556 | */ 1557 | public int code() throws HttpRequestException { 1558 | try { 1559 | closeOutput(); 1560 | return getConnection().getResponseCode(); 1561 | } catch (IOException e) { 1562 | throw new HttpRequestException(e); 1563 | } 1564 | } 1565 | 1566 | /** 1567 | * Set the value of the given {@link AtomicInteger} to the status code of the 1568 | * response 1569 | * 1570 | * @param output 1571 | * @return this request 1572 | * @throws HttpRequestException 1573 | */ 1574 | public HttpRequest code(final AtomicInteger output) 1575 | throws HttpRequestException { 1576 | output.set(code()); 1577 | return this; 1578 | } 1579 | 1580 | /** 1581 | * Is the response code a 200 OK? 1582 | * 1583 | * @return true if 200, false otherwise 1584 | * @throws HttpRequestException 1585 | */ 1586 | public boolean ok() throws HttpRequestException { 1587 | return HTTP_OK == code(); 1588 | } 1589 | 1590 | /** 1591 | * Is the response code a 201 Created? 1592 | * 1593 | * @return true if 201, false otherwise 1594 | * @throws HttpRequestException 1595 | */ 1596 | public boolean created() throws HttpRequestException { 1597 | return HTTP_CREATED == code(); 1598 | } 1599 | 1600 | /** 1601 | * Is the response code a 204 No Content? 1602 | * 1603 | * @return true if 204, false otherwise 1604 | * @throws HttpRequestException 1605 | */ 1606 | public boolean noContent() throws HttpRequestException { 1607 | return HTTP_NO_CONTENT == code(); 1608 | } 1609 | 1610 | /** 1611 | * Is the response code a 500 Internal Server Error? 1612 | * 1613 | * @return true if 500, false otherwise 1614 | * @throws HttpRequestException 1615 | */ 1616 | public boolean serverError() throws HttpRequestException { 1617 | return HTTP_INTERNAL_ERROR == code(); 1618 | } 1619 | 1620 | /** 1621 | * Is the response code a 400 Bad Request? 1622 | * 1623 | * @return true if 400, false otherwise 1624 | * @throws HttpRequestException 1625 | */ 1626 | public boolean badRequest() throws HttpRequestException { 1627 | return HTTP_BAD_REQUEST == code(); 1628 | } 1629 | 1630 | /** 1631 | * Is the response code a 404 Not Found? 1632 | * 1633 | * @return true if 404, false otherwise 1634 | * @throws HttpRequestException 1635 | */ 1636 | public boolean notFound() throws HttpRequestException { 1637 | return HTTP_NOT_FOUND == code(); 1638 | } 1639 | 1640 | /** 1641 | * Is the response code a 304 Not Modified? 1642 | * 1643 | * @return true if 304, false otherwise 1644 | * @throws HttpRequestException 1645 | */ 1646 | public boolean notModified() throws HttpRequestException { 1647 | return HTTP_NOT_MODIFIED == code(); 1648 | } 1649 | 1650 | /** 1651 | * Get status message of the response 1652 | * 1653 | * @return message 1654 | * @throws HttpRequestException 1655 | */ 1656 | public String message() throws HttpRequestException { 1657 | try { 1658 | closeOutput(); 1659 | return getConnection().getResponseMessage(); 1660 | } catch (IOException e) { 1661 | throw new HttpRequestException(e); 1662 | } 1663 | } 1664 | 1665 | /** 1666 | * Disconnect the connection 1667 | * 1668 | * @return this request 1669 | */ 1670 | public HttpRequest disconnect() { 1671 | getConnection().disconnect(); 1672 | return this; 1673 | } 1674 | 1675 | /** 1676 | * Set chunked streaming mode to the given size 1677 | * 1678 | * @param size 1679 | * @return this request 1680 | */ 1681 | public HttpRequest chunk(final int size) { 1682 | getConnection().setChunkedStreamingMode(size); 1683 | return this; 1684 | } 1685 | 1686 | /** 1687 | * Set the size used when buffering and copying between streams 1688 | *

1689 | * This size is also used for send and receive buffers created for both char 1690 | * and byte arrays 1691 | *

1692 | * The default buffer size is 8,192 bytes 1693 | * 1694 | * @param size 1695 | * @return this request 1696 | */ 1697 | public HttpRequest bufferSize(final int size) { 1698 | if (size < 1) 1699 | throw new IllegalArgumentException("Size must be greater than zero"); 1700 | bufferSize = size; 1701 | return this; 1702 | } 1703 | 1704 | /** 1705 | * Get the configured buffer size 1706 | *

1707 | * The default buffer size is 8,192 bytes 1708 | * 1709 | * @return buffer size 1710 | */ 1711 | public int bufferSize() { 1712 | return bufferSize; 1713 | } 1714 | 1715 | /** 1716 | * Set whether or not the response body should be automatically uncompressed 1717 | * when read from. 1718 | *

1719 | * This will only affect requests that have the 'Content-Encoding' response 1720 | * header set to 'gzip'. 1721 | *

1722 | * This causes all receive methods to use a {@link GZIPInputStream} when 1723 | * applicable so that higher level streams and readers can read the data 1724 | * uncompressed. 1725 | *

1726 | * Setting this option does not cause any request headers to be set 1727 | * automatically so {@link #acceptGzipEncoding()} should be used in 1728 | * conjunction with this setting to tell the server to gzip the response. 1729 | * 1730 | * @param uncompress 1731 | * @return this request 1732 | */ 1733 | public HttpRequest uncompress(final boolean uncompress) { 1734 | this.uncompress = uncompress; 1735 | return this; 1736 | } 1737 | 1738 | /** 1739 | * Create byte array output stream 1740 | * 1741 | * @return stream 1742 | */ 1743 | protected ByteArrayOutputStream byteStream() { 1744 | final int size = contentLength(); 1745 | if (size > 0) 1746 | return new ByteArrayOutputStream(size); 1747 | else 1748 | return new ByteArrayOutputStream(); 1749 | } 1750 | 1751 | /** 1752 | * Get response as {@link String} in given character set 1753 | *

1754 | * This will fall back to using the UTF-8 character set if the given charset 1755 | * is null 1756 | * 1757 | * @param charset 1758 | * @return string 1759 | * @throws HttpRequestException 1760 | */ 1761 | public String body(final String charset) throws HttpRequestException { 1762 | final ByteArrayOutputStream output = byteStream(); 1763 | try { 1764 | copy(buffer(), output); 1765 | return output.toString(getValidCharset(charset)); 1766 | } catch (IOException e) { 1767 | throw new HttpRequestException(e); 1768 | } 1769 | } 1770 | 1771 | /** 1772 | * Get response as {@link String} using character set returned from 1773 | * {@link #charset()} 1774 | * 1775 | * @return string 1776 | * @throws HttpRequestException 1777 | */ 1778 | public String body() throws HttpRequestException { 1779 | return body(charset()); 1780 | } 1781 | 1782 | /** 1783 | * Get the response body as a {@link String} and set it as the value of the 1784 | * given reference. 1785 | * 1786 | * @param output 1787 | * @return this request 1788 | * @throws HttpRequestException 1789 | */ 1790 | public HttpRequest body(final AtomicReference output) throws HttpRequestException { 1791 | output.set(body()); 1792 | return this; 1793 | } 1794 | 1795 | /** 1796 | * Get the response body as a {@link String} and set it as the value of the 1797 | * given reference. 1798 | * 1799 | * @param output 1800 | * @param charset 1801 | * @return this request 1802 | * @throws HttpRequestException 1803 | */ 1804 | public HttpRequest body(final AtomicReference output, final String charset) throws HttpRequestException { 1805 | output.set(body(charset)); 1806 | return this; 1807 | } 1808 | 1809 | 1810 | /** 1811 | * Is the response body empty? 1812 | * 1813 | * @return true if the Content-Length response header is 0, false otherwise 1814 | * @throws HttpRequestException 1815 | */ 1816 | public boolean isBodyEmpty() throws HttpRequestException { 1817 | return contentLength() == 0; 1818 | } 1819 | 1820 | /** 1821 | * Get response as byte array 1822 | * 1823 | * @return byte array 1824 | * @throws HttpRequestException 1825 | */ 1826 | public byte[] bytes() throws HttpRequestException { 1827 | final ByteArrayOutputStream output = byteStream(); 1828 | try { 1829 | copy(buffer(), output); 1830 | } catch (IOException e) { 1831 | throw new HttpRequestException(e); 1832 | } 1833 | return output.toByteArray(); 1834 | } 1835 | 1836 | /** 1837 | * Get response in a buffered stream 1838 | * 1839 | * @see #bufferSize(int) 1840 | * @return stream 1841 | * @throws HttpRequestException 1842 | */ 1843 | public BufferedInputStream buffer() throws HttpRequestException { 1844 | return new BufferedInputStream(stream(), bufferSize); 1845 | } 1846 | 1847 | /** 1848 | * Get stream to response body 1849 | * 1850 | * @return stream 1851 | * @throws HttpRequestException 1852 | */ 1853 | public InputStream stream() throws HttpRequestException { 1854 | InputStream stream; 1855 | if (code() < HTTP_BAD_REQUEST) 1856 | try { 1857 | stream = getConnection().getInputStream(); 1858 | } catch (IOException e) { 1859 | throw new HttpRequestException(e); 1860 | } 1861 | else { 1862 | stream = getConnection().getErrorStream(); 1863 | if (stream == null) 1864 | try { 1865 | stream = getConnection().getInputStream(); 1866 | } catch (IOException e) { 1867 | if (contentLength() > 0) 1868 | throw new HttpRequestException(e); 1869 | else 1870 | stream = new ByteArrayInputStream(new byte[0]); 1871 | } 1872 | } 1873 | 1874 | if (!uncompress || !ENCODING_GZIP.equals(contentEncoding())) 1875 | return stream; 1876 | else 1877 | try { 1878 | return new GZIPInputStream(stream); 1879 | } catch (IOException e) { 1880 | throw new HttpRequestException(e); 1881 | } 1882 | } 1883 | 1884 | /** 1885 | * Get reader to response body using given character set. 1886 | *

1887 | * This will fall back to using the UTF-8 character set if the given charset 1888 | * is null 1889 | * 1890 | * @param charset 1891 | * @return reader 1892 | * @throws HttpRequestException 1893 | */ 1894 | public InputStreamReader reader(final String charset) 1895 | throws HttpRequestException { 1896 | try { 1897 | return new InputStreamReader(stream(), getValidCharset(charset)); 1898 | } catch (UnsupportedEncodingException e) { 1899 | throw new HttpRequestException(e); 1900 | } 1901 | } 1902 | 1903 | /** 1904 | * Get reader to response body using the character set returned from 1905 | * {@link #charset()} 1906 | * 1907 | * @return reader 1908 | * @throws HttpRequestException 1909 | */ 1910 | public InputStreamReader reader() throws HttpRequestException { 1911 | return reader(charset()); 1912 | } 1913 | 1914 | /** 1915 | * Get buffered reader to response body using the given character set r and 1916 | * the configured buffer size 1917 | * 1918 | * 1919 | * @see #bufferSize(int) 1920 | * @param charset 1921 | * @return reader 1922 | * @throws HttpRequestException 1923 | */ 1924 | public BufferedReader bufferedReader(final String charset) 1925 | throws HttpRequestException { 1926 | return new BufferedReader(reader(charset), bufferSize); 1927 | } 1928 | 1929 | /** 1930 | * Get buffered reader to response body using the character set returned from 1931 | * {@link #charset()} and the configured buffer size 1932 | * 1933 | * @see #bufferSize(int) 1934 | * @return reader 1935 | * @throws HttpRequestException 1936 | */ 1937 | public BufferedReader bufferedReader() throws HttpRequestException { 1938 | return bufferedReader(charset()); 1939 | } 1940 | 1941 | /** 1942 | * Stream response body to file 1943 | * 1944 | * @param file 1945 | * @return this request 1946 | * @throws HttpRequestException 1947 | */ 1948 | public HttpRequest receive(final File file) throws HttpRequestException { 1949 | final OutputStream output; 1950 | try { 1951 | output = new BufferedOutputStream(new FileOutputStream(file), bufferSize); 1952 | } catch (FileNotFoundException e) { 1953 | throw new HttpRequestException(e); 1954 | } 1955 | return new CloseOperation(output, ignoreCloseExceptions) { 1956 | 1957 | @Override 1958 | protected HttpRequest run() throws HttpRequestException, IOException { 1959 | return receive(output); 1960 | } 1961 | }.call(); 1962 | } 1963 | 1964 | /** 1965 | * Stream response to given output stream 1966 | * 1967 | * @param output 1968 | * @return this request 1969 | * @throws HttpRequestException 1970 | */ 1971 | public HttpRequest receive(final OutputStream output) 1972 | throws HttpRequestException { 1973 | try { 1974 | return copy(buffer(), output); 1975 | } catch (IOException e) { 1976 | throw new HttpRequestException(e); 1977 | } 1978 | } 1979 | 1980 | /** 1981 | * Stream response to given print stream 1982 | * 1983 | * @param output 1984 | * @return this request 1985 | * @throws HttpRequestException 1986 | */ 1987 | public HttpRequest receive(final PrintStream output) 1988 | throws HttpRequestException { 1989 | return receive((OutputStream) output); 1990 | } 1991 | 1992 | /** 1993 | * Receive response into the given appendable 1994 | * 1995 | * @param appendable 1996 | * @return this request 1997 | * @throws HttpRequestException 1998 | */ 1999 | public HttpRequest receive(final Appendable appendable) 2000 | throws HttpRequestException { 2001 | final BufferedReader reader = bufferedReader(); 2002 | return new CloseOperation(reader, ignoreCloseExceptions) { 2003 | 2004 | @Override 2005 | public HttpRequest run() throws IOException { 2006 | final CharBuffer buffer = CharBuffer.allocate(bufferSize); 2007 | int read; 2008 | while ((read = reader.read(buffer)) != -1) { 2009 | buffer.rewind(); 2010 | appendable.append(buffer, 0, read); 2011 | buffer.rewind(); 2012 | } 2013 | return HttpRequest.this; 2014 | } 2015 | }.call(); 2016 | } 2017 | 2018 | /** 2019 | * Receive response into the given writer 2020 | * 2021 | * @param writer 2022 | * @return this request 2023 | * @throws HttpRequestException 2024 | */ 2025 | public HttpRequest receive(final Writer writer) throws HttpRequestException { 2026 | final BufferedReader reader = bufferedReader(); 2027 | return new CloseOperation(reader, ignoreCloseExceptions) { 2028 | 2029 | @Override 2030 | public HttpRequest run() throws IOException { 2031 | return copy(reader, writer); 2032 | } 2033 | }.call(); 2034 | } 2035 | 2036 | /** 2037 | * Set read timeout on connection to given value 2038 | * 2039 | * @param timeout 2040 | * @return this request 2041 | */ 2042 | public HttpRequest readTimeout(final int timeout) { 2043 | getConnection().setReadTimeout(timeout); 2044 | return this; 2045 | } 2046 | 2047 | /** 2048 | * Set connect timeout on connection to given value 2049 | * 2050 | * @param timeout 2051 | * @return this request 2052 | */ 2053 | public HttpRequest connectTimeout(final int timeout) { 2054 | getConnection().setConnectTimeout(timeout); 2055 | return this; 2056 | } 2057 | 2058 | /** 2059 | * Set header name to given value 2060 | * 2061 | * @param name 2062 | * @param value 2063 | * @return this request 2064 | */ 2065 | public HttpRequest header(final String name, final String value) { 2066 | getConnection().setRequestProperty(name, value); 2067 | return this; 2068 | } 2069 | 2070 | /** 2071 | * Set header name to given value 2072 | * 2073 | * @param name 2074 | * @param value 2075 | * @return this request 2076 | */ 2077 | public HttpRequest header(final String name, final Number value) { 2078 | return header(name, value != null ? value.toString() : null); 2079 | } 2080 | 2081 | /** 2082 | * Set all headers found in given map where the keys are the header names and 2083 | * the values are the header values 2084 | * 2085 | * @param headers 2086 | * @return this request 2087 | */ 2088 | public HttpRequest headers(final Map headers) { 2089 | if (!headers.isEmpty()) 2090 | for (Entry header : headers.entrySet()) 2091 | header(header); 2092 | return this; 2093 | } 2094 | 2095 | /** 2096 | * Set header to have given entry's key as the name and value as the value 2097 | * 2098 | * @param header 2099 | * @return this request 2100 | */ 2101 | public HttpRequest header(final Entry header) { 2102 | return header(header.getKey(), header.getValue()); 2103 | } 2104 | 2105 | /** 2106 | * Get a response header 2107 | * 2108 | * @param name 2109 | * @return response header 2110 | * @throws HttpRequestException 2111 | */ 2112 | public String header(final String name) throws HttpRequestException { 2113 | closeOutputQuietly(); 2114 | return getConnection().getHeaderField(name); 2115 | } 2116 | 2117 | /** 2118 | * Get all the response headers 2119 | * 2120 | * @return map of response header names to their value(s) 2121 | * @throws HttpRequestException 2122 | */ 2123 | public Map> headers() throws HttpRequestException { 2124 | closeOutputQuietly(); 2125 | return getConnection().getHeaderFields(); 2126 | } 2127 | 2128 | /** 2129 | * Get a date header from the response falling back to returning -1 if the 2130 | * header is missing or parsing fails 2131 | * 2132 | * @param name 2133 | * @return date, -1 on failures 2134 | * @throws HttpRequestException 2135 | */ 2136 | public long dateHeader(final String name) throws HttpRequestException { 2137 | return dateHeader(name, -1L); 2138 | } 2139 | 2140 | /** 2141 | * Get a date header from the response falling back to returning the given 2142 | * default value if the header is missing or parsing fails 2143 | * 2144 | * @param name 2145 | * @param defaultValue 2146 | * @return date, default value on failures 2147 | * @throws HttpRequestException 2148 | */ 2149 | public long dateHeader(final String name, final long defaultValue) 2150 | throws HttpRequestException { 2151 | closeOutputQuietly(); 2152 | return getConnection().getHeaderFieldDate(name, defaultValue); 2153 | } 2154 | 2155 | /** 2156 | * Get an integer header from the response falling back to returning -1 if the 2157 | * header is missing or parsing fails 2158 | * 2159 | * @param name 2160 | * @return header value as an integer, -1 when missing or parsing fails 2161 | * @throws HttpRequestException 2162 | */ 2163 | public int intHeader(final String name) throws HttpRequestException { 2164 | return intHeader(name, -1); 2165 | } 2166 | 2167 | /** 2168 | * Get an integer header value from the response falling back to the given 2169 | * default value if the header is missing or if parsing fails 2170 | * 2171 | * @param name 2172 | * @param defaultValue 2173 | * @return header value as an integer, default value when missing or parsing 2174 | * fails 2175 | * @throws HttpRequestException 2176 | */ 2177 | public int intHeader(final String name, final int defaultValue) 2178 | throws HttpRequestException { 2179 | closeOutputQuietly(); 2180 | return getConnection().getHeaderFieldInt(name, defaultValue); 2181 | } 2182 | 2183 | /** 2184 | * Get all values of the given header from the response 2185 | * 2186 | * @param name 2187 | * @return non-null but possibly empty array of {@link String} header values 2188 | */ 2189 | public String[] headers(final String name) { 2190 | final Map> headers = headers(); 2191 | if (headers == null || headers.isEmpty()) 2192 | return EMPTY_STRINGS; 2193 | 2194 | final List values = headers.get(name); 2195 | if (values != null && !values.isEmpty()) 2196 | return values.toArray(new String[values.size()]); 2197 | else 2198 | return EMPTY_STRINGS; 2199 | } 2200 | 2201 | /** 2202 | * Get parameter with given name from header value in response 2203 | * 2204 | * @param headerName 2205 | * @param paramName 2206 | * @return parameter value or null if missing 2207 | */ 2208 | public String parameter(final String headerName, final String paramName) { 2209 | return getParam(header(headerName), paramName); 2210 | } 2211 | 2212 | /** 2213 | * Get all parameters from header value in response 2214 | *

2215 | * This will be all key=value pairs after the first ';' that are separated by 2216 | * a ';' 2217 | * 2218 | * @param headerName 2219 | * @return non-null but possibly empty map of parameter headers 2220 | */ 2221 | public Map parameters(final String headerName) { 2222 | return getParams(header(headerName)); 2223 | } 2224 | 2225 | /** 2226 | * Get parameter values from header value 2227 | * 2228 | * @param header 2229 | * @return parameter value or null if none 2230 | */ 2231 | protected Map getParams(final String header) { 2232 | if (header == null || header.length() == 0) 2233 | return Collections.emptyMap(); 2234 | 2235 | final int headerLength = header.length(); 2236 | int start = header.indexOf(';') + 1; 2237 | if (start == 0 || start == headerLength) 2238 | return Collections.emptyMap(); 2239 | 2240 | int end = header.indexOf(';', start); 2241 | if (end == -1) 2242 | end = headerLength; 2243 | 2244 | Map params = new LinkedHashMap(); 2245 | while (start < end) { 2246 | int nameEnd = header.indexOf('=', start); 2247 | if (nameEnd != -1 && nameEnd < end) { 2248 | String name = header.substring(start, nameEnd).trim(); 2249 | if (name.length() > 0) { 2250 | String value = header.substring(nameEnd + 1, end).trim(); 2251 | int length = value.length(); 2252 | if (length != 0) 2253 | if (length > 2 && '"' == value.charAt(0) 2254 | && '"' == value.charAt(length - 1)) 2255 | params.put(name, value.substring(1, length - 1)); 2256 | else 2257 | params.put(name, value); 2258 | } 2259 | } 2260 | 2261 | start = end + 1; 2262 | end = header.indexOf(';', start); 2263 | if (end == -1) 2264 | end = headerLength; 2265 | } 2266 | 2267 | return params; 2268 | } 2269 | 2270 | /** 2271 | * Get parameter value from header value 2272 | * 2273 | * @param value 2274 | * @param paramName 2275 | * @return parameter value or null if none 2276 | */ 2277 | protected String getParam(final String value, final String paramName) { 2278 | if (value == null || value.length() == 0) 2279 | return null; 2280 | 2281 | final int length = value.length(); 2282 | int start = value.indexOf(';') + 1; 2283 | if (start == 0 || start == length) 2284 | return null; 2285 | 2286 | int end = value.indexOf(';', start); 2287 | if (end == -1) 2288 | end = length; 2289 | 2290 | while (start < end) { 2291 | int nameEnd = value.indexOf('=', start); 2292 | if (nameEnd != -1 && nameEnd < end 2293 | && paramName.equals(value.substring(start, nameEnd).trim())) { 2294 | String paramValue = value.substring(nameEnd + 1, end).trim(); 2295 | int valueLength = paramValue.length(); 2296 | if (valueLength != 0) 2297 | if (valueLength > 2 && '"' == paramValue.charAt(0) 2298 | && '"' == paramValue.charAt(valueLength - 1)) 2299 | return paramValue.substring(1, valueLength - 1); 2300 | else 2301 | return paramValue; 2302 | } 2303 | 2304 | start = end + 1; 2305 | end = value.indexOf(';', start); 2306 | if (end == -1) 2307 | end = length; 2308 | } 2309 | 2310 | return null; 2311 | } 2312 | 2313 | /** 2314 | * Get 'charset' parameter from 'Content-Type' response header 2315 | * 2316 | * @return charset or null if none 2317 | */ 2318 | public String charset() { 2319 | return parameter(HEADER_CONTENT_TYPE, PARAM_CHARSET); 2320 | } 2321 | 2322 | /** 2323 | * Set the 'User-Agent' header to given value 2324 | * 2325 | * @param userAgent 2326 | * @return this request 2327 | */ 2328 | public HttpRequest userAgent(final String userAgent) { 2329 | return header(HEADER_USER_AGENT, userAgent); 2330 | } 2331 | 2332 | /** 2333 | * Set the 'Referer' header to given value 2334 | * 2335 | * @param referer 2336 | * @return this request 2337 | */ 2338 | public HttpRequest referer(final String referer) { 2339 | return header(HEADER_REFERER, referer); 2340 | } 2341 | 2342 | /** 2343 | * Set value of {@link HttpURLConnection#setUseCaches(boolean)} 2344 | * 2345 | * @param useCaches 2346 | * @return this request 2347 | */ 2348 | public HttpRequest useCaches(final boolean useCaches) { 2349 | getConnection().setUseCaches(useCaches); 2350 | return this; 2351 | } 2352 | 2353 | /** 2354 | * Set the 'Accept-Encoding' header to given value 2355 | * 2356 | * @param acceptEncoding 2357 | * @return this request 2358 | */ 2359 | public HttpRequest acceptEncoding(final String acceptEncoding) { 2360 | return header(HEADER_ACCEPT_ENCODING, acceptEncoding); 2361 | } 2362 | 2363 | /** 2364 | * Set the 'Accept-Encoding' header to 'gzip' 2365 | * 2366 | * @see #uncompress(boolean) 2367 | * @return this request 2368 | */ 2369 | public HttpRequest acceptGzipEncoding() { 2370 | return acceptEncoding(ENCODING_GZIP); 2371 | } 2372 | 2373 | /** 2374 | * Set the 'Accept-Charset' header to given value 2375 | * 2376 | * @param acceptCharset 2377 | * @return this request 2378 | */ 2379 | public HttpRequest acceptCharset(final String acceptCharset) { 2380 | return header(HEADER_ACCEPT_CHARSET, acceptCharset); 2381 | } 2382 | 2383 | /** 2384 | * Get the 'Content-Encoding' header from the response 2385 | * 2386 | * @return this request 2387 | */ 2388 | public String contentEncoding() { 2389 | return header(HEADER_CONTENT_ENCODING); 2390 | } 2391 | 2392 | /** 2393 | * Get the 'Server' header from the response 2394 | * 2395 | * @return server 2396 | */ 2397 | public String server() { 2398 | return header(HEADER_SERVER); 2399 | } 2400 | 2401 | /** 2402 | * Get the 'Date' header from the response 2403 | * 2404 | * @return date value, -1 on failures 2405 | */ 2406 | public long date() { 2407 | return dateHeader(HEADER_DATE); 2408 | } 2409 | 2410 | /** 2411 | * Get the 'Cache-Control' header from the response 2412 | * 2413 | * @return cache control 2414 | */ 2415 | public String cacheControl() { 2416 | return header(HEADER_CACHE_CONTROL); 2417 | } 2418 | 2419 | /** 2420 | * Get the 'ETag' header from the response 2421 | * 2422 | * @return entity tag 2423 | */ 2424 | public String eTag() { 2425 | return header(HEADER_ETAG); 2426 | } 2427 | 2428 | /** 2429 | * Get the 'Expires' header from the response 2430 | * 2431 | * @return expires value, -1 on failures 2432 | */ 2433 | public long expires() { 2434 | return dateHeader(HEADER_EXPIRES); 2435 | } 2436 | 2437 | /** 2438 | * Get the 'Last-Modified' header from the response 2439 | * 2440 | * @return last modified value, -1 on failures 2441 | */ 2442 | public long lastModified() { 2443 | return dateHeader(HEADER_LAST_MODIFIED); 2444 | } 2445 | 2446 | /** 2447 | * Get the 'Location' header from the response 2448 | * 2449 | * @return location 2450 | */ 2451 | public String location() { 2452 | return header(HEADER_LOCATION); 2453 | } 2454 | 2455 | /** 2456 | * Set the 'Authorization' header to given value 2457 | * 2458 | * @param authorization 2459 | * @return this request 2460 | */ 2461 | public HttpRequest authorization(final String authorization) { 2462 | return header(HEADER_AUTHORIZATION, authorization); 2463 | } 2464 | 2465 | /** 2466 | * Set the 'Proxy-Authorization' header to given value 2467 | * 2468 | * @param proxyAuthorization 2469 | * @return this request 2470 | */ 2471 | public HttpRequest proxyAuthorization(final String proxyAuthorization) { 2472 | return header(HEADER_PROXY_AUTHORIZATION, proxyAuthorization); 2473 | } 2474 | 2475 | /** 2476 | * Set the 'Authorization' header to given values in Basic authentication 2477 | * format 2478 | * 2479 | * @param name 2480 | * @param password 2481 | * @return this request 2482 | */ 2483 | public HttpRequest basic(final String name, final String password) { 2484 | return authorization("Basic " + Base64.encode(name + ':' + password)); 2485 | } 2486 | 2487 | /** 2488 | * Set the 'Proxy-Authorization' header to given values in Basic authentication 2489 | * format 2490 | * 2491 | * @param name 2492 | * @param password 2493 | * @return this request 2494 | */ 2495 | public HttpRequest proxyBasic(final String name, final String password) { 2496 | return proxyAuthorization("Basic " + Base64.encode(name + ':' + password)); 2497 | } 2498 | 2499 | /** 2500 | * Set the 'If-Modified-Since' request header to the given value 2501 | * 2502 | * @param ifModifiedSince 2503 | * @return this request 2504 | */ 2505 | public HttpRequest ifModifiedSince(final long ifModifiedSince) { 2506 | getConnection().setIfModifiedSince(ifModifiedSince); 2507 | return this; 2508 | } 2509 | 2510 | /** 2511 | * Set the 'If-None-Match' request header to the given value 2512 | * 2513 | * @param ifNoneMatch 2514 | * @return this request 2515 | */ 2516 | public HttpRequest ifNoneMatch(final String ifNoneMatch) { 2517 | return header(HEADER_IF_NONE_MATCH, ifNoneMatch); 2518 | } 2519 | 2520 | /** 2521 | * Set the 'Content-Type' request header to the given value 2522 | * 2523 | * @param contentType 2524 | * @return this request 2525 | */ 2526 | public HttpRequest contentType(final String contentType) { 2527 | return contentType(contentType, null); 2528 | } 2529 | 2530 | /** 2531 | * Set the 'Content-Type' request header to the given value and charset 2532 | * 2533 | * @param contentType 2534 | * @param charset 2535 | * @return this request 2536 | */ 2537 | public HttpRequest contentType(final String contentType, final String charset) { 2538 | if (charset != null && charset.length() > 0) { 2539 | final String separator = "; " + PARAM_CHARSET + '='; 2540 | return header(HEADER_CONTENT_TYPE, contentType + separator + charset); 2541 | } else 2542 | return header(HEADER_CONTENT_TYPE, contentType); 2543 | } 2544 | 2545 | /** 2546 | * Get the 'Content-Type' header from the response 2547 | * 2548 | * @return response header value 2549 | */ 2550 | public String contentType() { 2551 | return header(HEADER_CONTENT_TYPE); 2552 | } 2553 | 2554 | /** 2555 | * Get the 'Content-Length' header from the response 2556 | * 2557 | * @return response header value 2558 | */ 2559 | public int contentLength() { 2560 | return intHeader(HEADER_CONTENT_LENGTH); 2561 | } 2562 | 2563 | /** 2564 | * Set the 'Content-Length' request header to the given value 2565 | * 2566 | * @param contentLength 2567 | * @return this request 2568 | */ 2569 | public HttpRequest contentLength(final String contentLength) { 2570 | return contentLength(Integer.parseInt(contentLength)); 2571 | } 2572 | 2573 | /** 2574 | * Set the 'Content-Length' request header to the given value 2575 | * 2576 | * @param contentLength 2577 | * @return this request 2578 | */ 2579 | public HttpRequest contentLength(final int contentLength) { 2580 | getConnection().setFixedLengthStreamingMode(contentLength); 2581 | return this; 2582 | } 2583 | 2584 | /** 2585 | * Set the 'Accept' header to given value 2586 | * 2587 | * @param accept 2588 | * @return this request 2589 | */ 2590 | public HttpRequest accept(final String accept) { 2591 | return header(HEADER_ACCEPT, accept); 2592 | } 2593 | 2594 | /** 2595 | * Set the 'Accept' header to 'application/json' 2596 | * 2597 | * @return this request 2598 | */ 2599 | public HttpRequest acceptJson() { 2600 | return accept(CONTENT_TYPE_JSON); 2601 | } 2602 | 2603 | /** 2604 | * Copy from input stream to output stream 2605 | * 2606 | * @param input 2607 | * @param output 2608 | * @return this request 2609 | * @throws IOException 2610 | */ 2611 | protected HttpRequest copy(final InputStream input, final OutputStream output) 2612 | throws IOException { 2613 | return new CloseOperation(input, ignoreCloseExceptions) { 2614 | 2615 | @Override 2616 | public HttpRequest run() throws IOException { 2617 | final byte[] buffer = new byte[bufferSize]; 2618 | int read; 2619 | while ((read = input.read(buffer)) != -1) { 2620 | output.write(buffer, 0, read); 2621 | totalWritten += read; 2622 | progress.onUpload(totalWritten, totalSize); 2623 | } 2624 | return HttpRequest.this; 2625 | } 2626 | }.call(); 2627 | } 2628 | 2629 | /** 2630 | * Copy from reader to writer 2631 | * 2632 | * @param input 2633 | * @param output 2634 | * @return this request 2635 | * @throws IOException 2636 | */ 2637 | protected HttpRequest copy(final Reader input, final Writer output) 2638 | throws IOException { 2639 | return new CloseOperation(input, ignoreCloseExceptions) { 2640 | 2641 | @Override 2642 | public HttpRequest run() throws IOException { 2643 | final char[] buffer = new char[bufferSize]; 2644 | int read; 2645 | while ((read = input.read(buffer)) != -1) { 2646 | output.write(buffer, 0, read); 2647 | totalWritten += read; 2648 | progress.onUpload(totalWritten, -1); 2649 | } 2650 | return HttpRequest.this; 2651 | } 2652 | }.call(); 2653 | } 2654 | 2655 | /** 2656 | * Set the UploadProgress callback for this request 2657 | * 2658 | * @param callback 2659 | * @return this request 2660 | */ 2661 | public HttpRequest progress(final UploadProgress callback) { 2662 | if (callback == null) 2663 | progress = UploadProgress.DEFAULT; 2664 | else 2665 | progress = callback; 2666 | return this; 2667 | } 2668 | 2669 | private HttpRequest incrementTotalSize(final long size) { 2670 | if (totalSize == -1) 2671 | totalSize = 0; 2672 | totalSize += size; 2673 | return this; 2674 | } 2675 | 2676 | /** 2677 | * Close output stream 2678 | * 2679 | * @return this request 2680 | * @throws HttpRequestException 2681 | * @throws IOException 2682 | */ 2683 | protected HttpRequest closeOutput() throws IOException { 2684 | progress(null); 2685 | if (output == null) 2686 | return this; 2687 | if (multipart) 2688 | output.write(CRLF + "--" + BOUNDARY + "--" + CRLF); 2689 | if (ignoreCloseExceptions) 2690 | try { 2691 | output.close(); 2692 | } catch (IOException ignored) { 2693 | // Ignored 2694 | } 2695 | else 2696 | output.close(); 2697 | output = null; 2698 | return this; 2699 | } 2700 | 2701 | /** 2702 | * Call {@link #closeOutput()} and re-throw a caught {@link IOException}s as 2703 | * an {@link HttpRequestException} 2704 | * 2705 | * @return this request 2706 | * @throws HttpRequestException 2707 | */ 2708 | protected HttpRequest closeOutputQuietly() throws HttpRequestException { 2709 | try { 2710 | return closeOutput(); 2711 | } catch (IOException e) { 2712 | throw new HttpRequestException(e); 2713 | } 2714 | } 2715 | 2716 | /** 2717 | * Open output stream 2718 | * 2719 | * @return this request 2720 | * @throws IOException 2721 | */ 2722 | protected HttpRequest openOutput() throws IOException { 2723 | if (output != null) 2724 | return this; 2725 | getConnection().setDoOutput(true); 2726 | final String charset = getParam( 2727 | getConnection().getRequestProperty(HEADER_CONTENT_TYPE), PARAM_CHARSET); 2728 | output = new RequestOutputStream(getConnection().getOutputStream(), charset, 2729 | bufferSize); 2730 | return this; 2731 | } 2732 | 2733 | /** 2734 | * Start part of a multipart 2735 | * 2736 | * @return this request 2737 | * @throws IOException 2738 | */ 2739 | protected HttpRequest startPart() throws IOException { 2740 | if (!multipart) { 2741 | multipart = true; 2742 | contentType(CONTENT_TYPE_MULTIPART).openOutput(); 2743 | output.write("--" + BOUNDARY + CRLF); 2744 | } else 2745 | output.write(CRLF + "--" + BOUNDARY + CRLF); 2746 | return this; 2747 | } 2748 | 2749 | /** 2750 | * Write part header 2751 | * 2752 | * @param name 2753 | * @param filename 2754 | * @return this request 2755 | * @throws IOException 2756 | */ 2757 | protected HttpRequest writePartHeader(final String name, final String filename) 2758 | throws IOException { 2759 | return writePartHeader(name, filename, null); 2760 | } 2761 | 2762 | /** 2763 | * Write part header 2764 | * 2765 | * @param name 2766 | * @param filename 2767 | * @param contentType 2768 | * @return this request 2769 | * @throws IOException 2770 | */ 2771 | protected HttpRequest writePartHeader(final String name, 2772 | final String filename, final String contentType) throws IOException { 2773 | final StringBuilder partBuffer = new StringBuilder(); 2774 | partBuffer.append("form-data; name=\"").append(name); 2775 | if (filename != null) 2776 | partBuffer.append("\"; filename=\"").append(filename); 2777 | partBuffer.append('"'); 2778 | partHeader("Content-Disposition", partBuffer.toString()); 2779 | if (contentType != null) 2780 | partHeader(HEADER_CONTENT_TYPE, contentType); 2781 | return send(CRLF); 2782 | } 2783 | 2784 | /** 2785 | * Write part of a multipart request to the request body 2786 | * 2787 | * @param name 2788 | * @param part 2789 | * @return this request 2790 | */ 2791 | public HttpRequest part(final String name, final String part) { 2792 | return part(name, null, part); 2793 | } 2794 | 2795 | /** 2796 | * Write part of a multipart request to the request body 2797 | * 2798 | * @param name 2799 | * @param filename 2800 | * @param part 2801 | * @return this request 2802 | * @throws HttpRequestException 2803 | */ 2804 | public HttpRequest part(final String name, final String filename, 2805 | final String part) throws HttpRequestException { 2806 | return part(name, filename, null, part); 2807 | } 2808 | 2809 | /** 2810 | * Write part of a multipart request to the request body 2811 | * 2812 | * @param name 2813 | * @param filename 2814 | * @param contentType 2815 | * value of the Content-Type part header 2816 | * @param part 2817 | * @return this request 2818 | * @throws HttpRequestException 2819 | */ 2820 | public HttpRequest part(final String name, final String filename, 2821 | final String contentType, final String part) throws HttpRequestException { 2822 | try { 2823 | startPart(); 2824 | writePartHeader(name, filename, contentType); 2825 | output.write(part); 2826 | } catch (IOException e) { 2827 | throw new HttpRequestException(e); 2828 | } 2829 | return this; 2830 | } 2831 | 2832 | /** 2833 | * Write part of a multipart request to the request body 2834 | * 2835 | * @param name 2836 | * @param part 2837 | * @return this request 2838 | * @throws HttpRequestException 2839 | */ 2840 | public HttpRequest part(final String name, final Number part) 2841 | throws HttpRequestException { 2842 | return part(name, null, part); 2843 | } 2844 | 2845 | /** 2846 | * Write part of a multipart request to the request body 2847 | * 2848 | * @param name 2849 | * @param filename 2850 | * @param part 2851 | * @return this request 2852 | * @throws HttpRequestException 2853 | */ 2854 | public HttpRequest part(final String name, final String filename, 2855 | final Number part) throws HttpRequestException { 2856 | return part(name, filename, part != null ? part.toString() : null); 2857 | } 2858 | 2859 | /** 2860 | * Write part of a multipart request to the request body 2861 | * 2862 | * @param name 2863 | * @param part 2864 | * @return this request 2865 | * @throws HttpRequestException 2866 | */ 2867 | public HttpRequest part(final String name, final File part) 2868 | throws HttpRequestException { 2869 | return part(name, null, part); 2870 | } 2871 | 2872 | /** 2873 | * Write part of a multipart request to the request body 2874 | * 2875 | * @param name 2876 | * @param filename 2877 | * @param part 2878 | * @return this request 2879 | * @throws HttpRequestException 2880 | */ 2881 | public HttpRequest part(final String name, final String filename, 2882 | final File part) throws HttpRequestException { 2883 | return part(name, filename, null, part); 2884 | } 2885 | 2886 | /** 2887 | * Write part of a multipart request to the request body 2888 | * 2889 | * @param name 2890 | * @param filename 2891 | * @param contentType 2892 | * value of the Content-Type part header 2893 | * @param part 2894 | * @return this request 2895 | * @throws HttpRequestException 2896 | */ 2897 | public HttpRequest part(final String name, final String filename, 2898 | final String contentType, final File part) throws HttpRequestException { 2899 | final InputStream stream; 2900 | try { 2901 | stream = new BufferedInputStream(new FileInputStream(part)); 2902 | incrementTotalSize(part.length()); 2903 | } catch (IOException e) { 2904 | throw new HttpRequestException(e); 2905 | } 2906 | return part(name, filename, contentType, stream); 2907 | } 2908 | 2909 | /** 2910 | * Write part of a multipart request to the request body 2911 | * 2912 | * @param name 2913 | * @param part 2914 | * @return this request 2915 | * @throws HttpRequestException 2916 | */ 2917 | public HttpRequest part(final String name, final InputStream part) 2918 | throws HttpRequestException { 2919 | return part(name, null, null, part); 2920 | } 2921 | 2922 | /** 2923 | * Write part of a multipart request to the request body 2924 | * 2925 | * @param name 2926 | * @param filename 2927 | * @param contentType 2928 | * value of the Content-Type part header 2929 | * @param part 2930 | * @return this request 2931 | * @throws HttpRequestException 2932 | */ 2933 | public HttpRequest part(final String name, final String filename, 2934 | final String contentType, final InputStream part) 2935 | throws HttpRequestException { 2936 | try { 2937 | startPart(); 2938 | writePartHeader(name, filename, contentType); 2939 | copy(part, output); 2940 | } catch (IOException e) { 2941 | throw new HttpRequestException(e); 2942 | } 2943 | return this; 2944 | } 2945 | 2946 | /** 2947 | * Write a multipart header to the response body 2948 | * 2949 | * @param name 2950 | * @param value 2951 | * @return this request 2952 | * @throws HttpRequestException 2953 | */ 2954 | public HttpRequest partHeader(final String name, final String value) 2955 | throws HttpRequestException { 2956 | return send(name).send(": ").send(value).send(CRLF); 2957 | } 2958 | 2959 | /** 2960 | * Write contents of file to request body 2961 | * 2962 | * @param input 2963 | * @return this request 2964 | * @throws HttpRequestException 2965 | */ 2966 | public HttpRequest send(final File input) throws HttpRequestException { 2967 | final InputStream stream; 2968 | try { 2969 | stream = new BufferedInputStream(new FileInputStream(input)); 2970 | incrementTotalSize(input.length()); 2971 | } catch (FileNotFoundException e) { 2972 | throw new HttpRequestException(e); 2973 | } 2974 | return send(stream); 2975 | } 2976 | 2977 | /** 2978 | * Write byte array to request body 2979 | * 2980 | * @param input 2981 | * @return this request 2982 | * @throws HttpRequestException 2983 | */ 2984 | public HttpRequest send(final byte[] input) throws HttpRequestException { 2985 | if (input != null) 2986 | incrementTotalSize(input.length); 2987 | return send(new ByteArrayInputStream(input)); 2988 | } 2989 | 2990 | /** 2991 | * Write stream to request body 2992 | *

2993 | * The given stream will be closed once sending completes 2994 | * 2995 | * @param input 2996 | * @return this request 2997 | * @throws HttpRequestException 2998 | */ 2999 | public HttpRequest send(final InputStream input) throws HttpRequestException { 3000 | try { 3001 | openOutput(); 3002 | copy(input, output); 3003 | } catch (IOException e) { 3004 | throw new HttpRequestException(e); 3005 | } 3006 | return this; 3007 | } 3008 | 3009 | /** 3010 | * Write reader to request body 3011 | *

3012 | * The given reader will be closed once sending completes 3013 | * 3014 | * @param input 3015 | * @return this request 3016 | * @throws HttpRequestException 3017 | */ 3018 | public HttpRequest send(final Reader input) throws HttpRequestException { 3019 | try { 3020 | openOutput(); 3021 | } catch (IOException e) { 3022 | throw new HttpRequestException(e); 3023 | } 3024 | final Writer writer = new OutputStreamWriter(output, 3025 | output.encoder.charset()); 3026 | return new FlushOperation(writer) { 3027 | 3028 | @Override 3029 | protected HttpRequest run() throws IOException { 3030 | return copy(input, writer); 3031 | } 3032 | }.call(); 3033 | } 3034 | 3035 | /** 3036 | * Write char sequence to request body 3037 | *

3038 | * The charset configured via {@link #contentType(String)} will be used and 3039 | * UTF-8 will be used if it is unset. 3040 | * 3041 | * @param value 3042 | * @return this request 3043 | * @throws HttpRequestException 3044 | */ 3045 | public HttpRequest send(final CharSequence value) throws HttpRequestException { 3046 | try { 3047 | openOutput(); 3048 | output.write(value.toString()); 3049 | } catch (IOException e) { 3050 | throw new HttpRequestException(e); 3051 | } 3052 | return this; 3053 | } 3054 | 3055 | /** 3056 | * Create writer to request output stream 3057 | * 3058 | * @return writer 3059 | * @throws HttpRequestException 3060 | */ 3061 | public OutputStreamWriter writer() throws HttpRequestException { 3062 | try { 3063 | openOutput(); 3064 | return new OutputStreamWriter(output, output.encoder.charset()); 3065 | } catch (IOException e) { 3066 | throw new HttpRequestException(e); 3067 | } 3068 | } 3069 | 3070 | /** 3071 | * Write the values in the map as form data to the request body 3072 | *

3073 | * The pairs specified will be URL-encoded in UTF-8 and sent with the 3074 | * 'application/x-www-form-urlencoded' content-type 3075 | * 3076 | * @param values 3077 | * @return this request 3078 | * @throws HttpRequestException 3079 | */ 3080 | public HttpRequest form(final Map values) throws HttpRequestException { 3081 | return form(values, CHARSET_UTF8); 3082 | } 3083 | 3084 | /** 3085 | * Write the key and value in the entry as form data to the request body 3086 | *

3087 | * The pair specified will be URL-encoded in UTF-8 and sent with the 3088 | * 'application/x-www-form-urlencoded' content-type 3089 | * 3090 | * @param entry 3091 | * @return this request 3092 | * @throws HttpRequestException 3093 | */ 3094 | public HttpRequest form(final Entry entry) throws HttpRequestException { 3095 | return form(entry, CHARSET_UTF8); 3096 | } 3097 | 3098 | /** 3099 | * Write the key and value in the entry as form data to the request body 3100 | *

3101 | * The pair specified will be URL-encoded and sent with the 3102 | * 'application/x-www-form-urlencoded' content-type 3103 | * 3104 | * @param entry 3105 | * @param charset 3106 | * @return this request 3107 | * @throws HttpRequestException 3108 | */ 3109 | public HttpRequest form(final Entry entry, final String charset) 3110 | throws HttpRequestException { 3111 | return form(entry.getKey(), entry.getValue(), charset); 3112 | } 3113 | 3114 | /** 3115 | * Write the name/value pair as form data to the request body 3116 | *

3117 | * The pair specified will be URL-encoded in UTF-8 and sent with the 3118 | * 'application/x-www-form-urlencoded' content-type 3119 | * 3120 | * @param name 3121 | * @param value 3122 | * @return this request 3123 | * @throws HttpRequestException 3124 | */ 3125 | public HttpRequest form(final Object name, final Object value) 3126 | throws HttpRequestException { 3127 | return form(name, value, CHARSET_UTF8); 3128 | } 3129 | 3130 | /** 3131 | * Write the name/value pair as form data to the request body 3132 | *

3133 | * The values specified will be URL-encoded and sent with the 3134 | * 'application/x-www-form-urlencoded' content-type 3135 | * 3136 | * @param name 3137 | * @param value 3138 | * @param charset 3139 | * @return this request 3140 | * @throws HttpRequestException 3141 | */ 3142 | public HttpRequest form(final Object name, final Object value, String charset) 3143 | throws HttpRequestException { 3144 | final boolean first = !form; 3145 | if (first) { 3146 | contentType(CONTENT_TYPE_FORM, charset); 3147 | form = true; 3148 | } 3149 | charset = getValidCharset(charset); 3150 | try { 3151 | openOutput(); 3152 | if (!first) 3153 | output.write('&'); 3154 | output.write(URLEncoder.encode(name.toString(), charset)); 3155 | output.write('='); 3156 | if (value != null) 3157 | output.write(URLEncoder.encode(value.toString(), charset)); 3158 | } catch (IOException e) { 3159 | throw new HttpRequestException(e); 3160 | } 3161 | return this; 3162 | } 3163 | 3164 | /** 3165 | * Write the values in the map as encoded form data to the request body 3166 | * 3167 | * @param values 3168 | * @param charset 3169 | * @return this request 3170 | * @throws HttpRequestException 3171 | */ 3172 | public HttpRequest form(final Map values, final String charset) 3173 | throws HttpRequestException { 3174 | if (!values.isEmpty()) 3175 | for (Entry entry : values.entrySet()) 3176 | form(entry, charset); 3177 | return this; 3178 | } 3179 | 3180 | /** 3181 | * Configure HTTPS connection to trust all certificates 3182 | *

3183 | * This method does nothing if the current request is not a HTTPS request 3184 | * 3185 | * @return this request 3186 | * @throws HttpRequestException 3187 | */ 3188 | public HttpRequest trustAllCerts() throws HttpRequestException { 3189 | final HttpURLConnection connection = getConnection(); 3190 | if (connection instanceof HttpsURLConnection) 3191 | ((HttpsURLConnection) connection) 3192 | .setSSLSocketFactory(getTrustedFactory()); 3193 | return this; 3194 | } 3195 | 3196 | /** 3197 | * Configure HTTPS connection to trust all hosts using a custom 3198 | * {@link HostnameVerifier} that always returns true for each 3199 | * host verified 3200 | *

3201 | * This method does nothing if the current request is not a HTTPS request 3202 | * 3203 | * @return this request 3204 | */ 3205 | public HttpRequest trustAllHosts() { 3206 | final HttpURLConnection connection = getConnection(); 3207 | if (connection instanceof HttpsURLConnection) 3208 | ((HttpsURLConnection) connection) 3209 | .setHostnameVerifier(getTrustedVerifier()); 3210 | return this; 3211 | } 3212 | 3213 | /** 3214 | * Get the {@link URL} of this request's connection 3215 | * 3216 | * @return request URL 3217 | */ 3218 | public URL url() { 3219 | return getConnection().getURL(); 3220 | } 3221 | 3222 | /** 3223 | * Get the HTTP method of this request 3224 | * 3225 | * @return method 3226 | */ 3227 | public String method() { 3228 | return getConnection().getRequestMethod(); 3229 | } 3230 | 3231 | /** 3232 | * Configure an HTTP proxy on this connection. Use {{@link #proxyBasic(String, String)} if 3233 | * this proxy requires basic authentication. 3234 | * 3235 | * @param proxyHost 3236 | * @param proxyPort 3237 | * @return this request 3238 | */ 3239 | public HttpRequest useProxy(final String proxyHost, final int proxyPort) { 3240 | if (connection != null) 3241 | throw new IllegalStateException("The connection has already been created. This method must be called before reading or writing to the request."); 3242 | 3243 | this.httpProxyHost = proxyHost; 3244 | this.httpProxyPort = proxyPort; 3245 | return this; 3246 | } 3247 | 3248 | /** 3249 | * Set whether or not the underlying connection should follow redirects in 3250 | * the response. 3251 | * 3252 | * @param followRedirects - true fo follow redirects, false to not. 3253 | * @return this request 3254 | */ 3255 | public HttpRequest followRedirects(final boolean followRedirects) { 3256 | getConnection().setInstanceFollowRedirects(followRedirects); 3257 | return this; 3258 | } 3259 | } 3260 | --------------------------------------------------------------------------------