The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

403 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. // (This file gets included by the mac + linux networking code)
  24. //==============================================================================
  25. /** A HTTP input stream that uses sockets.
  26. */
  27. class JUCE_HTTPSocketStream
  28. {
  29. public:
  30. //==============================================================================
  31. JUCE_HTTPSocketStream()
  32. : readPosition (0),
  33. socketHandle (-1),
  34. levelsOfRedirection (0),
  35. timeoutSeconds (15)
  36. {
  37. }
  38. ~JUCE_HTTPSocketStream()
  39. {
  40. closeSocket();
  41. }
  42. //==============================================================================
  43. bool open (const String& url,
  44. const String& headers,
  45. const MemoryBlock& postData,
  46. const bool isPost,
  47. URL::OpenStreamProgressCallback* callback,
  48. void* callbackContext)
  49. {
  50. closeSocket();
  51. String hostName, hostPath;
  52. int hostPort;
  53. if (! decomposeURL (url, hostName, hostPath, hostPort))
  54. return false;
  55. const struct hostent* host = 0;
  56. int port = 0;
  57. String proxyName, proxyPath;
  58. int proxyPort = 0;
  59. String proxyURL (getenv ("http_proxy"));
  60. if (proxyURL.startsWithIgnoreCase (T("http://")))
  61. {
  62. if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort))
  63. return false;
  64. host = gethostbyname ((const char*) proxyName.toUTF8());
  65. port = proxyPort;
  66. }
  67. else
  68. {
  69. host = gethostbyname ((const char*) hostName.toUTF8());
  70. port = hostPort;
  71. }
  72. if (host == 0)
  73. return false;
  74. struct sockaddr_in address;
  75. zerostruct (address);
  76. memcpy ((void*) &address.sin_addr, (const void*) host->h_addr, host->h_length);
  77. address.sin_family = host->h_addrtype;
  78. address.sin_port = htons (hostPort);
  79. socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0);
  80. if (socketHandle == -1)
  81. return false;
  82. int receiveBufferSize = 16384;
  83. setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize));
  84. setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
  85. #if JUCE_MAC
  86. setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0);
  87. #endif
  88. if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1)
  89. {
  90. closeSocket();
  91. return false;
  92. }
  93. const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort,
  94. proxyName, proxyPort,
  95. hostPath, url,
  96. headers, postData,
  97. isPost));
  98. int totalHeaderSent = 0;
  99. while (totalHeaderSent < requestHeader.getSize())
  100. {
  101. const int numToSend = jmin (1024, requestHeader.getSize() - totalHeaderSent);
  102. if (send (socketHandle,
  103. ((const char*) requestHeader.getData()) + totalHeaderSent,
  104. numToSend, 0)
  105. != numToSend)
  106. {
  107. closeSocket();
  108. return false;
  109. }
  110. totalHeaderSent += numToSend;
  111. if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize()))
  112. {
  113. closeSocket();
  114. return false;
  115. }
  116. }
  117. const String responseHeader (readResponse());
  118. if (responseHeader.isNotEmpty())
  119. {
  120. //DBG (responseHeader);
  121. StringArray lines;
  122. lines.addLines (responseHeader);
  123. // NB - using charToString() here instead of just T(" "), because that was
  124. // causing a mysterious gcc internal compiler error...
  125. const int statusCode = responseHeader.fromFirstOccurrenceOf (String::charToString (T(' ')), false, false)
  126. .substring (0, 3)
  127. .getIntValue();
  128. //int contentLength = findHeaderItem (lines, T("Content-Length:")).getIntValue();
  129. //bool isChunked = findHeaderItem (lines, T("Transfer-Encoding:")).equalsIgnoreCase ("chunked");
  130. String location (findHeaderItem (lines, T("Location:")));
  131. if (statusCode >= 300 && statusCode < 400
  132. && location.isNotEmpty())
  133. {
  134. if (! location.startsWithIgnoreCase (T("http://")))
  135. location = T("http://") + location;
  136. if (levelsOfRedirection++ < 3)
  137. return open (location, headers, postData, isPost, callback, callbackContext);
  138. }
  139. else
  140. {
  141. levelsOfRedirection = 0;
  142. return true;
  143. }
  144. }
  145. closeSocket();
  146. return false;
  147. }
  148. //==============================================================================
  149. int read (void* buffer, int bytesToRead)
  150. {
  151. fd_set readbits;
  152. FD_ZERO (&readbits);
  153. FD_SET (socketHandle, &readbits);
  154. struct timeval tv;
  155. tv.tv_sec = timeoutSeconds;
  156. tv.tv_usec = 0;
  157. if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
  158. return 0; // (timeout)
  159. const int bytesRead = jmax (0, recv (socketHandle, buffer, bytesToRead, MSG_WAITALL));
  160. readPosition += bytesRead;
  161. return bytesRead;
  162. }
  163. //==============================================================================
  164. int readPosition;
  165. //==============================================================================
  166. juce_UseDebuggingNewOperator
  167. private:
  168. int socketHandle, levelsOfRedirection;
  169. const int timeoutSeconds;
  170. //==============================================================================
  171. void closeSocket()
  172. {
  173. if (socketHandle >= 0)
  174. close (socketHandle);
  175. socketHandle = -1;
  176. }
  177. const MemoryBlock createRequestHeader (const String& hostName,
  178. const int hostPort,
  179. const String& proxyName,
  180. const int proxyPort,
  181. const String& hostPath,
  182. const String& originalURL,
  183. const String& headers,
  184. const MemoryBlock& postData,
  185. const bool isPost)
  186. {
  187. String header (isPost ? "POST " : "GET ");
  188. if (proxyName.isEmpty())
  189. {
  190. header << hostPath << " HTTP/1.0\r\nHost: "
  191. << hostName << ':' << hostPort;
  192. }
  193. else
  194. {
  195. header << originalURL << " HTTP/1.0\r\nHost: "
  196. << proxyName << ':' << proxyPort;
  197. }
  198. header << "\r\nUser-Agent: JUCE/"
  199. << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION
  200. << "\r\nConnection: Close\r\nContent-Length: "
  201. << postData.getSize() << "\r\n"
  202. << headers << "\r\n";
  203. MemoryBlock mb;
  204. mb.append (header.toUTF8(), (int) strlen (header.toUTF8()));
  205. mb.append (postData.getData(), postData.getSize());
  206. return mb;
  207. }
  208. const String readResponse()
  209. {
  210. int bytesRead = 0, numConsecutiveLFs = 0;
  211. MemoryBlock buffer (1024, true);
  212. while (numConsecutiveLFs < 2 && bytesRead < 32768)
  213. {
  214. fd_set readbits;
  215. FD_ZERO (&readbits);
  216. FD_SET (socketHandle, &readbits);
  217. struct timeval tv;
  218. tv.tv_sec = timeoutSeconds;
  219. tv.tv_usec = 0;
  220. if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
  221. return String::empty; // (timeout)
  222. buffer.ensureSize (bytesRead + 8, true);
  223. char* const dest = (char*) buffer.getData() + bytesRead;
  224. if (recv (socketHandle, dest, 1, 0) == -1)
  225. return String::empty;
  226. const char lastByte = *dest;
  227. ++bytesRead;
  228. if (lastByte == '\n')
  229. ++numConsecutiveLFs;
  230. else if (lastByte != '\r')
  231. numConsecutiveLFs = 0;
  232. }
  233. const String header (String::fromUTF8 ((const uint8*) buffer.getData()));
  234. if (header.startsWithIgnoreCase (T("HTTP/")))
  235. return header.trimEnd();
  236. return String::empty;
  237. }
  238. //==============================================================================
  239. static bool decomposeURL (const String& url,
  240. String& host, String& path, int& port)
  241. {
  242. if (! url.startsWithIgnoreCase (T("http://")))
  243. return false;
  244. const int nextSlash = url.indexOfChar (7, '/');
  245. int nextColon = url.indexOfChar (7, ':');
  246. if (nextColon > nextSlash && nextSlash > 0)
  247. nextColon = -1;
  248. if (nextColon >= 0)
  249. {
  250. host = url.substring (7, nextColon);
  251. if (nextSlash >= 0)
  252. port = url.substring (nextColon + 1, nextSlash).getIntValue();
  253. else
  254. port = url.substring (nextColon + 1).getIntValue();
  255. }
  256. else
  257. {
  258. port = 80;
  259. if (nextSlash >= 0)
  260. host = url.substring (7, nextSlash);
  261. else
  262. host = url.substring (7);
  263. }
  264. if (nextSlash >= 0)
  265. path = url.substring (nextSlash);
  266. else
  267. path = T("/");
  268. return true;
  269. }
  270. //==============================================================================
  271. static const String findHeaderItem (const StringArray& lines, const String& itemName)
  272. {
  273. for (int i = 0; i < lines.size(); ++i)
  274. if (lines[i].startsWithIgnoreCase (itemName))
  275. return lines[i].substring (itemName.length()).trim();
  276. return String::empty;
  277. }
  278. };
  279. //==============================================================================
  280. bool juce_isOnLine()
  281. {
  282. return true;
  283. }
  284. void* juce_openInternetFile (const String& url,
  285. const String& headers,
  286. const MemoryBlock& postData,
  287. const bool isPost,
  288. URL::OpenStreamProgressCallback* callback,
  289. void* callbackContext)
  290. {
  291. JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream();
  292. if (s->open (url, headers, postData, isPost,
  293. callback, callbackContext))
  294. return s;
  295. delete s;
  296. return 0;
  297. }
  298. void juce_closeInternetFile (void* handle)
  299. {
  300. JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle;
  301. if (s != 0)
  302. delete s;
  303. }
  304. int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead)
  305. {
  306. JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle;
  307. if (s != 0)
  308. return s->read (buffer, bytesToRead);
  309. return 0;
  310. }
  311. int juce_seekInInternetFile (void* handle, int newPosition)
  312. {
  313. JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle;
  314. if (s != 0)
  315. return s->readPosition;
  316. return 0;
  317. }