Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

HttpRequest.java 84KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095
  1. /*
  2. * Copyright (c) 2014 Kevin Sawicki <kevinsawicki@gmail.com>
  3. * modified by contributors of cordova-plugin-advanced-http
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to
  7. * deal in the Software without restriction, including without limitation the
  8. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9. * sell copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. * IN THE SOFTWARE.
  22. */
  23. package com.silkimen.http;
  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. import java.io.BufferedInputStream;
  33. import java.io.BufferedOutputStream;
  34. import java.io.BufferedReader;
  35. import java.io.ByteArrayInputStream;
  36. import java.io.ByteArrayOutputStream;
  37. import java.io.Closeable;
  38. import java.io.File;
  39. import java.io.FileInputStream;
  40. import java.io.FileNotFoundException;
  41. import java.io.FileOutputStream;
  42. import java.io.Flushable;
  43. import java.io.IOException;
  44. import java.io.InputStream;
  45. import java.io.InputStreamReader;
  46. import java.io.OutputStream;
  47. import java.io.OutputStreamWriter;
  48. import java.io.PrintStream;
  49. import java.io.Reader;
  50. import java.io.UnsupportedEncodingException;
  51. import java.io.Writer;
  52. import java.net.HttpURLConnection;
  53. import java.net.InetSocketAddress;
  54. import java.net.MalformedURLException;
  55. import java.net.Proxy;
  56. import java.net.URI;
  57. import java.net.URISyntaxException;
  58. import java.net.URL;
  59. import java.net.URLEncoder;
  60. import java.nio.ByteBuffer;
  61. import java.nio.CharBuffer;
  62. import java.nio.charset.Charset;
  63. import java.nio.charset.CharsetEncoder;
  64. import java.security.AccessController;
  65. import java.security.GeneralSecurityException;
  66. import java.security.PrivilegedAction;
  67. import java.security.SecureRandom;
  68. import java.security.cert.X509Certificate;
  69. import java.util.ArrayList;
  70. import java.util.Arrays;
  71. import java.util.Collections;
  72. import java.util.Iterator;
  73. import java.util.LinkedHashMap;
  74. import java.util.List;
  75. import java.util.Map;
  76. import java.util.Map.Entry;
  77. import java.util.concurrent.Callable;
  78. import java.util.concurrent.atomic.AtomicInteger;
  79. import java.util.concurrent.atomic.AtomicReference;
  80. import java.util.zip.GZIPInputStream;
  81. import javax.net.ssl.HostnameVerifier;
  82. import javax.net.ssl.HttpsURLConnection;
  83. import javax.net.ssl.SSLContext;
  84. import javax.net.ssl.SSLSession;
  85. import javax.net.ssl.SSLSocketFactory;
  86. import javax.net.ssl.TrustManager;
  87. import javax.net.ssl.X509TrustManager;
  88. /**
  89. * A fluid interface for making HTTP requests using an underlying
  90. * {@link HttpURLConnection} (or sub-class).
  91. * <p>
  92. * Each instance supports making a single request and cannot be reused for
  93. * further requests.
  94. */
  95. public class HttpRequest {
  96. /**
  97. * 'UTF-8' charset name
  98. */
  99. public static final String CHARSET_UTF8 = "UTF-8";
  100. /**
  101. * 'application/x-www-form-urlencoded' content type header value
  102. */
  103. public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
  104. /**
  105. * 'application/json' content type header value
  106. */
  107. public static final String CONTENT_TYPE_JSON = "application/json";
  108. /**
  109. * 'gzip' encoding header value
  110. */
  111. public static final String ENCODING_GZIP = "gzip";
  112. /**
  113. * 'Accept' header name
  114. */
  115. public static final String HEADER_ACCEPT = "Accept";
  116. /**
  117. * 'Accept-Charset' header name
  118. */
  119. public static final String HEADER_ACCEPT_CHARSET = "Accept-Charset";
  120. /**
  121. * 'Accept-Encoding' header name
  122. */
  123. public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
  124. /**
  125. * 'Authorization' header name
  126. */
  127. public static final String HEADER_AUTHORIZATION = "Authorization";
  128. /**
  129. * 'Cache-Control' header name
  130. */
  131. public static final String HEADER_CACHE_CONTROL = "Cache-Control";
  132. /**
  133. * 'Content-Encoding' header name
  134. */
  135. public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
  136. /**
  137. * 'Content-Length' header name
  138. */
  139. public static final String HEADER_CONTENT_LENGTH = "Content-Length";
  140. /**
  141. * 'Content-Type' header name
  142. */
  143. public static final String HEADER_CONTENT_TYPE = "Content-Type";
  144. /**
  145. * 'Date' header name
  146. */
  147. public static final String HEADER_DATE = "Date";
  148. /**
  149. * 'ETag' header name
  150. */
  151. public static final String HEADER_ETAG = "ETag";
  152. /**
  153. * 'Expires' header name
  154. */
  155. public static final String HEADER_EXPIRES = "Expires";
  156. /**
  157. * 'If-None-Match' header name
  158. */
  159. public static final String HEADER_IF_NONE_MATCH = "If-None-Match";
  160. /**
  161. * 'Last-Modified' header name
  162. */
  163. public static final String HEADER_LAST_MODIFIED = "Last-Modified";
  164. /**
  165. * 'Location' header name
  166. */
  167. public static final String HEADER_LOCATION = "Location";
  168. /**
  169. * 'Proxy-Authorization' header name
  170. */
  171. public static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization";
  172. /**
  173. * 'Referer' header name
  174. */
  175. public static final String HEADER_REFERER = "Referer";
  176. /**
  177. * 'Server' header name
  178. */
  179. public static final String HEADER_SERVER = "Server";
  180. /**
  181. * 'User-Agent' header name
  182. */
  183. public static final String HEADER_USER_AGENT = "User-Agent";
  184. /**
  185. * 'DELETE' request method
  186. */
  187. public static final String METHOD_DELETE = "DELETE";
  188. /**
  189. * 'GET' request method
  190. */
  191. public static final String METHOD_GET = "GET";
  192. /**
  193. * 'HEAD' request method
  194. */
  195. public static final String METHOD_HEAD = "HEAD";
  196. /**
  197. * 'OPTIONS' options method
  198. */
  199. public static final String METHOD_OPTIONS = "OPTIONS";
  200. /**
  201. * 'POST' request method
  202. */
  203. public static final String METHOD_POST = "POST";
  204. /**
  205. * 'PUT' request method
  206. */
  207. public static final String METHOD_PUT = "PUT";
  208. /**
  209. * 'TRACE' request method
  210. */
  211. public static final String METHOD_TRACE = "TRACE";
  212. /**
  213. * 'charset' header value parameter
  214. */
  215. public static final String PARAM_CHARSET = "charset";
  216. private static final String BOUNDARY = "00content0boundary00";
  217. private static final String CONTENT_TYPE_MULTIPART = "multipart/form-data; boundary=" + BOUNDARY;
  218. private static final String CRLF = "\r\n";
  219. private static final String[] EMPTY_STRINGS = new String[0];
  220. private static String getValidCharset(final String charset) {
  221. if (charset != null && charset.length() > 0)
  222. return charset;
  223. else
  224. return CHARSET_UTF8;
  225. }
  226. private static StringBuilder addPathSeparator(final String baseUrl, final StringBuilder result) {
  227. // Add trailing slash if the base URL doesn't have any path segments.
  228. //
  229. // The following test is checking for the last slash not being part of
  230. // the protocol to host separator: '://'.
  231. if (baseUrl.indexOf(':') + 2 == baseUrl.lastIndexOf('/'))
  232. result.append('/');
  233. return result;
  234. }
  235. private static StringBuilder addParamPrefix(final String baseUrl, final StringBuilder result) {
  236. // Add '?' if missing and add '&' if params already exist in base url
  237. final int queryStart = baseUrl.indexOf('?');
  238. final int lastChar = result.length() - 1;
  239. if (queryStart == -1)
  240. result.append('?');
  241. else if (queryStart < lastChar && baseUrl.charAt(lastChar) != '&')
  242. result.append('&');
  243. return result;
  244. }
  245. private static StringBuilder addParam(final Object key, Object value, final StringBuilder result) {
  246. if (value != null && value.getClass().isArray())
  247. value = arrayToList(value);
  248. if (value instanceof Iterable<?>) {
  249. Iterator<?> iterator = ((Iterable<?>) value).iterator();
  250. while (iterator.hasNext()) {
  251. result.append(key);
  252. result.append("[]=");
  253. Object element = iterator.next();
  254. if (element != null)
  255. result.append(element);
  256. if (iterator.hasNext())
  257. result.append("&");
  258. }
  259. } else {
  260. result.append(key);
  261. result.append("=");
  262. if (value != null)
  263. result.append(value);
  264. }
  265. return result;
  266. }
  267. /**
  268. * Creates {@link HttpURLConnection HTTP connections} for {@link URL urls}.
  269. */
  270. public interface ConnectionFactory {
  271. /**
  272. * Open an {@link HttpURLConnection} for the specified {@link URL}.
  273. *
  274. * @throws IOException
  275. */
  276. HttpURLConnection create(URL url) throws IOException;
  277. /**
  278. * Open an {@link HttpURLConnection} for the specified {@link URL} and
  279. * {@link Proxy}.
  280. *
  281. * @throws IOException
  282. */
  283. HttpURLConnection create(URL url, Proxy proxy) throws IOException;
  284. /**
  285. * A {@link ConnectionFactory} which uses the built-in
  286. * {@link URL#openConnection()}
  287. */
  288. ConnectionFactory DEFAULT = new ConnectionFactory() {
  289. public HttpURLConnection create(URL url) throws IOException {
  290. return (HttpURLConnection) url.openConnection();
  291. }
  292. public HttpURLConnection create(URL url, Proxy proxy) throws IOException {
  293. return (HttpURLConnection) url.openConnection(proxy);
  294. }
  295. };
  296. }
  297. private static ConnectionFactory CONNECTION_FACTORY = ConnectionFactory.DEFAULT;
  298. /**
  299. * Specify the {@link ConnectionFactory} used to create new requests.
  300. */
  301. public static void setConnectionFactory(final ConnectionFactory connectionFactory) {
  302. if (connectionFactory == null)
  303. CONNECTION_FACTORY = ConnectionFactory.DEFAULT;
  304. else
  305. CONNECTION_FACTORY = connectionFactory;
  306. }
  307. /**
  308. * Callback interface for reporting upload progress for a request.
  309. */
  310. public interface UploadProgress {
  311. /**
  312. * Callback invoked as data is uploaded by the request.
  313. *
  314. * @param uploaded The number of bytes already uploaded
  315. * @param total The total number of bytes that will be uploaded or -1 if the
  316. * length is unknown.
  317. */
  318. void onUpload(long uploaded, long total);
  319. UploadProgress DEFAULT = new UploadProgress() {
  320. public void onUpload(long uploaded, long total) {
  321. }
  322. };
  323. }
  324. /**
  325. * <p>
  326. * Encodes and decodes to and from Base64 notation.
  327. * </p>
  328. * <p>
  329. * I am placing this code in the Public Domain. Do with it as you will. This
  330. * software comes with no guarantees or warranties but with plenty of
  331. * well-wishing instead! Please visit
  332. * <a href="http://iharder.net/base64">http://iharder.net/base64</a>
  333. * periodically to check for updates or to contribute improvements.
  334. * </p>
  335. *
  336. * @author Robert Harder
  337. * @author rob@iharder.net
  338. * @version 2.3.7
  339. */
  340. public static class Base64 {
  341. /** The equals sign (=) as a byte. */
  342. private final static byte EQUALS_SIGN = (byte) '=';
  343. /** Preferred encoding. */
  344. private final static String PREFERRED_ENCODING = "US-ASCII";
  345. /** The 64 valid Base64 values. */
  346. private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E',
  347. (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
  348. (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W',
  349. (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
  350. (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
  351. (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
  352. (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
  353. (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' };
  354. /** Defeats instantiation. */
  355. private Base64() {
  356. }
  357. /**
  358. * <p>
  359. * Encodes up to three bytes of the array <var>source</var> and writes the
  360. * resulting four Base64 bytes to <var>destination</var>. The source and
  361. * destination arrays can be manipulated anywhere along their length by
  362. * specifying <var>srcOffset</var> and <var>destOffset</var>. This method does
  363. * not check to make sure your arrays are large enough to accomodate
  364. * <var>srcOffset</var> + 3 for the <var>source</var> array or
  365. * <var>destOffset</var> + 4 for the <var>destination</var> array. The actual
  366. * number of significant bytes in your array is given by <var>numSigBytes</var>.
  367. * </p>
  368. * <p>
  369. * This is the lowest level of the encoding methods with all possible
  370. * parameters.
  371. * </p>
  372. *
  373. * @param source the array to convert
  374. * @param srcOffset the index where conversion begins
  375. * @param numSigBytes the number of significant bytes in your array
  376. * @param destination the array to hold the conversion
  377. * @param destOffset the index where output will be put
  378. * @return the <var>destination</var> array
  379. * @since 1.3
  380. */
  381. private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination,
  382. int destOffset) {
  383. byte[] ALPHABET = _STANDARD_ALPHABET;
  384. int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
  385. | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
  386. | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
  387. switch (numSigBytes) {
  388. case 3:
  389. destination[destOffset] = ALPHABET[(inBuff >>> 18)];
  390. destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
  391. destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
  392. destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
  393. return destination;
  394. case 2:
  395. destination[destOffset] = ALPHABET[(inBuff >>> 18)];
  396. destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
  397. destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
  398. destination[destOffset + 3] = EQUALS_SIGN;
  399. return destination;
  400. case 1:
  401. destination[destOffset] = ALPHABET[(inBuff >>> 18)];
  402. destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
  403. destination[destOffset + 2] = EQUALS_SIGN;
  404. destination[destOffset + 3] = EQUALS_SIGN;
  405. return destination;
  406. default:
  407. return destination;
  408. }
  409. }
  410. /**
  411. * Encode string as a byte array in Base64 annotation.
  412. *
  413. * @param string
  414. * @return The Base64-encoded data as a string
  415. */
  416. public static String encode(String string) {
  417. byte[] bytes;
  418. try {
  419. bytes = string.getBytes(PREFERRED_ENCODING);
  420. } catch (UnsupportedEncodingException e) {
  421. bytes = string.getBytes();
  422. }
  423. return encodeBytes(bytes);
  424. }
  425. /**
  426. * Encodes a byte array into Base64 notation.
  427. *
  428. * @param source The data to convert
  429. * @return The Base64-encoded data as a String
  430. * @throws NullPointerException if source array is null
  431. * @throws IllegalArgumentException if source array, offset, or length are
  432. * invalid
  433. * @since 2.0
  434. */
  435. public static String encodeBytes(byte[] source) {
  436. return encodeBytes(source, 0, source.length);
  437. }
  438. /**
  439. * Encodes a byte array into Base64 notation.
  440. *
  441. * @param source The data to convert
  442. * @param off Offset in array where conversion should begin
  443. * @param len Length of data to convert
  444. * @return The Base64-encoded data as a String
  445. * @throws NullPointerException if source array is null
  446. * @throws IllegalArgumentException if source array, offset, or length are
  447. * invalid
  448. * @since 2.0
  449. */
  450. public static String encodeBytes(byte[] source, int off, int len) {
  451. byte[] encoded = encodeBytesToBytes(source, off, len);
  452. try {
  453. return new String(encoded, PREFERRED_ENCODING);
  454. } catch (UnsupportedEncodingException uue) {
  455. return new String(encoded);
  456. }
  457. }
  458. /**
  459. * Similar to {@link #encodeBytes(byte[], int, int)} but returns a byte array
  460. * instead of instantiating a String. This is more efficient if you're working
  461. * with I/O streams and have large data sets to encode.
  462. *
  463. *
  464. * @param source The data to convert
  465. * @param off Offset in array where conversion should begin
  466. * @param len Length of data to convert
  467. * @return The Base64-encoded data as a String if there is an error
  468. * @throws NullPointerException if source array is null
  469. * @throws IllegalArgumentException if source array, offset, or length are
  470. * invalid
  471. * @since 2.3.1
  472. */
  473. public static byte[] encodeBytesToBytes(byte[] source, int off, int len) {
  474. if (source == null)
  475. throw new NullPointerException("Cannot serialize a null array.");
  476. if (off < 0)
  477. throw new IllegalArgumentException("Cannot have negative offset: " + off);
  478. if (len < 0)
  479. throw new IllegalArgumentException("Cannot have length offset: " + len);
  480. if (off + len > source.length)
  481. throw new IllegalArgumentException(String
  482. .format("Cannot have offset of %d and length of %d with array of length %d", off, len, source.length));
  483. // Bytes needed for actual encoding
  484. int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0);
  485. byte[] outBuff = new byte[encLen];
  486. int d = 0;
  487. int e = 0;
  488. int len2 = len - 2;
  489. for (; d < len2; d += 3, e += 4)
  490. encode3to4(source, d + off, 3, outBuff, e);
  491. if (d < len) {
  492. encode3to4(source, d + off, len - d, outBuff, e);
  493. e += 4;
  494. }
  495. if (e <= outBuff.length - 1) {
  496. byte[] finalOut = new byte[e];
  497. System.arraycopy(outBuff, 0, finalOut, 0, e);
  498. return finalOut;
  499. } else
  500. return outBuff;
  501. }
  502. }
  503. /**
  504. * HTTP request exception whose cause is always an {@link IOException}
  505. */
  506. public static class HttpRequestException extends RuntimeException {
  507. private static final long serialVersionUID = -1170466989781746231L;
  508. /**
  509. * Create a new HttpRequestException with the given cause
  510. *
  511. * @param cause
  512. */
  513. public HttpRequestException(final IOException cause) {
  514. super(cause);
  515. }
  516. /**
  517. * Get {@link IOException} that triggered this request exception
  518. *
  519. * @return {@link IOException} cause
  520. */
  521. @Override
  522. public IOException getCause() {
  523. return (IOException) super.getCause();
  524. }
  525. }
  526. /**
  527. * Operation that handles executing a callback once complete and handling nested
  528. * exceptions
  529. *
  530. * @param <V>
  531. */
  532. protected static abstract class Operation<V> implements Callable<V> {
  533. /**
  534. * Run operation
  535. *
  536. * @return result
  537. * @throws HttpRequestException
  538. * @throws IOException
  539. */
  540. protected abstract V run() throws HttpRequestException, IOException;
  541. /**
  542. * Operation complete callback
  543. *
  544. * @throws IOException
  545. */
  546. protected abstract void done() throws IOException;
  547. public V call() throws HttpRequestException {
  548. boolean thrown = false;
  549. try {
  550. return run();
  551. } catch (HttpRequestException e) {
  552. thrown = true;
  553. throw e;
  554. } catch (IOException e) {
  555. thrown = true;
  556. throw new HttpRequestException(e);
  557. } finally {
  558. try {
  559. done();
  560. } catch (IOException e) {
  561. if (!thrown)
  562. throw new HttpRequestException(e);
  563. }
  564. }
  565. }
  566. }
  567. /**
  568. * Class that ensures a {@link Closeable} gets closed with proper exception
  569. * handling.
  570. *
  571. * @param <V>
  572. */
  573. protected static abstract class CloseOperation<V> extends Operation<V> {
  574. private final Closeable closeable;
  575. private final boolean ignoreCloseExceptions;
  576. /**
  577. * Create closer for operation
  578. *
  579. * @param closeable
  580. * @param ignoreCloseExceptions
  581. */
  582. protected CloseOperation(final Closeable closeable, final boolean ignoreCloseExceptions) {
  583. this.closeable = closeable;
  584. this.ignoreCloseExceptions = ignoreCloseExceptions;
  585. }
  586. @Override
  587. protected void done() throws IOException {
  588. if (closeable instanceof Flushable)
  589. ((Flushable) closeable).flush();
  590. if (ignoreCloseExceptions)
  591. try {
  592. closeable.close();
  593. } catch (IOException e) {
  594. // Ignored
  595. }
  596. else
  597. closeable.close();
  598. }
  599. }
  600. /**
  601. * Class that and ensures a {@link Flushable} gets flushed with proper exception
  602. * handling.
  603. *
  604. * @param <V>
  605. */
  606. protected static abstract class FlushOperation<V> extends Operation<V> {
  607. private final Flushable flushable;
  608. /**
  609. * Create flush operation
  610. *
  611. * @param flushable
  612. */
  613. protected FlushOperation(final Flushable flushable) {
  614. this.flushable = flushable;
  615. }
  616. @Override
  617. protected void done() throws IOException {
  618. flushable.flush();
  619. }
  620. }
  621. /**
  622. * Request output stream
  623. */
  624. public static class RequestOutputStream extends BufferedOutputStream {
  625. private final CharsetEncoder encoder;
  626. /**
  627. * Create request output stream
  628. *
  629. * @param stream
  630. * @param charset
  631. * @param bufferSize
  632. */
  633. public RequestOutputStream(final OutputStream stream, final String charset, final int bufferSize) {
  634. super(stream, bufferSize);
  635. encoder = Charset.forName(getValidCharset(charset)).newEncoder();
  636. }
  637. /**
  638. * Write string to stream
  639. *
  640. * @param value
  641. * @return this stream
  642. * @throws IOException
  643. */
  644. public RequestOutputStream write(final String value) throws IOException {
  645. final ByteBuffer bytes = encoder.encode(CharBuffer.wrap(value));
  646. super.write(bytes.array(), 0, bytes.limit());
  647. return this;
  648. }
  649. }
  650. /**
  651. * Represents array of any type as list of objects so we can easily iterate over
  652. * it
  653. *
  654. * @param array of elements
  655. * @return list with the same elements
  656. */
  657. private static List<Object> arrayToList(final Object array) {
  658. if (array instanceof Object[])
  659. return Arrays.asList((Object[]) array);
  660. List<Object> result = new ArrayList<Object>();
  661. // Arrays of the primitive types can't be cast to array of Object, so this:
  662. if (array instanceof int[])
  663. for (int value : (int[]) array)
  664. result.add(value);
  665. else if (array instanceof boolean[])
  666. for (boolean value : (boolean[]) array)
  667. result.add(value);
  668. else if (array instanceof long[])
  669. for (long value : (long[]) array)
  670. result.add(value);
  671. else if (array instanceof float[])
  672. for (float value : (float[]) array)
  673. result.add(value);
  674. else if (array instanceof double[])
  675. for (double value : (double[]) array)
  676. result.add(value);
  677. else if (array instanceof short[])
  678. for (short value : (short[]) array)
  679. result.add(value);
  680. else if (array instanceof byte[])
  681. for (byte value : (byte[]) array)
  682. result.add(value);
  683. else if (array instanceof char[])
  684. for (char value : (char[]) array)
  685. result.add(value);
  686. return result;
  687. }
  688. /**
  689. * Encode the given URL as an ASCII {@link String}
  690. * <p>
  691. * This method ensures the path and query segments of the URL are properly
  692. * encoded such as ' ' characters being encoded to '%20' or any UTF-8 characters
  693. * that are non-ASCII. No encoding of URLs is done by default by the
  694. * {@link HttpRequest} constructors and so if URL encoding is needed this method
  695. * should be called before calling the {@link HttpRequest} constructor.
  696. *
  697. * @param url
  698. * @return encoded URL
  699. * @throws HttpRequestException
  700. */
  701. public static String encode(final CharSequence url) throws HttpRequestException {
  702. URL parsed;
  703. try {
  704. parsed = new URL(url.toString());
  705. } catch (IOException e) {
  706. throw new HttpRequestException(e);
  707. }
  708. String host = parsed.getHost();
  709. int port = parsed.getPort();
  710. if (port != -1)
  711. host = host + ':' + Integer.toString(port);
  712. try {
  713. String encoded = new URI(parsed.getProtocol(), host, parsed.getPath(), parsed.getQuery(), null).toASCIIString();
  714. int paramsStart = encoded.indexOf('?');
  715. if (paramsStart > 0 && paramsStart + 1 < encoded.length())
  716. encoded = encoded.substring(0, paramsStart + 1) + encoded.substring(paramsStart + 1).replace("+", "%2B");
  717. return encoded;
  718. } catch (URISyntaxException e) {
  719. IOException io = new IOException("Parsing URI failed");
  720. io.initCause(e);
  721. throw new HttpRequestException(io);
  722. }
  723. }
  724. /**
  725. * Append given map as query parameters to the base URL
  726. * <p>
  727. * Each map entry's key will be a parameter name and the value's
  728. * {@link Object#toString()} will be the parameter value.
  729. *
  730. * @param url
  731. * @param params
  732. * @return URL with appended query params
  733. */
  734. public static String append(final CharSequence url, final Map<?, ?> params) {
  735. final String baseUrl = url.toString();
  736. if (params == null || params.isEmpty())
  737. return baseUrl;
  738. final StringBuilder result = new StringBuilder(baseUrl);
  739. addPathSeparator(baseUrl, result);
  740. addParamPrefix(baseUrl, result);
  741. Entry<?, ?> entry;
  742. Iterator<?> iterator = params.entrySet().iterator();
  743. entry = (Entry<?, ?>) iterator.next();
  744. addParam(entry.getKey().toString(), entry.getValue(), result);
  745. while (iterator.hasNext()) {
  746. result.append('&');
  747. entry = (Entry<?, ?>) iterator.next();
  748. addParam(entry.getKey().toString(), entry.getValue(), result);
  749. }
  750. return result.toString();
  751. }
  752. /**
  753. * Append given name/value pairs as query parameters to the base URL
  754. * <p>
  755. * The params argument is interpreted as a sequence of name/value pairs so the
  756. * given number of params must be divisible by 2.
  757. *
  758. * @param url
  759. * @param params name/value pairs
  760. * @return URL with appended query params
  761. */
  762. public static String append(final CharSequence url, final Object... params) {
  763. final String baseUrl = url.toString();
  764. if (params == null || params.length == 0)
  765. return baseUrl;
  766. if (params.length % 2 != 0)
  767. throw new IllegalArgumentException("Must specify an even number of parameter names/values");
  768. final StringBuilder result = new StringBuilder(baseUrl);
  769. addPathSeparator(baseUrl, result);
  770. addParamPrefix(baseUrl, result);
  771. addParam(params[0], params[1], result);
  772. for (int i = 2; i < params.length; i += 2) {
  773. result.append('&');
  774. addParam(params[i], params[i + 1], result);
  775. }
  776. return result.toString();
  777. }
  778. /**
  779. * Start a 'GET' request to the given URL
  780. *
  781. * @param url
  782. * @return request
  783. * @throws HttpRequestException
  784. */
  785. public static HttpRequest get(final CharSequence url) throws HttpRequestException {
  786. return new HttpRequest(url, METHOD_GET);
  787. }
  788. /**
  789. * Start a 'GET' request to the given URL
  790. *
  791. * @param url
  792. * @return request
  793. * @throws HttpRequestException
  794. */
  795. public static HttpRequest get(final URL url) throws HttpRequestException {
  796. return new HttpRequest(url, METHOD_GET);
  797. }
  798. /**
  799. * Start a 'GET' request to the given URL along with the query params
  800. *
  801. * @param baseUrl
  802. * @param params The query parameters to include as part of the baseUrl
  803. * @param encode true to encode the full URL
  804. *
  805. * @see #append(CharSequence, Map)
  806. * @see #encode(CharSequence)
  807. *
  808. * @return request
  809. */
  810. public static HttpRequest get(final CharSequence baseUrl, final Map<?, ?> params, final boolean encode) {
  811. String url = append(baseUrl, params);
  812. return get(encode ? encode(url) : url);
  813. }
  814. /**
  815. * Start a 'GET' request to the given URL along with the query params
  816. *
  817. * @param baseUrl
  818. * @param encode true to encode the full URL
  819. * @param params the name/value query parameter pairs to include as part of the
  820. * baseUrl
  821. *
  822. * @see #append(CharSequence, Object...)
  823. * @see #encode(CharSequence)
  824. *
  825. * @return request
  826. */
  827. public static HttpRequest get(final CharSequence baseUrl, final boolean encode, final Object... params) {
  828. String url = append(baseUrl, params);
  829. return get(encode ? encode(url) : url);
  830. }
  831. /**
  832. * Start a 'POST' request to the given URL
  833. *
  834. * @param url
  835. * @return request
  836. * @throws HttpRequestException
  837. */
  838. public static HttpRequest post(final CharSequence url) throws HttpRequestException {
  839. return new HttpRequest(url, METHOD_POST);
  840. }
  841. /**
  842. * Start a 'POST' request to the given URL
  843. *
  844. * @param url
  845. * @return request
  846. * @throws HttpRequestException
  847. */
  848. public static HttpRequest post(final URL url) throws HttpRequestException {
  849. return new HttpRequest(url, METHOD_POST);
  850. }
  851. /**
  852. * Start a 'POST' request to the given URL along with the query params
  853. *
  854. * @param baseUrl
  855. * @param params the query parameters to include as part of the baseUrl
  856. * @param encode true to encode the full URL
  857. *
  858. * @see #append(CharSequence, Map)
  859. * @see #encode(CharSequence)
  860. *
  861. * @return request
  862. */
  863. public static HttpRequest post(final CharSequence baseUrl, final Map<?, ?> params, final boolean encode) {
  864. String url = append(baseUrl, params);
  865. return post(encode ? encode(url) : url);
  866. }
  867. /**
  868. * Start a 'POST' request to the given URL along with the query params
  869. *
  870. * @param baseUrl
  871. * @param encode true to encode the full URL
  872. * @param params the name/value query parameter pairs to include as part of the
  873. * baseUrl
  874. *
  875. * @see #append(CharSequence, Object...)
  876. * @see #encode(CharSequence)
  877. *
  878. * @return request
  879. */
  880. public static HttpRequest post(final CharSequence baseUrl, final boolean encode, final Object... params) {
  881. String url = append(baseUrl, params);
  882. return post(encode ? encode(url) : url);
  883. }
  884. /**
  885. * Start a 'PUT' request to the given URL
  886. *
  887. * @param url
  888. * @return request
  889. * @throws HttpRequestException
  890. */
  891. public static HttpRequest put(final CharSequence url) throws HttpRequestException {
  892. return new HttpRequest(url, METHOD_PUT);
  893. }
  894. /**
  895. * Start a 'PUT' request to the given URL
  896. *
  897. * @param url
  898. * @return request
  899. * @throws HttpRequestException
  900. */
  901. public static HttpRequest put(final URL url) throws HttpRequestException {
  902. return new HttpRequest(url, METHOD_PUT);
  903. }
  904. /**
  905. * Start a 'PUT' request to the given URL along with the query params
  906. *
  907. * @param baseUrl
  908. * @param params the query parameters to include as part of the baseUrl
  909. * @param encode true to encode the full URL
  910. *
  911. * @see #append(CharSequence, Map)
  912. * @see #encode(CharSequence)
  913. *
  914. * @return request
  915. */
  916. public static HttpRequest put(final CharSequence baseUrl, final Map<?, ?> params, final boolean encode) {
  917. String url = append(baseUrl, params);
  918. return put(encode ? encode(url) : url);
  919. }
  920. /**
  921. * Start a 'PUT' request to the given URL along with the query params
  922. *
  923. * @param baseUrl
  924. * @param encode true to encode the full URL
  925. * @param params the name/value query parameter pairs to include as part of the
  926. * baseUrl
  927. *
  928. * @see #append(CharSequence, Object...)
  929. * @see #encode(CharSequence)
  930. *
  931. * @return request
  932. */
  933. public static HttpRequest put(final CharSequence baseUrl, final boolean encode, final Object... params) {
  934. String url = append(baseUrl, params);
  935. return put(encode ? encode(url) : url);
  936. }
  937. /**
  938. * Start a 'DELETE' request to the given URL
  939. *
  940. * @param url
  941. * @return request
  942. * @throws HttpRequestException
  943. */
  944. public static HttpRequest delete(final CharSequence url) throws HttpRequestException {
  945. return new HttpRequest(url, METHOD_DELETE);
  946. }
  947. /**
  948. * Start a 'DELETE' request to the given URL
  949. *
  950. * @param url
  951. * @return request
  952. * @throws HttpRequestException
  953. */
  954. public static HttpRequest delete(final URL url) throws HttpRequestException {
  955. return new HttpRequest(url, METHOD_DELETE);
  956. }
  957. /**
  958. * Start a 'DELETE' request to the given URL along with the query params
  959. *
  960. * @param baseUrl
  961. * @param params The query parameters to include as part of the baseUrl
  962. * @param encode true to encode the full URL
  963. *
  964. * @see #append(CharSequence, Map)
  965. * @see #encode(CharSequence)
  966. *
  967. * @return request
  968. */
  969. public static HttpRequest delete(final CharSequence baseUrl, final Map<?, ?> params, final boolean encode) {
  970. String url = append(baseUrl, params);
  971. return delete(encode ? encode(url) : url);
  972. }
  973. /**
  974. * Start a 'DELETE' request to the given URL along with the query params
  975. *
  976. * @param baseUrl
  977. * @param encode true to encode the full URL
  978. * @param params the name/value query parameter pairs to include as part of the
  979. * baseUrl
  980. *
  981. * @see #append(CharSequence, Object...)
  982. * @see #encode(CharSequence)
  983. *
  984. * @return request
  985. */
  986. public static HttpRequest delete(final CharSequence baseUrl, final boolean encode, final Object... params) {
  987. String url = append(baseUrl, params);
  988. return delete(encode ? encode(url) : url);
  989. }
  990. /**
  991. * Start a 'HEAD' request to the given URL
  992. *
  993. * @param url
  994. * @return request
  995. * @throws HttpRequestException
  996. */
  997. public static HttpRequest head(final CharSequence url) throws HttpRequestException {
  998. return new HttpRequest(url, METHOD_HEAD);
  999. }
  1000. /**
  1001. * Start a 'HEAD' request to the given URL
  1002. *
  1003. * @param url
  1004. * @return request
  1005. * @throws HttpRequestException
  1006. */
  1007. public static HttpRequest head(final URL url) throws HttpRequestException {
  1008. return new HttpRequest(url, METHOD_HEAD);
  1009. }
  1010. /**
  1011. * Start a 'HEAD' request to the given URL along with the query params
  1012. *
  1013. * @param baseUrl
  1014. * @param params The query parameters to include as part of the baseUrl
  1015. * @param encode true to encode the full URL
  1016. *
  1017. * @see #append(CharSequence, Map)
  1018. * @see #encode(CharSequence)
  1019. *
  1020. * @return request
  1021. */
  1022. public static HttpRequest head(final CharSequence baseUrl, final Map<?, ?> params, final boolean encode) {
  1023. String url = append(baseUrl, params);
  1024. return head(encode ? encode(url) : url);
  1025. }
  1026. /**
  1027. * Start a 'GET' request to the given URL along with the query params
  1028. *
  1029. * @param baseUrl
  1030. * @param encode true to encode the full URL
  1031. * @param params the name/value query parameter pairs to include as part of the
  1032. * baseUrl
  1033. *
  1034. * @see #append(CharSequence, Object...)
  1035. * @see #encode(CharSequence)
  1036. *
  1037. * @return request
  1038. */
  1039. public static HttpRequest head(final CharSequence baseUrl, final boolean encode, final Object... params) {
  1040. String url = append(baseUrl, params);
  1041. return head(encode ? encode(url) : url);
  1042. }
  1043. /**
  1044. * Start an 'OPTIONS' request to the given URL
  1045. *
  1046. * @param url
  1047. * @return request
  1048. * @throws HttpRequestException
  1049. */
  1050. public static HttpRequest options(final CharSequence url) throws HttpRequestException {
  1051. return new HttpRequest(url, METHOD_OPTIONS);
  1052. }
  1053. /**
  1054. * Start an 'OPTIONS' request to the given URL
  1055. *
  1056. * @param url
  1057. * @return request
  1058. * @throws HttpRequestException
  1059. */
  1060. public static HttpRequest options(final URL url) throws HttpRequestException {
  1061. return new HttpRequest(url, METHOD_OPTIONS);
  1062. }
  1063. /**
  1064. * Start a 'TRACE' request to the given URL
  1065. *
  1066. * @param url
  1067. * @return request
  1068. * @throws HttpRequestException
  1069. */
  1070. public static HttpRequest trace(final CharSequence url) throws HttpRequestException {
  1071. return new HttpRequest(url, METHOD_TRACE);
  1072. }
  1073. /**
  1074. * Start a 'TRACE' request to the given URL
  1075. *
  1076. * @param url
  1077. * @return request
  1078. * @throws HttpRequestException
  1079. */
  1080. public static HttpRequest trace(final URL url) throws HttpRequestException {
  1081. return new HttpRequest(url, METHOD_TRACE);
  1082. }
  1083. /**
  1084. * Set the 'http.keepAlive' property to the given value.
  1085. * <p>
  1086. * This setting will apply to all requests.
  1087. *
  1088. * @param keepAlive
  1089. */
  1090. public static void keepAlive(final boolean keepAlive) {
  1091. setProperty("http.keepAlive", Boolean.toString(keepAlive));
  1092. }
  1093. /**
  1094. * Set the 'http.maxConnections' property to the given value.
  1095. * <p>
  1096. * This setting will apply to all requests.
  1097. *
  1098. * @param maxConnections
  1099. */
  1100. public static void maxConnections(final int maxConnections) {
  1101. setProperty("http.maxConnections", Integer.toString(maxConnections));
  1102. }
  1103. /**
  1104. * Set the 'http.proxyHost' and 'https.proxyHost' properties to the given host
  1105. * value.
  1106. * <p>
  1107. * This setting will apply to all requests.
  1108. *
  1109. * @param host
  1110. */
  1111. public static void proxyHost(final String host) {
  1112. setProperty("http.proxyHost", host);
  1113. setProperty("https.proxyHost", host);
  1114. }
  1115. /**
  1116. * Set the 'http.proxyPort' and 'https.proxyPort' properties to the given port
  1117. * number.
  1118. * <p>
  1119. * This setting will apply to all requests.
  1120. *
  1121. * @param port
  1122. */
  1123. public static void proxyPort(final int port) {
  1124. final String portValue = Integer.toString(port);
  1125. setProperty("http.proxyPort", portValue);
  1126. setProperty("https.proxyPort", portValue);
  1127. }
  1128. /**
  1129. * Set the 'http.nonProxyHosts' property to the given host values.
  1130. * <p>
  1131. * Hosts will be separated by a '|' character.
  1132. * <p>
  1133. * This setting will apply to all requests.
  1134. *
  1135. * @param hosts
  1136. */
  1137. public static void nonProxyHosts(final String... hosts) {
  1138. if (hosts != null && hosts.length > 0) {
  1139. StringBuilder separated = new StringBuilder();
  1140. int last = hosts.length - 1;
  1141. for (int i = 0; i < last; i++)
  1142. separated.append(hosts[i]).append('|');
  1143. separated.append(hosts[last]);
  1144. setProperty("http.nonProxyHosts", separated.toString());
  1145. } else
  1146. setProperty("http.nonProxyHosts", null);
  1147. }
  1148. /**
  1149. * Set property to given value.
  1150. * <p>
  1151. * Specifying a null value will cause the property to be cleared
  1152. *
  1153. * @param name
  1154. * @param value
  1155. * @return previous value
  1156. */
  1157. private static String setProperty(final String name, final String value) {
  1158. final PrivilegedAction<String> action;
  1159. if (value != null)
  1160. action = new PrivilegedAction<String>() {
  1161. public String run() {
  1162. return System.setProperty(name, value);
  1163. }
  1164. };
  1165. else
  1166. action = new PrivilegedAction<String>() {
  1167. public String run() {
  1168. return System.clearProperty(name);
  1169. }
  1170. };
  1171. return AccessController.doPrivileged(action);
  1172. }
  1173. private HttpURLConnection connection = null;
  1174. private final URL url;
  1175. private final String requestMethod;
  1176. private RequestOutputStream output;
  1177. private boolean multipart;
  1178. private boolean form;
  1179. private boolean ignoreCloseExceptions = true;
  1180. private boolean uncompress = false;
  1181. private int bufferSize = 8192;
  1182. private long totalSize = -1;
  1183. private long totalWritten = 0;
  1184. private String httpProxyHost;
  1185. private int httpProxyPort;
  1186. private UploadProgress progress = UploadProgress.DEFAULT;
  1187. /**
  1188. * Create HTTP connection wrapper
  1189. *
  1190. * @param url Remote resource URL.
  1191. * @param method HTTP request method (e.g., "GET", "POST").
  1192. * @throws HttpRequestException
  1193. */
  1194. public HttpRequest(final CharSequence url, final String method) throws HttpRequestException {
  1195. try {
  1196. this.url = new URL(url.toString());
  1197. } catch (MalformedURLException e) {
  1198. throw new HttpRequestException(e);
  1199. }
  1200. this.requestMethod = method;
  1201. }
  1202. /**
  1203. * Create HTTP connection wrapper
  1204. *
  1205. * @param url Remote resource URL.
  1206. * @param method HTTP request method (e.g., "GET", "POST").
  1207. * @throws HttpRequestException
  1208. */
  1209. public HttpRequest(final URL url, final String method) throws HttpRequestException {
  1210. this.url = url;
  1211. this.requestMethod = method;
  1212. }
  1213. private Proxy createProxy() {
  1214. return new Proxy(HTTP, new InetSocketAddress(httpProxyHost, httpProxyPort));
  1215. }
  1216. private HttpURLConnection createConnection() {
  1217. try {
  1218. final HttpURLConnection connection;
  1219. if (httpProxyHost != null)
  1220. connection = CONNECTION_FACTORY.create(url, createProxy());
  1221. else
  1222. connection = CONNECTION_FACTORY.create(url);
  1223. connection.setRequestMethod(requestMethod);
  1224. return connection;
  1225. } catch (IOException e) {
  1226. throw new HttpRequestException(e);
  1227. }
  1228. }
  1229. @Override
  1230. public String toString() {
  1231. return method() + ' ' + url();
  1232. }
  1233. /**
  1234. * Get underlying connection
  1235. *
  1236. * @return connection
  1237. */
  1238. public HttpURLConnection getConnection() {
  1239. if (connection == null)
  1240. connection = createConnection();
  1241. return connection;
  1242. }
  1243. /**
  1244. * Set whether or not to ignore exceptions that occur from calling
  1245. * {@link Closeable#close()}
  1246. * <p>
  1247. * The default value of this setting is <code>true</code>
  1248. *
  1249. * @param ignore
  1250. * @return this request
  1251. */
  1252. public HttpRequest ignoreCloseExceptions(final boolean ignore) {
  1253. ignoreCloseExceptions = ignore;
  1254. return this;
  1255. }
  1256. /**
  1257. * Get whether or not exceptions thrown by {@link Closeable#close()} are ignored
  1258. *
  1259. * @return true if ignoring, false if throwing
  1260. */
  1261. public boolean ignoreCloseExceptions() {
  1262. return ignoreCloseExceptions;
  1263. }
  1264. /**
  1265. * Get the status code of the response
  1266. *
  1267. * @return the response code
  1268. * @throws HttpRequestException
  1269. */
  1270. public int code() throws HttpRequestException {
  1271. try {
  1272. closeOutput();
  1273. return getConnection().getResponseCode();
  1274. } catch (IOException e) {
  1275. throw new HttpRequestException(e);
  1276. }
  1277. }
  1278. /**
  1279. * Set the value of the given {@link AtomicInteger} to the status code of the
  1280. * response
  1281. *
  1282. * @param output
  1283. * @return this request
  1284. * @throws HttpRequestException
  1285. */
  1286. public HttpRequest code(final AtomicInteger output) throws HttpRequestException {
  1287. output.set(code());
  1288. return this;
  1289. }
  1290. /**
  1291. * Is the response code a 200 OK?
  1292. *
  1293. * @return true if 200, false otherwise
  1294. * @throws HttpRequestException
  1295. */
  1296. public boolean ok() throws HttpRequestException {
  1297. return HTTP_OK == code();
  1298. }
  1299. /**
  1300. * Is the response code a 201 Created?
  1301. *
  1302. * @return true if 201, false otherwise
  1303. * @throws HttpRequestException
  1304. */
  1305. public boolean created() throws HttpRequestException {
  1306. return HTTP_CREATED == code();
  1307. }
  1308. /**
  1309. * Is the response code a 204 No Content?
  1310. *
  1311. * @return true if 204, false otherwise
  1312. * @throws HttpRequestException
  1313. */
  1314. public boolean noContent() throws HttpRequestException {
  1315. return HTTP_NO_CONTENT == code();
  1316. }
  1317. /**
  1318. * Is the response code a 500 Internal Server Error?
  1319. *
  1320. * @return true if 500, false otherwise
  1321. * @throws HttpRequestException
  1322. */
  1323. public boolean serverError() throws HttpRequestException {
  1324. return HTTP_INTERNAL_ERROR == code();
  1325. }
  1326. /**
  1327. * Is the response code a 400 Bad Request?
  1328. *
  1329. * @return true if 400, false otherwise
  1330. * @throws HttpRequestException
  1331. */
  1332. public boolean badRequest() throws HttpRequestException {
  1333. return HTTP_BAD_REQUEST == code();
  1334. }
  1335. /**
  1336. * Is the response code a 404 Not Found?
  1337. *
  1338. * @return true if 404, false otherwise
  1339. * @throws HttpRequestException
  1340. */
  1341. public boolean notFound() throws HttpRequestException {
  1342. return HTTP_NOT_FOUND == code();
  1343. }
  1344. /**
  1345. * Is the response code a 304 Not Modified?
  1346. *
  1347. * @return true if 304, false otherwise
  1348. * @throws HttpRequestException
  1349. */
  1350. public boolean notModified() throws HttpRequestException {
  1351. return HTTP_NOT_MODIFIED == code();
  1352. }
  1353. /**
  1354. * Get status message of the response
  1355. *
  1356. * @return message
  1357. * @throws HttpRequestException
  1358. */
  1359. public String message() throws HttpRequestException {
  1360. try {
  1361. closeOutput();
  1362. return getConnection().getResponseMessage();
  1363. } catch (IOException e) {
  1364. throw new HttpRequestException(e);
  1365. }
  1366. }
  1367. /**
  1368. * Disconnect the connection
  1369. *
  1370. * @return this request
  1371. */
  1372. public HttpRequest disconnect() {
  1373. getConnection().disconnect();
  1374. return this;
  1375. }
  1376. /**
  1377. * Set chunked streaming mode to the given size
  1378. *
  1379. * @param size
  1380. * @return this request
  1381. */
  1382. public HttpRequest chunk(final int size) {
  1383. getConnection().setChunkedStreamingMode(size);
  1384. return this;
  1385. }
  1386. /**
  1387. * Set the size used when buffering and copying between streams
  1388. * <p>
  1389. * This size is also used for send and receive buffers created for both char and
  1390. * byte arrays
  1391. * <p>
  1392. * The default buffer size is 8,192 bytes
  1393. *
  1394. * @param size
  1395. * @return this request
  1396. */
  1397. public HttpRequest bufferSize(final int size) {
  1398. if (size < 1)
  1399. throw new IllegalArgumentException("Size must be greater than zero");
  1400. bufferSize = size;
  1401. return this;
  1402. }
  1403. /**
  1404. * Get the configured buffer size
  1405. * <p>
  1406. * The default buffer size is 8,192 bytes
  1407. *
  1408. * @return buffer size
  1409. */
  1410. public int bufferSize() {
  1411. return bufferSize;
  1412. }
  1413. /**
  1414. * Set whether or not the response body should be automatically uncompressed
  1415. * when read from.
  1416. * <p>
  1417. * This will only affect requests that have the 'Content-Encoding' response
  1418. * header set to 'gzip'.
  1419. * <p>
  1420. * This causes all receive methods to use a {@link GZIPInputStream} when
  1421. * applicable so that higher level streams and readers can read the data
  1422. * uncompressed.
  1423. * <p>
  1424. * Setting this option does not cause any request headers to be set
  1425. * automatically so {@link #acceptGzipEncoding()} should be used in conjunction
  1426. * with this setting to tell the server to gzip the response.
  1427. *
  1428. * @param uncompress
  1429. * @return this request
  1430. */
  1431. public HttpRequest uncompress(final boolean uncompress) {
  1432. this.uncompress = uncompress;
  1433. return this;
  1434. }
  1435. /**
  1436. * Create byte array output stream
  1437. *
  1438. * @return stream
  1439. */
  1440. protected ByteArrayOutputStream byteStream() {
  1441. final int size = contentLength();
  1442. if (size > 0)
  1443. return new ByteArrayOutputStream(size);
  1444. else
  1445. return new ByteArrayOutputStream();
  1446. }
  1447. /**
  1448. * Get response as {@link String} in given character set
  1449. * <p>
  1450. * This will fall back to using the UTF-8 character set if the given charset is
  1451. * null
  1452. *
  1453. * @param charset
  1454. * @return string
  1455. * @throws HttpRequestException
  1456. */
  1457. public String body(final String charset) throws HttpRequestException {
  1458. final ByteArrayOutputStream output = byteStream();
  1459. try {
  1460. copy(buffer(), output);
  1461. return output.toString(getValidCharset(charset));
  1462. } catch (IOException e) {
  1463. throw new HttpRequestException(e);
  1464. }
  1465. }
  1466. /**
  1467. * Get response as {@link String} using character set returned from
  1468. * {@link #charset()}
  1469. *
  1470. * @return string
  1471. * @throws HttpRequestException
  1472. */
  1473. public String body() throws HttpRequestException {
  1474. return body(charset());
  1475. }
  1476. /**
  1477. * Get the response body as a {@link String} and set it as the value of the
  1478. * given reference.
  1479. *
  1480. * @param output
  1481. * @return this request
  1482. * @throws HttpRequestException
  1483. */
  1484. public HttpRequest body(final AtomicReference<String> output) throws HttpRequestException {
  1485. output.set(body());
  1486. return this;
  1487. }
  1488. /**
  1489. * Get the response body as a {@link String} and set it as the value of the
  1490. * given reference.
  1491. *
  1492. * @param output
  1493. * @param charset
  1494. * @return this request
  1495. * @throws HttpRequestException
  1496. */
  1497. public HttpRequest body(final AtomicReference<String> output, final String charset) throws HttpRequestException {
  1498. output.set(body(charset));
  1499. return this;
  1500. }
  1501. /**
  1502. * Is the response body empty?
  1503. *
  1504. * @return true if the Content-Length response header is 0, false otherwise
  1505. * @throws HttpRequestException
  1506. */
  1507. public boolean isBodyEmpty() throws HttpRequestException {
  1508. return contentLength() == 0;
  1509. }
  1510. /**
  1511. * Get response as byte array
  1512. *
  1513. * @return byte array
  1514. * @throws HttpRequestException
  1515. */
  1516. public byte[] bytes() throws HttpRequestException {
  1517. final ByteArrayOutputStream output = byteStream();
  1518. try {
  1519. copy(buffer(), output);
  1520. } catch (IOException e) {
  1521. throw new HttpRequestException(e);
  1522. }
  1523. return output.toByteArray();
  1524. }
  1525. /**
  1526. * Get response in a buffered stream
  1527. *
  1528. * @see #bufferSize(int)
  1529. * @return stream
  1530. * @throws HttpRequestException
  1531. */
  1532. public BufferedInputStream buffer() throws HttpRequestException {
  1533. return new BufferedInputStream(stream(), bufferSize);
  1534. }
  1535. /**
  1536. * Get stream to response body
  1537. *
  1538. * @return stream
  1539. * @throws HttpRequestException
  1540. */
  1541. public InputStream stream() throws HttpRequestException {
  1542. InputStream stream;
  1543. if (code() < HTTP_BAD_REQUEST)
  1544. try {
  1545. stream = getConnection().getInputStream();
  1546. } catch (IOException e) {
  1547. throw new HttpRequestException(e);
  1548. }
  1549. else {
  1550. stream = getConnection().getErrorStream();
  1551. if (stream == null)
  1552. try {
  1553. stream = getConnection().getInputStream();
  1554. } catch (IOException e) {
  1555. if (contentLength() > 0)
  1556. throw new HttpRequestException(e);
  1557. else
  1558. stream = new ByteArrayInputStream(new byte[0]);
  1559. }
  1560. }
  1561. if (!uncompress || !ENCODING_GZIP.equals(contentEncoding()))
  1562. return stream;
  1563. else
  1564. try {
  1565. return new GZIPInputStream(stream);
  1566. } catch (IOException e) {
  1567. throw new HttpRequestException(e);
  1568. }
  1569. }
  1570. /**
  1571. * Get reader to response body using given character set.
  1572. * <p>
  1573. * This will fall back to using the UTF-8 character set if the given charset is
  1574. * null
  1575. *
  1576. * @param charset
  1577. * @return reader
  1578. * @throws HttpRequestException
  1579. */
  1580. public InputStreamReader reader(final String charset) throws HttpRequestException {
  1581. try {
  1582. return new InputStreamReader(stream(), getValidCharset(charset));
  1583. } catch (UnsupportedEncodingException e) {
  1584. throw new HttpRequestException(e);
  1585. }
  1586. }
  1587. /**
  1588. * Get reader to response body using the character set returned from
  1589. * {@link #charset()}
  1590. *
  1591. * @return reader
  1592. * @throws HttpRequestException
  1593. */
  1594. public InputStreamReader reader() throws HttpRequestException {
  1595. return reader(charset());
  1596. }
  1597. /**
  1598. * Get buffered reader to response body using the given character set r and the
  1599. * configured buffer size
  1600. *
  1601. *
  1602. * @see #bufferSize(int)
  1603. * @param charset
  1604. * @return reader
  1605. * @throws HttpRequestException
  1606. */
  1607. public BufferedReader bufferedReader(final String charset) throws HttpRequestException {
  1608. return new BufferedReader(reader(charset), bufferSize);
  1609. }
  1610. /**
  1611. * Get buffered reader to response body using the character set returned from
  1612. * {@link #charset()} and the configured buffer size
  1613. *
  1614. * @see #bufferSize(int)
  1615. * @return reader
  1616. * @throws HttpRequestException
  1617. */
  1618. public BufferedReader bufferedReader() throws HttpRequestException {
  1619. return bufferedReader(charset());
  1620. }
  1621. /**
  1622. * Stream response body to file
  1623. *
  1624. * @param file
  1625. * @return this request
  1626. * @throws HttpRequestException
  1627. */
  1628. public HttpRequest receive(final File file) throws HttpRequestException {
  1629. final OutputStream output;
  1630. try {
  1631. output = new BufferedOutputStream(new FileOutputStream(file), bufferSize);
  1632. } catch (FileNotFoundException e) {
  1633. throw new HttpRequestException(e);
  1634. }
  1635. return new CloseOperation<HttpRequest>(output, ignoreCloseExceptions) {
  1636. @Override
  1637. protected HttpRequest run() throws HttpRequestException, IOException {
  1638. return receive(output);
  1639. }
  1640. }.call();
  1641. }
  1642. /**
  1643. * Stream response to given output stream
  1644. *
  1645. * @param output
  1646. * @return this request
  1647. * @throws HttpRequestException
  1648. */
  1649. public HttpRequest receive(final OutputStream output) throws HttpRequestException {
  1650. try {
  1651. return copy(buffer(), output);
  1652. } catch (IOException e) {
  1653. throw new HttpRequestException(e);
  1654. }
  1655. }
  1656. /**
  1657. * Stream response to given print stream
  1658. *
  1659. * @param output
  1660. * @return this request
  1661. * @throws HttpRequestException
  1662. */
  1663. public HttpRequest receive(final PrintStream output) throws HttpRequestException {
  1664. return receive((OutputStream) output);
  1665. }
  1666. /**
  1667. * Receive response into the given appendable
  1668. *
  1669. * @param appendable
  1670. * @return this request
  1671. * @throws HttpRequestException
  1672. */
  1673. public HttpRequest receive(final Appendable appendable) throws HttpRequestException {
  1674. final BufferedReader reader = bufferedReader();
  1675. return new CloseOperation<HttpRequest>(reader, ignoreCloseExceptions) {
  1676. @Override
  1677. public HttpRequest run() throws IOException {
  1678. final CharBuffer buffer = CharBuffer.allocate(bufferSize);
  1679. int read;
  1680. while ((read = reader.read(buffer)) != -1) {
  1681. buffer.rewind();
  1682. appendable.append(buffer, 0, read);
  1683. buffer.rewind();
  1684. }
  1685. return HttpRequest.this;
  1686. }
  1687. }.call();
  1688. }
  1689. /**
  1690. * Receive response into the given writer
  1691. *
  1692. * @param writer
  1693. * @return this request
  1694. * @throws HttpRequestException
  1695. */
  1696. public HttpRequest receive(final Writer writer) throws HttpRequestException {
  1697. final BufferedReader reader = bufferedReader();
  1698. return new CloseOperation<HttpRequest>(reader, ignoreCloseExceptions) {
  1699. @Override
  1700. public HttpRequest run() throws IOException {
  1701. return copy(reader, writer);
  1702. }
  1703. }.call();
  1704. }
  1705. /**
  1706. * Set read timeout on connection to given value
  1707. *
  1708. * @param timeout
  1709. * @return this request
  1710. */
  1711. public HttpRequest readTimeout(final int timeout) {
  1712. getConnection().setReadTimeout(timeout);
  1713. return this;
  1714. }
  1715. /**
  1716. * Set connect timeout on connection to given value
  1717. *
  1718. * @param timeout
  1719. * @return this request
  1720. */
  1721. public HttpRequest connectTimeout(final int timeout) {
  1722. getConnection().setConnectTimeout(timeout);
  1723. return this;
  1724. }
  1725. /**
  1726. * Set header name to given value
  1727. *
  1728. * @param name
  1729. * @param value
  1730. * @return this request
  1731. */
  1732. public HttpRequest header(final String name, final String value) {
  1733. getConnection().setRequestProperty(name, value);
  1734. return this;
  1735. }
  1736. /**
  1737. * Set header name to given value
  1738. *
  1739. * @param name
  1740. * @param value
  1741. * @return this request
  1742. */
  1743. public HttpRequest header(final String name, final Number value) {
  1744. return header(name, value != null ? value.toString() : null);
  1745. }
  1746. /**
  1747. * Set all headers found in given map where the keys are the header names and
  1748. * the values are the header values
  1749. *
  1750. * @param headers
  1751. * @return this request
  1752. */
  1753. public HttpRequest headers(final Map<String, String> headers) {
  1754. if (!headers.isEmpty())
  1755. for (Entry<String, String> header : headers.entrySet())
  1756. header(header);
  1757. return this;
  1758. }
  1759. /**
  1760. * Set header to have given entry's key as the name and value as the value
  1761. *
  1762. * @param header
  1763. * @return this request
  1764. */
  1765. public HttpRequest header(final Entry<String, String> header) {
  1766. return header(header.getKey(), header.getValue());
  1767. }
  1768. /**
  1769. * Get a response header
  1770. *
  1771. * @param name
  1772. * @return response header
  1773. * @throws HttpRequestException
  1774. */
  1775. public String header(final String name) throws HttpRequestException {
  1776. closeOutputQuietly();
  1777. return getConnection().getHeaderField(name);
  1778. }
  1779. /**
  1780. * Get all the response headers
  1781. *
  1782. * @return map of response header names to their value(s)
  1783. * @throws HttpRequestException
  1784. */
  1785. public Map<String, List<String>> headers() throws HttpRequestException {
  1786. closeOutputQuietly();
  1787. return getConnection().getHeaderFields();
  1788. }
  1789. /**
  1790. * Get a date header from the response falling back to returning -1 if the
  1791. * header is missing or parsing fails
  1792. *
  1793. * @param name
  1794. * @return date, -1 on failures
  1795. * @throws HttpRequestException
  1796. */
  1797. public long dateHeader(final String name) throws HttpRequestException {
  1798. return dateHeader(name, -1L);
  1799. }
  1800. /**
  1801. * Get a date header from the response falling back to returning the given
  1802. * default value if the header is missing or parsing fails
  1803. *
  1804. * @param name
  1805. * @param defaultValue
  1806. * @return date, default value on failures
  1807. * @throws HttpRequestException
  1808. */
  1809. public long dateHeader(final String name, final long defaultValue) throws HttpRequestException {
  1810. closeOutputQuietly();
  1811. return getConnection().getHeaderFieldDate(name, defaultValue);
  1812. }
  1813. /**
  1814. * Get an integer header from the response falling back to returning -1 if the
  1815. * header is missing or parsing fails
  1816. *
  1817. * @param name
  1818. * @return header value as an integer, -1 when missing or parsing fails
  1819. * @throws HttpRequestException
  1820. */
  1821. public int intHeader(final String name) throws HttpRequestException {
  1822. return intHeader(name, -1);
  1823. }
  1824. /**
  1825. * Get an integer header value from the response falling back to the given
  1826. * default value if the header is missing or if parsing fails
  1827. *
  1828. * @param name
  1829. * @param defaultValue
  1830. * @return header value as an integer, default value when missing or parsing
  1831. * fails
  1832. * @throws HttpRequestException
  1833. */
  1834. public int intHeader(final String name, final int defaultValue) throws HttpRequestException {
  1835. closeOutputQuietly();
  1836. return getConnection().getHeaderFieldInt(name, defaultValue);
  1837. }
  1838. /**
  1839. * Get all values of the given header from the response
  1840. *
  1841. * @param name
  1842. * @return non-null but possibly empty array of {@link String} header values
  1843. */
  1844. public String[] headers(final String name) {
  1845. final Map<String, List<String>> headers = headers();
  1846. if (headers == null || headers.isEmpty())
  1847. return EMPTY_STRINGS;
  1848. final List<String> values = headers.get(name);
  1849. if (values != null && !values.isEmpty())
  1850. return values.toArray(new String[values.size()]);
  1851. else
  1852. return EMPTY_STRINGS;
  1853. }
  1854. /**
  1855. * Get parameter with given name from header value in response
  1856. *
  1857. * @param headerName
  1858. * @param paramName
  1859. * @return parameter value or null if missing
  1860. */
  1861. public String parameter(final String headerName, final String paramName) {
  1862. return getParam(header(headerName), paramName);
  1863. }
  1864. /**
  1865. * Get all parameters from header value in response
  1866. * <p>
  1867. * This will be all key=value pairs after the first ';' that are separated by a
  1868. * ';'
  1869. *
  1870. * @param headerName
  1871. * @return non-null but possibly empty map of parameter headers
  1872. */
  1873. public Map<String, String> parameters(final String headerName) {
  1874. return getParams(header(headerName));
  1875. }
  1876. /**
  1877. * Get parameter values from header value
  1878. *
  1879. * @param header
  1880. * @return parameter value or null if none
  1881. */
  1882. protected Map<String, String> getParams(final String header) {
  1883. if (header == null || header.length() == 0)
  1884. return Collections.emptyMap();
  1885. final int headerLength = header.length();
  1886. int start = header.indexOf(';') + 1;
  1887. if (start == 0 || start == headerLength)
  1888. return Collections.emptyMap();
  1889. int end = header.indexOf(';', start);
  1890. if (end == -1)
  1891. end = headerLength;
  1892. Map<String, String> params = new LinkedHashMap<String, String>();
  1893. while (start < end) {
  1894. int nameEnd = header.indexOf('=', start);
  1895. if (nameEnd != -1 && nameEnd < end) {
  1896. String name = header.substring(start, nameEnd).trim();
  1897. if (name.length() > 0) {
  1898. String value = header.substring(nameEnd + 1, end).trim();
  1899. int length = value.length();
  1900. if (length != 0)
  1901. if (length > 2 && '"' == value.charAt(0) && '"' == value.charAt(length - 1))
  1902. params.put(name, value.substring(1, length - 1));
  1903. else
  1904. params.put(name, value);
  1905. }
  1906. }
  1907. start = end + 1;
  1908. end = header.indexOf(';', start);
  1909. if (end == -1)
  1910. end = headerLength;
  1911. }
  1912. return params;
  1913. }
  1914. /**
  1915. * Get parameter value from header value
  1916. *
  1917. * @param value
  1918. * @param paramName
  1919. * @return parameter value or null if none
  1920. */
  1921. protected String getParam(final String value, final String paramName) {
  1922. if (value == null || value.length() == 0)
  1923. return null;
  1924. final int length = value.length();
  1925. int start = value.indexOf(';') + 1;
  1926. if (start == 0 || start == length)
  1927. return null;
  1928. int end = value.indexOf(';', start);
  1929. if (end == -1)
  1930. end = length;
  1931. while (start < end) {
  1932. int nameEnd = value.indexOf('=', start);
  1933. if (nameEnd != -1 && nameEnd < end && paramName.equals(value.substring(start, nameEnd).trim())) {
  1934. String paramValue = value.substring(nameEnd + 1, end).trim();
  1935. int valueLength = paramValue.length();
  1936. if (valueLength != 0)
  1937. if (valueLength > 2 && '"' == paramValue.charAt(0) && '"' == paramValue.charAt(valueLength - 1))
  1938. return paramValue.substring(1, valueLength - 1);
  1939. else
  1940. return paramValue;
  1941. }
  1942. start = end + 1;
  1943. end = value.indexOf(';', start);
  1944. if (end == -1)
  1945. end = length;
  1946. }
  1947. return null;
  1948. }
  1949. /**
  1950. * Get 'charset' parameter from 'Content-Type' response header
  1951. *
  1952. * @return charset or null if none
  1953. */
  1954. public String charset() {
  1955. return parameter(HEADER_CONTENT_TYPE, PARAM_CHARSET);
  1956. }
  1957. /**
  1958. * Set the 'User-Agent' header to given value
  1959. *
  1960. * @param userAgent
  1961. * @return this request
  1962. */
  1963. public HttpRequest userAgent(final String userAgent) {
  1964. return header(HEADER_USER_AGENT, userAgent);
  1965. }
  1966. /**
  1967. * Set the 'Referer' header to given value
  1968. *
  1969. * @param referer
  1970. * @return this request
  1971. */
  1972. public HttpRequest referer(final String referer) {
  1973. return header(HEADER_REFERER, referer);
  1974. }
  1975. /**
  1976. * Set value of {@link HttpURLConnection#setUseCaches(boolean)}
  1977. *
  1978. * @param useCaches
  1979. * @return this request
  1980. */
  1981. public HttpRequest useCaches(final boolean useCaches) {
  1982. getConnection().setUseCaches(useCaches);
  1983. return this;
  1984. }
  1985. /**
  1986. * Set the 'Accept-Encoding' header to given value
  1987. *
  1988. * @param acceptEncoding
  1989. * @return this request
  1990. */
  1991. public HttpRequest acceptEncoding(final String acceptEncoding) {
  1992. return header(HEADER_ACCEPT_ENCODING, acceptEncoding);
  1993. }
  1994. /**
  1995. * Set the 'Accept-Encoding' header to 'gzip'
  1996. *
  1997. * @see #uncompress(boolean)
  1998. * @return this request
  1999. */
  2000. public HttpRequest acceptGzipEncoding() {
  2001. return acceptEncoding(ENCODING_GZIP);
  2002. }
  2003. /**
  2004. * Set the 'Accept-Charset' header to given value
  2005. *
  2006. * @param acceptCharset
  2007. * @return this request
  2008. */
  2009. public HttpRequest acceptCharset(final String acceptCharset) {
  2010. return header(HEADER_ACCEPT_CHARSET, acceptCharset);
  2011. }
  2012. /**
  2013. * Get the 'Content-Encoding' header from the response
  2014. *
  2015. * @return this request
  2016. */
  2017. public String contentEncoding() {
  2018. return header(HEADER_CONTENT_ENCODING);
  2019. }
  2020. /**
  2021. * Get the 'Server' header from the response
  2022. *
  2023. * @return server
  2024. */
  2025. public String server() {
  2026. return header(HEADER_SERVER);
  2027. }
  2028. /**
  2029. * Get the 'Date' header from the response
  2030. *
  2031. * @return date value, -1 on failures
  2032. */
  2033. public long date() {
  2034. return dateHeader(HEADER_DATE);
  2035. }
  2036. /**
  2037. * Get the 'Cache-Control' header from the response
  2038. *
  2039. * @return cache control
  2040. */
  2041. public String cacheControl() {
  2042. return header(HEADER_CACHE_CONTROL);
  2043. }
  2044. /**
  2045. * Get the 'ETag' header from the response
  2046. *
  2047. * @return entity tag
  2048. */
  2049. public String eTag() {
  2050. return header(HEADER_ETAG);
  2051. }
  2052. /**
  2053. * Get the 'Expires' header from the response
  2054. *
  2055. * @return expires value, -1 on failures
  2056. */
  2057. public long expires() {
  2058. return dateHeader(HEADER_EXPIRES);
  2059. }
  2060. /**
  2061. * Get the 'Last-Modified' header from the response
  2062. *
  2063. * @return last modified value, -1 on failures
  2064. */
  2065. public long lastModified() {
  2066. return dateHeader(HEADER_LAST_MODIFIED);
  2067. }
  2068. /**
  2069. * Get the 'Location' header from the response
  2070. *
  2071. * @return location
  2072. */
  2073. public String location() {
  2074. return header(HEADER_LOCATION);
  2075. }
  2076. /**
  2077. * Set the 'Authorization' header to given value
  2078. *
  2079. * @param authorization
  2080. * @return this request
  2081. */
  2082. public HttpRequest authorization(final String authorization) {
  2083. return header(HEADER_AUTHORIZATION, authorization);
  2084. }
  2085. /**
  2086. * Set the 'Proxy-Authorization' header to given value
  2087. *
  2088. * @param proxyAuthorization
  2089. * @return this request
  2090. */
  2091. public HttpRequest proxyAuthorization(final String proxyAuthorization) {
  2092. return header(HEADER_PROXY_AUTHORIZATION, proxyAuthorization);
  2093. }
  2094. /**
  2095. * Set the 'Authorization' header to given values in Basic authentication format
  2096. *
  2097. * @param name
  2098. * @param password
  2099. * @return this request
  2100. */
  2101. public HttpRequest basic(final String name, final String password) {
  2102. return authorization("Basic " + Base64.encode(name + ':' + password));
  2103. }
  2104. /**
  2105. * Set the 'Proxy-Authorization' header to given values in Basic authentication
  2106. * format
  2107. *
  2108. * @param name
  2109. * @param password
  2110. * @return this request
  2111. */
  2112. public HttpRequest proxyBasic(final String name, final String password) {
  2113. return proxyAuthorization("Basic " + Base64.encode(name + ':' + password));
  2114. }
  2115. /**
  2116. * Set the 'If-Modified-Since' request header to the given value
  2117. *
  2118. * @param ifModifiedSince
  2119. * @return this request
  2120. */
  2121. public HttpRequest ifModifiedSince(final long ifModifiedSince) {
  2122. getConnection().setIfModifiedSince(ifModifiedSince);
  2123. return this;
  2124. }
  2125. /**
  2126. * Set the 'If-None-Match' request header to the given value
  2127. *
  2128. * @param ifNoneMatch
  2129. * @return this request
  2130. */
  2131. public HttpRequest ifNoneMatch(final String ifNoneMatch) {
  2132. return header(HEADER_IF_NONE_MATCH, ifNoneMatch);
  2133. }
  2134. /**
  2135. * Set the 'Content-Type' request header to the given value
  2136. *
  2137. * @param contentType
  2138. * @return this request
  2139. */
  2140. public HttpRequest contentType(final String contentType) {
  2141. return contentType(contentType, null);
  2142. }
  2143. /**
  2144. * Set the 'Content-Type' request header to the given value and charset
  2145. *
  2146. * @param contentType
  2147. * @param charset
  2148. * @return this request
  2149. */
  2150. public HttpRequest contentType(final String contentType, final String charset) {
  2151. if (charset != null && charset.length() > 0) {
  2152. final String separator = "; " + PARAM_CHARSET + '=';
  2153. return header(HEADER_CONTENT_TYPE, contentType + separator + charset);
  2154. } else
  2155. return header(HEADER_CONTENT_TYPE, contentType);
  2156. }
  2157. /**
  2158. * Get the 'Content-Type' header from the response
  2159. *
  2160. * @return response header value
  2161. */
  2162. public String contentType() {
  2163. return header(HEADER_CONTENT_TYPE);
  2164. }
  2165. /**
  2166. * Get the 'Content-Length' header from the response
  2167. *
  2168. * @return response header value
  2169. */
  2170. public int contentLength() {
  2171. return intHeader(HEADER_CONTENT_LENGTH);
  2172. }
  2173. /**
  2174. * Set the 'Content-Length' request header to the given value
  2175. *
  2176. * @param contentLength
  2177. * @return this request
  2178. */
  2179. public HttpRequest contentLength(final String contentLength) {
  2180. return contentLength(Integer.parseInt(contentLength));
  2181. }
  2182. /**
  2183. * Set the 'Content-Length' request header to the given value
  2184. *
  2185. * @param contentLength
  2186. * @return this request
  2187. */
  2188. public HttpRequest contentLength(final int contentLength) {
  2189. getConnection().setFixedLengthStreamingMode(contentLength);
  2190. return this;
  2191. }
  2192. /**
  2193. * Set the 'Accept' header to given value
  2194. *
  2195. * @param accept
  2196. * @return this request
  2197. */
  2198. public HttpRequest accept(final String accept) {
  2199. return header(HEADER_ACCEPT, accept);
  2200. }
  2201. /**
  2202. * Set the 'Accept' header to 'application/json'
  2203. *
  2204. * @return this request
  2205. */
  2206. public HttpRequest acceptJson() {
  2207. return accept(CONTENT_TYPE_JSON);
  2208. }
  2209. /**
  2210. * Copy from input stream to output stream
  2211. *
  2212. * @param input
  2213. * @param output
  2214. * @return this request
  2215. * @throws IOException
  2216. */
  2217. protected HttpRequest copy(final InputStream input, final OutputStream output) throws IOException {
  2218. return new CloseOperation<HttpRequest>(input, ignoreCloseExceptions) {
  2219. @Override
  2220. public HttpRequest run() throws IOException {
  2221. final byte[] buffer = new byte[bufferSize];
  2222. int read;
  2223. while ((read = input.read(buffer)) != -1) {
  2224. output.write(buffer, 0, read);
  2225. totalWritten += read;
  2226. progress.onUpload(totalWritten, totalSize);
  2227. }
  2228. return HttpRequest.this;
  2229. }
  2230. }.call();
  2231. }
  2232. /**
  2233. * Copy from reader to writer
  2234. *
  2235. * @param input
  2236. * @param output
  2237. * @return this request
  2238. * @throws IOException
  2239. */
  2240. protected HttpRequest copy(final Reader input, final Writer output) throws IOException {
  2241. return new CloseOperation<HttpRequest>(input, ignoreCloseExceptions) {
  2242. @Override
  2243. public HttpRequest run() throws IOException {
  2244. final char[] buffer = new char[bufferSize];
  2245. int read;
  2246. while ((read = input.read(buffer)) != -1) {
  2247. output.write(buffer, 0, read);
  2248. totalWritten += read;
  2249. progress.onUpload(totalWritten, -1);
  2250. }
  2251. return HttpRequest.this;
  2252. }
  2253. }.call();
  2254. }
  2255. /**
  2256. * Set the UploadProgress callback for this request
  2257. *
  2258. * @param callback
  2259. * @return this request
  2260. */
  2261. public HttpRequest progress(final UploadProgress callback) {
  2262. if (callback == null)
  2263. progress = UploadProgress.DEFAULT;
  2264. else
  2265. progress = callback;
  2266. return this;
  2267. }
  2268. private HttpRequest incrementTotalSize(final long size) {
  2269. if (totalSize == -1)
  2270. totalSize = 0;
  2271. totalSize += size;
  2272. return this;
  2273. }
  2274. /**
  2275. * Close output stream
  2276. *
  2277. * @return this request
  2278. * @throws HttpRequestException
  2279. * @throws IOException
  2280. */
  2281. protected HttpRequest closeOutput() throws IOException {
  2282. progress(null);
  2283. if (output == null)
  2284. return this;
  2285. if (multipart)
  2286. output.write(CRLF + "--" + BOUNDARY + "--" + CRLF);
  2287. if (ignoreCloseExceptions)
  2288. try {
  2289. output.close();
  2290. } catch (IOException ignored) {
  2291. // Ignored
  2292. }
  2293. else
  2294. output.close();
  2295. output = null;
  2296. return this;
  2297. }
  2298. /**
  2299. * Call {@link #closeOutput()} and re-throw a caught {@link IOException}s as an
  2300. * {@link HttpRequestException}
  2301. *
  2302. * @return this request
  2303. * @throws HttpRequestException
  2304. */
  2305. protected HttpRequest closeOutputQuietly() throws HttpRequestException {
  2306. try {
  2307. return closeOutput();
  2308. } catch (IOException e) {
  2309. throw new HttpRequestException(e);
  2310. }
  2311. }
  2312. /**
  2313. * Open output stream
  2314. *
  2315. * @return this request
  2316. * @throws IOException
  2317. */
  2318. protected HttpRequest openOutput() throws IOException {
  2319. if (output != null)
  2320. return this;
  2321. getConnection().setDoOutput(true);
  2322. final String charset = getParam(getConnection().getRequestProperty(HEADER_CONTENT_TYPE), PARAM_CHARSET);
  2323. output = new RequestOutputStream(getConnection().getOutputStream(), charset, bufferSize);
  2324. return this;
  2325. }
  2326. /**
  2327. * Start part of a multipart
  2328. *
  2329. * @return this request
  2330. * @throws IOException
  2331. */
  2332. protected HttpRequest startPart() throws IOException {
  2333. if (!multipart) {
  2334. multipart = true;
  2335. contentType(CONTENT_TYPE_MULTIPART).openOutput();
  2336. output.write("--" + BOUNDARY + CRLF);
  2337. } else
  2338. output.write(CRLF + "--" + BOUNDARY + CRLF);
  2339. return this;
  2340. }
  2341. /**
  2342. * Write part header
  2343. *
  2344. * @param name
  2345. * @param filename
  2346. * @return this request
  2347. * @throws IOException
  2348. */
  2349. protected HttpRequest writePartHeader(final String name, final String filename) throws IOException {
  2350. return writePartHeader(name, filename, null);
  2351. }
  2352. /**
  2353. * Write part header
  2354. *
  2355. * @param name
  2356. * @param filename
  2357. * @param contentType
  2358. * @return this request
  2359. * @throws IOException
  2360. */
  2361. protected HttpRequest writePartHeader(final String name, final String filename, final String contentType)
  2362. throws IOException {
  2363. final StringBuilder partBuffer = new StringBuilder();
  2364. partBuffer.append("form-data; name=\"").append(name);
  2365. if (filename != null)
  2366. partBuffer.append("\"; filename=\"").append(filename);
  2367. partBuffer.append('"');
  2368. partHeader("Content-Disposition", partBuffer.toString());
  2369. if (contentType != null)
  2370. partHeader(HEADER_CONTENT_TYPE, contentType);
  2371. return send(CRLF);
  2372. }
  2373. /**
  2374. * Write part of a multipart request to the request body
  2375. *
  2376. * @param name
  2377. * @param part
  2378. * @return this request
  2379. */
  2380. public HttpRequest part(final String name, final String part) {
  2381. return part(name, null, part);
  2382. }
  2383. /**
  2384. * Write part of a multipart request to the request body
  2385. *
  2386. * @param name
  2387. * @param filename
  2388. * @param part
  2389. * @return this request
  2390. * @throws HttpRequestException
  2391. */
  2392. public HttpRequest part(final String name, final String filename, final String part) throws HttpRequestException {
  2393. return part(name, filename, null, part);
  2394. }
  2395. /**
  2396. * Write part of a multipart request to the request body
  2397. *
  2398. * @param name
  2399. * @param filename
  2400. * @param contentType value of the Content-Type part header
  2401. * @param part
  2402. * @return this request
  2403. * @throws HttpRequestException
  2404. */
  2405. public HttpRequest part(final String name, final String filename, final String contentType, final String part)
  2406. throws HttpRequestException {
  2407. try {
  2408. startPart();
  2409. writePartHeader(name, filename, contentType);
  2410. output.write(part);
  2411. } catch (IOException e) {
  2412. throw new HttpRequestException(e);
  2413. }
  2414. return this;
  2415. }
  2416. /**
  2417. * Write part of a multipart request to the request body
  2418. *
  2419. * @param name
  2420. * @param part
  2421. * @return this request
  2422. * @throws HttpRequestException
  2423. */
  2424. public HttpRequest part(final String name, final Number part) throws HttpRequestException {
  2425. return part(name, null, part);
  2426. }
  2427. /**
  2428. * Write part of a multipart request to the request body
  2429. *
  2430. * @param name
  2431. * @param filename
  2432. * @param part
  2433. * @return this request
  2434. * @throws HttpRequestException
  2435. */
  2436. public HttpRequest part(final String name, final String filename, final Number part) throws HttpRequestException {
  2437. return part(name, filename, part != null ? part.toString() : null);
  2438. }
  2439. /**
  2440. * Write part of a multipart request to the request body
  2441. *
  2442. * @param name
  2443. * @param part
  2444. * @return this request
  2445. * @throws HttpRequestException
  2446. */
  2447. public HttpRequest part(final String name, final File part) throws HttpRequestException {
  2448. return part(name, null, part);
  2449. }
  2450. /**
  2451. * Write part of a multipart request to the request body
  2452. *
  2453. * @param name
  2454. * @param filename
  2455. * @param part
  2456. * @return this request
  2457. * @throws HttpRequestException
  2458. */
  2459. public HttpRequest part(final String name, final String filename, final File part) throws HttpRequestException {
  2460. return part(name, filename, null, part);
  2461. }
  2462. /**
  2463. * Write part of a multipart request to the request body
  2464. *
  2465. * @param name
  2466. * @param filename
  2467. * @param contentType value of the Content-Type part header
  2468. * @param part
  2469. * @return this request
  2470. * @throws HttpRequestException
  2471. */
  2472. public HttpRequest part(final String name, final String filename, final String contentType, final File part)
  2473. throws HttpRequestException {
  2474. final InputStream stream;
  2475. try {
  2476. stream = new BufferedInputStream(new FileInputStream(part));
  2477. incrementTotalSize(part.length());
  2478. } catch (IOException e) {
  2479. throw new HttpRequestException(e);
  2480. }
  2481. return part(name, filename, contentType, stream);
  2482. }
  2483. /**
  2484. * Write part of a multipart request to the request body
  2485. *
  2486. * @param name
  2487. * @param part
  2488. * @return this request
  2489. * @throws HttpRequestException
  2490. */
  2491. public HttpRequest part(final String name, final InputStream part) throws HttpRequestException {
  2492. return part(name, null, null, part);
  2493. }
  2494. /**
  2495. * Write part of a multipart request to the request body
  2496. *
  2497. * @param name
  2498. * @param filename
  2499. * @param contentType value of the Content-Type part header
  2500. * @param part
  2501. * @return this request
  2502. * @throws HttpRequestException
  2503. */
  2504. public HttpRequest part(final String name, final String filename, final String contentType, final InputStream part)
  2505. throws HttpRequestException {
  2506. try {
  2507. startPart();
  2508. writePartHeader(name, filename, contentType);
  2509. copy(part, output);
  2510. } catch (IOException e) {
  2511. throw new HttpRequestException(e);
  2512. }
  2513. return this;
  2514. }
  2515. /**
  2516. * Write a multipart header to the response body
  2517. *
  2518. * @param name
  2519. * @param value
  2520. * @return this request
  2521. * @throws HttpRequestException
  2522. */
  2523. public HttpRequest partHeader(final String name, final String value) throws HttpRequestException {
  2524. return send(name).send(": ").send(value).send(CRLF);
  2525. }
  2526. /**
  2527. * Write contents of file to request body
  2528. *
  2529. * @param input
  2530. * @return this request
  2531. * @throws HttpRequestException
  2532. */
  2533. public HttpRequest send(final File input) throws HttpRequestException {
  2534. final InputStream stream;
  2535. try {
  2536. stream = new BufferedInputStream(new FileInputStream(input));
  2537. incrementTotalSize(input.length());
  2538. } catch (FileNotFoundException e) {
  2539. throw new HttpRequestException(e);
  2540. }
  2541. return send(stream);
  2542. }
  2543. /**
  2544. * Write byte array to request body
  2545. *
  2546. * @param input
  2547. * @return this request
  2548. * @throws HttpRequestException
  2549. */
  2550. public HttpRequest send(final byte[] input) throws HttpRequestException {
  2551. if (input != null)
  2552. incrementTotalSize(input.length);
  2553. return send(new ByteArrayInputStream(input));
  2554. }
  2555. /**
  2556. * Write stream to request body
  2557. * <p>
  2558. * The given stream will be closed once sending completes
  2559. *
  2560. * @param input
  2561. * @return this request
  2562. * @throws HttpRequestException
  2563. */
  2564. public HttpRequest send(final InputStream input) throws HttpRequestException {
  2565. try {
  2566. openOutput();
  2567. copy(input, output);
  2568. } catch (IOException e) {
  2569. throw new HttpRequestException(e);
  2570. }
  2571. return this;
  2572. }
  2573. /**
  2574. * Write reader to request body
  2575. * <p>
  2576. * The given reader will be closed once sending completes
  2577. *
  2578. * @param input
  2579. * @return this request
  2580. * @throws HttpRequestException
  2581. */
  2582. public HttpRequest send(final Reader input) throws HttpRequestException {
  2583. try {
  2584. openOutput();
  2585. } catch (IOException e) {
  2586. throw new HttpRequestException(e);
  2587. }
  2588. final Writer writer = new OutputStreamWriter(output, output.encoder.charset());
  2589. return new FlushOperation<HttpRequest>(writer) {
  2590. @Override
  2591. protected HttpRequest run() throws IOException {
  2592. return copy(input, writer);
  2593. }
  2594. }.call();
  2595. }
  2596. /**
  2597. * Write char sequence to request body
  2598. * <p>
  2599. * The charset configured via {@link #contentType(String)} will be used and
  2600. * UTF-8 will be used if it is unset.
  2601. *
  2602. * @param value
  2603. * @return this request
  2604. * @throws HttpRequestException
  2605. */
  2606. public HttpRequest send(final CharSequence value) throws HttpRequestException {
  2607. try {
  2608. openOutput();
  2609. output.write(value.toString());
  2610. } catch (IOException e) {
  2611. throw new HttpRequestException(e);
  2612. }
  2613. return this;
  2614. }
  2615. /**
  2616. * Create writer to request output stream
  2617. *
  2618. * @return writer
  2619. * @throws HttpRequestException
  2620. */
  2621. public OutputStreamWriter writer() throws HttpRequestException {
  2622. try {
  2623. openOutput();
  2624. return new OutputStreamWriter(output, output.encoder.charset());
  2625. } catch (IOException e) {
  2626. throw new HttpRequestException(e);
  2627. }
  2628. }
  2629. /**
  2630. * Write the values in the map as form data to the request body
  2631. * <p>
  2632. * The pairs specified will be URL-encoded in UTF-8 and sent with the
  2633. * 'application/x-www-form-urlencoded' content-type
  2634. *
  2635. * @param values
  2636. * @return this request
  2637. * @throws HttpRequestException
  2638. */
  2639. public HttpRequest form(final Map<?, ?> values) throws HttpRequestException {
  2640. return form(values, CHARSET_UTF8);
  2641. }
  2642. /**
  2643. * Write the key and value in the entry as form data to the request body
  2644. * <p>
  2645. * The pair specified will be URL-encoded in UTF-8 and sent with the
  2646. * 'application/x-www-form-urlencoded' content-type
  2647. *
  2648. * @param entry
  2649. * @return this request
  2650. * @throws HttpRequestException
  2651. */
  2652. public HttpRequest form(final Entry<?, ?> entry) throws HttpRequestException {
  2653. return form(entry, CHARSET_UTF8);
  2654. }
  2655. /**
  2656. * Write the key and value in the entry as form data to the request body
  2657. * <p>
  2658. * The pair specified will be URL-encoded and sent with the
  2659. * 'application/x-www-form-urlencoded' content-type
  2660. *
  2661. * @param entry
  2662. * @param charset
  2663. * @return this request
  2664. * @throws HttpRequestException
  2665. */
  2666. public HttpRequest form(final Entry<?, ?> entry, final String charset) throws HttpRequestException {
  2667. return form(entry.getKey(), entry.getValue(), charset);
  2668. }
  2669. /**
  2670. * Write the name/value pair as form data to the request body
  2671. * <p>
  2672. * The pair specified will be URL-encoded in UTF-8 and sent with the
  2673. * 'application/x-www-form-urlencoded' content-type
  2674. *
  2675. * @param name
  2676. * @param value
  2677. * @return this request
  2678. * @throws HttpRequestException
  2679. */
  2680. public HttpRequest form(final Object name, final Object value) throws HttpRequestException {
  2681. return form(name, value, CHARSET_UTF8);
  2682. }
  2683. /**
  2684. * Write the name/value pair as form data to the request body
  2685. * <p>
  2686. * The values specified will be URL-encoded and sent with the
  2687. * 'application/x-www-form-urlencoded' content-type
  2688. *
  2689. * @param name
  2690. * @param value
  2691. * @param charset
  2692. * @return this request
  2693. * @throws HttpRequestException
  2694. */
  2695. public HttpRequest form(final Object name, final Object value, String charset) throws HttpRequestException {
  2696. final boolean first = !form;
  2697. if (first) {
  2698. contentType(CONTENT_TYPE_FORM, charset);
  2699. form = true;
  2700. }
  2701. charset = getValidCharset(charset);
  2702. try {
  2703. openOutput();
  2704. if (!first)
  2705. output.write('&');
  2706. output.write(URLEncoder.encode(name.toString(), charset));
  2707. output.write('=');
  2708. if (value != null)
  2709. output.write(URLEncoder.encode(value.toString(), charset));
  2710. } catch (IOException e) {
  2711. throw new HttpRequestException(e);
  2712. }
  2713. return this;
  2714. }
  2715. /**
  2716. * Write the values in the map as encoded form data to the request body
  2717. *
  2718. * @param values
  2719. * @param charset
  2720. * @return this request
  2721. * @throws HttpRequestException
  2722. */
  2723. public HttpRequest form(final Map<?, ?> values, final String charset) throws HttpRequestException {
  2724. if (!values.isEmpty())
  2725. for (Entry<?, ?> entry : values.entrySet())
  2726. form(entry, charset);
  2727. return this;
  2728. }
  2729. public HttpRequest setSSLSocketFactory(SSLSocketFactory socketFactory) throws HttpRequestException {
  2730. final HttpURLConnection connection = getConnection();
  2731. if (connection instanceof HttpsURLConnection)
  2732. ((HttpsURLConnection) connection).setSSLSocketFactory(socketFactory);
  2733. return this;
  2734. }
  2735. public HttpRequest setHostnameVerifier(HostnameVerifier verifier) {
  2736. final HttpURLConnection connection = getConnection();
  2737. if (connection instanceof HttpsURLConnection)
  2738. ((HttpsURLConnection) connection).setHostnameVerifier(verifier);
  2739. return this;
  2740. }
  2741. /**
  2742. * Get the {@link URL} of this request's connection
  2743. *
  2744. * @return request URL
  2745. */
  2746. public URL url() {
  2747. return getConnection().getURL();
  2748. }
  2749. /**
  2750. * Get the HTTP method of this request
  2751. *
  2752. * @return method
  2753. */
  2754. public String method() {
  2755. return getConnection().getRequestMethod();
  2756. }
  2757. /**
  2758. * Configure an HTTP proxy on this connection. Use
  2759. * {{@link #proxyBasic(String, String)} if this proxy requires basic
  2760. * authentication.
  2761. *
  2762. * @param proxyHost
  2763. * @param proxyPort
  2764. * @return this request
  2765. */
  2766. public HttpRequest useProxy(final String proxyHost, final int proxyPort) {
  2767. if (connection != null)
  2768. throw new IllegalStateException(
  2769. "The connection has already been created. This method must be called before reading or writing to the request.");
  2770. this.httpProxyHost = proxyHost;
  2771. this.httpProxyPort = proxyPort;
  2772. return this;
  2773. }
  2774. /**
  2775. * Set whether or not the underlying connection should follow redirects in the
  2776. * response.
  2777. *
  2778. * @param followRedirects - true fo follow redirects, false to not.
  2779. * @return this request
  2780. */
  2781. public HttpRequest followRedirects(final boolean followRedirects) {
  2782. getConnection().setInstanceFollowRedirects(followRedirects);
  2783. return this;
  2784. }
  2785. }