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