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