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