1 mike 1.2 //%/////////////////////////////////////////////////////////////////////////////
2 //
|
3 kumpf 1.27 // Copyright (c) 2000, 2001, 2002 BMC Software, Hewlett-Packard Company, IBM,
|
4 mike 1.2 // The Open Group, Tivoli Systems
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 kumpf 1.27 // of this software and associated documentation files (the "Software"), to
8 // deal in the Software without restriction, including without limitation the
9 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
10 mike 1.2 // sell copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
|
13 kumpf 1.27 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
|
14 mike 1.2 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
15 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
16 kumpf 1.27 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
18 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19 mike 1.2 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 //
22 //==============================================================================
23 //
24 // Author: Mike Brasher (mbrasher@bmc.com)
25 //
26 // Modified By:
27 // Nag Boranna, Hewlett-Packard Company(nagaraja_boranna@hp.com)
28 // Jenny Yu, Hewlett-Packard Company (jenny_yu@hp.com)
29 //
30 //%/////////////////////////////////////////////////////////////////////////////
31
32 #include <Pegasus/Common/Config.h>
|
33 kumpf 1.16 #include <Pegasus/Common/Constants.h>
|
34 mike 1.2
|
35 kumpf 1.35 #if defined(PEGASUS_PLATFORM_LINUX_IX86_GNU) || defined(PEGASUS_PLATFORM_LINUX_GENERIC_GNU) || defined(PEGASUS_PLATFORM_HPUX_ACC)
|
36 mike 1.2 #include <Pegasus/Common/Signal.h>
37 #endif
38
39 #include <iostream>
40 #include <cctype>
41 #include <cstdlib>
42 #include "Socket.h"
43 #include "TLS.h"
44 #include "HTTPConnection.h"
45 #include "MessageQueue.h"
46 #include "Monitor.h"
47 #include "HTTPMessage.h"
|
48 kumpf 1.3 #include "Tracer.h"
|
49 mike 1.2
|
50 kumpf 1.15
|
51 mike 1.2 PEGASUS_USING_STD;
52
53 PEGASUS_NAMESPACE_BEGIN
54
55 // initialize the request count
56
57 AtomicInt HTTPConnection::_requestCount = 0;
58
59 ////////////////////////////////////////////////////////////////////////////////
60 //
61 // Local routines:
62 //
63 ////////////////////////////////////////////////////////////////////////////////
64
65 static inline Uint32 _Min(Uint32 x, Uint32 y)
66 {
67 return x < y ? x : y;
68 }
69
70 static char* _FindSeparator(const char* data, Uint32 size)
71 {
72 mike 1.2 const char* p = data;
73 const char* end = p + size;
74
75 while (p != end)
76 {
77 if (*p == '\r')
78 {
79 Uint32 n = end - p;
80
81 if (n >= 2 && p[1] == '\n')
82 return (char*)p;
83 }
84 else if (*p == '\n')
85 return (char*)p;
86
87 p++;
88 }
89
90 return 0;
91 }
92
93 mike 1.2 ////////////////////////////////////////////////////////////////////////////////
94 //
95 // HTTPConnection
96 //
97 ////////////////////////////////////////////////////////////////////////////////
98
99 HTTPConnection::HTTPConnection(
100 Monitor* monitor,
101 //Sint32 socket,
102 MP_Socket* socket,
|
103 mday 1.19 MessageQueue* ownerMessageQueue,
104 MessageQueue* outputMessageQueue)
|
105 mike 1.2 :
|
106 kumpf 1.16 Base(PEGASUS_QUEUENAME_HTTPCONNECTION),
|
107 mday 1.4 _monitor(monitor),
108 _socket(socket),
109 _ownerMessageQueue(ownerMessageQueue),
110 _outputMessageQueue(outputMessageQueue),
111 _contentOffset(-1),
112 _contentLength(-1)
|
113 mike 1.2 {
|
114 kumpf 1.7 PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::HTTPConnection");
115
|
116 mday 1.4 //Socket::disableBlocking(_socket);
117 _socket->disableBlocking();
|
118 kumpf 1.12 _authInfo = new AuthenticationInfo(true);
|
119 kumpf 1.7
120 PEG_METHOD_EXIT();
|
121 mike 1.2 }
122
123 HTTPConnection::~HTTPConnection()
124 {
|
125 kumpf 1.7 PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::~HTTPConnection");
126
|
127 mike 1.2 _socket->close();
128 delete _socket;
129 delete _authInfo;
|
130 kumpf 1.7
131 PEG_METHOD_EXIT();
|
132 mike 1.2 }
133
|
134 mday 1.5
135 void HTTPConnection::handleEnqueue(Message *message)
|
136 mike 1.2 {
|
137 kumpf 1.7 PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::handleEnqueue");
138
|
139 mday 1.19 if( ! message || _dying.value() > 0 )
|
140 kumpf 1.7 {
141 PEG_METHOD_EXIT();
|
142 mday 1.5 return;
|
143 kumpf 1.7 }
|
144 mday 1.18
|
145 mday 1.5
|
146 mday 1.11 // #ifdef ENABLETIMEOUTWORKAROUNDHACK
147 // << Wed Mar 6 12:30:38 2002 mdd >>
|
148 mday 1.24 static Mutex handleEnqueue_mut = Mutex();
149 Boolean LockAcquired = false;
|
150 mday 1.11 // #endif
|
151 kumpf 1.3
|
152 mike 1.2
|
153 mday 1.11 // #ifdef ENABLETIMEOUTWORKAROUNDHACK
154 // << Wed Mar 6 12:30:48 2002 mdd >>
|
155 mday 1.24 if (pegasus_thread_self() != handleEnqueue_mut.get_owner())
156 {
157 handleEnqueue_mut.lock(pegasus_thread_self());
158 LockAcquired = true;
159 }
|
160 mday 1.11 // #endif
|
161 kumpf 1.3
|
162 mday 1.5 switch (message->getType())
163 {
164 case SOCKET_MESSAGE:
165 {
|
166 mday 1.18
|
167 kumpf 1.14 Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
|
168 kumpf 1.7 "HTTPConnection::handleEnqueue - SOCKET_MESSAGE");
|
169 mday 1.18
|
170 mday 1.5 SocketMessage* socketMessage = (SocketMessage*)message;
|
171 mike 1.2
|
172 mday 1.5 if (socketMessage->events & SocketMessage::READ)
173 _handleReadEvent();
|
174 mike 1.2
|
175 mday 1.5 break;
176 }
|
177 mike 1.2
|
178 mday 1.5 case HTTP_MESSAGE:
179 {
|
180 kumpf 1.14 Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
|
181 kumpf 1.7 "HTTPConnection::handleEnqueue - HTTP_MESSAGE");
|
182 mday 1.18
|
183 mday 1.5 HTTPMessage* httpMessage = (HTTPMessage*)message;
|
184 mike 1.2
|
185 mday 1.5 // ATTN: convert over to asynchronous write scheme:
|
186 mike 1.2
|
187 mday 1.5 // Send response message to the client (use synchronous I/O for now:
|
188 mike 1.2
|
189 kumpf 1.22 #ifdef LOCK_CONNECTION_ENABLED
|
190 mday 1.29 // lock_connection();
|
191 kumpf 1.22 #endif
|
192 mday 1.5 _socket->enableBlocking();
|
193 mike 1.2
|
194 mday 1.5 const Array<Sint8>& buffer = httpMessage->message;
195 const Uint32 CHUNK_SIZE = 16 * 1024;
|
196 mike 1.2
|
197 kumpf 1.35 #if defined(PEGASUS_PLATFORM_LINUX_IX86_GNU) || defined(PEGASUS_PLATFORM_LINUX_GENERIC_GNU) || defined(PEGASUS_PLATFORM_HPUX_ACC)
|
198 mday 1.5 SignalHandler::ignore(SIGPIPE);
|
199 mike 1.2
|
200 mday 1.5 //getSigHandle()->registerHandler(SIGSEGV,sig_act);
201 //getSigHandle()->activate(SIGSEGV);
202 // use the next two lines to test the SIGSEGV handler
203 //Thread t(::segmentation_faulter,NULL,false);
204 //t.run();
|
205 mike 1.2 #endif
|
206 kumpf 1.22
207 Uint32 totalBytesWritten = 0;
|
208 mday 1.5 for (Uint32 bytesRemaining = buffer.size(); bytesRemaining > 0; )
209 {
210 Uint32 bytesToWrite = _Min(bytesRemaining, CHUNK_SIZE);
211
212 Sint32 bytesWritten = _socket->write(
213 buffer.getData() + buffer.size() - bytesRemaining,
214 bytesToWrite);
215
216 if (bytesWritten < 0)
217 break;
218 //throw ConnectionBroken();
219
|
220 kumpf 1.22 totalBytesWritten += bytesWritten;
|
221 mday 1.5 bytesRemaining -= bytesWritten;
222 }
223 //
224 // decrement request count
225 //
226 _requestCount--;
|
227 kumpf 1.22 _socket->disableBlocking();
228
229 #ifdef LOCK_CONNECTION_ENABLED
|
230 mday 1.29 // unlock_connection();
|
231 kumpf 1.22 #endif
|
232 kumpf 1.7 Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
|
233 kumpf 1.22 "Total bytes written = %d; Buffer Size = %d; _requestCount = %d",
234 totalBytesWritten, buffer.size(), _requestCount.value());
|
235 mday 1.5
236 break;
237 }
238
239 default:
240 // ATTN: need unexpected message error!
241 break;
|
242 mday 1.19 }
|
243 mike 1.2
|
244 mday 1.5 delete message;
|
245 kumpf 1.3
|
246 mday 1.11 // #ifdef ENABLETIMEOUTWORKAROUNDHACK
247 // << Wed Mar 6 12:31:03 2002 mdd >>
|
248 mday 1.24 if (LockAcquired)
249 {
250 handleEnqueue_mut.unlock();
251 }
|
252 mday 1.11 // #endif
|
253 kumpf 1.7 PEG_METHOD_EXIT();
|
254 mday 1.5 }
255
256
257 void HTTPConnection::handleEnqueue()
258 {
259 Message* message = dequeue();
260
261 if (!message)
262 return;
263 handleEnqueue(message);
|
264 mike 1.2 }
265
266 Boolean _IsBodylessMessage(const char* line)
267 {
268 //ATTN: Make sure this is the right place to check for HTTP/1.1 and
269 // HTTP/1.0 that is part of the authentication challenge header.
|
270 kumpf 1.9 // ATTN-RK-P2-20020305: How do we make sure we have the complete list?
|
271 mike 1.2 const char* METHOD_NAMES[] =
272 {
273 "GET",
|
274 kumpf 1.9 "HTTP/1.1 400",
275 "HTTP/1.0 400",
|
276 mike 1.2 "HTTP/1.1 401",
|
277 kumpf 1.10 "HTTP/1.0 401",
278 "HTTP/1.1 501",
279 "HTTP/1.0 501"
|
280 mike 1.2 };
281
282 const Uint32 METHOD_NAMES_SIZE = sizeof(METHOD_NAMES) / sizeof(char*);
283
284 for (Uint32 i = 0; i < METHOD_NAMES_SIZE; i++)
285 {
286 Uint32 n = strlen(METHOD_NAMES[i]);
287
288 if (strncmp(line, METHOD_NAMES[i], n) == 0 && isspace(line[n]))
289 return true;
290 }
291
292 return false;
293 }
294
295 void HTTPConnection::_getContentLengthAndContentOffset()
296 {
297 char* data = (char*)_incomingBuffer.getData();
298 Uint32 size = _incomingBuffer.size();
299 char* line = (char*)data;
300 char* sep;
301 mike 1.2 Uint32 lineNum = 0;
302 Boolean bodylessMessage = false;
303
304 while ((sep = _FindSeparator(line, size - (line - data))))
305 {
306 char save = *sep;
307 *sep = '\0';
308
309 // Did we find the double separator which terminates the headers?
310
311 if (line == sep)
312 {
313 *sep = save;
314 line = sep + ((save == '\r') ? 2 : 1);
315 _contentOffset = line - _incomingBuffer.getData();
316 break;
317 }
318
319 // If this is one of the bodyless methods, then we can assume the
320 // message is complete when the "\r\n\r\n" is encountered.
321
322 mike 1.2 if (lineNum == 0 && _IsBodylessMessage(line))
323 bodylessMessage = true;
324
325 // Look for the content-length if not already found:
326
327 char* colon = strchr(line, ':');
328
329 if (colon)
330 {
331 *colon = '\0';
332
|
333 kumpf 1.37 if (System::strcasecmp(line, "content-length") == 0)
|
334 mike 1.2 _contentLength = atoi(colon + 1);
335
336 *colon = ':';
337 }
338
339 *sep = save;
340 line = sep + ((save == '\r') ? 2 : 1);
341 lineNum++;
342 }
343
344 if (_contentOffset != -1 && bodylessMessage)
345 _contentLength = 0;
346 }
347
348 void HTTPConnection::_clearIncoming()
349 {
350 _contentOffset = -1;
351 _contentLength = -1;
352 _incomingBuffer.clear();
353 }
354
355 mike 1.2 void HTTPConnection::_closeConnection()
356 {
|
357 mday 1.19 // return - don't send the close connection message.
358 // let the monitor dispatch function do the cleanup.
359 PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::_closeConnection");
360 _dying = 1;
361 PEG_METHOD_EXIT();
|
362 kumpf 1.3
|
363 mday 1.19 // Message* message= new CloseConnectionMessage(_socket->getSocket());
364 // message->dest = _ownerMessageQueue->getQueueId();
|
365 mday 1.6 // SendForget(message);
|
366 mday 1.19 // _ownerMessageQueue->enqueue(message);
|
367 mike 1.2 }
368
369 void HTTPConnection::_handleReadEvent()
370 {
|
371 kumpf 1.7 PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnection::_handleReadEvent");
372
|
373 mike 1.2 // -- Append all data waiting on socket to incoming buffer:
374
|
375 kumpf 1.22 #ifdef LOCK_CONNECTION_ENABLED
376 lock_connection();
377 #endif
378
|
379 mike 1.2 Sint32 bytesRead = 0;
|
380 kumpf 1.38 Boolean incompleteSecureReadOccurred = false;
|
381 mike 1.2 for (;;)
382 {
383 char buffer[4096];
384 Sint32 n = _socket->read(buffer, sizeof(buffer));
385
386 if (n <= 0)
|
387 kumpf 1.38 {
388 if (_socket->isSecure() && bytesRead == 0)
389 {
390 // It is possible that SSL_read was not able to
391 // read the entire SSL record. This could happen
392 // if the record was send in multiple packets
393 // over the network and only some of the packets
394 // are available. Since SSL requires the entire
395 // record to successfully decrypt, the SSL_read
396 // operation will return "0 bytes" read.
397 // Once all the bytes of the SSL record have been read,
398 // SSL_read will return the entire record.
399 // The following test was added to allow
400 // handleReadEvent to distinguish between a
401 // disconnect and partial read of an SSL record.
402 //
403 incompleteSecureReadOccurred = !_socket->incompleteReadOccurred(n);
404 }
|
405 mike 1.2 break;
|
406 kumpf 1.38 }
|
407 mike 1.2
408 _incomingBuffer.append(buffer, n);
409 bytesRead += n;
410 }
|
411 mday 1.19 Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
|
412 kumpf 1.7 "_socket->read bytesRead = %d", bytesRead);
|
413 mday 1.18
|
414 mike 1.2 // -- If still waiting for beginning of content!
415
416 if (_contentOffset == -1)
417 _getContentLengthAndContentOffset();
418
419 // -- See if the end of the message was reached (some peers signal end of
420 // -- the message by closing the connection; others use the content length
421 // -- HTTP header and then there are those messages which have no bodies
422 // -- at all).
423
|
424 kumpf 1.38 if ((bytesRead == 0 && !incompleteSecureReadOccurred) ||
|
425 mike 1.2 _contentLength != -1 &&
426 (Sint32(_incomingBuffer.size()) >= _contentLength + _contentOffset))
427 {
428 HTTPMessage* message = new HTTPMessage(_incomingBuffer, getQueueId());
429 message->authInfo = _authInfo;
430
431 //
432 // increment request count
433 //
434 _requestCount++;
|
435 kumpf 1.7 Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
436 "_requestCount = %d", _requestCount.value());
|
437 mday 1.6 message->dest = _outputMessageQueue->getQueueId();
438 // SendForget(message);
439
|
440 kumpf 1.22 #ifndef LOCK_CONNECTION_ENABLED
|
441 mike 1.2 _outputMessageQueue->enqueue(message);
|
442 kumpf 1.22 #endif
|
443 mike 1.2 _clearIncoming();
444
|
445 kumpf 1.22 #ifdef LOCK_CONNECTION_ENABLED
446 unlock_connection();
447
448 if (bytesRead > 0)
449 {
450 _outputMessageQueue->enqueue(message);
451 }
452 else
453 #else
|
454 mike 1.2 if (bytesRead == 0)
|
455 kumpf 1.22 #endif
|
456 mike 1.2 {
|
457 mday 1.19 Tracer::trace(TRC_HTTP, Tracer::LEVEL3,
458 "HTTPConnection::_handleReadEvent - bytesRead == 0 - Conection being closed.");
459 _closeConnection();
460
461 //
462 // decrement request count
463 //
464 _requestCount--;
465 Tracer::trace(TRC_HTTP, Tracer::LEVEL4,
466 "_requestCount = %d", _requestCount.value());
467
468 PEG_METHOD_EXIT();
469 return;
|
470 mike 1.2 }
471 }
|
472 kumpf 1.7 PEG_METHOD_EXIT();
|
473 mike 1.2 }
474
475 Uint32 HTTPConnection::getRequestCount()
476 {
477 return(_requestCount.value());
478 }
|
479 mday 1.18
|
480 mday 1.19
481 Boolean HTTPConnection::run(Uint32 milliseconds)
482 {
483 if( _dying.value() > 0)
484 return false;
485
|
486 mday 1.23 Boolean handled_events = false;
487 int events = 0;
488
|
489 mday 1.26 fd_set fdread; // , fdwrite;
|
490 mday 1.23 do
491 {
|
492 mday 1.25 struct timeval tv = { 0, 1 };
|
493 mday 1.23 FD_ZERO(&fdread);
494 FD_SET(getSocket(), &fdread);
|
495 mday 1.26 events = select(FD_SETSIZE, &fdread, NULL, NULL, &tv);
|
496 kumpf 1.20 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
497 mday 1.23 if(events && events != SOCKET_ERROR && _dying.value() == 0 )
|
498 kumpf 1.20 #else
|
499 mday 1.23 if(events && events != -1 && _dying.value() == 0 )
|
500 kumpf 1.20 #endif
|
501 mday 1.19 {
|
502 mday 1.23 events = 0;
503 if( FD_ISSET(getSocket(), &fdread))
504 {
505 events |= SocketMessage::READ;
|
506 mday 1.26 Message *msg = new SocketMessage(getSocket(), events);
|
507 mday 1.30 try
508 {
509 handleEnqueue(msg);
510 }
511 catch(...)
512 {
513 _monitor->_entries[_entry_index]._status = _MonitorEntry::IDLE;
514 return true;
515 }
|
516 mday 1.26 handled_events = true;
|
517 mday 1.23 }
|
518 mday 1.26 else
519 break;
|
520 mday 1.19 }
|
521 mday 1.23 else
522 break;
|
523 mday 1.24 } while(events != 0 && _dying.value() == 0);
|
524 mday 1.30 _monitor->_entries[_entry_index]._status = _MonitorEntry::IDLE;
|
525 mday 1.23 return handled_events;
|
526 mday 1.19 }
|
527 mike 1.2
528 PEGASUS_NAMESPACE_END
|