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