/src/xerces-c/src/xercesc/util/NetAccessors/BinHTTPInputStreamCommon.cpp
Line | Count | Source |
1 | | /* |
2 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
3 | | * contributor license agreements. See the NOTICE file distributed with |
4 | | * this work for additional information regarding copyright ownership. |
5 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
6 | | * (the "License"); you may not use this file except in compliance with |
7 | | * the License. You may obtain a copy of the License at |
8 | | * |
9 | | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | | * |
11 | | * Unless required by applicable law or agreed to in writing, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | |
18 | | /* |
19 | | * $Id: BinFileInputStream.cpp 553903 2007-07-06 14:43:42Z amassari $ |
20 | | */ |
21 | | |
22 | | |
23 | | // --------------------------------------------------------------------------- |
24 | | // Includes |
25 | | // --------------------------------------------------------------------------- |
26 | | |
27 | | #if HAVE_CONFIG_H |
28 | | # include <config.h> |
29 | | #endif |
30 | | |
31 | | #include <stdlib.h> |
32 | | #include <string.h> |
33 | | |
34 | | #include <xercesc/util/NetAccessors/BinHTTPInputStreamCommon.hpp> |
35 | | |
36 | | #include <xercesc/util/XMLString.hpp> |
37 | | #include <xercesc/util/XMLExceptMsgs.hpp> |
38 | | #include <xercesc/util/Janitor.hpp> |
39 | | #include <xercesc/util/TransService.hpp> |
40 | | #include <xercesc/util/PlatformUtils.hpp> |
41 | | #include <xercesc/util/Base64.hpp> |
42 | | |
43 | | XERCES_CPP_NAMESPACE_BEGIN |
44 | | |
45 | | BinHTTPInputStreamCommon::BinHTTPInputStreamCommon(MemoryManager *manager) |
46 | 213 | : fBytesProcessed(0) |
47 | 213 | , fBuffer(1023, manager) |
48 | 213 | , fBufferPos(0) |
49 | 213 | , fContentType(0) |
50 | 213 | , fEncoding(0) |
51 | 213 | , fMemoryManager(manager) |
52 | 213 | { |
53 | 213 | } |
54 | | |
55 | | |
56 | | BinHTTPInputStreamCommon::~BinHTTPInputStreamCommon() |
57 | 213 | { |
58 | 213 | if(fContentType) fMemoryManager->deallocate(fContentType); |
59 | 213 | if(fEncoding) fMemoryManager->deallocate(fEncoding); |
60 | 213 | } |
61 | | |
62 | | static const char *CRLF = "\r\n"; |
63 | | |
64 | | void BinHTTPInputStreamCommon::createHTTPRequest(const XMLURL &urlSource, const XMLNetHTTPInfo *httpInfo, CharBuffer &buffer) |
65 | 0 | { |
66 | 0 | static const char *GET = "GET "; |
67 | 0 | static const char *PUT = "PUT "; |
68 | 0 | static const char *POST = "POST "; |
69 | 0 | static const char *HTTP10 = " HTTP/1.0\r\n"; |
70 | 0 | static const char *HOST = "Host: "; |
71 | 0 | static const char *AUTHORIZATION = "Authorization: Basic "; |
72 | 0 | static const char *COLON = ":"; |
73 | |
|
74 | 0 | XMLTransService::Codes failReason; |
75 | 0 | const XMLSize_t blockSize = 2048; |
76 | |
|
77 | 0 | XMLTranscoder* trans = XMLPlatformUtils::fgTransService->makeNewTranscoderFor("ISO8859-1", failReason, blockSize, fMemoryManager); |
78 | 0 | Janitor<XMLTranscoder> janTrans(trans); |
79 | |
|
80 | 0 | TranscodeToStr hostName(urlSource.getHost(), trans, fMemoryManager); |
81 | 0 | TranscodeToStr path(urlSource.getPath(), trans, fMemoryManager); |
82 | 0 | TranscodeToStr fragment(urlSource.getFragment(), trans, fMemoryManager); |
83 | 0 | TranscodeToStr query(urlSource.getQuery(), trans, fMemoryManager); |
84 | | |
85 | | // Build up the http GET command to send to the server. |
86 | | // To do: We should really support http 1.1. This implementation |
87 | | // is weak. |
88 | 0 | if(httpInfo) { |
89 | 0 | switch(httpInfo->fHTTPMethod) { |
90 | 0 | case XMLNetHTTPInfo::GET: buffer.append(GET); break; |
91 | 0 | case XMLNetHTTPInfo::PUT: buffer.append(PUT); break; |
92 | 0 | case XMLNetHTTPInfo::POST: buffer.append(POST); break; |
93 | 0 | } |
94 | 0 | } |
95 | 0 | else { |
96 | 0 | buffer.append(GET); |
97 | 0 | } |
98 | | |
99 | 0 | if(path.str() != 0) { |
100 | 0 | buffer.append((char*)path.str()); |
101 | 0 | } |
102 | 0 | else { |
103 | 0 | buffer.append("/"); |
104 | 0 | } |
105 | |
|
106 | 0 | if(query.str() != 0) { |
107 | 0 | buffer.append("?"); |
108 | 0 | buffer.append((char*)query.str()); |
109 | 0 | } |
110 | |
|
111 | 0 | if(fragment.str() != 0) { |
112 | 0 | buffer.append((char*)fragment.str()); |
113 | 0 | } |
114 | 0 | buffer.append(HTTP10); |
115 | |
|
116 | 0 | buffer.append(HOST); |
117 | 0 | buffer.append((char*)hostName.str()); |
118 | 0 | if(urlSource.getPortNum() != 80) |
119 | 0 | { |
120 | 0 | buffer.append(COLON); |
121 | 0 | buffer.appendDecimalNumber(urlSource.getPortNum()); |
122 | 0 | } |
123 | 0 | buffer.append(CRLF); |
124 | |
|
125 | 0 | const XMLCh *username = urlSource.getUser(); |
126 | 0 | const XMLCh *password = urlSource.getPassword(); |
127 | 0 | if(username && password) { |
128 | 0 | XMLBuffer userPassBuf(256, fMemoryManager); |
129 | 0 | userPassBuf.append(username); |
130 | 0 | userPassBuf.append(chColon); |
131 | 0 | userPassBuf.append(password); |
132 | |
|
133 | 0 | TranscodeToStr userPass(userPassBuf.getRawBuffer(), trans, fMemoryManager); |
134 | |
|
135 | 0 | XMLSize_t len; |
136 | 0 | XMLByte* encodedData = Base64::encode(userPass.str(), userPass.length(), &len, fMemoryManager); |
137 | 0 | ArrayJanitor<XMLByte> janBuf2(encodedData, fMemoryManager); |
138 | |
|
139 | 0 | if(encodedData) { |
140 | | // HTTP doesn't want the 0x0A separating the data in chunks of 76 chars per line |
141 | 0 | XMLByte* authData = (XMLByte*)fMemoryManager->allocate((len+1)*sizeof(XMLByte)); |
142 | 0 | ArrayJanitor<XMLByte> janBuf(authData, fMemoryManager); |
143 | 0 | XMLByte *cursor = authData; |
144 | 0 | for(XMLSize_t i = 0; i < len; ++i) |
145 | 0 | if(encodedData[i] != chLF) |
146 | 0 | *cursor++ = encodedData[i]; |
147 | 0 | *cursor++ = 0; |
148 | 0 | buffer.append(AUTHORIZATION); |
149 | 0 | buffer.append((char*)authData); |
150 | 0 | buffer.append(CRLF); |
151 | 0 | } |
152 | 0 | } |
153 | |
|
154 | 0 | if(httpInfo && httpInfo->fHeaders) |
155 | 0 | buffer.append(httpInfo->fHeaders, httpInfo->fHeadersLen); |
156 | |
|
157 | 0 | buffer.append(CRLF); |
158 | 0 | } |
159 | | |
160 | | XMLCh *BinHTTPInputStreamCommon::findHeader(const char *name) |
161 | 0 | { |
162 | 0 | XMLSize_t len = strlen(name); |
163 | |
|
164 | 0 | char *p = strstr(fBuffer.getRawBuffer(), name); |
165 | 0 | while(p != 0) { |
166 | 0 | if(*(p - 1) == '\n' && |
167 | 0 | *(p + len) == ':' && |
168 | 0 | *(p + len + 1) == ' ') { |
169 | |
|
170 | 0 | p += len + 2; |
171 | |
|
172 | 0 | char *endP = strstr(p, CRLF); |
173 | 0 | if(endP == 0) { |
174 | 0 | for(endP = p; *endP != 0; ++endP) ; |
175 | 0 | } |
176 | | |
177 | | // Transcode from iso-8859-1 |
178 | 0 | TranscodeFromStr value((XMLByte*)p, endP - p, "ISO8859-1", fMemoryManager); |
179 | 0 | return value.adopt(); |
180 | 0 | } |
181 | | |
182 | 0 | p = strstr(p + 1, name); |
183 | 0 | } |
184 | | |
185 | 0 | return 0; |
186 | 0 | } |
187 | | |
188 | | int BinHTTPInputStreamCommon::sendRequest(const XMLURL &url, const XMLNetHTTPInfo *httpInfo) |
189 | 0 | { |
190 | | // |
191 | | // Constants in ASCII to send/check in the HTTP request/response |
192 | | // |
193 | |
|
194 | 0 | static const char *CRLF2X = "\r\n\r\n"; |
195 | 0 | static const char *LF2X = "\n\n"; |
196 | | |
197 | | // The port is open and ready to go. |
198 | | // Build up the http GET command to send to the server. |
199 | 0 | CharBuffer requestBuffer(1023, fMemoryManager); |
200 | 0 | createHTTPRequest(url, httpInfo, requestBuffer); |
201 | | |
202 | | // Send the http request |
203 | 0 | if(!send(requestBuffer.getRawBuffer(), requestBuffer.getLen())) { |
204 | 0 | ThrowXMLwithMemMgr1(NetAccessorException, |
205 | 0 | XMLExcepts::NetAcc_WriteSocket, url.getURLText(), fMemoryManager); |
206 | 0 | } |
207 | | |
208 | 0 | if(httpInfo && httpInfo->fPayload) { |
209 | 0 | if(!send(httpInfo->fPayload, httpInfo->fPayloadLen)) { |
210 | 0 | ThrowXMLwithMemMgr1(NetAccessorException, |
211 | 0 | XMLExcepts::NetAcc_WriteSocket, url.getURLText(), fMemoryManager); |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | | // |
216 | | // get the response, check the http header for errors from the server. |
217 | | // |
218 | 0 | char tmpBuf[1024]; |
219 | 0 | int ret; |
220 | |
|
221 | 0 | fBuffer.reset(); |
222 | 0 | while(true) { |
223 | 0 | ret = receive(tmpBuf, sizeof(tmpBuf)); |
224 | 0 | if(ret == -1) { |
225 | 0 | ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, url.getURLText(), fMemoryManager); |
226 | 0 | } |
227 | | |
228 | | // connection closed |
229 | 0 | if(ret == 0) |
230 | 0 | break; |
231 | | |
232 | 0 | fBuffer.append(tmpBuf, ret); |
233 | |
|
234 | 0 | fBufferPos = strstr(fBuffer.getRawBuffer(), CRLF2X); |
235 | 0 | if(fBufferPos != 0) { |
236 | 0 | fBufferPos += 4; |
237 | 0 | *(fBufferPos - 2) = 0; |
238 | 0 | break; |
239 | 0 | } |
240 | | |
241 | 0 | fBufferPos = strstr(fBuffer.getRawBuffer(), LF2X); |
242 | 0 | if(fBufferPos != 0) { |
243 | 0 | fBufferPos += 2; |
244 | 0 | *(fBufferPos - 1) = 0; |
245 | 0 | break; |
246 | 0 | } |
247 | 0 | } |
248 | | |
249 | | // Parse the response status |
250 | 0 | char *p = strstr(fBuffer.getRawBuffer(), "HTTP"); |
251 | 0 | if(p == 0) { |
252 | 0 | ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, url.getURLText(), fMemoryManager); |
253 | 0 | } |
254 | | |
255 | 0 | p = strchr(p, chSpace); |
256 | 0 | if(p == 0) { |
257 | 0 | ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, url.getURLText(), fMemoryManager); |
258 | 0 | } |
259 | | |
260 | 0 | return atoi(p); |
261 | 0 | } |
262 | | |
263 | | const XMLCh *BinHTTPInputStreamCommon::getContentType() const |
264 | 0 | { |
265 | 0 | if(fContentType == 0) { |
266 | | // mutable |
267 | 0 | const_cast<BinHTTPInputStreamCommon*>(this)->fContentType = |
268 | 0 | const_cast<BinHTTPInputStreamCommon*>(this)->findHeader("Content-Type"); |
269 | 0 | } |
270 | 0 | return fContentType; |
271 | 0 | } |
272 | | |
273 | | const XMLCh *BinHTTPInputStreamCommon::getEncoding() const |
274 | 0 | { |
275 | 0 | if(fEncoding == 0) { |
276 | 0 | const XMLCh* contentTypeHeader = getContentType(); |
277 | 0 | if(contentTypeHeader) |
278 | 0 | { |
279 | 0 | const XMLCh szCharsetEquals[] = {chLatin_c, chLatin_h, chLatin_a, chLatin_r, chLatin_s, chLatin_e, chLatin_t, chEqual, chNull }; |
280 | |
|
281 | 0 | BaseRefVectorOf<XMLCh>* tokens=XMLString::tokenizeString(contentTypeHeader, chSemiColon, fMemoryManager); |
282 | 0 | for(XMLSize_t i=0;i<tokens->size();i++) |
283 | 0 | { |
284 | 0 | XMLString::removeWS(tokens->elementAt(i), fMemoryManager); |
285 | 0 | if(XMLString::startsWithI(tokens->elementAt(i), szCharsetEquals)) |
286 | 0 | { |
287 | | // mutable |
288 | 0 | const XMLCh* encodingName=tokens->elementAt(i)+XMLString::stringLen(szCharsetEquals); |
289 | 0 | const_cast<BinHTTPInputStreamCommon*>(this)->fEncoding = XMLString::replicate(encodingName, fMemoryManager); |
290 | 0 | break; |
291 | 0 | } |
292 | 0 | } |
293 | | // if the charset=value entry was not present, check if we should use a default value |
294 | 0 | if(fEncoding==0 && tokens->size()>0) |
295 | 0 | { |
296 | 0 | const XMLCh szTextSlash[] = { chLatin_t, chLatin_e, chLatin_x, chLatin_t, chForwardSlash, chNull }; |
297 | 0 | const XMLCh szXml[] = {chLatin_x, chLatin_m, chLatin_l, chNull }; |
298 | 0 | const XMLCh szXmlDash[] = {chLatin_x, chLatin_m, chLatin_l, chDash, chNull }; |
299 | |
|
300 | 0 | XMLBuffer contentType(XMLString::stringLen(contentTypeHeader), fMemoryManager); |
301 | 0 | contentType.set(tokens->elementAt(0)); |
302 | |
|
303 | 0 | XMLCh* strType = contentType.getRawBuffer(); |
304 | 0 | XMLString::removeWS(strType, fMemoryManager); |
305 | 0 | if(XMLString::startsWithI(strType, szTextSlash)) |
306 | 0 | { |
307 | | // text/* has a default encoding of iso-8859-1 |
308 | | |
309 | | // text/xml, text/xml-external-parsed-entity, or a subtype like text/AnythingAtAll+xml |
310 | | // has a default encoding of us-ascii |
311 | 0 | XMLCh* subType = strType+XMLString::stringLen(szTextSlash); |
312 | |
|
313 | 0 | BaseRefVectorOf<XMLCh>* tokens=XMLString::tokenizeString(subType, chPlus, fMemoryManager); |
314 | 0 | for(XMLSize_t i=0;i<tokens->size();i++) |
315 | 0 | { |
316 | 0 | XMLCh* part=tokens->elementAt(i); |
317 | 0 | if(XMLString::compareIStringASCII(part, szXml)==0 || XMLString::startsWithI(part, szXmlDash)) |
318 | 0 | { |
319 | 0 | const_cast<BinHTTPInputStreamCommon*>(this)->fEncoding = XMLString::replicate(XMLUni::fgUSASCIIEncodingString, fMemoryManager); |
320 | 0 | break; |
321 | 0 | } |
322 | 0 | } |
323 | 0 | if(fEncoding==0) |
324 | 0 | const_cast<BinHTTPInputStreamCommon*>(this)->fEncoding = XMLString::replicate(XMLUni::fgISO88591EncodingString, fMemoryManager); |
325 | 0 | delete tokens; |
326 | 0 | } |
327 | 0 | } |
328 | 0 | delete tokens; |
329 | 0 | } |
330 | 0 | } |
331 | 0 | return fEncoding; |
332 | 0 | } |
333 | | |
334 | | XMLSize_t BinHTTPInputStreamCommon::readBytes(XMLByte* const toFill, |
335 | | const XMLSize_t maxToRead) |
336 | 0 | { |
337 | 0 | XMLSize_t len = fBuffer.getRawBuffer() + fBuffer.getLen() - fBufferPos; |
338 | 0 | if(len > 0) |
339 | 0 | { |
340 | | // If there's any data left over in the buffer into which we first |
341 | | // read from the server (to get the http header), return that. |
342 | 0 | if (len > maxToRead) |
343 | 0 | len = maxToRead; |
344 | 0 | memcpy(toFill, fBufferPos, len); |
345 | 0 | fBufferPos += len; |
346 | 0 | } |
347 | 0 | else |
348 | 0 | { |
349 | | // There was no data in the local buffer. |
350 | | // Read some from the socket, straight into our caller's buffer. |
351 | | // |
352 | 0 | int cbRead = receive((char *)toFill, maxToRead); |
353 | 0 | if (cbRead == -1) |
354 | 0 | { |
355 | 0 | ThrowXMLwithMemMgr(NetAccessorException, XMLExcepts::NetAcc_ReadSocket, fMemoryManager); |
356 | 0 | } |
357 | 0 | len = cbRead; |
358 | 0 | } |
359 | | |
360 | 0 | fBytesProcessed += len; |
361 | 0 | return len; |
362 | 0 | } |
363 | | |
364 | | XERCES_CPP_NAMESPACE_END |