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