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