-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/*\r
- * $Id: CurlURLInputStream.cpp 471747 2006-11-06 14:31:56Z amassari $\r
- */\r
-\r
-#include "internal.h"\r
-#include "util/CurlURLInputStream.h"\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <errno.h>\r
-#ifdef HAVE_UNISTD_H\r
-# include <unistd.h>\r
-#endif\r
-#include <sys/types.h>\r
-\r
-#include <xercesc/util/XercesDefs.hpp>\r
-#include <xercesc/util/XMLNetAccessor.hpp>\r
-#include <xercesc/util/XMLString.hpp>\r
-#include <xercesc/util/XMLExceptMsgs.hpp>\r
-#include <xercesc/util/Janitor.hpp>\r
-#include <xercesc/util/XMLUniDefs.hpp>\r
-#include <xercesc/util/TransService.hpp>\r
-#include <xercesc/util/TranscodingException.hpp>\r
-#include <xercesc/util/PlatformUtils.hpp>\r
-\r
-\r
-using namespace xmltooling;\r
-\r
-CurlURLInputStream::CurlURLInputStream(const XMLURL& urlSource, const XMLNetHTTPInfo* httpInfo/*=0*/)\r
- : fMulti(0)\r
- , fEasy(0)\r
- , fMemoryManager(urlSource.getMemoryManager())\r
- , fURLSource(urlSource)\r
- , fURL(0)\r
- , fTotalBytesRead(0)\r
- , fWritePtr(0)\r
- , fBytesRead(0)\r
- , fBytesToRead(0)\r
- , fDataAvailable(false)\r
- , fBufferHeadPtr(fBuffer)\r
- , fBufferTailPtr(fBuffer)\r
-{\r
- // Allocate the curl multi handle\r
- fMulti = curl_multi_init();\r
- \r
- // Allocate the curl easy handle\r
- fEasy = curl_easy_init();\r
- \r
- // Get the text of the URL we're going to use\r
- fURL.reset(XMLString::transcode(fURLSource.getURLText(), fMemoryManager), fMemoryManager);\r
-\r
- //printf("Curl trying to fetch %s\n", fURL.get());\r
-\r
- // Set URL option\r
- curl_easy_setopt(fEasy, CURLOPT_URL, fURL.get());\r
- curl_easy_setopt(fEasy, CURLOPT_WRITEDATA, this); // Pass this pointer to write function\r
- curl_easy_setopt(fEasy, CURLOPT_WRITEFUNCTION, staticWriteCallback); // Our static write function\r
- \r
- // Add easy handle to the multi stack\r
- curl_multi_add_handle(fMulti, fEasy);\r
-}\r
-\r
-\r
-CurlURLInputStream::~CurlURLInputStream()\r
-{\r
- // Remove the easy handle from the multi stack\r
- curl_multi_remove_handle(fMulti, fEasy);\r
- \r
- // Cleanup the easy handle\r
- curl_easy_cleanup(fEasy);\r
- \r
- // Cleanup the multi handle\r
- curl_multi_cleanup(fMulti);\r
-}\r
-\r
-\r
-size_t\r
-CurlURLInputStream::staticWriteCallback(char *buffer,\r
- size_t size,\r
- size_t nitems,\r
- void *outstream)\r
-{\r
- return ((CurlURLInputStream*)outstream)->writeCallback(buffer, size, nitems);\r
-}\r
-\r
-\r
-\r
-size_t\r
-CurlURLInputStream::writeCallback(char *buffer,\r
- size_t size,\r
- size_t nitems)\r
-{\r
- XMLSize_t cnt = size * nitems;\r
- XMLSize_t totalConsumed = 0;\r
- \r
- // Consume as many bytes as possible immediately into the buffer\r
- XMLSize_t consume = (cnt > fBytesToRead) ? fBytesToRead : cnt;\r
- memcpy(fWritePtr, buffer, consume);\r
- fWritePtr += consume;\r
- fBytesRead += consume;\r
- fTotalBytesRead += consume;\r
- fBytesToRead -= consume;\r
-\r
- //printf("write callback consuming %d bytes\n", consume);\r
-\r
- // If bytes remain, rebuffer as many as possible into our holding buffer\r
- buffer += consume;\r
- totalConsumed += consume;\r
- cnt -= consume;\r
- if (cnt > 0)\r
- {\r
- XMLSize_t bufAvail = sizeof(fBuffer) - (fBufferHeadPtr - fBuffer);\r
- consume = (cnt > bufAvail) ? bufAvail : cnt;\r
- memcpy(fBufferHeadPtr, buffer, consume);\r
- fBufferHeadPtr += consume;\r
- buffer += consume;\r
- totalConsumed += consume;\r
- //printf("write callback rebuffering %d bytes\n", consume);\r
- }\r
- \r
- // Return the total amount we've consumed. If we don't consume all the bytes\r
- // then an error will be generated. Since our buffer size is equal to the\r
- // maximum size that curl will write, this should never happen unless there\r
- // is a logic error somewhere here.\r
- return totalConsumed;\r
-}\r
-\r
-\r
-\r
-unsigned int\r
-CurlURLInputStream::readBytes(XMLByte* const toFill\r
- , const unsigned int maxToRead)\r
-{\r
- fBytesRead = 0;\r
- fBytesToRead = maxToRead;\r
- fWritePtr = toFill;\r
- \r
- for (bool tryAgain = true; fBytesToRead > 0 && (tryAgain || fBytesRead == 0); )\r
- {\r
- // First, any buffered data we have available\r
- XMLSize_t bufCnt = fBufferHeadPtr - fBufferTailPtr;\r
- bufCnt = (bufCnt > fBytesToRead) ? fBytesToRead : bufCnt;\r
- if (bufCnt > 0)\r
- {\r
- memcpy(fWritePtr, fBufferTailPtr, bufCnt);\r
- fWritePtr += bufCnt;\r
- fBytesRead += bufCnt;\r
- fTotalBytesRead += bufCnt;\r
- fBytesToRead -= bufCnt;\r
- \r
- fBufferTailPtr += bufCnt;\r
- if (fBufferTailPtr == fBufferHeadPtr)\r
- fBufferHeadPtr = fBufferTailPtr = fBuffer;\r
- \r
- //printf("consuming %d buffered bytes\n", bufCnt);\r
-\r
- tryAgain = true;\r
- continue;\r
- }\r
- \r
- // Ask the curl to do some work\r
- int runningHandles = 0;\r
- CURLMcode curlResult = curl_multi_perform(fMulti, &runningHandles);\r
- tryAgain = (curlResult == CURLM_CALL_MULTI_PERFORM);\r
- \r
- // Process messages from curl\r
- int msgsInQueue = 0;\r
- for (CURLMsg* msg = NULL; (msg = curl_multi_info_read(fMulti, &msgsInQueue)) != NULL; )\r
- {\r
- //printf("msg %d, %d from curl\n", msg->msg, msg->data.result);\r
-\r
- if (msg->msg != CURLMSG_DONE)\r
- continue;\r
- \r
- switch (msg->data.result)\r
- {\r
- case CURLE_OK:\r
- // We completed successfully. runningHandles should have dropped to zero, so we'll bail out below...\r
- break;\r
- \r
- case CURLE_UNSUPPORTED_PROTOCOL:\r
- ThrowXMLwithMemMgr(MalformedURLException, XMLExcepts::URL_UnsupportedProto, fMemoryManager);\r
- break;\r
-\r
- case CURLE_COULDNT_RESOLVE_HOST:\r
- case CURLE_COULDNT_RESOLVE_PROXY:\r
- ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_TargetResolution, fURLSource.getHost(), fMemoryManager);\r
- break;\r
- \r
- case CURLE_COULDNT_CONNECT:\r
- ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_ConnSocket, fURLSource.getURLText(), fMemoryManager);\r
- \r
- case CURLE_RECV_ERROR:\r
- ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, fURLSource.getURLText(), fMemoryManager);\r
- break;\r
-\r
- default:\r
- ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_InternalError, fURLSource.getURLText(), fMemoryManager);\r
- break;\r
- }\r
- }\r
- \r
- // If nothing is running any longer, bail out\r
- if (runningHandles == 0)\r
- break;\r
- \r
- // If there is no further data to read, and we haven't\r
- // read any yet on this invocation, call select to wait for data\r
- if (!tryAgain && fBytesRead == 0)\r
- {\r
- fd_set readSet[16];\r
- fd_set writeSet[16];\r
- fd_set exceptSet[16];\r
- int fdcnt = 16;\r
- \r
- // As curl for the file descriptors to wait on\r
- (void) curl_multi_fdset(fMulti, readSet, writeSet, exceptSet, &fdcnt);\r
- \r
- // Wait on the file descriptors\r
- timeval tv;\r
- tv.tv_sec = 2;\r
- tv.tv_usec = 0;\r
- (void) select(fdcnt, readSet, writeSet, exceptSet, &tv);\r
- }\r
- }\r
- \r
- return fBytesRead;\r
-}\r