1 karl 1.35 //%2006////////////////////////////////////////////////////////////////////////
|
2 mike 1.2 //
|
3 karl 1.19 // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
4 // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
5 // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
|
6 karl 1.14 // IBM Corp.; EMC Corporation, The Open Group.
|
7 karl 1.19 // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
8 // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
|
9 karl 1.21 // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
10 // EMC Corporation; VERITAS Software Corporation; The Open Group.
|
11 karl 1.35 // Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.;
12 // EMC Corporation; Symantec Corporation; The Open Group.
|
13 mike 1.2 //
14 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
15 kumpf 1.9 // of this software and associated documentation files (the "Software"), to
16 // deal in the Software without restriction, including without limitation the
17 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
18 mike 1.2 // sell copies of the Software, and to permit persons to whom the Software is
19 // furnished to do so, subject to the following conditions:
|
20 karl 1.35 //
|
21 kumpf 1.9 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
|
22 mike 1.2 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
23 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
24 kumpf 1.9 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
25 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
27 mike 1.2 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 //==============================================================================
31 //
32 //%/////////////////////////////////////////////////////////////////////////////
33
34 #include <Pegasus/Common/Config.h>
35 #include <iostream>
|
36 mike 1.29 #include <cstring>
|
37 mike 1.2 #include "HTTPMessage.h"
|
38 mike 1.34 #include "ArrayIterator.h"
|
39 mike 1.2
40 PEGASUS_USING_STD;
41
42 PEGASUS_NAMESPACE_BEGIN
43
|
44 mike 1.33 static const String _HTTP_HEADER_CONTENT_TYPE = "content-type";
|
45 mike 1.2
46 //------------------------------------------------------------------------------
47 //
48 // Implementation notes:
49 //
|
50 david.dillard 1.22 // According to the HTTP specification:
|
51 mike 1.2 //
52 // 1. Method names are case-sensitive.
53 // 2. Field names are case-insensitive.
54 // 3. The first line of a message is known as the "start-line".
55 // 4. Subsequent lines are known as headers.
56 // 5. Headers have a field-name and field-value.
57 // 6. Start-lines may be request-lines or status-lines. Request lines
58 // have this form:
59 //
60 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
61 //
62 // Status-lines have this form:
63 //
64 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
65 //
66 //------------------------------------------------------------------------------
67
|
68 sushma.fernandes 1.41 char* HTTPMessage::findSeparator(const char* data, Uint32 size)
|
69 mike 1.2 {
|
70 mike 1.31 const char* p = data;
|
71 mike 1.2 const char* end = p + size;
72
73 while (p != end)
74 {
|
75 kumpf 1.36 if (*p == '\r')
76 {
|
77 a.dunfey 1.38 size_t n = end - p;
|
78 kumpf 1.36
79 if (n >= 2 && p[1] == '\n')
80 return (char*)p;
81 }
82 else if (*p == '\n')
83 return (char*)p;
|
84 mike 1.2
|
85 kumpf 1.36 p++;
|
86 mike 1.2 }
87
88 return 0;
89 }
90
|
91 sushma.fernandes 1.41 void HTTPMessage::skipHeaderWhitespace(const char*& str)
|
92 venkat.puvvada 1.40 {
|
93 sushma.fernandes 1.41 while (*str && (*str == ' ' || *str == '\t'))
|
94 venkat.puvvada 1.40 {
95 ++str;
96 }
97 }
98
|
99 sushma.fernandes 1.41 Boolean HTTPMessage::expectHeaderToken(const char*& str, const char *token)
|
100 venkat.puvvada 1.40 {
101 PEGASUS_ASSERT(token);
|
102 sushma.fernandes 1.41
103 skipHeaderWhitespace(str);
|
104 venkat.puvvada 1.40 for ( ; *token ; ++str, ++token)
105 {
106 if (!*str || tolower(*str) != tolower(*token))
107 {
108 return false;
109 }
110 }
111 return true;
112 }
113
|
114 mike 1.2 HTTPMessage::HTTPMessage(
|
115 mike 1.28 const Buffer& message_,
|
116 brian.campbell 1.16 Uint32 queueId_, const CIMException *cimException_)
|
117 mike 1.2 :
|
118 david.dillard 1.22 Message(HTTP_MESSAGE),
119 message(message_),
|
120 humberto 1.15 queueId(queueId_),
|
121 kumpf 1.26 authInfo(0),
|
122 humberto 1.15 acceptLanguagesDecoded(false),
123 contentLanguagesDecoded(false)
|
124 mike 1.2 {
|
125 kumpf 1.36 if (cimException_)
126 cimException = *cimException_;
|
127 mike 1.2 }
128
|
129 mday 1.3
|
130 se.gupta 1.18 HTTPMessage::HTTPMessage(const HTTPMessage & msg)
|
131 kumpf 1.36 : Base(msg)
|
132 mday 1.3 {
|
133 kumpf 1.36 message = msg.message;
134 queueId = msg.queueId;
135 authInfo = msg.authInfo;
136 acceptLanguages = msg.acceptLanguages;
137 contentLanguages = msg.contentLanguages;
138 acceptLanguagesDecoded = msg.acceptLanguagesDecoded;
139 contentLanguagesDecoded = msg.contentLanguagesDecoded;
140 cimException = msg.cimException;
141 }
|
142 mday 1.3
143
|
144 mike 1.2 void HTTPMessage::parse(
145 String& startLine,
146 Array<HTTPHeader>& headers,
147 Uint32& contentLength) const
148 {
149 startLine.clear();
150 headers.clear();
151 contentLength = 0;
152
153 char* data = (char*)message.getData();
|
154 kumpf 1.39 Uint32 size = message.size();
|
155 david.dillard 1.20 char* line = data;
|
156 mike 1.2 char* sep;
157 Boolean firstTime = true;
158
|
159 sushma.fernandes 1.41 while ((sep = findSeparator(line, (Uint32)(size - (line - data)))))
|
160 mike 1.2 {
|
161 kumpf 1.36 // Look for double separator which terminates the header?
|
162 mike 1.2
|
163 kumpf 1.36 if (line == sep)
164 {
165 // Establish pointer to content (account for "\n" and "\r\n").
166
167 char* content = line + ((*sep == '\r') ? 2 : 1);
168
169 // Determine length of content:
170
|
171 a.dunfey 1.38 contentLength = (Uint32)(message.size() - (content - data));
|
172 kumpf 1.36 break;
173 }
174
|
175 a.dunfey 1.38 Uint32 lineLength = (Uint32)(sep - line);
|
176 kumpf 1.36
177 if (firstTime)
178 startLine.assign(line, lineLength);
179 else
180 {
181 // Find the colon:
182
183 char* colon = 0;
184
185 for (Uint32 i = 0; i < lineLength; i++)
186 {
187 if (line[i] == ':')
188 {
189 colon = &line[i];
190 break;
191 }
192 }
193
194 // This should always be true:
195
196 if (colon)
197 kumpf 1.36 {
198 // Get the name part:
199
200 char* end;
201
202 for (end = colon - 1; end > line && isspace(*end); end--)
203 ;
204
205 end++;
206
|
207 a.dunfey 1.38 String name(line, (Uint32)(end - line));
|
208 kumpf 1.36
209 // Get the value part:
210
211 char* start;
212
213 for (start = colon + 1; start < sep && isspace(*start); start++)
214 ;
215
|
216 a.dunfey 1.38 String value(start, (Uint32)(sep - start));
|
217 kumpf 1.36
218 // From the HTTP/1.1 specification (RFC 2616) section 4.2
219 // Message Headers:
220 //
221 // Multiple message-header fields with the same field-name
222 // MAY be present in a message if and only if the entire
223 // field-value for that header field is defined as a
224 // comma-separated list [i.e., #(values)]. It MUST be
225 // possible to combine the multiple header fields into one
226 // "field-name: field-value" pair, without changing the
227 // semantics of the message, by appending each subsequent
228 // field-value to the first, each separated by a comma. The
229 // order in which header fields with the same field-name are
230 // received is therefore significant to the interpretation
231 // of the combined field value, and thus a proxy MUST NOT
232 // change the order of these field values when a message is
233 // forwarded.
234
235 // This implementation concatenates duplicate header values,
236 // with a comma separator. If the resulting value is invalid,
237 // that should be detected when the value is used.
238 kumpf 1.36
239 Uint32 headerIndex = 0;
240 for (; headerIndex < headers.size(); headerIndex++)
241 {
242 if (headers[headerIndex].first == name)
243 {
244 break;
245 }
246 }
247
248 if (headerIndex == headers.size())
249 {
250 headers.append(HTTPHeader(name, value));
251 }
252 else
253 {
254 headers[headerIndex].second.append(", ").append(value);
255 }
256 }
257 }
|
258 mike 1.2
|
259 kumpf 1.36 line = sep + ((*sep == '\r') ? 2 : 1);
260 firstTime = false;
|
261 mike 1.2 }
262 }
263
|
264 joyce.j 1.24
265 #ifdef PEGASUS_DEBUG
|
266 mike 1.2 void HTTPMessage::printAll(ostream& os) const
267 {
268 Message::print(os);
269
270 String startLine;
271 Array<HTTPHeader> headers;
272 Uint32 contentLength;
273 parse(startLine, headers, contentLength);
274
|
275 karl 1.13 // get pointer to start of data.
|
276 david.dillard 1.20 const char* content = message.getData() + message.size() - contentLength;
|
277 mike 1.2 // Print the first line:
278
|
279 karl 1.13 os << endl << startLine << endl;
|
280 mike 1.2
281 // Print the headers:
282
283 Boolean image = false;
284
285 for (Uint32 i = 0; i < headers.size(); i++)
286 {
|
287 kumpf 1.36 cout << headers[i].first << ": " << headers[i].second << endl;
|
288 david.dillard 1.22
|
289 kumpf 1.36 if (String::equalNoCase(headers[i].first, _HTTP_HEADER_CONTENT_TYPE))
290 {
291 if (headers[i].second.find("image/") == 0)
292 image = true;
293 }
|
294 mike 1.2 }
295
|
296 karl 1.13 os << endl;
|
297 mike 1.2
298 // Print the content:
299
300 for (Uint32 i = 0; i < contentLength; i++)
301 {
|
302 kumpf 1.36 //char c = content[i];
|
303 mike 1.2
|
304 kumpf 1.36 if (image)
305 {
306 if ((i % 60) == 0)
307 os << endl;
308
309 char c = content[i];
310
311 if (c >= ' ' && c < '~')
312 os << c;
313 else
314 os << '.';
315 }
316 else
317 cout << content[i];
|
318 mike 1.2 }
319
|
320 karl 1.13 os << endl;
|
321 mike 1.2 }
|
322 joyce.j 1.24 #endif
|
323 mike 1.2
|
324 brian.campbell 1.16 /*
325 * Find the header prefix (i.e 2-digit number in front of cim keyword) if any.
|
326 david.dillard 1.22 * If a fieldName is given it will use that, otherwise the FIRST field
327 * starting with the standard keyword will be used. Given field names that do
|
328 brian.campbell 1.16 * not start with the standard keyword will never match.
329 * if there is a keyword match, the prefix will be populated, else set to empty
330 */
331
332 void HTTPMessage::lookupHeaderPrefix(
|
333 mike 1.34 Array<HTTPHeader>& headers_,
|
334 brian.campbell 1.16 const String& fieldName,
335 String& prefix)
336 {
|
337 kumpf 1.36 ArrayIterator<HTTPHeader> headers(headers_);
|
338 mike 1.34
|
339 kumpf 1.36 static const char keyword[] = "CIM";
340 prefix.clear();
|
341 brian.campbell 1.16
|
342 kumpf 1.36 for (Uint32 i = 0, n = headers.size(); i < n; i++)
343 {
344 const String &h = headers[i].first;
|
345 brian.campbell 1.16
|
346 kumpf 1.25 if ((h.size() >= 3) &&
347 (h[0] >= '0') && (h[0] <= '9') &&
348 (h[1] >= '0') && (h[1] <= '9') &&
349 (h[2] == Char16('-')))
|
350 kumpf 1.36 {
351 String fieldNameCurrent = h.subString(3);
|
352 brian.campbell 1.16
|
353 kumpf 1.37 // ONLY fields starting with keyword can have prefixed according
354 // to spec
|
355 kumpf 1.36 if (String::equalNoCase(fieldNameCurrent, keyword) == false)
356 continue;
357
358 prefix = h.subString(0,3);
359
360 // no field name given, just return the first prefix encountered
361 if (fieldName.size() == 0)
362 break;
363
364 if (String::equalNoCase(fieldNameCurrent, fieldName) == false)
365 prefix.clear();
366 else break;
367 }
368 }
|
369 brian.campbell 1.16 }
370
|
371 mike 1.2 Boolean HTTPMessage::lookupHeader(
|
372 mike 1.34 Array<HTTPHeader>& headers_,
|
373 mike 1.2 const String& fieldName,
374 String& fieldValue,
|
375 kumpf 1.5 Boolean allowNamespacePrefix)
|
376 mike 1.2 {
|
377 mike 1.34 ArrayIterator<HTTPHeader> headers(headers_);
378
|
379 mike 1.2 for (Uint32 i = 0, n = headers.size(); i < n; i++)
380 {
|
381 kumpf 1.5 if (String::equalNoCase(headers[i].first, fieldName) ||
382 (allowNamespacePrefix && (headers[i].first.size() >= 3) &&
|
383 kumpf 1.25 (headers[i].first[0] >= '0') && (headers[i].first[0] <= '9') &&
384 (headers[i].first[1] >= '0') && (headers[i].first[1] <= '9') &&
|
385 kumpf 1.5 (headers[i].first[2] == Char16('-')) &&
386 String::equalNoCase(headers[i].first.subString(3), fieldName)))
|
387 kumpf 1.36 {
388 fieldValue = headers[i].second;
389 return true;
390 }
|
391 mike 1.2 }
392
393 // Not found:
394 return false;
395 }
396
397 Boolean HTTPMessage::parseRequestLine(
398 const String& startLine,
399 String& methodName,
400 String& requestUri,
401 String& httpVersion)
402 {
403 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
404
405 // Extract the method-name:
406
407 Uint32 space1 = startLine.find(' ');
408
|
409 kumpf 1.10 if (space1 == PEG_NOT_FOUND)
|
410 kumpf 1.36 return false;
|
411 mike 1.2
412 methodName = startLine.subString(0, space1);
413
414 // Extrat the request-URI:
415
416 Uint32 space2 = startLine.find(space1 + 1, ' ');
417
|
418 kumpf 1.10 if (space2 == PEG_NOT_FOUND)
|
419 kumpf 1.36 return false;
|
420 mike 1.2
421 Uint32 uriPos = space1 + 1;
422
423 requestUri = startLine.subString(uriPos, space2 - uriPos);
424
425 // Extract the HTTP version:
426
427 httpVersion = startLine.subString(space2 + 1);
|
428 kumpf 1.4
429 return true;
430 }
431
432 Boolean HTTPMessage::parseStatusLine(
433 const String& statusLine,
434 String& httpVersion,
435 Uint32& statusCode,
436 String& reasonPhrase)
437 {
438 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
439 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
440
441 // Extract the HTTP version:
442
443 Uint32 space1 = statusLine.find(' ');
444
|
445 kumpf 1.10 if (space1 == PEG_NOT_FOUND)
|
446 kumpf 1.36 return false;
|
447 kumpf 1.4
448 httpVersion = statusLine.subString(0, space1);
449
450 // Extract the status code:
451
452 Uint32 space2 = statusLine.find(space1 + 1, ' ');
453
|
454 kumpf 1.10 if (space2 == PEG_NOT_FOUND)
|
455 kumpf 1.36 return false;
|
456 kumpf 1.4
457 Uint32 statusCodePos = space1 + 1;
458 String statusCodeStr;
459 statusCodeStr = statusLine.subString(statusCodePos, space2 - statusCodePos);
|
460 kumpf 1.11 if (!sscanf(statusCodeStr.getCString(), "%u", &statusCode))
|
461 kumpf 1.4 return false;
462
463 // Extract the reason phrase:
464
465 reasonPhrase = statusLine.subString(space2 + 1);
|
466 mike 1.2
467 return true;
468 }
469
|
470 kumpf 1.44 Boolean HTTPMessage::parseContentTypeHeader(
471 const String& contentTypeHeader,
472 String& type,
473 String& charset)
|
474 venkat.puvvada 1.40 {
|
475 kumpf 1.44 CString cstr = contentTypeHeader.getCString();
476 const char* str = (const char*) cstr;
477 skipHeaderWhitespace(str);
478
479 // Get the type string
|
480 venkat.puvvada 1.40
|
481 kumpf 1.44 const char* end = str;
482 while (*end && (*end != ' ') && (*end != '\t') && (*end != ';'))
|
483 venkat.puvvada 1.40 {
|
484 kumpf 1.44 end++;
|
485 venkat.puvvada 1.40 }
486
|
487 kumpf 1.44 type.assign(str, end-str);
488 str = end;
|
489 sushma.fernandes 1.41 skipHeaderWhitespace(str);
|
490 venkat.puvvada 1.40
|
491 kumpf 1.44 // Get the charset
492
493 if (*str == ';')
|
494 venkat.puvvada 1.40 {
|
495 kumpf 1.44 str++;
496 if (!expectHeaderToken(str, "charset") ||
497 !expectHeaderToken(str, "="))
498 {
499 return false;
500 }
501 skipHeaderWhitespace(str);
|
502 venkat.puvvada 1.40
|
503 kumpf 1.44 // The value may optionally be enclosed in quotes
504 if (*str == '"')
505 {
506 str++;
507 end = strchr(str, '"');
508 if (!end)
509 {
510 return false;
511 }
512 charset.assign(str, end-str);
513 str = end + 1;
514 }
515 else
|
516 dave.sudlik 1.42 {
|
517 kumpf 1.44 end = str;
518 while (*end && (*end != ' ') && (*end != '\t'))
519 {
520 end++;
521 }
522 charset.assign(str, end-str);
523 str = end;
|
524 dave.sudlik 1.42 }
525 }
|
526 kumpf 1.44 else
527 {
528 // No charset specified; assume UTF-8.
529 charset = "utf-8";
530 }
|
531 dave.sudlik 1.42
|
532 sushma.fernandes 1.41 skipHeaderWhitespace(str);
|
533 venkat.puvvada 1.40
|
534 kumpf 1.44 // Check for unexpected characters at the end of the value
|
535 venkat.puvvada 1.40 return !*str;
536 }
537
|
538 thilo.boehm 1.43 //
539 // parse the local authentication header
540 //
541 Boolean HTTPMessage::parseLocalAuthHeader(
542 const String& authHeader,
543 String& authType,
544 String& userName,
545 String& cookie)
546 {
547 PEG_METHOD_ENTER(TRC_HTTP, "HTTPMessage::parseLocalAuthHeader()");
548
549 //
550 // Extract the authentication type:
551 //
552 Uint32 space = authHeader.find(' ');
553
554 if ( space == PEG_NOT_FOUND )
555 {
556 PEG_METHOD_EXIT();
557 return false;
558 }
559 thilo.boehm 1.43
560 authType = authHeader.subString(0, space);
561
562 Uint32 startQuote = authHeader.find(space, '"');
563
564 if ( startQuote == PEG_NOT_FOUND )
565 {
566 PEG_METHOD_EXIT();
567 return false;
568 }
569
570 Uint32 endQuote = authHeader.find(startQuote + 1, '"');
571
572 if ( endQuote == PEG_NOT_FOUND )
573 {
574 PEG_METHOD_EXIT();
575 return false;
576 }
577
578 String temp = authHeader.subString(
579 startQuote + 1, (endQuote - startQuote - 1));
580 thilo.boehm 1.43
581 //
582 // Extract the user name and cookie:
583 //
584 Uint32 colon = temp.find(0, ':');
585
586 if ( colon == PEG_NOT_FOUND )
587 {
588 userName = temp;
589 }
590 else
591 {
592 userName = temp.subString(0, colon);
593 cookie = temp;
594 }
595
596 PEG_METHOD_EXIT();
597
598 return true;
599 }
600
601 thilo.boehm 1.43 //
602 // parse the HTTP authentication header
603 //
604 Boolean HTTPMessage::parseHttpAuthHeader(
605 const String& authHeader, String& authTypeString, String& cookie)
606 {
607 PEG_METHOD_ENTER(TRC_HTTP, "HTTPMessage::parseHttpAuthHeader()");
608
609 //
610 // Extract the authentication type:
611 //
612 Uint32 space = authHeader.find(' ');
613
614 if ( space == PEG_NOT_FOUND )
615 {
616 PEG_METHOD_EXIT();
617 return false;
618 }
619
620 authTypeString = authHeader.subString(0, space);
621
622 thilo.boehm 1.43 //
623 // Extract the cookie:
624 //
625 cookie = authHeader.subString(space + 1);
626
627 PEG_METHOD_EXIT();
628
629 return true;
630 }
631
|
632 mike 1.2 PEGASUS_NAMESPACE_END
|