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.

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