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.

421 lines
14KB

  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. int timeOutMs)
  50. {
  51. closeSocket();
  52. uint32 timeOutTime = Time::getMillisecondCounter();
  53. if (timeOutMs == 0)
  54. timeOutTime += 60000;
  55. else if (timeOutMs < 0)
  56. timeOutTime = 0xffffffff;
  57. else
  58. timeOutTime += timeOutMs;
  59. String hostName, hostPath;
  60. int hostPort;
  61. if (! decomposeURL (url, hostName, hostPath, hostPort))
  62. return false;
  63. const struct hostent* host = 0;
  64. int port = 0;
  65. String proxyName, proxyPath;
  66. int proxyPort = 0;
  67. String proxyURL (getenv ("http_proxy"));
  68. if (proxyURL.startsWithIgnoreCase (T("http://")))
  69. {
  70. if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort))
  71. return false;
  72. host = gethostbyname ((const char*) proxyName.toUTF8());
  73. port = proxyPort;
  74. }
  75. else
  76. {
  77. host = gethostbyname ((const char*) hostName.toUTF8());
  78. port = hostPort;
  79. }
  80. if (host == 0)
  81. return false;
  82. struct sockaddr_in address;
  83. zerostruct (address);
  84. memcpy ((void*) &address.sin_addr, (const void*) host->h_addr, host->h_length);
  85. address.sin_family = host->h_addrtype;
  86. address.sin_port = htons (port);
  87. socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0);
  88. if (socketHandle == -1)
  89. return false;
  90. int receiveBufferSize = 16384;
  91. setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize));
  92. setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
  93. #if JUCE_MAC
  94. setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0);
  95. #endif
  96. if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1)
  97. {
  98. closeSocket();
  99. return false;
  100. }
  101. const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort,
  102. proxyName, proxyPort,
  103. hostPath, url,
  104. headers, postData,
  105. isPost));
  106. int totalHeaderSent = 0;
  107. while (totalHeaderSent < requestHeader.getSize())
  108. {
  109. if (Time::getMillisecondCounter() > timeOutTime)
  110. {
  111. closeSocket();
  112. return false;
  113. }
  114. const int numToSend = jmin (1024, requestHeader.getSize() - totalHeaderSent);
  115. if (send (socketHandle,
  116. ((const char*) requestHeader.getData()) + totalHeaderSent,
  117. numToSend, 0)
  118. != numToSend)
  119. {
  120. closeSocket();
  121. return false;
  122. }
  123. totalHeaderSent += numToSend;
  124. if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize()))
  125. {
  126. closeSocket();
  127. return false;
  128. }
  129. }
  130. const String responseHeader (readResponse (timeOutTime));
  131. if (responseHeader.isNotEmpty())
  132. {
  133. //DBG (responseHeader);
  134. StringArray lines;
  135. lines.addLines (responseHeader);
  136. // NB - using charToString() here instead of just T(" "), because that was
  137. // causing a mysterious gcc internal compiler error...
  138. const int statusCode = responseHeader.fromFirstOccurrenceOf (String::charToString (T(' ')), false, false)
  139. .substring (0, 3)
  140. .getIntValue();
  141. //int contentLength = findHeaderItem (lines, T("Content-Length:")).getIntValue();
  142. //bool isChunked = findHeaderItem (lines, T("Transfer-Encoding:")).equalsIgnoreCase ("chunked");
  143. String location (findHeaderItem (lines, T("Location:")));
  144. if (statusCode >= 300 && statusCode < 400
  145. && location.isNotEmpty())
  146. {
  147. if (! location.startsWithIgnoreCase (T("http://")))
  148. location = T("http://") + location;
  149. if (levelsOfRedirection++ < 3)
  150. return open (location, headers, postData, isPost, callback, callbackContext, timeOutMs);
  151. }
  152. else
  153. {
  154. levelsOfRedirection = 0;
  155. return true;
  156. }
  157. }
  158. closeSocket();
  159. return false;
  160. }
  161. //==============================================================================
  162. int read (void* buffer, int bytesToRead)
  163. {
  164. fd_set readbits;
  165. FD_ZERO (&readbits);
  166. FD_SET (socketHandle, &readbits);
  167. struct timeval tv;
  168. tv.tv_sec = timeoutSeconds;
  169. tv.tv_usec = 0;
  170. if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
  171. return 0; // (timeout)
  172. const int bytesRead = jmax (0, recv (socketHandle, buffer, bytesToRead, MSG_WAITALL));
  173. readPosition += bytesRead;
  174. return bytesRead;
  175. }
  176. //==============================================================================
  177. int readPosition;
  178. //==============================================================================
  179. juce_UseDebuggingNewOperator
  180. private:
  181. int socketHandle, levelsOfRedirection;
  182. const int timeoutSeconds;
  183. //==============================================================================
  184. void closeSocket()
  185. {
  186. if (socketHandle >= 0)
  187. close (socketHandle);
  188. socketHandle = -1;
  189. }
  190. const MemoryBlock createRequestHeader (const String& hostName,
  191. const int hostPort,
  192. const String& proxyName,
  193. const int proxyPort,
  194. const String& hostPath,
  195. const String& originalURL,
  196. const String& headers,
  197. const MemoryBlock& postData,
  198. const bool isPost)
  199. {
  200. String header (isPost ? "POST " : "GET ");
  201. if (proxyName.isEmpty())
  202. {
  203. header << hostPath << " HTTP/1.0\r\nHost: "
  204. << hostName << ':' << hostPort;
  205. }
  206. else
  207. {
  208. header << originalURL << " HTTP/1.0\r\nHost: "
  209. << proxyName << ':' << proxyPort;
  210. }
  211. header << "\r\nUser-Agent: JUCE/"
  212. << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION
  213. << "\r\nConnection: Close\r\nContent-Length: "
  214. << postData.getSize() << "\r\n"
  215. << headers << "\r\n";
  216. MemoryBlock mb;
  217. mb.append (header.toUTF8(), (int) strlen (header.toUTF8()));
  218. mb.append (postData.getData(), postData.getSize());
  219. return mb;
  220. }
  221. const String readResponse (const uint32 timeOutTime)
  222. {
  223. int bytesRead = 0, numConsecutiveLFs = 0;
  224. MemoryBlock buffer (1024, true);
  225. while (numConsecutiveLFs < 2 && bytesRead < 32768
  226. && Time::getMillisecondCounter() <= timeOutTime)
  227. {
  228. fd_set readbits;
  229. FD_ZERO (&readbits);
  230. FD_SET (socketHandle, &readbits);
  231. struct timeval tv;
  232. tv.tv_sec = timeoutSeconds;
  233. tv.tv_usec = 0;
  234. if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
  235. return String::empty; // (timeout)
  236. buffer.ensureSize (bytesRead + 8, true);
  237. char* const dest = (char*) buffer.getData() + bytesRead;
  238. if (recv (socketHandle, dest, 1, 0) == -1)
  239. return String::empty;
  240. const char lastByte = *dest;
  241. ++bytesRead;
  242. if (lastByte == '\n')
  243. ++numConsecutiveLFs;
  244. else if (lastByte != '\r')
  245. numConsecutiveLFs = 0;
  246. }
  247. const String header (String::fromUTF8 ((const uint8*) buffer.getData()));
  248. if (header.startsWithIgnoreCase (T("HTTP/")))
  249. return header.trimEnd();
  250. return String::empty;
  251. }
  252. //==============================================================================
  253. static bool decomposeURL (const String& url,
  254. String& host, String& path, int& port)
  255. {
  256. if (! url.startsWithIgnoreCase (T("http://")))
  257. return false;
  258. const int nextSlash = url.indexOfChar (7, '/');
  259. int nextColon = url.indexOfChar (7, ':');
  260. if (nextColon > nextSlash && nextSlash > 0)
  261. nextColon = -1;
  262. if (nextColon >= 0)
  263. {
  264. host = url.substring (7, nextColon);
  265. if (nextSlash >= 0)
  266. port = url.substring (nextColon + 1, nextSlash).getIntValue();
  267. else
  268. port = url.substring (nextColon + 1).getIntValue();
  269. }
  270. else
  271. {
  272. port = 80;
  273. if (nextSlash >= 0)
  274. host = url.substring (7, nextSlash);
  275. else
  276. host = url.substring (7);
  277. }
  278. if (nextSlash >= 0)
  279. path = url.substring (nextSlash);
  280. else
  281. path = T("/");
  282. return true;
  283. }
  284. //==============================================================================
  285. static const String findHeaderItem (const StringArray& lines, const String& itemName)
  286. {
  287. for (int i = 0; i < lines.size(); ++i)
  288. if (lines[i].startsWithIgnoreCase (itemName))
  289. return lines[i].substring (itemName.length()).trim();
  290. return String::empty;
  291. }
  292. };
  293. //==============================================================================
  294. bool juce_isOnLine()
  295. {
  296. return true;
  297. }
  298. void* juce_openInternetFile (const String& url,
  299. const String& headers,
  300. const MemoryBlock& postData,
  301. const bool isPost,
  302. URL::OpenStreamProgressCallback* callback,
  303. void* callbackContext,
  304. int timeOutMs)
  305. {
  306. JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream();
  307. if (s->open (url, headers, postData, isPost,
  308. callback, callbackContext, timeOutMs))
  309. return s;
  310. delete s;
  311. return 0;
  312. }
  313. void juce_closeInternetFile (void* handle)
  314. {
  315. JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle;
  316. if (s != 0)
  317. delete s;
  318. }
  319. int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead)
  320. {
  321. JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle;
  322. if (s != 0)
  323. return s->read (buffer, bytesToRead);
  324. return 0;
  325. }
  326. int juce_seekInInternetFile (void* handle, int newPosition)
  327. {
  328. JUCE_HTTPSocketStream* const s = (JUCE_HTTPSocketStream*) handle;
  329. if (s != 0)
  330. return s->readPosition;
  331. return 0;
  332. }