1 karl 1.107 //%2006////////////////////////////////////////////////////////////////////////
|
2 mike 1.2 //
|
3 karl 1.79 // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
4 // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
5 // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
|
6 karl 1.59 // IBM Corp.; EMC Corporation, The Open Group.
|
7 karl 1.79 // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
8 // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
|
9 karl 1.91 // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
10 // EMC Corporation; VERITAS Software Corporation; The Open Group.
|
11 karl 1.107 // Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.;
12 // EMC Corporation; Symantec Corporation; The Open Group.
|
13 mike 1.2 //
14 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
15 kumpf 1.27 // of this software and associated documentation files (the "Software"), to
16 // deal in the Software without restriction, including without limitation the
17 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
18 mike 1.2 // sell copies of the Software, and to permit persons to whom the Software is
19 // furnished to do so, subject to the following conditions:
|
20 karl 1.107 //
|
21 kumpf 1.27 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
|
22 mike 1.2 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
23 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
24 kumpf 1.27 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
25 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
27 mike 1.2 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
|
30 kumpf 1.120 //==============================================================================
31 //
|
32 mike 1.2 //%/////////////////////////////////////////////////////////////////////////////
33
34 #include <Pegasus/Common/Config.h>
|
35 kumpf 1.16 #include <Pegasus/Common/Constants.h>
|
36 mike 1.2
|
37 mike 1.112 #include "Network.h"
|
38 mike 1.2 #include <iostream>
39 #include <cctype>
40 #include <cstdlib>
|
41 mike 1.116 #include "Signal.h"
|
42 mike 1.2 #include "Socket.h"
43 #include "TLS.h"
44 #include "HTTPConnection.h"
45 #include "MessageQueue.h"
46 #include "Monitor.h"
47 #include "HTTPMessage.h"
|
48 kumpf 1.3 #include "Tracer.h"
|
49 mike 1.104 #include "Buffer.h"
|
50 kumpf 1.105 #include "LanguageParser.h"
|
51 mike 1.2
|
52 gerarda 1.44 #ifdef PEGASUS_KERBEROS_AUTHENTICATION
|
53 gerarda 1.46 #include <Pegasus/Common/CIMKerberosSecurityAssociation.h>
|
54 gerarda 1.44 #endif
|
55 kumpf 1.63 #include <Pegasus/Common/XmlWriter.h>
|
56 gerarda 1.44
|
57 mike 1.2 PEGASUS_USING_STD;
58
59 PEGASUS_NAMESPACE_BEGIN
60
61 // initialize the request count
62
|
63 mike 1.103 AtomicInt HTTPConnection::_requestCount(0);
|
64 mike 1.2
65 ////////////////////////////////////////////////////////////////////////////////
66 //
67 // Local routines:
68 //
69 ////////////////////////////////////////////////////////////////////////////////
70
|
71 brian.campbell 1.72 /*
72 * string and number constants for HTTP sending/receiving
73 */
74
75 // buffer size for sending receiving
76 static const Uint32 httpTcpBufferSize = 8192;
77
|
78 david.dillard 1.93 // string constants for HTTP header. "Name" represents strings on the left
79 // side of headerNameTerminator and "Value" represents strings on the right
|
80 brian.campbell 1.72 // side of headerNameTerminator
81
82 #define headerNameTrailer "Trailer"
83 #undef CRLF
84 #define CRLF "\r\n"
85
86 static const char headerNameTransferTE[] = "TE";
87 static const char headerNameTransferEncoding[] = "transfer-encoding";
88 static const char headerNameContentLength[] = "content-length";
89 static const char headerValueTransferEncodingChunked[] = "chunked";
90 static const char headerValueTransferEncodingIdentity[] = "identity";
91 static const char headerValueTEchunked[] = "chunked";
92 static const char headerValueTEtrailers[] = "trailers";
93 static const char headerNameError[] = "CIMError";
94 static const char headerNameCode[] = "CIMStatusCode";
95 static const char headerNameDescription[] = "CIMStatusCodeDescription";
96 static const char headerNameOperation[] = "CIMOperation";
|
97 brian.campbell 1.85 static const char headerNameContentLanguage[] = "Content-Language";
|
98 brian.campbell 1.72
99 // the names comes from the HTTP specification on chunked transfer encoding
100
101 static const char headerNameTerminator[] = ": ";
102 static const char headerValueSeparator[] = ", ";
103 static const char headerLineTerminator[] = CRLF;
104 static const char headerTerminator[] = CRLF CRLF;
|
105 david.dillard 1.88 static const char chunkLineTerminator[] = CRLF;
106 static const char chunkTerminator[] = CRLF;
107 static const char chunkBodyTerminator[] = CRLF;
108 static const char trailerTerminator[] = CRLF;
109 static const char chunkExtensionTerminator[] = ";";
|
110 brian.campbell 1.72
111 // string sizes
112
|
113 kumpf 1.122 static const Uint32 headerNameContentLengthLength =
114 sizeof(headerNameContentLength) - 1;
115 static const Uint32 headerValueTransferEncodingChunkedLength =
116 sizeof(headerValueTransferEncodingChunked) - 1;
117 static const Uint32 headerNameTransferEncodingLength =
118 sizeof(headerNameTransferEncoding) - 1;
|
119 brian.campbell 1.72 static const Uint32 headerNameTerminatorLength =sizeof(headerNameTerminator)-1;
120 static const Uint32 headerLineTerminatorLength =sizeof(headerLineTerminator)-1;
121 static const Uint32 chunkLineTerminatorLength = sizeof(chunkLineTerminator)-1;
122 static const Uint32 chunkTerminatorLength = sizeof(chunkTerminator)-1;
123 static const Uint32 chunkBodyTerminatorLength = sizeof(chunkBodyTerminator)-1;
124 static const Uint32 trailerTerminatorLength = sizeof(trailerTerminator)-1;
|
125 kumpf 1.122 static const Uint32 chunkExtensionTerminatorLength =
126 sizeof(chunkExtensionTerminator) - 1;
|
127 brian.campbell 1.72
128 // the number of bytes it takes to place a Uint32 into a string (minus null)
129 static const Uint32 numberAsStringLength = 10;
130
131 /*
132 * given an HTTP status code, return the description. not all codes are listed
133 * here. Unmapped codes result in the internal error string.
134 * Add any required future codes here.
135 */
136
137 static const String httpDetailDelimiter = headerValueSeparator;
138 static const String httpStatusInternal = HTTP_STATUS_INTERNALSERVERERROR;
139
140 /*
141 * throw given http code with detail, file, line
142 * This is shared client/server code. The caller will decide what to do
143 * with the thrown message
144 */
145
146 static void _throwEventFailure(const String &status, const String &detail,
|
147 kumpf 1.98 const char *func,
148 const char *file , Uint32 line)
|
149 david.dillard 1.93 {
|
150 kumpf 1.98 String message = status + httpDetailDelimiter + detail;
|
151 marek 1.131 PEG_TRACE_STRING(TRC_HTTP, Tracer::LEVEL2, message);
|
152 kumpf 1.98 if (status == httpStatusInternal)
153 throw AssertionFailureException(file, line, message);
154 else throw Exception(message);
|
155 brian.campbell 1.72 }
156
157 // throw a http exception. This is used for both client and server common code.
158 // The macro allows is used for file, line inclusion for debugging
159
160 #define _throwEventFailure(status, detail) \
161 _throwEventFailure(status, String(detail), func, __FILE__, __LINE__)
162
|
163 kumpf 1.113 #define _socketWriteError() \
164 do \
165 { \
|
166 marek 1.131 PEG_TRACE((__FILE__, __LINE__, TRC_DISCARDED_DATA, Tracer::LEVEL2, \
|
167 kumpf 1.113 "Socket write failed with error %d; could not write response " \
168 "to client. (Client may have timed out.)", \
|
169 marek 1.131 getSocketError())); \
|
170 kumpf 1.113 throw Exception("socket write error"); \
171 } \
172 while (0)
173
|
174 david.dillard 1.93 static inline Uint32 _Min(Uint32 x, Uint32 y)
|
175 mike 1.2 {
|
176 david.dillard 1.93 return x < y ? x : y;
|
177 mike 1.2 }
178
|
179 kumpf 1.42 // Used to test signal handling
180 void * sigabrt_generator(void * parm)
181 {
182 abort();
183 return 0;
184 }
185
186
|
187 mike 1.2 ////////////////////////////////////////////////////////////////////////////////
188 //
189 // HTTPConnection
190 //
191 ////////////////////////////////////////////////////////////////////////////////
192
193 HTTPConnection::HTTPConnection(
194 Monitor* monitor,
|
195 kumpf 1.137 SharedPtr<MP_Socket>& socket,
|
196 kumpf 1.120 const String& ipAddress,
|
197 mday 1.19 MessageQueue* ownerMessageQueue,
|
198 sushma.fernandes 1.119 MessageQueue* outputMessageQueue)
|
199 david.dillard 1.93 :
|
200 kumpf 1.98 Base(PEGASUS_QUEUENAME_HTTPCONNECTION),
201 _monitor(monitor),
202 _socket(socket),
|
203 kumpf 1.120 _ipAddress(ipAddress),
|
204 kumpf 1.98 _ownerMessageQueue(ownerMessageQueue),
205 _outputMessageQueue(outputMessageQueue),
206 _contentOffset(-1),
207 _contentLength(-1),
|
208 kumpf 1.102 _connectionClosePending(false),
209 _acceptPending(false)
|
210 kumpf 1.98 {
211 PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::HTTPConnection");
212
213 _socket->disableBlocking();
214 _authInfo.reset(new AuthenticationInfo(true));
|
215 kumpf 1.7
|
216 kumpf 1.98 // Add SSL verification information to the authentication information
217 if (_socket->isSecure())
218 {
|
219 thilo.boehm 1.117 #ifndef PEGASUS_PLATFORM_ZOS_ZSERIES_IBM
|
220 kumpf 1.122 if (_socket->isPeerVerificationEnabled() &&
221 _socket->isCertificateVerified())
|
222 kumpf 1.98 {
|
223 sushma.fernandes 1.132 _authInfo->setConnectionAuthenticated(true);
|
224 kumpf 1.98 _authInfo->setAuthType(AuthenticationInfoRep::AUTH_TYPE_SSL);
|
225 kumpf 1.122 _authInfo->setClientCertificateChain(
226 _socket->getPeerCertificateChain());
|
227 kumpf 1.98 }
|
228 thilo.boehm 1.117 #else
|
229 kumpf 1.123 if (_socket->isClientAuthenticated())
|
230 thilo.boehm 1.117 {
|
231 sushma.fernandes 1.132 _authInfo->setConnectionAuthenticated(true);
|
232 thilo.boehm 1.117 _authInfo->setAuthenticatedUser(_socket->getAuthenticatedUser());
233 }
234 #endif
235
|
236 kumpf 1.98 }
237
238 _responsePending = false;
239 _connectionRequestCount = 0;
240 _transferEncodingChunkOffset = 0;
241
|
242 kumpf 1.120 PEG_TRACE_STRING(TRC_HTTP, Tracer::LEVEL2,
243 "Connection IP address = " + _ipAddress);
244
|
245 sushma.fernandes 1.121 _authInfo->setIpAddress(_ipAddress);
246
|
247 kumpf 1.98 PEG_METHOD_EXIT();
|
248 mike 1.2 }
249
250 HTTPConnection::~HTTPConnection()
251 {
|
252 kumpf 1.98 PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::~HTTPConnection");
|
253 kumpf 1.7
|
254 kumpf 1.98 _socket->close();
|
255 kumpf 1.7
|
256 kumpf 1.98 PEG_METHOD_EXIT();
|
257 mike 1.2 }
258
|
259 kumpf 1.118 void HTTPConnection::enqueue(Message *message)
260 {
261 handleEnqueue(message);
262 }
263
|
264 mday 1.5 void HTTPConnection::handleEnqueue(Message *message)
|
265 mike 1.2 {
|
266 kumpf 1.98 PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::handleEnqueue");
267
|
268 kumpf 1.123 if (! message)
|
269 kumpf 1.98 {
270 PEG_METHOD_EXIT();
271 return;
272 }
273
|
274 mike 1.116 AutoMutex connectionLock(_connection_mut);
|
275 kumpf 1.7
|
276 kumpf 1.98 switch (message->getType())
277 {
278 case SOCKET_MESSAGE:
279 {
|
280 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
|
281 kumpf 1.98 "HTTPConnection::handleEnqueue - SOCKET_MESSAGE");
282 SocketMessage* socketMessage = (SocketMessage*)message;
283 if (socketMessage->events & SocketMessage::READ)
284 _handleReadEvent();
285 break;
286 }
287
288 case HTTP_MESSAGE:
289 {
|
290 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
|
291 kumpf 1.98 "HTTPConnection::handleEnqueue - HTTP_MESSAGE");
292
293 _handleWriteEvent(*message);
294 break;
295 }
296
297 default:
298 // ATTN: need unexpected message error!
299 break;
|
300 kumpf 1.122 } // switch
|
301 david.dillard 1.93
|
302 kumpf 1.122 delete message;
|
303 david.dillard 1.93
|
304 kumpf 1.122 PEG_METHOD_EXIT();
|
305 brian.campbell 1.72 }
|
306 mike 1.2
|
307 brian.campbell 1.72 /*
|
308 david.dillard 1.93 * handle the message coming down from the above. This is shared client and
309 * server code. If the message is coming in chunks, then validate the chunk
|
310 brian.campbell 1.72 * sequence number. If the message is being processed on the server side,
311 * make sure the client has requested transfer encodings and/or trailers before
312 * sending them. If not, the message must be queued up until a complete flag
313 * has arrived.
314 */
315
316 Boolean HTTPConnection::_handleWriteEvent(Message &message)
317 {
|
318 kumpf 1.98 static const char func[] = "HTTPConnection::_handleWriteEvent";
319 String httpStatus;
320 HTTPMessage& httpMessage = *(HTTPMessage*)&message;
|
321 mike 1.104 Buffer& buffer = httpMessage.message;
|
322 kumpf 1.98 Boolean isFirst = message.isFirst();
323 Boolean isLast = message.isComplete();
324 Sint32 totalBytesWritten = 0;
|
325 kumpf 1.128 Uint32 messageLength = buffer.size();
|
326 mike 1.2
|
327 kumpf 1.98 try
328 {
|
329 chuck 1.95 // delivery behavior:
|
330 kumpf 1.98 // 1 handler.processing() : header + optional body?
331 // 2 handler.deliver() : 1+ fully XML encoded object(s)
332 // 3 handler.complete() : deliver() + isLast = true
333
334 Uint32 bytesRemaining = messageLength;
335 char *messageStart = (char *) buffer.getData();
336 Uint32 bytesToWrite = httpTcpBufferSize;
337 Uint32 messageIndex = message.getIndex();
338 Boolean isChunkResponse = false;
339 Boolean isChunkRequest = false;
340 Boolean isFirstException = false;
341
342 if (_isClient() == false)
343 {
344 // for null termination
345 buffer.reserveCapacity(messageLength + 1);
346 messageStart = (char *) buffer.getData();
|
347 david.dillard 1.89 messageStart[messageLength] = 0;
|
348 marek 1.131 PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2,
|
349 kumpf 1.130 "<!-- Response: queue id: %u -->\n%s",
|
350 dave.sudlik 1.125 getQueueId(),
|
351 marek 1.131 buffer.getData()));
|
352 kumpf 1.98 if (isFirst == true)
353 {
354 _incomingBuffer.clear();
355 // tracks the message coming from above
356 _transferEncodingChunkOffset = 0;
357 _mpostPrefix.clear();
358 cimException = CIMException();
359 }
360 else
361 {
362 // this is coming from our own internal code, therefore it is an
363 // internal error. somehow the chunks came out of order.
364 if (_transferEncodingChunkOffset+1 != messageIndex)
|
365 kumpf 1.122 _throwEventFailure(
366 httpStatusInternal, "chunk sequence mismatch");
|
367 kumpf 1.98 _transferEncodingChunkOffset++;
368 }
369
370 // save the first error
371 if (httpMessage.cimException.getCode() != CIM_ERR_SUCCESS)
372 {
373 httpStatus = httpMessage.cimException.getMessage();
374 if (cimException.getCode() == CIM_ERR_SUCCESS)
375 {
376 cimException = httpMessage.cimException;
|
377 kumpf 1.122 // set language to first error language (overriding
378 // anything there)
|
379 kumpf 1.98 contentLanguages = cimException.getContentLanguages();
380 isFirstException = true;
381 }
382 }
383 else if (cimException.getCode() == CIM_ERR_SUCCESS)
384 {
385 if (isFirst == true)
386 contentLanguages = httpMessage.contentLanguages;
387 else if (httpMessage.contentLanguages != contentLanguages)
|
388 kumpf 1.105 contentLanguages.clear();
|
389 kumpf 1.98 else contentLanguages = httpMessage.contentLanguages;
390 }
|
391 kumpf 1.122 // check to see if the client requested chunking OR trailers.
392 // trailers are tightly integrated with chunking, so it can also
393 // be used.
|
394 kumpf 1.98
395 if (isChunkRequested() == true)
396 {
397 isChunkRequest = true;
398 }
399 else
400 {
|
401 kumpf 1.122 // we are not sending chunks because the client did not
402 // request it
|
403 kumpf 1.98
|
404 kumpf 1.122 // save the entire FIRST error response for non-chunked error
405 // responses this will be used as the error message
|
406 kumpf 1.98
407 if (isFirstException == true)
408 {
409 // this shouldnt happen, but this is defensive ...
410 if (messageLength == 0)
411 {
412 CIMStatusCode code = httpMessage.cimException.getCode();
413 String httpDetail(cimStatusCodeToString(code));
414 char s[21];
415 sprintf(s, "%u", code);
416 String httpStatus(s);
|
417 mike 1.104 Buffer message = XmlWriter::formatHttpErrorRspMessage
|
418 kumpf 1.98 (httpStatus, String(), httpDetail);
|
419 kumpf 1.128 messageLength = message.size();
|
420 kumpf 1.98 message.reserveCapacity(messageLength+1);
421 messageStart = (char *) message.getData();
422 messageStart[messageLength] = 0;
423 }
424 cimException = CIMException(cimException.getCode(),
425 String(messageStart, messageLength));
426 }
427
428 if (isFirst == false)
429 {
430 // subsequent chunks from the server, just append
431
|
432 kumpf 1.128 messageLength += _incomingBuffer.size();
|
433 kumpf 1.98 _incomingBuffer.reserveCapacity(messageLength+1);
|
434 mike 1.104 _incomingBuffer.append(buffer.getData(), buffer.size());
|
435 kumpf 1.98 buffer.clear();
436 // null terminate
437 messageStart = (char *) _incomingBuffer.getData();
438 messageStart[messageLength] = 0;
|
439 kumpf 1.122 // put back in buffer, so the httpMessage parser can work
440 // below
|
441 kumpf 1.98 _incomingBuffer.swap(buffer);
442 }
443
444 if (isLast == false)
445 {
|
446 kumpf 1.122 // this tells the send loop below to do nothing until we
447 // are at the last response
|
448 kumpf 1.98 bytesRemaining = 0;
449 }
450 else
451 {
452 if (cimException.getCode() != CIM_ERR_SUCCESS)
453 {
454 buffer.clear();
455 // discard all data collected to this point
456 _incomingBuffer.clear();
457 String messageS = cimException.getMessage();
458 CString messageC = messageS.getCString();
459 messageStart = (char *) (const char *) messageC;
|
460 a.dunfey 1.126 messageLength = (Uint32)strlen(messageStart);
|
461 kumpf 1.98 buffer.reserveCapacity(messageLength+1);
462 buffer.append(messageStart, messageLength);
463 // null terminate
464 messageStart = (char *) buffer.getData();
465 messageStart[messageLength] = 0;
466 }
467 bytesRemaining = messageLength;
468 }
469
470 } // if not sending chunks
471
472 // We now need to adjust the contentLength line.
|
473 kumpf 1.122 // If chunking was requested and this is the first chunk, then
474 // we need to enter this block so we can adjust the header and
475 // send to the client the first set of bytes right away.
476 // If chunking was NOT requested, we have to wait for the last
477 // chunk of the message to get (and set) the size of the content
478 // because we are going to send it the traditional (i.e
479 // non-chunked) way
|
480 kumpf 1.98
481 if (isChunkRequest == true && isFirst == true ||
482 isChunkRequest == false && isLast == true)
483 {
484 // need to find the end of the header
485 String startLine;
486 Array<HTTPHeader> headers;
487 Uint32 contentLength = 0;
488
|
489 kumpf 1.122 // Note: this gets the content length from subtracting the
490 // header length from the messageLength, not by parsing the
491 // content length header field
|
492 kumpf 1.98
493 httpMessage.parse(startLine, headers, contentLength);
494 Uint32 httpStatusCode = 0;
495 String httpVersion;
496 String reasonPhrase;
|
497 kumpf 1.122 Boolean isValid = httpMessage.parseStatusLine(
498 startLine, httpVersion, httpStatusCode, reasonPhrase);
|
499 kumpf 1.98 Uint32 headerLength = messageLength - contentLength;
500 char save = messageStart[headerLength];
501 messageStart[headerLength] = 0;
502
503 char *contentLengthStart =
504 strstr(messageStart, headerNameContentLength);
505
506 char* contentLengthEnd = contentLengthStart ?
507 strstr(contentLengthStart, headerLineTerminator) : 0;
508
509 messageStart[headerLength] = save;
510
511 // the message may or may not have the content length specified
512 // depending on the type of request it is
513
514 if (contentLengthStart)
515 {
516 // the message has the content length specified.
|
517 kumpf 1.122 // If we are NOT sending a chunked response, then we need
518 // to overlay the contentLength number to reflect the
519 // actual byte count of the content (i.e message body).
520 // If we ARE sending a chunked response, then we will
521 // overlay the transferEncoding keyword name and value
|
522 kumpf 1.98 // on top of the contentLength keyword and value.
523
524 // Important note:
|
525 kumpf 1.122 // for performance reasons, the contentLength and/or
526 // transferEncoding strings are being overlayed
527 // DIRECTLY inside the message buffer WITHOUT changing
528 // the actual length in bytes of the message.
529 // The XmlWriter has been modified to pad out the
530 // maximum number in zeros to accomodate any number.
531 // The maximum contentLength name and value is identical
532 // to the transferEncoding name and value and can
533 // be easily interchanged. By doing this, we do not have
534 // to piece together the header (and more importantly,
535 // the lengthy body) all over again!
|
536 kumpf 1.98 // This is why the http line lengths are validated below
537
538 Uint32 transferEncodingLineLengthExpected =
539 headerNameTransferEncodingLength +
540 headerNameTerminatorLength +
541 headerValueTransferEncodingChunkedLength;
542
543 Uint32 contentLengthLineLengthExpected =
544 headerNameContentLengthLength +
545 headerNameTerminatorLength + numberAsStringLength;
546
547 Uint32 contentLengthLineLengthFound =
|
548 a.dunfey 1.126 (Uint32)(contentLengthEnd - contentLengthStart);
|
549 kumpf 1.98
550 if (isValid == false || ! contentLengthEnd ||
551 contentLengthLineLengthFound !=
552 transferEncodingLineLengthExpected ||
553 transferEncodingLineLengthExpected !=
554 contentLengthLineLengthExpected)
555 {
|
556 kumpf 1.122 // these should match up since this is coming
557 // directly from our code in XmlWriter! If not,
558 // some code changes have got out of sync
|
559 kumpf 1.98
560 _throwEventFailure(httpStatusInternal,
561 "content length was incorrectly formatted");
562 }
563
564 // we will be sending a chunk response if:
565 // 1. chunking has been requested AND
566 // 2. contentLength has been set
567 // (meaning a non-bodyless message has come in) OR
568 // 3. this is not the last message
|
569 kumpf 1.122 // (meaning the data is coming in pieces and we should
570 // send chunked)
|
571 kumpf 1.98
|
572 kumpf 1.122 if (isChunkRequest == true &&
573 (contentLength > 0 || isLast == false))
574 {
|
575 kumpf 1.98 isChunkResponse = true;
|
576 kumpf 1.122 }
|
577 kumpf 1.98
578 save = contentLengthStart[contentLengthLineLengthExpected];
579 contentLengthStart[contentLengthLineLengthExpected] = 0;
580
581 // overlay the contentLength value
582 if (isChunkResponse == false)
583 {
|
584 kumpf 1.122 // overwrite the content length number with the actual
585 // byte count
|
586 kumpf 1.98 char *contentLengthNumberStart = contentLengthStart +
|
587 kumpf 1.122 headerNameContentLengthLength +
588 headerNameTerminatorLength;
|
589 kumpf 1.98 char format[6];
590 sprintf (format, "%%.%uu", numberAsStringLength);
|
591 kumpf 1.122 // overwrite the bytes in buffer with the content
592 //encoding length
593 sprintf(contentLengthNumberStart,
594 format, contentLength);
595 contentLengthStart[contentLengthLineLengthExpected] =
596 save;
|
597 kumpf 1.98 }
598 else
599 {
600 // overlay the contentLength name and value with the
601 // transferEncoding name and value
602
|
603 kumpf 1.122 sprintf(contentLengthStart, "%s%s%s",
604 headerNameTransferEncoding,
605 headerNameTerminator,
606 headerValueTransferEncodingChunked);
|
607 kumpf 1.98 bytesToWrite = messageLength - contentLength;
608
|
609 kumpf 1.122 contentLengthStart[contentLengthLineLengthExpected] =
610 save;
|
611 kumpf 1.98 String operationName = headerNameOperation;
612 // look for 2-digit prefix (if mpost was use)
613 HTTPMessage::lookupHeaderPrefix(headers, operationName,
614 _mpostPrefix);
615 } // else chunk response is true
616
617 } // if content length was found
618
619 if (isChunkRequest == false)
620 {
621 if (isLast == true)
622 {
|
623 kumpf 1.105 if (contentLanguages.size() != 0)
|
624 kumpf 1.98 {
|
625 kumpf 1.122 // we must insert the content-language into the
626 // header
|
627 mike 1.104 Buffer contentLanguagesString;
|
628 kumpf 1.98
|
629 kumpf 1.122 // this is the keyword:value(s) + header line
630 // terminator
631 contentLanguagesString <<
632 headerNameContentLanguage <<
|
633 kumpf 1.98 headerNameTerminator <<
|
634 kumpf 1.105 LanguageParser::buildContentLanguageHeader(
635 contentLanguages).getCString() <<
|
636 kumpf 1.98 headerLineTerminator;
637
|
638 kumpf 1.122 Uint32 insertOffset =
639 headerLength - headerLineTerminatorLength;
640 messageLength =
|
641 kumpf 1.128 contentLanguagesString.size() + buffer.size();
|
642 kumpf 1.98 buffer.reserveCapacity(messageLength+1);
|
643 kumpf 1.128 messageLength = contentLanguagesString.size();
|
644 kumpf 1.122 messageStart =
645 (char *)contentLanguagesString.getData();
646 // insert the content language line before end
647 // of header
|
648 kumpf 1.98 // note: this can be expensive on large payloads
|
649 kumpf 1.122 buffer.insert(
650 insertOffset, messageStart, messageLength);
|
651 kumpf 1.128 messageLength = buffer.size();
|
652 kumpf 1.98 // null terminate
653 messageStart = (char *) buffer.getData();
654 messageStart[messageLength] = 0;
655 bytesRemaining = messageLength;
656 } // if there were any content languages
|
657 chuck 1.95
658 #ifdef PEGASUS_KERBEROS_AUTHENTICATION
|
659 kumpf 1.122 // The following is processing to wrap (encrypt) the
660 // response from the server when using kerberos
661 // authentications.
662 // If the security association does not exist then
663 // kerberos authentication is not being used.
664 CIMKerberosSecurityAssociation *sa =
665 _authInfo->getSecurityAssociation();
|
666 chuck 1.95
|
667 chuck 1.96 if (sa)
668 {
|
669 kumpf 1.122 // The message needs to be parsed in order to
670 // distinguish between the headers and content.
671 // When parsing, the code breaks out of the loop
672 // as soon as it finds the double separator that
673 // terminates the headers so the headers and
674 // content can be easily separated.
|
675 chuck 1.95
|
676 chuck 1.96 Boolean authrecExists = false;
|
677 kumpf 1.136 String authorization;
|
678 kumpf 1.122 if (HTTPMessage::lookupHeader(
679 headers, "WWW-Authenticate",
680 authorization, false))
|
681 chuck 1.96 {
682 authrecExists = true;
|
683 kumpf 1.98 }
684
|
685 kumpf 1.122 // The following is processing to wrap (encrypt)
686 // the response from the server when using
687 // kerberos authentications.
688 sa->wrapResponseMessage(
689 buffer, contentLength, authrecExists);
|
690 chuck 1.96 messageLength = buffer.size();
691
692 // null terminate
693 messageStart = (char *) buffer.getData();
694 messageStart[messageLength] = 0;
695 bytesRemaining = messageLength;
696 } // endif kerberos security assoc exists
|
697 chuck 1.95 #endif
|
698 kumpf 1.98 } // if this is the last chunk
699 else bytesRemaining = 0;
700 } // if chunk request is false
701 } // if this is the first chunk containing the header
702 else
703 {
704 // if chunking was requested, then subsequent messages that are
705 // received need to turn response chunking on
706
707 if (isChunkRequest == true && messageIndex > 0)
708 {
709 isChunkResponse = true;
710 bytesToWrite = messageLength;
711 }
712 }
713
714 // the data is sitting in buffer, but we want to cache it in
715 // _incomingBuffer because there may be more chunks to append
716 if (isChunkRequest == false)
717 _incomingBuffer.swap(buffer);
718
719 kumpf 1.98 } // if not a client
720
|
721 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4,
|
722 thilo.boehm 1.129 "HTTPConnection::_handleWriteEvent: Server write event.");
723
|
724 kumpf 1.98 SignalHandler::ignore(PEGASUS_SIGPIPE);
725
726 // use the next four lines to test the SIGABRT handler
727 //getSigHandle()->registerHandler(PEGASUS_SIGABRT, sig_act);
728 //getSigHandle()->activate(PEGASUS_SIGABRT);
729 //Thread t(sigabrt_generator, NULL, false);
730 //t.run();
731
732 char *sendStart = messageStart;
733 Sint32 bytesWritten = 0;
734
735 if (isFirst == true && isChunkResponse == true && bytesToWrite > 0)
736 {
737 // send the header first for chunked reponses.
738
739 // dont include header terminator yet
740 Uint32 headerLength = bytesToWrite;
741 bytesToWrite -= headerLineTerminatorLength;
|
742 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4,
|
743 thilo.boehm 1.129 "HTTPConnection::_handleWriteEvent: "
744 "Sending header for chunked reponses.");
|
745 kumpf 1.98
746 bytesWritten = _socket->write(sendStart, bytesToWrite);
747 if (bytesWritten < 0)
|
748 kumpf 1.113 _socketWriteError();
|
749 kumpf 1.98 totalBytesWritten += bytesWritten;
750 bytesRemaining -= bytesWritten;
751
752 // put in trailer header.
|
753 mike 1.104 Buffer trailer;
|
754 kumpf 1.98 trailer << headerNameTrailer << headerNameTerminator <<
755 _mpostPrefix << headerNameCode << headerValueSeparator <<
756 _mpostPrefix << headerNameDescription << headerValueSeparator <<
757 headerNameContentLanguage << headerLineTerminator;
758 sendStart = (char *) trailer.getData();
|
759 kumpf 1.128 bytesToWrite = trailer.size();
|
760 thilo.boehm 1.129
|
761 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4,
|
762 thilo.boehm 1.129 "HTTPConnection::_handleWriteEvent: "
763 "Sending trailer header for chunked responses.");
764
|
765 kumpf 1.98 bytesWritten = _socket->write(sendStart, bytesToWrite);
766
767 if (bytesWritten < 0)
|
768 kumpf 1.113 _socketWriteError();
|
769 kumpf 1.98 totalBytesWritten += bytesWritten;
770 // the trailer is outside the header buffer, so dont include in
771 // tracking variables
772
773 // now send header terminator
774 bytesToWrite = headerLineTerminatorLength;
775 sendStart = messageStart + headerLength - bytesToWrite;
|
776 thilo.boehm 1.129
|
777 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4,
|
778 thilo.boehm 1.129 "HTTPConnection::_handleWriteEvent: "
779 "Sending header terminator for chunked responses.");
780
|
781 kumpf 1.98 bytesWritten = _socket->write(sendStart, bytesToWrite);
782 if (bytesWritten < 0)
|
783 kumpf 1.113 _socketWriteError();
|
784 kumpf 1.98 totalBytesWritten += bytesWritten;
785 bytesRemaining -= bytesWritten;
786
787 messageStart += headerLength;
788 messageLength -= headerLength;
789 sendStart = messageStart;
790 bytesWritten = 0;
791 bytesToWrite = bytesRemaining;
792 } // if first chunk of chunked response
793
794 // room enough for hex string representing chunk length and terminator
795 char chunkLine[sizeof(Uint32)*2 + chunkLineTerminatorLength+1];
796
797 for (; bytesRemaining > 0; )
798 {
799 if (isChunkResponse == true)
800 {
|
801 kumpf 1.122 // send chunk line containing hex string and chunk line
802 // terminator
|
803 kumpf 1.98 sprintf(chunkLine, "%x%s", bytesToWrite, chunkLineTerminator);
804 sendStart = chunkLine;
|
805 a.dunfey 1.126 Sint32 chunkBytesToWrite = (Sint32)strlen(sendStart);
|
806 thilo.boehm 1.129
|
807 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4,
|
808 thilo.boehm 1.129 "HTTPConnection::_handleWriteEvent: "
809 "Sending chunk with chunk line terminator.");
810
|
811 kumpf 1.98 bytesWritten = _socket->write(sendStart, chunkBytesToWrite);
812 if (bytesWritten < 0)
|
813 kumpf 1.113 _socketWriteError();
|
814 kumpf 1.98 totalBytesWritten += bytesWritten;
815 }
816
817 // for chunking, we will send the entire chunk data in one send, but
818 // for non-chunking, we will send incrementally
819 else bytesToWrite = _Min(bytesRemaining, bytesToWrite);
820
821 // send non-chunked data
|
822 kumpf 1.122 //
|
823 gs.keenan 1.114 // Socket writes larger than 64K cause some platforms to return
824 // errors. When the socket write can't send the full buffer at
825 // one time, subtract the bytes sent and loop until the whole
826 // buffer has gone. Use httpTcpBufferSize for maximum send size.
|
827 kumpf 1.122 //
|
828 gs.keenan 1.114 for (; bytesRemaining > 0; )
829 {
830 sendStart = messageStart + messageLength - bytesRemaining;
831 bytesToWrite = _Min(httpTcpBufferSize, bytesRemaining);
|
832 thilo.boehm 1.129
|
833 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4,
|
834 thilo.boehm 1.129 "HTTPConnection::_handleWriteEvent: "
835 "Sending non-chunked data.");
836
|
837 gs.keenan 1.114 bytesWritten = _socket->write(sendStart, bytesToWrite);
838 if (bytesWritten < 0)
839 _socketWriteError();
840 totalBytesWritten += bytesWritten;
841 bytesRemaining -= bytesWritten;
842 }
|
843 kumpf 1.98
844 if (isChunkResponse == true)
845 {
|
846 kumpf 1.122 // send chunk terminator, on the last chunk, it is the chunk
847 // body terminator
|
848 mike 1.104 Buffer trailer;
|
849 dave.sudlik 1.125 Boolean traceTrailer = false;
|
850 kumpf 1.98 trailer << chunkLineTerminator;
851
|
852 kumpf 1.122 // on the last chunk, attach the last chunk termination
853 // sequence: 0 + last chunk terminator + optional trailer +
854 // chunkBodyTerminator
|
855 kumpf 1.98
856 if (isLast == true)
857 {
858 if (bytesRemaining > 0)
859 _throwEventFailure(httpStatusInternal,
860 "more bytes after indicated last chunk");
861 trailer << "0" << chunkLineTerminator;
862 Uint32 httpStatus = cimException.getCode();
863
864 if (httpStatus != 0)
865 {
866 char httpStatusP[11];
867 sprintf(httpStatusP, "%u",httpStatus);
868
|
869 dave.sudlik 1.125 traceTrailer = true;
|
870 kumpf 1.122 trailer << _mpostPrefix << headerNameCode <<
871 headerNameTerminator << httpStatusP <<
872 headerLineTerminator;
873 const String& httpDescription =
874 cimException.getMessage();
|
875 kumpf 1.98 if (httpDescription.size() != 0)
876 trailer << _mpostPrefix << headerNameDescription <<
|
877 kumpf 1.122 headerNameTerminator << httpDescription <<
878 headerLineTerminator;
|
879 kumpf 1.98 }
880
881 // Add Content-Language to the trailer if requested
|
882 kumpf 1.105 if (contentLanguages.size() != 0)
|
883 kumpf 1.98 {
|
884 dave.sudlik 1.125 traceTrailer = true;
|
885 kumpf 1.98 trailer << _mpostPrefix
886 << headerNameContentLanguage << headerNameTerminator
|
887 kumpf 1.105 << LanguageParser::buildContentLanguageHeader(
888 contentLanguages)
|
889 kumpf 1.98 << headerLineTerminator;
890 }
891
892 // now add chunkBodyTerminator
893 trailer << chunkBodyTerminator;
894 } // if isLast
895
|
896 dave.sudlik 1.125 if (traceTrailer)
897 {
|
898 marek 1.131 PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2,
|
899 kumpf 1.130 "<!-- Trailer: queue id: %u -->\n%s",
|
900 dave.sudlik 1.125 getQueueId(),
|
901 marek 1.131 trailer.getData()));
|
902 dave.sudlik 1.125 }
|
903 kumpf 1.98 sendStart = (char *) trailer.getData();
904 Sint32 chunkBytesToWrite = (Sint32) trailer.size();
|
905 thilo.boehm 1.129
|
906 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP,Tracer::LEVEL4,
|
907 thilo.boehm 1.129 "HTTPConnection::_handleWriteEvent: "
908 "Sending the last chunk with chunk body terminator ");
909
|
910 kumpf 1.98 bytesWritten = _socket->write(sendStart, chunkBytesToWrite);
911 if (bytesWritten < 0)
|
912 kumpf 1.113 _socketWriteError();
|
913 kumpf 1.98 totalBytesWritten += bytesWritten;
914 } // isChunkResponse == true
915
916 } // for all bytes in message
917
918 } // try
919
920 catch (Exception &e)
921 {
922 httpStatus = e.getMessage();
923 }
924 catch (...)
925 {
926 httpStatus = HTTP_STATUS_INTERNALSERVERERROR;
|
927 marek 1.134 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL2, "Unknown internal error");
|
928 kumpf 1.98 }
929
930 if (isLast == true)
931 {
932 _incomingBuffer.clear();
933 _transferEncodingTEValues.clear();
934
|
935 a.dunfey 1.127 // Reset the transfer encoding chunk offset. If it is not reset here,
936 // then a request sent with chunked encoding may not be properly read
937 // off of a connection in which a chunked response has been sent.
938 _transferEncodingChunkOffset = 0;
939
|
940 kumpf 1.98 //
941 // decrement request count
942 //
943
944 _requestCount--;
|
945 kumpf 1.133 _responsePending = false;
|
946 kumpf 1.98
947 if (httpStatus.size() == 0)
948 {
949 static const char msg[] =
950 "A response has been sent (%d of %d bytes have been written).\n"
951 "There are %d requests pending within the CIM Server.\n"
|
952 kumpf 1.122 "A total of %d requests have been processed on this "
953 "connection.";
|
954 kumpf 1.98
|
955 marek 1.131 PEG_TRACE((TRC_HTTP, Tracer::LEVEL4, msg, totalBytesWritten,
956 messageLength, _requestCount.get(), _connectionRequestCount));
|
957 kumpf 1.98 }
958
959 //
960 // Since we are done writing, update the status of entry to IDLE
961 // and notify the Monitor.
962 //
963 if (_isClient() == false)
964 {
|
965 j.alex 1.99 // Check for message to close
|
966 kumpf 1.123 if (message.getCloseConnect()== true)
|
967 j.alex 1.99 {
|
968 marek 1.131 PEG_TRACE((
|
969 j.alex 1.99 TRC_HTTP,
970 Tracer::LEVEL3,
|
971 kumpf 1.122 "HTTPConnection::_handleWriteEvent - Connection: Close "
|
972 kumpf 1.135 "in client message."));
973 _closeConnection();
|
974 kumpf 1.122 }
975 else
976 {
|
977 marek 1.131 PEG_TRACE((TRC_HTTP, Tracer::LEVEL2,
978 "Now setting state to %d", _MonitorEntry::IDLE));
|
979 j.alex 1.99 _monitor->setState (_entry_index, _MonitorEntry::IDLE);
980 _monitor->tickle();
981 }
|
982 kumpf 1.98 cimException = CIMException();
983 }
984 }
|
985 sushma.fernandes 1.78
|
986 kumpf 1.98 return httpStatus.size() == 0 ? false : true;
|
987 mday 1.5
988 }
989
990 void HTTPConnection::handleEnqueue()
991 {
992 Message* message = dequeue();
993
994 if (!message)
995 return;
996 handleEnqueue(message);
|
997 mike 1.2 }
998
999 Boolean _IsBodylessMessage(const char* line)
1000 {
1001 //ATTN: Make sure this is the right place to check for HTTP/1.1 and
1002 // HTTP/1.0 that is part of the authentication challenge header.
|
1003 kumpf 1.9 // ATTN-RK-P2-20020305: How do we make sure we have the complete list?
|
1004 h.sterling 1.75
1005 // List of request methods which do not have message bodies
1006 const char* METHOD_NAMES[] =
1007 {
1008 "GET",
1009 "HEAD"
1010 };
1011
|
1012 kumpf 1.122 // List of response codes which the client accepts and which should not
1013 // (normally) have message bodies. The RFC is vague regarding which
1014 // response codes support or require bodies. These are being reported by
1015 // class (4xx, 5xx, etc) because the CIM client should be able to handle
1016 // any status code, including those not explicitly defined in RFC 2616.
1017 // Therefore, listing codes individually will not work because the client
1018 // socket will hang on a code not in this list if no content length is
1019 // specified.
|
1020 h.sterling 1.75 // See bugzilla 1586
1021 const char* RESPONSE_CODES[] =
1022 {
|
1023 kumpf 1.98 "HTTP/1.1 3XX",
1024 "HTTP/1.0 3XX",
|
1025 h.sterling 1.75 "HTTP/1.1 4XX",
1026 "HTTP/1.0 4XX",
1027 "HTTP/1.1 5XX",
1028 "HTTP/1.0 5XX"
1029 };
1030
|
1031 david.dillard 1.93 // Check for bodyless HTTP request method
|
1032 h.sterling 1.75 const Uint32 METHOD_NAMES_SIZE = sizeof(METHOD_NAMES) / sizeof(char*);
1033
1034 for (Uint32 i = 0; i < METHOD_NAMES_SIZE; i++)
1035 {
|
1036 a.dunfey 1.126 Uint32 n = (Uint32)strlen(METHOD_NAMES[i]);
|
1037 h.sterling 1.75
|
1038 kumpf 1.98 if (strncmp(line, METHOD_NAMES[i], n) == 0 && isspace(line[n]))
1039 return true;
|
1040 h.sterling 1.75 }
1041
1042 // Check for bodyless HTTP status code
1043 const Uint32 RESPONSE_CODES_SIZE = sizeof(RESPONSE_CODES) / sizeof(char*);
1044
1045 for (Uint32 i = 0; i < RESPONSE_CODES_SIZE; i++)
1046 {
|
1047 a.dunfey 1.126 Uint32 n = (Uint32)strlen(RESPONSE_CODES[i]);
|
1048 h.sterling 1.75
|
1049 kumpf 1.98 if (strncmp(line, RESPONSE_CODES[i], n - 2) == 0 && isspace(line[n]))
|
1050 h.sterling 1.75 return true;
1051 }
1052
1053 return false;
1054 }
1055
1056 /*
1057 Boolean _IsBodylessMessage(const char* line)
1058 {
1059 //ATTN: Make sure this is the right place to check for HTTP/1.1 and
1060 // HTTP/1.0 that is part of the authentication challenge header.
1061 // ATTN-RK-P2-20020305: How do we make sure we have the complete list?
|
1062 mike 1.2 const char* METHOD_NAMES[] =
1063 {
1064 "GET",
|
1065 kumpf 1.9 "HTTP/1.1 400",
1066 "HTTP/1.0 400",
|
1067 mike 1.2 "HTTP/1.1 401",
|
1068 kumpf 1.10 "HTTP/1.0 401",
|
1069 kumpf 1.63 "HTTP/1.1 413",
1070 "HTTP/1.0 413",
|
1071 kumpf 1.60 "HTTP/1.1 500",
1072 "HTTP/1.0 500",
|
1073 kumpf 1.10 "HTTP/1.1 501",
|
1074 kumpf 1.60 "HTTP/1.0 501",
1075 "HTTP/1.1 503",
1076 "HTTP/1.0 503"
|
1077 mike 1.2 };
1078
1079 const Uint32 METHOD_NAMES_SIZE = sizeof(METHOD_NAMES) / sizeof(char*);
1080
1081 for (Uint32 i = 0; i < METHOD_NAMES_SIZE; i++)
1082 {
|
1083 kumpf 1.98 Uint32 n = strlen(METHOD_NAMES[i]);
|
1084 mike 1.2
|
1085 kumpf 1.98 if (strncmp(line, METHOD_NAMES[i], n) == 0 && isspace(line[n]))
1086 return true;
|
1087 mike 1.2 }
1088
1089 return false;
|
1090 h.sterling 1.75 }*/
|
1091 mike 1.2
|
1092 david.dillard 1.93 void HTTPConnection::_getContentLengthAndContentOffset()
|
1093 mike 1.2 {
|
1094 kumpf 1.98 static const char func[] =
|
1095 brian.campbell 1.72 "HTTPConnection::_getContentLengthAndContentOffset";
|
1096 kumpf 1.128 Uint32 size = _incomingBuffer.size();
|
1097 kumpf 1.98 if (size == 0)
1098 return;
|
1099 mike 1.2 char* data = (char*)_incomingBuffer.getData();
1100 char* line = (char*)data;
1101 char* sep;
1102 Uint32 lineNum = 0;
1103 Boolean bodylessMessage = false;
|
1104 kumpf 1.109 Boolean gotContentLength = false;
1105 Boolean gotTransferEncoding = false;
1106 Boolean gotContentLanguage = false;
1107 Boolean gotTransferTE = false;
|
1108 mike 1.2
|
1109 sushma.fernandes 1.138 while ((sep =
1110 HTTPMessage::findSeparator(line, (Uint32)(size - (line - data)))))
|
1111 mike 1.2 {
|
1112 kumpf 1.98 char save = *sep;
1113 *sep = '\0';
1114
1115 // Did we find the double separator which terminates the headers?
1116
1117 if (line == sep)
1118 {
1119 *sep = save;
1120 line = sep + ((save == '\r') ? 2 : 1);
|
1121 a.dunfey 1.126 _contentOffset = (Sint32)(line - _incomingBuffer.getData());
|
1122 kumpf 1.98
1123 // reserve space for entire non-chunked message
1124 if (_contentLength > 0)
1125 {
|
1126 dave.sudlik 1.110 try
1127 {
|
1128 kumpf 1.122 Uint32 capacity = (Uint32)(_contentLength +
|
1129 dave.sudlik 1.110 _contentOffset + 1);
1130 _incomingBuffer.reserveCapacity(capacity);
1131 data = (char *)_incomingBuffer.getData();
1132 data[capacity-1] = 0;
1133 }
|
1134 kumpf 1.123 catch (const PEGASUS_STD(bad_alloc)&)
|
1135 dave.sudlik 1.110 {
1136 _throwEventFailure(HTTP_STATUS_REQUEST_TOO_LARGE,
1137 "Error reserving space for non-chunked message");
1138 }
|
1139 kumpf 1.123 catch (...)
|
1140 dave.sudlik 1.110 {
|
1141 kumpf 1.122 _throwEventFailure(
1142 httpStatusInternal, "unexpected exception");
|
1143 dave.sudlik 1.110 }
|
1144 kumpf 1.98 }
1145
1146 break;
1147 }
|
1148 mike 1.2
|
1149 kumpf 1.98 // If this is one of the bodyless methods, then we can assume the
1150 // message is complete when the "\r\n\r\n" is encountered.
|
1151 mike 1.2
|
1152 kumpf 1.98 if (lineNum == 0 && _IsBodylessMessage(line))
1153 bodylessMessage = true;
1154
1155 // Look for the content-length if not already found:
1156
1157 char* colon = strchr(line, ':');
1158
1159 if (colon)
1160 {
1161 *colon = '\0';
1162
1163 // remove whitespace after colon before value
1164 char *valueStart = colon + 1;
|
1165 kumpf 1.123 while (*valueStart == ' ' || *valueStart == '\t')
|
1166 kumpf 1.98 valueStart++;
1167
1168 // we found some non-whitespace token
1169 if (valueStart != sep)
1170 {
1171 char *valueEnd = sep - 1;
1172
|
1173 kumpf 1.122 // now remove whitespace from end of line back to last byte
1174 // of value
|
1175 kumpf 1.123 while (*valueEnd == ' ' || *valueEnd == '\t')
|
1176 kumpf 1.98 valueEnd--;
1177
1178 char valueSave = *(valueEnd+1);
1179
1180 if (System::strcasecmp(line, headerNameContentLength) == 0)
1181 {
|
1182 kumpf 1.109 if (gotContentLength)
1183 {
1184 _throwEventFailure(HTTP_STATUS_BADREQUEST,
1185 "Duplicate Content-Length header detected");
1186 }
1187 gotContentLength = true;
1188
|
1189 kumpf 1.98 if (_transferEncodingValues.size() == 0)
|
1190 kumpf 1.109 {
1191 // Use a dummy character conversion to catch an
1192 // invalid character in the value.
1193 char dummy;
1194 if (sscanf(valueStart, "%d%c",
1195 &_contentLength, &dummy) != 1)
1196 {
1197 _throwEventFailure(HTTP_STATUS_BADREQUEST,
1198 "Invalid Content-Length header detected");
1199 }
1200 }
1201 else
1202 {
1203 _contentLength = -1;
1204 }
|
1205 kumpf 1.98 }
|
1206 kumpf 1.122 else if (System::strcasecmp(
1207 line, headerNameTransferEncoding) == 0)
|
1208 kumpf 1.98 {
|
1209 kumpf 1.109 if (gotTransferEncoding)
1210 {
1211 _throwEventFailure(HTTP_STATUS_BADREQUEST,
1212 "Duplicate Transfer-Encoding header detected");
1213 }
1214 gotTransferEncoding = true;
1215
|
1216 kumpf 1.98 _transferEncodingValues.clear();
1217
|
1218 kumpf 1.122 if (strcmp(valueStart,
1219 headerValueTransferEncodingChunked) == 0)
1220 _transferEncodingValues.append(
1221 headerValueTransferEncodingChunked);
1222 else if (strcmp(valueStart,
1223 headerValueTransferEncodingIdentity) == 0)
|
1224 kumpf 1.98 ; // do nothing
1225 else _throwEventFailure(HTTP_STATUS_NOTIMPLEMENTED,
|
1226 kumpf 1.122 "unimplemented transfer-encoding value");
|
1227 kumpf 1.98 _contentLength = -1;
1228 }
|
1229 kumpf 1.122 else if (System::strcasecmp(
1230 line, headerNameContentLanguage) == 0)
|
1231 kumpf 1.98 {
|
1232 kumpf 1.122 // note: if this is a chunked header, then this will be
1233 // ignored later
1234 String contentLanguagesString(
|
1235 a.dunfey 1.126 valueStart, (Uint32)(valueEnd - valueStart + 1));
|
1236 kumpf 1.98 try
1237 {
|
1238 kumpf 1.109 ContentLanguageList contentLanguagesValue =
|
1239 kumpf 1.105 LanguageParser::parseContentLanguageHeader(
1240 contentLanguagesString);
|
1241 kumpf 1.109
1242 if (gotContentLanguage)
1243 {
1244 // Append these content languages to the existing
1245 // list.
1246 for (Uint32 i = 0;
1247 i < contentLanguagesValue.size(); i++)
1248 {
1249 contentLanguages.append(
1250 contentLanguagesValue.getLanguageTag(i));
1251 }
1252 }
1253 else
1254 {
1255 contentLanguages = contentLanguagesValue;
1256 gotContentLanguage = true;
1257 }
|
1258 kumpf 1.98 }
|
1259 kumpf 1.123 catch (...)
|
1260 kumpf 1.98 {
|
1261 marek 1.131 PEG_TRACE((TRC_HTTP, Tracer::LEVEL2,
|
1262 kumpf 1.122 "HTTPConnection: ERROR: contentLanguages had "
1263 "parsing failure. clearing languages. error "
1264 "data=%s",
|
1265 marek 1.131 (const char *)contentLanguagesString.getCString()));
|
1266 kumpf 1.105 contentLanguages.clear();
|
1267 kumpf 1.98 }
1268 }
1269 else if (System::strcasecmp(line, headerNameTransferTE) == 0)
1270 {
|
1271 kumpf 1.109 if (gotTransferTE)
1272 {
1273 _throwEventFailure(HTTP_STATUS_BADREQUEST,
1274 "Duplicate TE header detected");
1275 }
1276 gotTransferTE = true;
1277
|
1278 kumpf 1.98 _transferEncodingTEValues.clear();
1279 static const char valueDelimiter = ',';
1280 char *valuesStart = valueStart;
1281
1282 // now tokenize the values
1283 while (*valuesStart)
1284 {
1285 // strip off whitepsace from the front
|
1286 kumpf 1.123 while (*valuesStart == ' ' || *valuesStart == '\t')
|
1287 kumpf 1.98 valuesStart++;
1288
1289 if (valuesStart == valueEnd)
1290 break;
1291
1292 char *v = strchr(valuesStart, valueDelimiter);
1293 if (v)
1294 {
1295 if (v == valuesStart)
1296 {
1297 valuesStart++;
1298 continue;
1299 }
1300 v--;
1301 // strip off whitespace from the end
|
1302 kumpf 1.123 while (*v == ' ' || *v == '\t')
|
1303 kumpf 1.98 v--;
1304 v++;
1305 *v = 0;
1306 }
1307
1308 _transferEncodingTEValues.append(valuesStart);
1309
1310 if (v)
1311 {
1312 *v = valueDelimiter;
1313 valuesStart = v+1;
1314 }
1315 else break;
1316 }
1317 }
1318
1319 *(valueEnd+1) = valueSave;
1320 } // if some value tokens
1321
1322 *colon = ':';
1323 }
1324 kumpf 1.98
1325 *sep = save;
1326 line = sep + ((save == '\r') ? 2 : 1);
1327 lineNum++;
|
1328 mike 1.2 }
1329
1330 if (_contentOffset != -1 && bodylessMessage)
|
1331 kumpf 1.98 _contentLength = 0;
|
1332 mike 1.2 }
1333
1334 void HTTPConnection::_clearIncoming()
1335 {
1336 _contentOffset = -1;
1337 _contentLength = -1;
1338 _incomingBuffer.clear();
|
1339 kumpf 1.98 _mpostPrefix.clear();
1340 contentLanguages.clear();
|
1341 mike 1.2 }
1342
1343 void HTTPConnection::_closeConnection()
1344 {
|
1345 kumpf 1.98 // return - don't send the close connection message.
1346 // let the monitor dispatch function do the cleanup.
1347 PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::_closeConnection");
1348 _connectionClosePending = true;
1349
1350 // NOTE: if there is a response pending while a close connection request
1351 // occurs, then this indicates potential activity on this connection apart
1352 // from this thread of execution (although this code here is locked, other
1353 // threads may be waiting on this one.)
1354 // The caller MUST check this value before attempting a delete of this
1355 // connnection, otherwise the delete may occur while other chunked responses
1356 // are waiting to be delivered through this connection.
1357 // This condition may happen on a client error/timeout/interrupt/disconnect
1358
1359 if (_isClient() == false)
1360 {
1361 if (_responsePending == true)
1362 {
|
1363 marek 1.131 PEG_TRACE((TRC_DISCARDED_DATA, Tracer::LEVEL2,
|
1364 kumpf 1.122 "HTTPConnection::_closeConnection - Close connection "
1365 "requested while responses are still expected on this "
1366 "connection. connection=0x%p, socket=%d\n",
|
1367 marek 1.131 (void*)this, getSocket()));
|
1368 kumpf 1.61
|
1369 kumpf 1.98 }
1370
1371 // still set to DYING
|
1372 marek 1.131 PEG_TRACE((TRC_HTTP, Tracer::LEVEL2,
1373 "Now setting state to %d", _MonitorEntry::DYING));
|
1374 kumpf 1.98 _monitor->setState (_entry_index, _MonitorEntry::DYING);
1375 _monitor->tickle();
1376 }
1377
1378 if (_connectionRequestCount == 0)
1379 {
|
1380 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
|
1381 kumpf 1.122 "HTTPConnection::_closeConnection - Connection being closed "
1382 "without receiving any requests.");
|
1383 kumpf 1.98 }
1384
1385 PEG_METHOD_EXIT();
|
1386 kumpf 1.3
|
1387 kumpf 1.98 // Message* message= new CloseConnectionMessage(_socket->getSocket));
1388 // message->dest = _ownerMessageQueue->getQueueId();
|
1389 mday 1.6 // SendForget(message);
|
1390 mday 1.19 // _ownerMessageQueue->enqueue(message);
|
1391 mike 1.2 }
1392
|
1393 brian.campbell 1.81 Boolean HTTPConnection::isChunkRequested()
1394 {
|
1395 kumpf 1.98 Boolean answer = false;
1396 if (_transferEncodingTEValues.size() > 0 &&
1397 (Contains(_transferEncodingTEValues, String(headerValueTEchunked)) ||
1398 Contains(_transferEncodingTEValues, String(headerValueTEtrailers))))
1399 answer = true;
|
1400 chuck 1.95
1401 #ifdef PEGASUS_KERBEROS_AUTHENTICATION
|
1402 kumpf 1.98 CIMKerberosSecurityAssociation *sa = _authInfo->getSecurityAssociation();
|
1403 chuck 1.95
|
1404 kumpf 1.98 if (sa)
1405 {
1406 answer = false;
1407 }
|
1408 chuck 1.95 #endif
1409
|
1410 kumpf 1.98 return answer;
|
1411 brian.campbell 1.81 }
1412
|
1413 marek 1.115 void HTTPConnection::setSocketWriteTimeout(Uint32 socketWriteTimeout)
1414 {
1415 _socket->setSocketWriteTimeout(socketWriteTimeout);
1416 }
1417
1418
|
1419 brian.campbell 1.72 // determine if the current code being executed is on the client side
1420
1421 Boolean HTTPConnection::_isClient()
1422 {
|
1423 kumpf 1.98 return strcmp(get_owner().getQueueName(),
1424 PEGASUS_QUEUENAME_HTTPCONNECTOR) == 0 ? true : false;
|
1425 brian.campbell 1.72 }
1426
1427 /*
1428 * determine if the data read in should be treated as transfer encoded data.
1429 * If so, proceed to strip out the transfer encoded meta data within the body
|
1430 david.dillard 1.93 * but the headers relating to transfer encoding will remain unchanged.
1431 * One should refer to the transfer encoding section of the HTTP protocol
|
1432 brian.campbell 1.72 * specification to understand the parsing semantics below.
1433 * NOTE: this function is coded as a syncronous read! The entire message will
1434 * be read in before the message leaves this class and is passed up to the
1435 * client application.
1436 */
1437
|
1438 david.dillard 1.94 void HTTPConnection::_handleReadEventTransferEncoding()
|
1439 brian.campbell 1.72 {
|
1440 kumpf 1.122 static const char func[] =
1441 "HTTPConnection::_handleReadEventTransferEncoding";
|
1442 kumpf 1.98 PEG_METHOD_ENTER(TRC_HTTP, func);
|
1443 kumpf 1.122
|
1444 kumpf 1.128 Uint32 messageLength = _incomingBuffer.size();
|
1445 kumpf 1.98 Uint32 headerLength = (Uint32) _contentOffset;
1446
1447 // return immediately under these conditions:
1448
1449 // - Header terminator has not been reached yet (_contentOffset < 0)
1450 // - This is a non-transfer encoded message because the content length
1451 // has been set from the given header value (_contentLength > 0)
1452 // (_contentLength == 0 means bodyless, so return too - section 4.3)
1453 // - The message read in so far is <= to the header length
1454 // - No transfer encoding has been declared in the header.
1455
1456 if (_contentOffset < 0 || _contentLength >= 0 ||
1457 messageLength <= headerLength || _transferEncodingValues.size() == 0)
1458 {
1459 PEG_METHOD_EXIT();
1460 return;
1461 }
1462
1463 // on the first chunk in the message, set the encoding offset to the content
1464 // offset
1465
1466 kumpf 1.98 if (_transferEncodingChunkOffset == 0)
1467 _transferEncodingChunkOffset = (Uint32) _contentOffset;
|
1468 brian.campbell 1.72
|
1469 kumpf 1.98 char *headerStart = (char *) _incomingBuffer.getData();
1470 char *messageStart = headerStart;
|
1471 brian.campbell 1.72
|
1472 kumpf 1.98 // loop thru the received data (so far) and strip out all chunked meta data.
1473 // this logic assumes that the data read in may be only partial at any point
1474 // during the parsing. the variable _transferEncodingChunkOffset represents
1475 // the byte offset (from the start of the message) of the last NON completed
1476 // chunk parsed within the message. Remember that the tcp reader has padded
1477 // the buffer with a terminating null for easy string parsing.
|
1478 brian.campbell 1.72
|
1479 kumpf 1.98 for (;;)
1480 {
1481 // we have parsed the length, but not all bytes of chunk have been read
1482 // in yet
1483 if (_transferEncodingChunkOffset >= messageLength)
1484 break;
1485
1486 // this is the length from _transferEncodingChunkOffset to the end
1487 // of the message (so far). It represents the bytes that have not been
1488 // processed yet
1489
1490 Uint32 remainderLength = messageLength - _transferEncodingChunkOffset;
1491
1492 // the start of the first fully non-parsed chunk of this interation
1493 char *chunkLineStart = messageStart + _transferEncodingChunkOffset;
1494 char *chunkLineEnd = chunkLineStart;
1495
|
1496 kumpf 1.122 // Find the end of the hex string representing the data portion
1497 // length of the current chunk. Note that we must hit at least one
1498 // non-hexdigit (except null) to know we have read in the complete
1499 // number
|
1500 kumpf 1.98
1501 while (isxdigit(*chunkLineEnd))
1502 chunkLineEnd++;
1503
1504 if (! *chunkLineEnd)
1505 break;
1506
1507 // This is the parsed chunk length in hex. From here on, this many bytes
1508 // plus the chunk terminator (AFTER this chunk line is done) must be
1509 // read in to constitute a complete chunk in which
1510 // _transferEncodingChunkOffset can be incremented to the next chunk
1511
1512 Uint32 chunkLengthParsed =
1513 (Uint32) strtoul((const char *)chunkLineStart, 0, 16);
1514
1515 // this also covers strings stated even larger
1516 if (chunkLengthParsed == PEG_NOT_FOUND)
1517 _throwEventFailure(HTTP_STATUS_REQUEST_TOO_LARGE,
1518 "stated chunk length too large");
1519
1520 char *chunkExtensionStart = chunkLineEnd;
1521 kumpf 1.98 chunkLineEnd = strstr(chunkLineEnd, chunkLineTerminator);
1522
|
1523 kumpf 1.122 // If we have not received the chunk line terminator yet, then
1524 // return and wait for the next iteration. This is done because the
1525 // hex length given only represents the non-meta data, not the chunk
1526 // line itself.
|
1527 kumpf 1.98
1528 if (!chunkLineEnd)
1529 break;
1530
|
1531 kumpf 1.122 // the token after the hex digit must be either the chunk line
1532 // terminator or the chunk extension terminator. If not, the sender
1533 // has sent an illegal chunked encoding syntax.
|
1534 kumpf 1.98
1535 if (strncmp(chunkExtensionStart, chunkExtensionTerminator,
1536 chunkExtensionTerminatorLength) != 0 &&
1537 strncmp(chunkExtensionStart, chunkLineTerminator,
1538 chunkLineTerminatorLength) != 0)
|
1539 kumpf 1.122 {
1540 _throwEventFailure(
1541 HTTP_STATUS_BADREQUEST, "missing chunk extension");
1542 }
|
1543 kumpf 1.98
1544 chunkLineEnd += chunkLineTerminatorLength;
|
1545 a.dunfey 1.126 Uint32 chunkLineLength = (Uint32)(chunkLineEnd - chunkLineStart);
|
1546 kumpf 1.98 Uint32 chunkMetaLength = chunkLineLength;
1547 if (chunkLengthParsed > 0)
1548 chunkMetaLength += chunkTerminatorLength;
1549 Uint32 chunkTerminatorOffset = _transferEncodingChunkOffset +
1550 chunkLineLength + chunkLengthParsed;
1551
1552 // The parsed length represents the non-meta data bytes which starts
1553 // after the chunk line terminator has been received.
|
1554 kumpf 1.122 // If we dont have enough remainder bytes to process from the length
1555 // parsed then return and wait for the next iteration.
|
1556 kumpf 1.98
|
1557 jim.wunderlich 1.108 //
1558 // Also, if this is the last chunk, then we have to know if there
|
1559 kumpf 1.122 // is enough data in here to be able to verify that meta crlf for
|
1560 jim.wunderlich 1.108 // the end of the whole chunked message is present.
|
1561 kumpf 1.122 // If chunkLengthParsed + chunkMetaLenght == reminderLength, it
|
1562 jim.wunderlich 1.108 // means that there is a space only for meta crlf of the last chunk.
|
1563 kumpf 1.122 // Therefore go back and re-read socket until you get enough data
1564 // for at least 2 crlfs. One for the end of the last chunk or
1565 // the end of the optional trailer, and one for the end of whole
|
1566 jim.wunderlich 1.108 // message.
1567
1568 if (chunkLengthParsed + chunkMetaLength >= remainderLength)
|
1569 kumpf 1.98 break;
1570
|
1571 kumpf 1.122 // at this point we have a complete chunk. proceed and strip out
1572 // meta-data
1573 // NOTE: any time "remove" is called on the buffer, many variables
1574 // must be recomputed to reflect the data removed.
|
1575 brian.campbell 1.72
|
1576 kumpf 1.98 // remove the chunk length line
1577 _incomingBuffer.remove(_transferEncodingChunkOffset, chunkLineLength);
|
1578 kumpf 1.128 messageLength = _incomingBuffer.size();
|
1579 kumpf 1.98 // always keep the byte after the last data byte null for easy string
1580 // processing.
|
1581 brian.campbell 1.72 messageStart[messageLength] = 0;
1582
|
1583 kumpf 1.98 // recalculate since we just removed the chunk length line
1584 chunkTerminatorOffset -= chunkLineLength;
1585
1586 // is this the last chunk ?
1587 if (chunkLengthParsed == 0)
1588 {
1589 // We are at the last chunk. The only remaining data should be:
1590 // 1. optional trailer first
|
1591 kumpf 1.122 // 2. message terminator (will remain on incoming buffer and
1592 // passed up)
|
1593 kumpf 1.98
1594 remainderLength -= chunkLineLength;
1595
1596 CIMStatusCode cimStatusCode = CIM_ERR_SUCCESS;
1597 Uint32 httpStatusCode = HTTP_STATUSCODE_OK;
1598 String httpStatus;
1599 String cimErrorValue;
1600
1601 // is there an optional trailer ?
1602 if (remainderLength > chunkBodyTerminatorLength)
1603 {
|
1604 kumpf 1.122 Uint32 trailerLength =
1605 remainderLength - chunkBodyTerminatorLength;
|
1606 kumpf 1.98 Uint32 trailerOffset = _transferEncodingChunkOffset;
1607 char *trailerStart = messageStart + trailerOffset;
1608 char *trailerTerminatorStart = trailerStart + trailerLength -
1609 trailerTerminatorLength;
1610
1611 // no trailer terminator before end of chunk body ?
1612 if (strncmp(trailerTerminatorStart, trailerTerminator,
1613 trailerTerminatorLength) != 0)
1614 _throwEventFailure(HTTP_STATUS_BADREQUEST,
1615 "No chunk trailer terminator received");
1616
|
1617 mike 1.104 Buffer trailer;
|
1618 kumpf 1.98 // add a dummy startLine so that the parser works
1619 trailer << " " << headerLineTerminator;
1620
1621 char save = trailerStart[trailerLength];
1622 trailerStart[trailerLength] = 0;
1623 trailer << trailerStart;
1624 trailerStart[trailerLength] = save;
1625
1626 _incomingBuffer.remove(trailerOffset, trailerLength);
|
1627 kumpf 1.128 messageLength = _incomingBuffer.size();
|
1628 kumpf 1.98 messageStart[messageLength] = 0;
1629 remainderLength -= trailerLength;
1630
1631 // parse the trailer looking for the code and description
1632 String startLine;
1633 Array<HTTPHeader> headers;
1634 Uint32 contentLength = 0;
1635 HTTPMessage httpTrailer(trailer);
1636 httpTrailer.parse(startLine, headers, contentLength);
1637
1638 String cimErrorName = headerNameError;
1639 // first look for cim error. this is an http level error
1640 Boolean found = false;
1641
|
1642 kumpf 1.122 found = httpTrailer.lookupHeader(
1643 headers, cimErrorName, cimErrorValue, true);
|
1644 kumpf 1.98
1645 if (found == true)
1646 {
|
1647 kumpf 1.122 // we have a cim error. parse the header to get the
1648 // original http level error if any, otherwise, we have
1649 // to make one up.
|
1650 kumpf 1.98
|
1651 mike 1.104 Buffer header(messageStart, headerLength);
|
1652 kumpf 1.98 String startLine;
1653 Array<HTTPHeader> headers;
1654 Uint32 contentLength = 0;
1655 HTTPMessage httpHeader(header);
1656 httpHeader.parse(startLine, headers, contentLength);
1657 String httpVersion;
|
1658 kumpf 1.122 Boolean isValid = httpHeader.parseStatusLine(
1659 startLine, httpVersion, httpStatusCode,httpStatus);
|
1660 kumpf 1.98 if (isValid == false || httpStatusCode == 0 ||
1661 httpStatusCode == HTTP_STATUSCODE_OK)
1662 {
1663 // ATTN: make up our own http code if not given ?
1664 httpStatusCode = (Uint32) HTTP_STATUSCODE_BADREQUEST;
1665 httpStatus = HTTP_STATUS_BADREQUEST;
1666 }
1667 }
1668 else
1669 {
1670 String codeName = headerNameCode;
1671 String codeValue;
|
1672 kumpf 1.122 found = httpTrailer.lookupHeader(
1673 headers, codeName, codeValue, true);
|
1674 kumpf 1.98 if (found == true && codeValue.size() > 0 &&
|
1675 kumpf 1.122 (cimStatusCode = (CIMStatusCode)atoi(
1676 codeValue.getCString())) > 0)
|
1677 kumpf 1.98 {
|
1678 kumpf 1.122 HTTPMessage::lookupHeaderPrefix(
1679 headers, codeName, _mpostPrefix);
1680 httpStatus = _mpostPrefix + codeName +
1681 headerNameTerminator + codeValue +
1682 headerLineTerminator;
|
1683 kumpf 1.98
1684 // look for cim status description
1685 String descriptionName = headerNameDescription;
1686 String descriptionValue;
|
1687 kumpf 1.122 found = httpTrailer.lookupHeader(
1688 headers, descriptionName, descriptionValue, true);
|
1689 kumpf 1.98 if (descriptionValue.size() == 0)
|
1690 kumpf 1.122 {
1691 descriptionValue =
1692 cimStatusCodeToString(cimStatusCode);
1693 }
|
1694 kumpf 1.98
|
1695 kumpf 1.122 httpStatus = httpStatus + _mpostPrefix +
1696 descriptionName + headerNameTerminator +
1697 descriptionValue + headerLineTerminator;
|
1698 kumpf 1.98
1699 } // if found a cim status code
1700
1701 // Get Content-Language out of the trailer, if it is there
1702 String contentLanguagesString;
1703 found = httpTrailer.lookupHeader(headers,
1704 headerNameContentLanguage,
1705 contentLanguagesString,
1706 true);
1707
|
1708 kumpf 1.105 contentLanguages.clear();
|
1709 kumpf 1.98 if (found == true && contentLanguagesString.size() > 0)
1710 {
1711 try
1712 {
|
1713 kumpf 1.105 contentLanguages =
1714 LanguageParser::parseContentLanguageHeader(
1715 contentLanguagesString);
|
1716 kumpf 1.98 }
|
1717 kumpf 1.123 catch (...)
|
1718 kumpf 1.98 {
|
1719 marek 1.131 PEG_TRACE((TRC_HTTP, Tracer::LEVEL2,
|
1720 kumpf 1.122 "HTTPConnection: ERROR: contentLanguages had "
1721 "parsing failure. clearing languages. "
1722 "error data=%s",
1723 (const char *)contentLanguagesString.
|
1724 marek 1.131 getCString()));
|
1725 kumpf 1.105 contentLanguages.clear();
|
1726 kumpf 1.98 }
1727 }
1728
1729 } // else not a cim error
1730 } // if optional trailer present
1731
1732 char *chunkBodyTerminatorStart =
1733 messageStart + _transferEncodingChunkOffset;
1734
1735 // look for chunk body terminator
1736 if (remainderLength != chunkBodyTerminatorLength ||
1737 strncmp(chunkBodyTerminatorStart, chunkBodyTerminator,
1738 chunkBodyTerminatorLength) != 0)
1739 _throwEventFailure(HTTP_STATUS_BADREQUEST,
1740 "No chunk body terminator received");
1741
1742 // else the remainder is just the terminator, which we will leave
1743 // on the incoming buffer and pass up
1744 // (as if a non-transfer message arrived)
|
1745 brian.campbell 1.72
|
1746 kumpf 1.98 _transferEncodingChunkOffset = 0;
1747 _contentLength = messageLength - headerLength;
|
1748 brian.campbell 1.72
|
1749 kumpf 1.98 if (httpStatusCode != HTTP_STATUSCODE_OK)
1750 {
1751 _handleReadEventFailure(httpStatus, cimErrorValue);
1752 }
1753 else if (cimStatusCode != CIM_ERR_SUCCESS)
1754 {
|
1755 kumpf 1.122 // discard the XML payload data (body) according to cim
1756 // operations spec and add code and description to the
1757 // header so the next layer can interpret the error correctly
|
1758 kumpf 1.98
1759 _incomingBuffer.remove(headerLength, _contentLength);
1760 // remove the header line terminator
|
1761 kumpf 1.122 _incomingBuffer.remove(
1762 headerLength - headerLineTerminatorLength,
|
1763 kumpf 1.98 headerLineTerminatorLength);
1764 // append new status
|
1765 kumpf 1.122 _incomingBuffer.append(
1766 httpStatus.getCString(), httpStatus.size());
|
1767 kumpf 1.98
|
1768 kumpf 1.122 _incomingBuffer.append(
1769 headerLineTerminator, headerLineTerminatorLength);
1770 // null terminate - the buffer is at least as long after
1771 // removing
|
1772 kumpf 1.98 char *data = (char *)_incomingBuffer.getData();
1773 data[_incomingBuffer.size()] = 0;
1774 _contentLength = 0;
1775 _contentOffset = 0;
1776 }
|
1777 brian.campbell 1.72
|
1778 kumpf 1.98 break;
1779 } // if last chunk
1780
|
1781 kumpf 1.122 // we are NOT on the last chunk! validate that the offset where the
1782 // chunk terminator was found matches what the parsed chunk length
1783 // claimed.
|
1784 kumpf 1.98
1785 if (strncmp(messageStart + chunkTerminatorOffset, chunkTerminator,
|
1786 brian.campbell 1.72 chunkTerminatorLength) != 0)
|
1787 kumpf 1.98 _throwEventFailure(HTTP_STATUS_BADREQUEST, "Bad chunk terminator");
|
1788 brian.campbell 1.72
|
1789 kumpf 1.98 // now remove the chunk terminator
1790 _incomingBuffer.remove(chunkTerminatorOffset, chunkTerminatorLength);
|
1791 kumpf 1.128 messageLength = _incomingBuffer.size();
|
1792 kumpf 1.98 messageStart[messageLength] = 0;
|
1793 brian.campbell 1.72
|
1794 kumpf 1.122 // jump to the start of the next chunk (which may not have been
1795 // read yet)
|
1796 kumpf 1.98 _transferEncodingChunkOffset = chunkTerminatorOffset;
1797 } // for all remaining bytes containing chunks
1798
1799 PEG_METHOD_EXIT();
|
1800 brian.campbell 1.72 }
1801
1802 /*
|
1803 david.dillard 1.93 * Handle a failure on the read or an HTTP error. This is NOT meant for
|
1804 brian.campbell 1.72 * errors found in the cim response or the trailer.
1805 * The http status MAY have the detailed message attached to it using the
1806 * detail delimiter.
1807 */
1808
|
1809 kumpf 1.109 void HTTPConnection::_handleReadEventFailure(
1810 const String& httpStatusWithDetail,
1811 const String& cimError)
|
1812 brian.campbell 1.72 {
|
1813 kumpf 1.98 Uint32 delimiterFound = httpStatusWithDetail.find(httpDetailDelimiter);
1814 String httpDetail;
|
1815 kumpf 1.109 String httpStatus = httpStatusWithDetail.subString(0, delimiterFound);
|
1816 brian.campbell 1.72
|
1817 kumpf 1.98 if (delimiterFound != PEG_NOT_FOUND)
1818 {
|
1819 kumpf 1.109 httpDetail = httpStatusWithDetail.subString(
1820 delimiterFound + httpDetailDelimiter.size());
|
1821 kumpf 1.98 }
|
1822 brian.campbell 1.72
|
1823 marek 1.131 PEG_TRACE_STRING(TRC_HTTP, Tracer::LEVEL2,
|
1824 kumpf 1.109 httpStatus + httpDetailDelimiter + httpDetail +
1825 httpDetailDelimiter + cimError);
|
1826 brian.campbell 1.72
|
1827 kumpf 1.98 _requestCount++;
|
1828 mike 1.104 Buffer message;
|
1829 kumpf 1.98 message = XmlWriter::formatHttpErrorRspMessage(httpStatus, cimError,
1830 httpDetail);
1831 HTTPMessage* httpMessage = new HTTPMessage(message);
1832
1833 // this is common error code. If we are the server side, we want to send
1834 // back the error to the client, but if we are the client side, then we
1835 // simply want to queue up this error locally so the client app can receive
1836 // the error. The client side's own message queue name will be the same
1837 // as the connector name (the server would be acceptor)
|
1838 brian.campbell 1.72
|
1839 kumpf 1.98 if (_isClient() == true)
1840 {
1841 httpMessage->dest = _outputMessageQueue->getQueueId();
|
1842 brian.campbell 1.72
|
1843 kumpf 1.98 _outputMessageQueue->enqueue(httpMessage);
|
1844 brian.campbell 1.72
|
1845 kumpf 1.98 _clearIncoming();
1846 }
1847 else
1848 {
1849 // else server side processing error - send back to client
|
1850 marek 1.131 PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2,
|
1851 kumpf 1.130 "<!-- Error response: queue id: %u -->\n%s",
1852 getQueueId(),
|
1853 marek 1.131 httpMessage->message.getData()));
|
1854 kumpf 1.98 handleEnqueue(httpMessage);
1855 }
1856 _closeConnection();
|
1857 brian.campbell 1.72 }
1858
|
1859 mike 1.2 void HTTPConnection::_handleReadEvent()
1860 {
|
1861 brian.campbell 1.72 static const char func[] = "HTTPConnection::_handleReadEvent()";
1862 PEG_METHOD_ENTER(TRC_HTTP, func);
|
1863 kumpf 1.7
|
1864 kumpf 1.102 if (_acceptPending)
1865 {
1866 PEGASUS_ASSERT(!_isClient());
1867
1868 Sint32 socketAcceptStatus = _socket->accept();
1869
1870 if (socketAcceptStatus < 0)
1871 {
|
1872 marek 1.131 PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
|
1873 kumpf 1.102 "HTTPConnection: SSL_accept() failed");
1874 _closeConnection();
1875 PEG_METHOD_EXIT();
1876 return;
1877 }
1878 else if (socketAcceptStatus == 0)
1879 {
1880 // Not enough data yet to complete the SSL handshake
|
1881 marek 1.131 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL2,
|
1882 kumpf 1.102 "HTTPConnection: SSL_accept() pending");
1883 PEG_METHOD_EXIT();
1884 return;
1885 }
1886 else
1887 {
1888 // Add SSL verification information to the authentication info
|
1889 thilo.boehm 1.117 if (_socket->isSecure())
1890 {
1891 #ifndef PEGASUS_PLATFORM_ZOS_ZSERIES_IBM
|
1892 kumpf 1.122 if (_socket->isPeerVerificationEnabled() &&
1893 _socket->isCertificateVerified())
1894 {
|
1895 sushma.fernandes 1.132 _authInfo->setConnectionAuthenticated(true);
|
1896 kumpf 1.122 _authInfo->setAuthType(
1897 AuthenticationInfoRep::AUTH_TYPE_SSL);
1898 _authInfo->setClientCertificateChain(
1899 _socket->getPeerCertificateChain());
1900 }
|
1901 thilo.boehm 1.117 #else
|
1902 kumpf 1.123 if (_socket->isClientAuthenticated())
|
1903 thilo.boehm 1.117 {
|
1904 sushma.fernandes 1.132 _authInfo->setConnectionAuthenticated(true);
|
1905 kumpf 1.122 _authInfo->setAuthenticatedUser(
1906 _socket->getAuthenticatedUser());
|
1907 thilo.boehm 1.117 }
1908 #endif
1909 }
|
1910 kumpf 1.102
1911 // Go back to the select() and wait for data on the connection
1912 _acceptPending = false;
1913 PEG_METHOD_EXIT();
1914 return;
1915 }
1916 }
1917
|
1918 mike 1.2 // -- Append all data waiting on socket to incoming buffer:
1919
1920 Sint32 bytesRead = 0;
|
1921 kumpf 1.38 Boolean incompleteSecureReadOccurred = false;
|
1922 brian.campbell 1.72
|
1923 mike 1.2 for (;;)
1924 {
|
1925 kumpf 1.133 char buffer[httpTcpBufferSize];
|
1926 kumpf 1.98
1927 Sint32 n = _socket->read(buffer, sizeof(buffer)-1);
1928
1929 if (n <= 0)
1930 {
|
1931 h.sterling 1.101 if (_socket->isSecure())
|
1932 kumpf 1.98 {
1933 // It is possible that SSL_read was not able to
1934 // read the entire SSL record. This could happen
1935 // if the record was send in multiple packets
1936 // over the network and only some of the packets
1937 // are available. Since SSL requires the entire
1938 // record to successfully decrypt, the SSL_read
1939 // operation will return "0 bytes" read.
1940 // Once all the bytes of the SSL record have been read,
1941 // SSL_read will return the entire record.
1942 // The following test was added to allow
1943 // handleReadEvent to distinguish between a
1944 // disconnect and partial read of an SSL record.
1945 //
|
1946 kumpf 1.122 incompleteSecureReadOccurred =
1947 _socket->incompleteReadOccurred(n);
|
1948 kumpf 1.98 }
1949 break;
|
1950 kumpf 1.63 }
|
1951 david.dillard 1.93
|
1952 kumpf 1.98 try
1953 {
|
1954 kumpf 1.133 _incomingBuffer.reserveCapacity(_incomingBuffer.size() + n);
|
1955 kumpf 1.98 _incomingBuffer.append(buffer, n);
1956 }
|
1957 kumpf 1.123 catch (...)
|
1958 kumpf 1.98 {
1959 static const char detailP[] =
1960 "Unable to append the request to the input buffer";
|
1961 kumpf 1.133 String httpStatus =
|
1962 kumpf 1.98 HTTP_STATUS_REQUEST_TOO_LARGE + httpDetailDelimiter + detailP;
1963 _handleReadEventFailure(httpStatus);
1964 PEG_METHOD_EXIT();
1965 return;
1966 }
|
1967 david.dillard 1.93
|
1968 kumpf 1.98 bytesRead += n;
|
1969 gs.keenan 1.92 #if defined (PEGASUS_OS_VMS)
1970 if (n < sizeof(buffer))
1971 {
|
1972 kumpf 1.98 //
1973 // Read is smaller than the buffer size.
1974 // No more to read, continue.
1975 //
1976 break;
|
1977 gs.keenan 1.92 }
1978 #endif
|
1979 mike 1.2 }
|
1980 mday 1.49
|
1981 marek 1.131 PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
|
1982 kumpf 1.98 "Total bytesRead = %d; Bytes read this iteration = %d",
|
1983 marek 1.131 _incomingBuffer.size(), bytesRead));
|
1984 mike 1.2
|
1985 brian.campbell 1.72 try
1986 {
|
1987 kumpf 1.98 if (_contentOffset == -1)
1988 _getContentLengthAndContentOffset();
1989 _handleReadEventTransferEncoding();
|
1990 brian.campbell 1.72 }
|
1991 kumpf 1.123 catch (Exception& e)
|
1992 brian.campbell 1.72 {
|
1993 kumpf 1.133 _handleReadEventFailure(e.getMessage());
|
1994 kumpf 1.98 PEG_METHOD_EXIT();
1995 return;
|
1996 brian.campbell 1.72 }
|
1997 mike 1.2
|
1998 david.dillard 1.93 // -- See if the end of the message was reached (some peers signal end of
|
1999 mike 1.2 // -- the message by closing the connection; others use the content length
2000 // -- HTTP header and then there are those messages which have no bodies
2001 // -- at all).
2002
|
2003 david.dillard 1.93 if ((bytesRead == 0 && !incompleteSecureReadOccurred) ||
|
2004 h.sterling 1.101 (_contentLength != -1 && _contentOffset != -1 &&
2005 (Sint32(_incomingBuffer.size()) >= _contentLength + _contentOffset)))
|
2006 mike 1.2 {
|
2007 kumpf 1.133 // If no message was received, just close the connection
2008 if (_incomingBuffer.size() == 0)
2009 {
2010 _clearIncoming();
2011
2012 PEG_TRACE((TRC_XML_IO, Tracer::LEVEL2,
2013 "<!-- No request message received; connection closed: "
2014 "queue id: %u -->",
2015 getQueueId()));
2016 _closeConnection();
2017
2018 PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
2019 "_requestCount = %d", _requestCount.get()));
2020
2021 PEG_METHOD_EXIT();
2022 return;
2023 }
2024
2025 // A message was received, so process it. If the connection was
2026 // closed, we will handle that in the next iteration.
2027
|
2028 kumpf 1.98 HTTPMessage* message = new HTTPMessage(_incomingBuffer, getQueueId());
|
2029 a.arora 1.77 message->authInfo = _authInfo.get();
|
2030 kumpf 1.120 message->ipAddress = _ipAddress;
|
2031 kumpf 1.98 message->contentLanguages = contentLanguages;
|
2032 kumpf 1.133 message->dest = _outputMessageQueue->getQueueId();
|
2033 brian.campbell 1.85
|
2034 kumpf 1.130 if (_isClient() == false)
2035 {
|
2036 sushma.fernandes 1.138 PEG_TRACE((
2037 TRC_XML_IO,
2038 Tracer::LEVEL2,
2039 "<!-- Request: queue id: %u -->\n%s",
|
2040 kumpf 1.130 getQueueId(),
|
2041 sushma.fernandes 1.138 Tracer::getHTTPRequestMessage(
2042 _incomingBuffer).get()));
|
2043 kumpf 1.130 }
2044
|
2045 mike 1.2 //
|
2046 david.dillard 1.93 // increment request count
|
2047 mike 1.2 //
|
2048 kumpf 1.133 _requestCount++;
2049 _connectionRequestCount++;
2050 _responsePending = true;
2051
|
2052 marek 1.131 PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
2053 "_requestCount = %d", _requestCount.get()));
|
2054 david.dillard 1.93
|
2055 sushma.fernandes 1.78 //
2056 // Set the entry status to BUSY.
2057 //
2058 if (_isClient() == false && !_connectionClosePending)
2059 {
|
2060 marek 1.131 PEG_TRACE((TRC_HTTP, Tracer::LEVEL2,
2061 "Now setting state to %d", _MonitorEntry::BUSY));
|
2062 sushma.fernandes 1.78 _monitor->setState (_entry_index, _MonitorEntry::BUSY);
2063 _monitor->tickle();
2064 }
|
2065 kumpf 1.133
|
2066 mreddy 1.124 try
2067 {
2068 _outputMessageQueue->enqueue(message);
2069 }
2070 catch (Exception& e)
2071 {
2072 String httpStatus =
2073 HTTP_STATUS_BADREQUEST + httpDetailDelimiter + e.getMessage();
2074 _handleReadEventFailure(httpStatus);
2075 }
2076
|
2077 kumpf 1.98 _clearIncoming();
|
2078 mike 1.2 }
|
2079 kumpf 1.7 PEG_METHOD_EXIT();
|
2080 mike 1.2 }
2081
2082 Uint32 HTTPConnection::getRequestCount()
2083 {
|
2084 kumpf 1.123 return _requestCount.get();
|
2085 mike 1.2 }
|
2086 mday 1.18
|
2087 mday 1.19 Boolean HTTPConnection::run(Uint32 milliseconds)
2088 {
|
2089 kumpf 1.98 Boolean handled_events = false;
2090 int events = 0;
|
2091 kumpf 1.133 fd_set fdread;
|
2092 kumpf 1.98 struct timeval tv = { 0, 1 };
2093 FD_ZERO(&fdread);
2094 FD_SET(getSocket(), &fdread);
2095 events = select(FD_SETSIZE, &fdread, NULL, NULL, &tv);
|
2096 mike 1.111
2097 if (events == PEGASUS_SOCKET_ERROR)
|
2098 kumpf 1.98 return false;
2099
2100 if (events)
2101 {
2102 events = 0;
|
2103 kumpf 1.123 if (FD_ISSET(getSocket(), &fdread))
|
2104 kumpf 1.98 {
2105 events |= SocketMessage::READ;
2106 Message *msg = new SocketMessage(getSocket(), events);
2107 try
2108 {
2109 handleEnqueue(msg);
2110 }
|
2111 kumpf 1.123 catch (...)
|
2112 kumpf 1.98 {
|
2113 marek 1.131 PEG_TRACE_CSTRING(
|
2114 mreddy 1.124 TRC_DISCARDED_DATA,
2115 Tracer::LEVEL2,
|
2116 kumpf 1.98 "HTTPConnection::run handleEnqueue(msg) failure");
2117 return true;
2118 }
2119 handled_events = true;
2120 }
2121 }
|
2122 sushma.fernandes 1.78
|
2123 kumpf 1.98 return handled_events;
|
2124 mday 1.19 }
|
2125 mday 1.54
|
2126 mike 1.2 PEGASUS_NAMESPACE_END
|