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.

395 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& optionalPostText,
  48. const bool isPost)
  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 (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1)
  71. {
  72. closeSocket();
  73. return false;
  74. }
  75. String proxyURL (getenv ("http_proxy"));
  76. if (! proxyURL.startsWithIgnoreCase (T("http://")))
  77. proxyURL = String::empty;
  78. const String requestHeader (createRequestHeader (hostName, hostPath,
  79. proxyURL, url,
  80. hostPort, optionalPostText,
  81. isPost));
  82. const char* const utf8Header = (const char*) requestHeader.toUTF8();
  83. const int headerLen = strlen (utf8Header);
  84. if (! send (socketHandle, utf8Header, headerLen, 0) == headerLen)
  85. {
  86. closeSocket();
  87. return false;
  88. }
  89. const String responseHeader (readResponse());
  90. if (responseHeader.isNotEmpty())
  91. {
  92. //DBG (responseHeader);
  93. StringArray lines;
  94. lines.addLines (responseHeader);
  95. statusCode = responseHeader.fromFirstOccurrenceOf (T(" "), false, false)
  96. .substring (4).getIntValue();
  97. //int contentLength = findHeaderItem (lines, T("Content-Length:")).getIntValue();
  98. //bool isChunked = findHeaderItem (lines, T("Transfer-Encoding:")).equalsIgnoreCase ("chunked");
  99. String location (findHeaderItem (lines, T("Location:")));
  100. if (statusCode >= 300 && statusCode < 400
  101. && location.isNotEmpty())
  102. {
  103. if (! location.startsWithIgnoreCase (T("http://")))
  104. location = T("http://") + location;
  105. if (levelsOfRedirection++ < 3)
  106. return open (location, optionalPostText, isPost);
  107. }
  108. else
  109. {
  110. levelsOfRedirection = 0;
  111. return true;
  112. }
  113. }
  114. closeSocket();
  115. return false;
  116. }
  117. //==============================================================================
  118. int read (void* buffer, int bytesToRead)
  119. {
  120. fd_set readbits;
  121. FD_ZERO (&readbits);
  122. FD_SET (socketHandle, &readbits);
  123. struct timeval tv;
  124. tv.tv_sec = timeoutSeconds;
  125. tv.tv_usec = 0;
  126. if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
  127. return 0; // (timeout)
  128. const int bytesRead = jmax (0, recv (socketHandle, buffer, bytesToRead, MSG_WAITALL));
  129. readPosition += bytesRead;
  130. return bytesRead;
  131. }
  132. //==============================================================================
  133. int statusCode, readPosition;
  134. //==============================================================================
  135. juce_UseDebuggingNewOperator
  136. private:
  137. int socketHandle, levelsOfRedirection;
  138. const int timeoutSeconds;
  139. //==============================================================================
  140. void closeSocket()
  141. {
  142. if (socketHandle >= 0)
  143. close (socketHandle);
  144. socketHandle = -1;
  145. }
  146. const String createRequestHeader (const String& hostName,
  147. const String& hostPath,
  148. const String& proxyURL,
  149. const String& originalURL,
  150. const int hostPort,
  151. const String& optionalPostText,
  152. const bool isPost)
  153. {
  154. String header (isPost ? "POST " : "GET ");
  155. if (proxyURL.isEmpty())
  156. {
  157. header << hostPath << " HTTP/1.1\r\nHost: "
  158. << hostName << ':' << hostPort;
  159. }
  160. else
  161. {
  162. String proxyName, proxyPath;
  163. int proxyPort;
  164. if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort))
  165. return String::empty;
  166. header << originalURL << " HTTP/1.1\r\nHost: "
  167. << proxyName << ':' << proxyPort;
  168. /* xxx needs finishing
  169. const char* proxyAuth = getenv ("http_proxy_auth");
  170. if (proxyAuth != 0)
  171. header << T("\r\nProxy-Authorization: ") << Base64Encode (proxyAuth);
  172. */
  173. }
  174. header << "\r\nUser-Agent: JUCE/"
  175. << JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION
  176. << "\r\nConnection: Close\r\n";
  177. if (isPost && optionalPostText.isNotEmpty())
  178. {
  179. const char* const postTextUTF8 = (const char*) optionalPostText.toUTF8();
  180. header << "Content-type: application/x-www-form-urlencoded\r\nContent-length: "
  181. << (int) strlen (postTextUTF8) << "\r\n\r\n"
  182. << optionalPostText;
  183. }
  184. header << "\r\n";
  185. //DBG (header);
  186. return header;
  187. }
  188. const String readResponse()
  189. {
  190. int bytesRead = 0, numConsecutiveLFs = 0;
  191. MemoryBlock buffer (1024, true);
  192. while (numConsecutiveLFs < 2 && bytesRead < 32768)
  193. {
  194. fd_set readbits;
  195. FD_ZERO (&readbits);
  196. FD_SET (socketHandle, &readbits);
  197. struct timeval tv;
  198. tv.tv_sec = timeoutSeconds;
  199. tv.tv_usec = 0;
  200. if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
  201. return String::empty; // (timeout)
  202. buffer.ensureSize (bytesRead + 8, true);
  203. char* const dest = (char*) buffer.getData() + bytesRead;
  204. if (recv (socketHandle, dest, 1, 0) == -1)
  205. return String::empty;
  206. const char lastByte = *dest;
  207. ++bytesRead;
  208. if (lastByte == '\n')
  209. ++numConsecutiveLFs;
  210. else if (lastByte != '\r')
  211. numConsecutiveLFs = 0;
  212. }
  213. const String header (String::fromUTF8 ((const uint8*) buffer.getData()));
  214. if (header.startsWithIgnoreCase (T("HTTP/")))
  215. return header.trimEnd();
  216. return String::empty;
  217. }
  218. //==============================================================================
  219. static bool decomposeURL (const String& url,
  220. String& host, String& path, int& port)
  221. {
  222. if (! url.startsWithIgnoreCase (T("http://")))
  223. return false;
  224. const int nextSlash = url.indexOfChar (7, '/');
  225. int nextColon = url.indexOfChar (7, ':');
  226. if (nextColon > nextSlash && nextSlash > 0)
  227. nextColon = -1;
  228. if (nextColon >= 0)
  229. {
  230. host = url.substring (7, nextColon);
  231. if (nextSlash >= 0)
  232. port = url.substring (nextColon + 1, nextSlash).getIntValue();
  233. else
  234. port = url.substring (nextColon + 1).getIntValue();
  235. }
  236. else
  237. {
  238. port = 80;
  239. if (nextSlash >= 0)
  240. host = url.substring (7, nextSlash);
  241. else
  242. host = url.substring (7);
  243. }
  244. if (nextSlash >= 0)
  245. path = url.substring (nextSlash);
  246. else
  247. path = T("/");
  248. return true;
  249. }
  250. //==============================================================================
  251. static const String findHeaderItem (const StringArray& lines, const String& itemName)
  252. {
  253. for (int i = 0; i < lines.size(); ++i)
  254. if (lines[i].startsWithIgnoreCase (itemName))
  255. return lines[i].substring (itemName.length()).trim();
  256. return String::empty;
  257. }
  258. };
  259. //==============================================================================
  260. bool juce_isOnLine()
  261. {
  262. return true;
  263. }
  264. void* juce_openInternetFile (const String& url,
  265. const String& optionalPostText,
  266. const bool isPost)
  267. {
  268. JUCE_HTTPSocketStream* const s = new JUCE_HTTPSocketStream();
  269. if (s->open (url, optionalPostText, 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__