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.

394 lines
12KB

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