| /* |
| * 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: BinFileInputStream.cpp 553903 2007-07-06 14:43:42Z amassari $ |
| */ |
| |
| |
| // --------------------------------------------------------------------------- |
| // Includes |
| // --------------------------------------------------------------------------- |
| |
| #if HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include <cstdlib> |
| #include <cstring> |
| |
| #include <xercesc/util/NetAccessors/BinHTTPInputStreamCommon.hpp> |
| |
| #include <xercesc/util/XMLString.hpp> |
| #include <xercesc/util/XMLExceptMsgs.hpp> |
| #include <xercesc/util/Janitor.hpp> |
| #include <xercesc/util/TransService.hpp> |
| #include <xercesc/util/PlatformUtils.hpp> |
| #include <xercesc/util/Base64.hpp> |
| |
| namespace XERCES_CPP_NAMESPACE { |
| |
| BinHTTPInputStreamCommon::BinHTTPInputStreamCommon(MemoryManager *manager) |
| : fBytesProcessed(0) |
| , fBuffer(1023, manager) |
| , fBufferPos(0) |
| , fContentType(0) |
| , fEncoding(0) |
| , fMemoryManager(manager) |
| { |
| } |
| |
| |
| BinHTTPInputStreamCommon::~BinHTTPInputStreamCommon() |
| { |
| if(fContentType) fMemoryManager->deallocate(fContentType); |
| if(fEncoding) fMemoryManager->deallocate(fEncoding); |
| } |
| |
| static const char *CRLF = "\r\n"; |
| |
| void BinHTTPInputStreamCommon::createHTTPRequest(const XMLURL &urlSource, const XMLNetHTTPInfo *httpInfo, CharBuffer &buffer) |
| { |
| static const char *GET = "GET "; |
| static const char *PUT = "PUT "; |
| static const char *POST = "POST "; |
| static const char *HTTP10 = " HTTP/1.0\r\n"; |
| static const char *HOST = "Host: "; |
| static const char *AUTHORIZATION = "Authorization: Basic "; |
| static const char *COLON = ":"; |
| |
| XMLTransService::Codes failReason; |
| const XMLSize_t blockSize = 2048; |
| |
| XMLTranscoder* trans = XMLPlatformUtils::fgTransService->makeNewTranscoderFor("ISO8859-1", failReason, blockSize, fMemoryManager); |
| Janitor<XMLTranscoder> janTrans(trans); |
| |
| TranscodeToStr hostName(urlSource.getHost(), trans, fMemoryManager); |
| TranscodeToStr path(urlSource.getPath(), trans, fMemoryManager); |
| TranscodeToStr fragment(urlSource.getFragment(), trans, fMemoryManager); |
| TranscodeToStr query(urlSource.getQuery(), trans, fMemoryManager); |
| |
| // Build up the http GET command to send to the server. |
| // To do: We should really support http 1.1. This implementation |
| // is weak. |
| if(httpInfo) { |
| switch(httpInfo->fHTTPMethod) { |
| case XMLNetHTTPInfo::GET: buffer.append(GET); break; |
| case XMLNetHTTPInfo::PUT: buffer.append(PUT); break; |
| case XMLNetHTTPInfo::POST: buffer.append(POST); break; |
| } |
| } |
| else { |
| buffer.append(GET); |
| } |
| |
| if(path.str() != 0) { |
| buffer.append((char*)path.str()); |
| } |
| else { |
| buffer.append("/"); |
| } |
| |
| if(query.str() != 0) { |
| buffer.append("?"); |
| buffer.append((char*)query.str()); |
| } |
| |
| if(fragment.str() != 0) { |
| buffer.append((char*)fragment.str()); |
| } |
| buffer.append(HTTP10); |
| |
| buffer.append(HOST); |
| buffer.append((char*)hostName.str()); |
| if(urlSource.getPortNum() != 80) |
| { |
| buffer.append(COLON); |
| buffer.appendDecimalNumber(urlSource.getPortNum()); |
| } |
| buffer.append(CRLF); |
| |
| const XMLCh *username = urlSource.getUser(); |
| const XMLCh *password = urlSource.getPassword(); |
| if(username && password) { |
| XMLBuffer userPassBuf(256, fMemoryManager); |
| userPassBuf.append(username); |
| userPassBuf.append(chColon); |
| userPassBuf.append(password); |
| |
| TranscodeToStr userPass(userPassBuf.getRawBuffer(), trans, fMemoryManager); |
| |
| XMLSize_t len; |
| XMLByte* encodedData = Base64::encode(userPass.str(), userPass.length(), &len, fMemoryManager); |
| ArrayJanitor<XMLByte> janBuf2(encodedData, fMemoryManager); |
| |
| if(encodedData) { |
| // HTTP doesn't want the 0x0A separating the data in chunks of 76 chars per line |
| XMLByte* authData = (XMLByte*)fMemoryManager->allocate((len+1)*sizeof(XMLByte)); |
| ArrayJanitor<XMLByte> janBuf(authData, fMemoryManager); |
| XMLByte *cursor = authData; |
| for(XMLSize_t i = 0; i < len; ++i) |
| if(encodedData[i] != chLF) |
| *cursor++ = encodedData[i]; |
| *cursor++ = 0; |
| buffer.append(AUTHORIZATION); |
| buffer.append((char*)authData); |
| buffer.append(CRLF); |
| } |
| } |
| |
| if(httpInfo && httpInfo->fHeaders) |
| buffer.append(httpInfo->fHeaders, httpInfo->fHeadersLen); |
| |
| buffer.append(CRLF); |
| } |
| |
| XMLCh *BinHTTPInputStreamCommon::findHeader(const char *name) |
| { |
| XMLSize_t len = strlen(name); |
| |
| char *p = strstr(fBuffer.getRawBuffer(), name); |
| while(p != 0) { |
| if(*(p - 1) == '\n' && |
| *(p + len) == ':' && |
| *(p + len + 1) == ' ') { |
| |
| p += len + 2; |
| |
| char *endP = strstr(p, CRLF); |
| if(endP == 0) { |
| for(endP = p; *endP != 0; ++endP) ; |
| } |
| |
| // Transcode from iso-8859-1 |
| TranscodeFromStr value((XMLByte*)p, endP - p, "ISO8859-1", fMemoryManager); |
| return value.adopt(); |
| } |
| |
| p = strstr(p + 1, name); |
| } |
| |
| return 0; |
| } |
| |
| int BinHTTPInputStreamCommon::sendRequest(const XMLURL &url, const XMLNetHTTPInfo *httpInfo) |
| { |
| // |
| // Constants in ASCII to send/check in the HTTP request/response |
| // |
| |
| static const char *CRLF2X = "\r\n\r\n"; |
| static const char *LF2X = "\n\n"; |
| |
| // The port is open and ready to go. |
| // Build up the http GET command to send to the server. |
| CharBuffer requestBuffer(1023, fMemoryManager); |
| createHTTPRequest(url, httpInfo, requestBuffer); |
| |
| // Send the http request |
| if(!send(requestBuffer.getRawBuffer(), requestBuffer.getLen())) { |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::NetAcc_WriteSocket, url.getURLText(), fMemoryManager); |
| } |
| |
| if(httpInfo && httpInfo->fPayload) { |
| if(!send(httpInfo->fPayload, httpInfo->fPayloadLen)) { |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::NetAcc_WriteSocket, url.getURLText(), fMemoryManager); |
| } |
| } |
| |
| // |
| // get the response, check the http header for errors from the server. |
| // |
| char tmpBuf[1024]; |
| int ret; |
| |
| fBuffer.reset(); |
| while(true) { |
| ret = receive(tmpBuf, sizeof(tmpBuf)); |
| if(ret == -1) { |
| ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, url.getURLText(), fMemoryManager); |
| } |
| |
| // connection closed |
| if(ret == 0) |
| break; |
| |
| fBuffer.append(tmpBuf, ret); |
| |
| fBufferPos = strstr(fBuffer.getRawBuffer(), CRLF2X); |
| if(fBufferPos != 0) { |
| fBufferPos += 4; |
| *(fBufferPos - 2) = 0; |
| break; |
| } |
| |
| fBufferPos = strstr(fBuffer.getRawBuffer(), LF2X); |
| if(fBufferPos != 0) { |
| fBufferPos += 2; |
| *(fBufferPos - 1) = 0; |
| break; |
| } |
| } |
| |
| // Parse the response status |
| char *p = strstr(fBuffer.getRawBuffer(), "HTTP"); |
| if(p == 0) { |
| ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, url.getURLText(), fMemoryManager); |
| } |
| |
| p = strchr(p, chSpace); |
| if(p == 0) { |
| ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, url.getURLText(), fMemoryManager); |
| } |
| |
| return atoi(p); |
| } |
| |
| const XMLCh *BinHTTPInputStreamCommon::getContentType() const |
| { |
| if(fContentType == 0) { |
| // mutable |
| const_cast<BinHTTPInputStreamCommon*>(this)->fContentType = |
| const_cast<BinHTTPInputStreamCommon*>(this)->findHeader("Content-Type"); |
| } |
| return fContentType; |
| } |
| |
| const XMLCh *BinHTTPInputStreamCommon::getEncoding() const |
| { |
| if(fEncoding == 0) { |
| const XMLCh* contentTypeHeader = getContentType(); |
| if(contentTypeHeader) |
| { |
| const XMLCh szCharsetEquals[] = {chLatin_c, chLatin_h, chLatin_a, chLatin_r, chLatin_s, chLatin_e, chLatin_t, chEqual, chNull }; |
| |
| BaseRefVectorOf<XMLCh>* tokens=XMLString::tokenizeString(contentTypeHeader, chSemiColon, fMemoryManager); |
| for(XMLSize_t i=0;i<tokens->size();i++) |
| { |
| XMLString::removeWS(tokens->elementAt(i), fMemoryManager); |
| if(XMLString::startsWithI(tokens->elementAt(i), szCharsetEquals)) |
| { |
| // mutable |
| const XMLCh* encodingName=tokens->elementAt(i)+XMLString::stringLen(szCharsetEquals); |
| const_cast<BinHTTPInputStreamCommon*>(this)->fEncoding = XMLString::replicate(encodingName, fMemoryManager); |
| break; |
| } |
| } |
| // if the charset=value entry was not present, check if we should use a default value |
| if(fEncoding==0 && tokens->size()>0) |
| { |
| const XMLCh szTextSlash[] = { chLatin_t, chLatin_e, chLatin_x, chLatin_t, chForwardSlash, chNull }; |
| const XMLCh szXml[] = {chLatin_x, chLatin_m, chLatin_l, chNull }; |
| const XMLCh szXmlDash[] = {chLatin_x, chLatin_m, chLatin_l, chDash, chNull }; |
| |
| XMLBuffer contentType(XMLString::stringLen(contentTypeHeader), fMemoryManager); |
| contentType.set(tokens->elementAt(0)); |
| |
| XMLCh* strType = contentType.getRawBuffer(); |
| XMLString::removeWS(strType, fMemoryManager); |
| if(XMLString::startsWithI(strType, szTextSlash)) |
| { |
| // text/* has a default encoding of iso-8859-1 |
| |
| // text/xml, text/xml-external-parsed-entity, or a subtype like text/AnythingAtAll+xml |
| // has a default encoding of us-ascii |
| XMLCh* subType = strType+XMLString::stringLen(szTextSlash); |
| |
| BaseRefVectorOf<XMLCh>* tokens=XMLString::tokenizeString(subType, chPlus, fMemoryManager); |
| for(XMLSize_t i=0;i<tokens->size();i++) |
| { |
| XMLCh* part=tokens->elementAt(i); |
| if(XMLString::compareIStringASCII(part, szXml)==0 || XMLString::startsWithI(part, szXmlDash)) |
| { |
| const_cast<BinHTTPInputStreamCommon*>(this)->fEncoding = XMLString::replicate(XMLUni::fgUSASCIIEncodingString, fMemoryManager); |
| break; |
| } |
| } |
| if(fEncoding==0) |
| const_cast<BinHTTPInputStreamCommon*>(this)->fEncoding = XMLString::replicate(XMLUni::fgISO88591EncodingString, fMemoryManager); |
| delete tokens; |
| } |
| } |
| delete tokens; |
| } |
| } |
| return fEncoding; |
| } |
| |
| XMLSize_t BinHTTPInputStreamCommon::readBytes(XMLByte* const toFill, |
| const XMLSize_t maxToRead) |
| { |
| XMLSize_t len = fBuffer.getRawBuffer() + fBuffer.getLen() - fBufferPos; |
| if(len > 0) |
| { |
| // If there's any data left over in the buffer into which we first |
| // read from the server (to get the http header), return that. |
| if (len > maxToRead) |
| len = maxToRead; |
| memcpy(toFill, fBufferPos, len); |
| fBufferPos += len; |
| } |
| else |
| { |
| // There was no data in the local buffer. |
| // Read some from the socket, straight into our caller's buffer. |
| // |
| int cbRead = receive((char *)toFill, maxToRead); |
| if (cbRead == -1) |
| { |
| ThrowXMLwithMemMgr(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, fMemoryManager); |
| } |
| len = cbRead; |
| } |
| |
| fBytesProcessed += len; |
| return len; |
| } |
| |
| } |