blob: 5d50adae5ded055927c4d79d12da7c4f0c53fc75 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: URLAccessBinInputStream.cpp 568078 2007-08-21 11:43:25Z amassari $
*/
#include <xercesc/util/XMLNetAccessor.hpp>
#include <xercesc/util/NetAccessors/MacOSURLAccess/URLAccessBinInputStream.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLExceptMsgs.hpp>
#include <xercesc/util/Janitor.hpp>
#include <cstdlib>
#include <cstring>
XERCES_CPP_NAMESPACE_BEGIN
URLAccessBinInputStream::URLAccessBinInputStream(const XMLURL& urlSource)
: mBytesProcessed(0),
mURLReference(NULL),
mBuffer(NULL),
mBufPos(NULL),
mBufAvailable(0)
{
OSStatus status = noErr;
// Get the full URL from the source
char* url = XMLString::transcode(urlSource.getURLText(), urlSource.getMemoryManager());
ArrayJanitor<char> janBuf(url, urlSource.getMemoryManager());
// Create a URL reference from the URL
status = URLNewReference(url, &mURLReference);
// Begin the transfer
if (status == noErr)
status = URLOpen(
mURLReference,
NULL, // FSSpec* (not reading to file)
0, // URLOpenFlags
NULL, // URLNotifyUPP
0, // URLEventMask
0); // userContext
// If we failed, we throw
switch (status)
{
case noErr:
break;
case kURLInvalidURLError:
ThrowXML(MalformedURLException, XMLExcepts::URL_MalformedURL);
break;
case kURLUnsupportedSchemeError:
ThrowXML(MalformedURLException, XMLExcepts::URL_UnsupportedProto);
break;
default:
ThrowXML1(NetAccessorException, XMLExcepts::NetAcc_ConnSocket, urlSource.getURLText());
break;
}
}
URLAccessBinInputStream::~URLAccessBinInputStream()
{
OSStatus status = noErr;
// Release any buffer we've still got a hold of
if (status == noErr && mBuffer)
{
status = URLReleaseBuffer(mURLReference, mBuffer);
mBuffer = NULL;
}
// Get the current state
URLState state = 0;
if (status == noErr)
status = URLGetCurrentState(mURLReference, &state);
// Abort if we're not completed
if (status == noErr && state != kURLNullState && state != kURLCompletedState)
status = URLAbort(mURLReference);
// Loop til we reach a terminal state.
// This may be unneeded by URLAccess, but the docs are
// not very good.
while (
status == noErr
&& state != kURLNullState
&& state != kURLErrorOccurredState
&& state != kURLCompletedState
)
{
status = URLIdle();
if (status == noErr)
status = URLGetCurrentState(mURLReference, &state);
}
// Dispose the reference
status = URLDisposeReference(mURLReference);
}
//
// Call URLAccess to fullfill the read request. Since it
// passes us back buffers full of data, our object maintains
// a partial buffer across calls.
//
unsigned int
URLAccessBinInputStream::readBytes(XMLByte* const toFill
, const unsigned int maxToRead)
{
OSStatus status = noErr;
XMLByte* writePos = toFill;
std::size_t bytesDesired = maxToRead;
URLState state = kURLNullState;
while ( // while...
status == noErr // there's been no error
&& bytesDesired > 0 // more data is wanted
&& (status = URLGetCurrentState(mURLReference, &state)) == noErr // we can get the state
&& (state != kURLErrorOccurredState) // no error has occurred in the transaction
&& (mBuffer || state != kURLCompletedState) // we have data still buffered or the request isn't complete
&& (mBuffer || bytesDesired == maxToRead) // we have data still buffered or we've supplied absolutely none
)
{
// Give time to URLAccess
status = URLIdle();
// If we've got buffered data, use it
if (status == noErr && mBuffer)
{
// Supply as much as we can from the buffer
std::size_t n = mBufAvailable;
if (n > bytesDesired)
n = bytesDesired;
// If we've got data, copy it over and update our pointers
if (n > 0)
{
std::memcpy(writePos, mBufPos, n);
writePos += n;
bytesDesired -= n;
mBufPos += n;
mBufAvailable -= n;
mBytesProcessed += n;
}
// If we exhausted the buffer, release it
if (mBufAvailable == 0)
{
status = URLReleaseBuffer(mURLReference, mBuffer);
mBuffer = NULL;
}
}
// If the buffer is exhausted, get a new one
if (status == noErr && !mBuffer)
{
status = URLGetBuffer(mURLReference, &mBuffer, &mBufAvailable);
if (status == noErr)
mBufPos = reinterpret_cast<char*>(mBuffer);
}
}
// Throw on any error
if (status != noErr || state == kURLErrorOccurredState)
ThrowXML(NetAccessorException, XMLExcepts::NetAcc_ReadSocket);
// Return number of bytes delivered
return maxToRead - bytesDesired;
}
XERCES_CPP_NAMESPACE_END