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