PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/core/de/enough/glaze/content/io/RedirectHttpConnection.java

https://github.com/Enough-Software/glaze
Java | 555 lines | 266 code | 56 blank | 233 comment | 44 complexity | 05b89f6de50c7bcabe49a8933c5b6a50 MD5 | raw file
  1. /*
  2. *
  3. * Copyright: (c) 2012 Enough Software GmbH & Co. KG
  4. *
  5. * Licensed under:
  6. * 1. MIT: http://www.opensource.org/licenses/mit-license.php
  7. * 2. Apache 2.0: http://opensource.org/licenses/apache2.0
  8. * 3. GPL with classpath exception: http://www.gnu.org/software/classpath/license.html
  9. *
  10. * You may not use this file except in compliance with these licenses.
  11. *
  12. */
  13. package de.enough.glaze.content.io;
  14. import java.io.ByteArrayOutputStream;
  15. import java.io.DataInputStream;
  16. import java.io.DataOutputStream;
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.io.OutputStream;
  20. import java.util.Date;
  21. import java.util.Enumeration;
  22. import java.util.Hashtable;
  23. import javax.microedition.io.Connector;
  24. import javax.microedition.io.HttpConnection;
  25. import de.enough.glaze.style.Url;
  26. /**
  27. * Provides a <code>HttpConnection</code> that supports HTTP redirects. This
  28. * class is compatible to <code>javax.microedition.io.HttpConnection</code>.
  29. *
  30. * <p>
  31. * When connecting to an URL and a HTTP redirect is return this class follows
  32. * the redirect and uses the following HTTP connection. This works over multiple
  33. * levels. By default five redirects are supported. The number of supported
  34. * redirects can be tuned by setting the preprocessing variable
  35. * <code>polish.Browser.MaxRedirects</code> to some integer value.
  36. * </p>
  37. *
  38. * @see HttpConnection
  39. */
  40. public class RedirectHttpConnection implements HttpConnection {
  41. private static final int MAX_REDIRECTS = 5;
  42. private final String originalUrl;
  43. private String requestMethod = HttpConnection.GET;
  44. private Hashtable requestProperties;
  45. HttpConnection httpConnection;
  46. private ByteArrayOutputStream byteArrayOutputStream;
  47. private InputStream inputStream;
  48. private HttpConnection currentHttpConnection;
  49. private boolean limitContentLengthParams;
  50. /**
  51. * Creates a new http connection that understands redirects.
  52. *
  53. * @param url
  54. * the url to connect to
  55. * @throws IOException
  56. * when Connector.open() fails
  57. */
  58. public RedirectHttpConnection(String url) throws IOException {
  59. this(url, null);
  60. }
  61. /**
  62. * Creates a new http connection that understands redirects.
  63. *
  64. * @param url
  65. * the url to connect to
  66. * @param requestProperties
  67. * the request properties to be set for each http request
  68. * @throws IOException
  69. * when Connector.open() fails
  70. */
  71. public RedirectHttpConnection(String url, Hashtable requestProperties)
  72. throws IOException {
  73. this.originalUrl = url;
  74. this.requestProperties = new Hashtable();
  75. if (requestProperties != null) {
  76. Enumeration keys = requestProperties.keys();
  77. while (keys.hasMoreElements()) {
  78. String key = (String) keys.nextElement();
  79. String value = (String) requestProperties.get(key);
  80. setRequestProperty(key, value);
  81. }
  82. }
  83. this.currentHttpConnection = (HttpConnection) Connector.open(url,
  84. Connector.READ_WRITE, true);
  85. }
  86. /**
  87. * Makes sure that the http connect got created. This method redirects until
  88. * the final connection is created.
  89. *
  90. * @throws IOException
  91. * when the connection failed
  92. */
  93. protected synchronized void ensureConnectionCreated() throws IOException {
  94. if (this.httpConnection != null) {
  95. return;
  96. }
  97. HttpConnection tmpHttpConnection = this.currentHttpConnection;
  98. InputStream tmpIn = null;
  99. int redirects = 0;
  100. String url = this.originalUrl;
  101. while (true) {
  102. url += Url.getConnectionSuffix();
  103. if (tmpHttpConnection == null) {
  104. tmpHttpConnection = (HttpConnection) Connector.open(url,
  105. Connector.READ_WRITE, true);
  106. }
  107. tmpHttpConnection.setRequestMethod(this.requestMethod);
  108. if (this.requestProperties != null) {
  109. Enumeration keys = requestProperties.keys();
  110. if (keys != null) {
  111. while (keys.hasMoreElements()) {
  112. String key = (String) keys.nextElement();
  113. tmpHttpConnection.setRequestProperty(key,
  114. (String) this.requestProperties.get(key));
  115. }
  116. }
  117. }
  118. // Send POST data if exists.
  119. if (this.byteArrayOutputStream != null) {
  120. byte[] postData = this.byteArrayOutputStream.toByteArray();
  121. if (postData != null && postData.length > 0) {
  122. tmpHttpConnection.setRequestProperty("Content-Length",
  123. Integer.toString(postData.length));
  124. if (!this.limitContentLengthParams) {
  125. tmpHttpConnection.setRequestProperty("Content-length",
  126. Integer.toString(postData.length));
  127. }
  128. OutputStream out = tmpHttpConnection.openOutputStream();
  129. out.write(postData);
  130. out.close();
  131. }
  132. }
  133. // Opens the connection.
  134. tmpIn = tmpHttpConnection.openInputStream();
  135. int resultCode = tmpHttpConnection.getResponseCode();
  136. if (resultCode == HttpConnection.HTTP_MOVED_TEMP
  137. || resultCode == HttpConnection.HTTP_MOVED_PERM
  138. || resultCode == HttpConnection.HTTP_SEE_OTHER
  139. || resultCode == HttpConnection.HTTP_TEMP_REDIRECT) {
  140. String tmpUrl = tmpHttpConnection.getHeaderField("Location");
  141. // Check if url is relative.
  142. if (!tmpUrl.startsWith("http://")
  143. && !tmpUrl.startsWith("https://")) {
  144. url += tmpUrl;
  145. } else {
  146. url = tmpUrl;
  147. }
  148. tmpIn.close(); // close input stream - needed for moto devices,
  149. // for example
  150. tmpHttpConnection.close();
  151. tmpHttpConnection = null; // setting to null is needed for
  152. // some series 40 devices
  153. if (++redirects > MAX_REDIRECTS) {
  154. throw new IOException("too many redirects");
  155. }
  156. continue;
  157. }
  158. // no redirect, we are at the final connection:
  159. break;
  160. }
  161. this.httpConnection = tmpHttpConnection;
  162. this.currentHttpConnection = tmpHttpConnection;
  163. this.inputStream = tmpIn;
  164. }
  165. /**
  166. * Allows to disable sending of both "Content-Length" and "Content-length"
  167. * parameters.
  168. *
  169. * @param limit
  170. * false, when only the "Content-Length" header should be set,
  171. * not the "Content-length" request header.
  172. */
  173. public void setLimitContentLengthParams(boolean limit) {
  174. this.limitContentLengthParams = limit;
  175. }
  176. /*
  177. * (non-Javadoc)
  178. *
  179. * @see javax.microedition.io.HttpConnection#getDate()
  180. */
  181. public long getDate() throws IOException {
  182. ensureConnectionCreated();
  183. return this.httpConnection.getDate();
  184. }
  185. /*
  186. * (non-Javadoc)
  187. *
  188. * @see javax.microedition.io.HttpConnection#getExpiration()
  189. */
  190. public long getExpiration() throws IOException {
  191. ensureConnectionCreated();
  192. return this.httpConnection.getExpiration();
  193. }
  194. /*
  195. * (non-Javadoc)
  196. *
  197. * @see javax.microedition.io.HttpConnection#getFile()
  198. */
  199. public String getFile() {
  200. try {
  201. ensureConnectionCreated();
  202. return this.httpConnection.getFile();
  203. } catch (IOException e) {
  204. // #debug error
  205. System.out.println("Unable to open connection" + e);
  206. return null;
  207. }
  208. }
  209. /*
  210. * (non-Javadoc)
  211. *
  212. * @see
  213. * javax.microedition.io.HttpConnection#getHeaderField(java.lang.String)
  214. */
  215. public String getHeaderField(String name) throws IOException {
  216. ensureConnectionCreated();
  217. return this.httpConnection.getHeaderField(name);
  218. }
  219. /*
  220. * (non-Javadoc)
  221. *
  222. * @see javax.microedition.io.HttpConnection#getHeaderField(int)
  223. */
  224. public String getHeaderField(int n) throws IOException {
  225. ensureConnectionCreated();
  226. return this.httpConnection.getHeaderField(n);
  227. }
  228. /*
  229. * (non-Javadoc)
  230. *
  231. * @see
  232. * javax.microedition.io.HttpConnection#getHeaderFieldDate(java.lang.String,
  233. * long)
  234. */
  235. public long getHeaderFieldDate(String name, long def) throws IOException {
  236. ensureConnectionCreated();
  237. return this.httpConnection.getHeaderFieldDate(name, def);
  238. }
  239. /*
  240. * (non-Javadoc)
  241. *
  242. * @see
  243. * javax.microedition.io.HttpConnection#getHeaderFieldInt(java.lang.String,
  244. * int)
  245. */
  246. public int getHeaderFieldInt(String name, int def) throws IOException {
  247. ensureConnectionCreated();
  248. return this.httpConnection.getHeaderFieldInt(name, def);
  249. }
  250. /*
  251. * (non-Javadoc)
  252. *
  253. * @see javax.microedition.io.HttpConnection#getHeaderFieldKey(int)
  254. */
  255. public String getHeaderFieldKey(int n) throws IOException {
  256. ensureConnectionCreated();
  257. return this.httpConnection.getHeaderFieldKey(n);
  258. }
  259. /*
  260. * (non-Javadoc)
  261. *
  262. * @see javax.microedition.io.HttpConnection#getHost()
  263. */
  264. public String getHost() {
  265. return this.currentHttpConnection.getHost();
  266. }
  267. /*
  268. * (non-Javadoc)
  269. *
  270. * @see javax.microedition.io.HttpConnection#getLastModified()
  271. */
  272. public long getLastModified() throws IOException {
  273. ensureConnectionCreated();
  274. return this.httpConnection.getLastModified();
  275. }
  276. /*
  277. * (non-Javadoc)
  278. *
  279. * @see javax.microedition.io.HttpConnection#getPort()
  280. */
  281. public int getPort() {
  282. return this.currentHttpConnection.getPort();
  283. }
  284. /*
  285. * (non-Javadoc)
  286. *
  287. * @see javax.microedition.io.HttpConnection#getProtocol()
  288. */
  289. public String getProtocol() {
  290. return this.currentHttpConnection.getProtocol();
  291. }
  292. /*
  293. * (non-Javadoc)
  294. *
  295. * @see javax.microedition.io.HttpConnection#getQuery()
  296. */
  297. public String getQuery() {
  298. return this.currentHttpConnection.getQuery();
  299. }
  300. /*
  301. * (non-Javadoc)
  302. *
  303. * @see javax.microedition.io.HttpConnection#getRef()
  304. */
  305. public String getRef() {
  306. return this.currentHttpConnection.getRef();
  307. }
  308. /*
  309. * (non-Javadoc)
  310. *
  311. * @see javax.microedition.io.HttpConnection#getRequestMethod()
  312. */
  313. public String getRequestMethod() {
  314. return this.requestMethod;
  315. }
  316. /*
  317. * (non-Javadoc)
  318. *
  319. * @see
  320. * javax.microedition.io.HttpConnection#getRequestProperty(java.lang.String)
  321. */
  322. public String getRequestProperty(String key) {
  323. return (String) this.requestProperties.get(key);
  324. }
  325. /*
  326. * (non-Javadoc)
  327. *
  328. * @see javax.microedition.io.HttpConnection#getResponseCode()
  329. */
  330. public int getResponseCode() throws IOException {
  331. ensureConnectionCreated();
  332. return this.httpConnection.getResponseCode();
  333. }
  334. /*
  335. * (non-Javadoc)
  336. *
  337. * @see javax.microedition.io.HttpConnection#getResponseMessage()
  338. */
  339. public String getResponseMessage() throws IOException {
  340. ensureConnectionCreated();
  341. return this.httpConnection.getResponseMessage();
  342. }
  343. /*
  344. * (non-Javadoc)
  345. *
  346. * @see javax.microedition.io.HttpConnection#getURL()
  347. */
  348. public String getURL() {
  349. return this.originalUrl;
  350. }
  351. /*
  352. * (non-Javadoc)
  353. *
  354. * @see
  355. * javax.microedition.io.HttpConnection#setRequestMethod(java.lang.String)
  356. */
  357. public void setRequestMethod(String requestMethod) throws IOException {
  358. this.requestMethod = requestMethod;
  359. }
  360. /*
  361. * (non-Javadoc)
  362. *
  363. * @see
  364. * javax.microedition.io.HttpConnection#setRequestProperty(java.lang.String,
  365. * java.lang.String)
  366. */
  367. public void setRequestProperty(String key, String value) throws IOException {
  368. if (this.requestProperties == null) {
  369. this.requestProperties = new Hashtable();
  370. }
  371. // #if polish.Bugs.HttpIfModifiedSince
  372. if ("if-modified-since".equals(key.toLowerCase())) {
  373. Date d = new Date();
  374. this.requestProperties.put("IF-Modified-Since", d.toString());
  375. }
  376. // #endif
  377. this.requestProperties.put(key, value);
  378. }
  379. /*
  380. * (non-Javadoc)
  381. *
  382. * @see javax.microedition.io.ContentConnection#getEncoding()
  383. */
  384. public String getEncoding() {
  385. try {
  386. ensureConnectionCreated();
  387. return this.httpConnection.getEncoding();
  388. } catch (IOException e) {
  389. // #debug error
  390. System.out.println("Unable to establish connection" + e);
  391. return this.currentHttpConnection.getEncoding();
  392. }
  393. }
  394. /*
  395. * (non-Javadoc)
  396. *
  397. * @see javax.microedition.io.ContentConnection#getLength()
  398. */
  399. public long getLength() {
  400. try {
  401. ensureConnectionCreated();
  402. return this.httpConnection.getLength();
  403. } catch (IOException e) {
  404. // #debug error
  405. System.out.println("Unable to establish connection" + e);
  406. return this.currentHttpConnection.getLength();
  407. }
  408. }
  409. /*
  410. * (non-Javadoc)
  411. *
  412. * @see javax.microedition.io.ContentConnection#getType()
  413. */
  414. public String getType() {
  415. try {
  416. ensureConnectionCreated();
  417. return this.httpConnection.getType();
  418. } catch (IOException e) {
  419. // #debug error
  420. System.out.println("Unable to establish connection" + e);
  421. return this.currentHttpConnection.getType();
  422. }
  423. }
  424. /*
  425. * (non-Javadoc)
  426. *
  427. * @see javax.microedition.io.InputConnection#openDataInputStream()
  428. */
  429. public DataInputStream openDataInputStream() throws IOException {
  430. // TODO: Needs to be synnchronized and the DataInputStream should only
  431. // be created once.
  432. return new DataInputStream(openInputStream());
  433. }
  434. /*
  435. * (non-Javadoc)
  436. *
  437. * @see javax.microedition.io.InputConnection#openInputStream()
  438. */
  439. public InputStream openInputStream() throws IOException {
  440. ensureConnectionCreated();
  441. return this.inputStream;
  442. }
  443. /*
  444. * (non-Javadoc)
  445. *
  446. * @see javax.microedition.io.Connection#close()
  447. */
  448. public void close() throws IOException {
  449. // Check if there is a connection to actually close.
  450. if (this.httpConnection != null) {
  451. if (this.inputStream != null) {
  452. try {
  453. this.inputStream.close();
  454. } catch (Exception e) {
  455. // #debug error
  456. System.out.println("Error while closing input stream" + e);
  457. }
  458. this.inputStream = null;
  459. }
  460. if (this.byteArrayOutputStream != null) {
  461. try {
  462. this.byteArrayOutputStream.close();
  463. } catch (Exception e) {
  464. // #debug error
  465. System.out.println("Error while closing output stream" + e);
  466. }
  467. this.byteArrayOutputStream = null;
  468. }
  469. this.httpConnection.close();
  470. this.httpConnection = null;
  471. this.currentHttpConnection = null;
  472. }
  473. }
  474. /*
  475. * (non-Javadoc)
  476. *
  477. * @see javax.microedition.io.OutputConnection#openDataOutputStream()
  478. */
  479. public DataOutputStream openDataOutputStream() throws IOException {
  480. // TODO: Needs to be synchronized and the DataOutputStream should only
  481. // be
  482. // created once.
  483. return new DataOutputStream(openOutputStream());
  484. }
  485. /*
  486. * (non-Javadoc)
  487. *
  488. * @see javax.microedition.io.OutputConnection#openOutputStream()
  489. */
  490. public synchronized OutputStream openOutputStream() throws IOException {
  491. if (this.httpConnection != null) {
  492. return this.httpConnection.openOutputStream();
  493. }
  494. if (this.byteArrayOutputStream == null) {
  495. this.byteArrayOutputStream = new ByteArrayOutputStream();
  496. }
  497. return this.byteArrayOutputStream;
  498. }
  499. }