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 // Author: Mike Brasher (mbrasher@bmc.com)
33 //
|
34 kumpf 1.4 // Modified By: Roger Kumpf, Hewlett-Packard Company (roger_kumpf@hp.com)
|
35 david 1.12 // Dave Rosckes (rosckes@us.ibm.com)
|
36 brian.campbell 1.16 // Brian G. Campbell, EMC (campbell_brian@emc.com) - PEP140/phase1
|
37 a.arora 1.17 // Amit K Arora, IBM (amita@in.ibm.com) for PEP101
|
38 kumpf 1.36 // Seema Gupta (gseema@in.ibm.com) for Bug#1096
|
39 david.dillard 1.20 // David Dillard, VERITAS Software Corp.
40 // (david.dillard@veritas.com)
|
41 mike 1.2 //
42 //%/////////////////////////////////////////////////////////////////////////////
43
44 #include <Pegasus/Common/Config.h>
45 #include <iostream>
|
46 mike 1.29 #include <cstring>
|
47 mike 1.2 #include "HTTPMessage.h"
|
48 mike 1.34 #include "ArrayIterator.h"
|
49 mike 1.2
50 PEGASUS_USING_STD;
51
52 PEGASUS_NAMESPACE_BEGIN
53
|
54 mike 1.33 static const String _HTTP_HEADER_CONTENT_TYPE = "content-type";
|
55 mike 1.2
56 //------------------------------------------------------------------------------
57 //
58 // Implementation notes:
59 //
|
60 david.dillard 1.22 // According to the HTTP specification:
|
61 mike 1.2 //
62 // 1. Method names are case-sensitive.
63 // 2. Field names are case-insensitive.
64 // 3. The first line of a message is known as the "start-line".
65 // 4. Subsequent lines are known as headers.
66 // 5. Headers have a field-name and field-value.
67 // 6. Start-lines may be request-lines or status-lines. Request lines
68 // have this form:
69 //
70 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
71 //
72 // Status-lines have this form:
73 //
74 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
75 //
76 //------------------------------------------------------------------------------
77
78 static char* _FindSeparator(const char* data, Uint32 size)
79 {
|
80 mike 1.31 const char* p = data;
|
81 mike 1.30
82 // Short-circuit by using memchr(). This will work whenever there is a
83 // "\r\n" sequence. Some clients may send incorrect headers which are
84 // only separated by "\n". In this case the code below this block
85 // will handle it.
86 {
87 const char* q = (char*)memchr(data, '\r', size);
88
89 if (q && q[1] == '\n')
90 return (char*)q;
91 }
|
92 mike 1.29
|
93 mike 1.2 const char* end = p + size;
94
95 while (p != end)
96 {
|
97 kumpf 1.36 if (*p == '\r')
98 {
99 Uint32 n = end - p;
100
101 if (n >= 2 && p[1] == '\n')
102 return (char*)p;
103 }
104 else if (*p == '\n')
105 return (char*)p;
|
106 mike 1.2
|
107 kumpf 1.36 p++;
|
108 mike 1.2 }
109
110 return 0;
111 }
112
113 HTTPMessage::HTTPMessage(
|
114 mike 1.28 const Buffer& message_,
|
115 brian.campbell 1.16 Uint32 queueId_, const CIMException *cimException_)
|
116 mike 1.2 :
|
117 david.dillard 1.22 Message(HTTP_MESSAGE),
118 message(message_),
|
119 humberto 1.15 queueId(queueId_),
|
120 kumpf 1.26 authInfo(0),
|
121 humberto 1.15 acceptLanguagesDecoded(false),
122 contentLanguagesDecoded(false)
|
123 mike 1.2 {
|
124 kumpf 1.36 if (cimException_)
125 cimException = *cimException_;
|
126 mike 1.2 }
127
|
128 mday 1.3
|
129 se.gupta 1.18 HTTPMessage::HTTPMessage(const HTTPMessage & msg)
|
130 kumpf 1.36 : Base(msg)
|
131 mday 1.3 {
|
132 kumpf 1.36 message = msg.message;
133 queueId = msg.queueId;
134 authInfo = msg.authInfo;
135 acceptLanguages = msg.acceptLanguages;
136 contentLanguages = msg.contentLanguages;
137 acceptLanguagesDecoded = msg.acceptLanguagesDecoded;
138 contentLanguagesDecoded = msg.contentLanguagesDecoded;
139 cimException = msg.cimException;
140 }
|
141 mday 1.3
142
|
143 mike 1.2 void HTTPMessage::parse(
144 String& startLine,
145 Array<HTTPHeader>& headers,
146 Uint32& contentLength) const
147 {
148 startLine.clear();
149 headers.clear();
150 contentLength = 0;
151
152 char* data = (char*)message.getData();
153 Uint32 size = message.size();
|
154 david.dillard 1.20 char* line = data;
|
155 mike 1.2 char* sep;
156 Boolean firstTime = true;
157
158 while ((sep = _FindSeparator(line, size - (line - data))))
159 {
|
160 kumpf 1.36 // Look for double separator which terminates the header?
|
161 mike 1.2
|
162 kumpf 1.36 if (line == sep)
163 {
164 // Establish pointer to content (account for "\n" and "\r\n").
165
166 char* content = line + ((*sep == '\r') ? 2 : 1);
167
168 // Determine length of content:
169
170 contentLength = message.size() - (content - data);
171 break;
172 }
173
174 Uint32 lineLength = sep - line;
175
176 if (firstTime)
177 startLine.assign(line, lineLength);
178 else
179 {
180 // Find the colon:
181
182 char* colon = 0;
183 kumpf 1.36
184 for (Uint32 i = 0; i < lineLength; i++)
185 {
186 if (line[i] == ':')
187 {
188 colon = &line[i];
189 break;
190 }
191 }
192
193 // This should always be true:
194
195 if (colon)
196 {
197 // Get the name part:
198
199 char* end;
200
201 for (end = colon - 1; end > line && isspace(*end); end--)
202 ;
203
204 kumpf 1.36 end++;
205
206 String name(line, end - line);
207
208 // Get the value part:
209
210 char* start;
211
212 for (start = colon + 1; start < sep && isspace(*start); start++)
213 ;
214
215 String value(start, sep - start);
216
217 // From the HTTP/1.1 specification (RFC 2616) section 4.2
218 // Message Headers:
219 //
220 // Multiple message-header fields with the same field-name
221 // MAY be present in a message if and only if the entire
222 // field-value for that header field is defined as a
223 // comma-separated list [i.e., #(values)]. It MUST be
224 // possible to combine the multiple header fields into one
225 kumpf 1.36 // "field-name: field-value" pair, without changing the
226 // semantics of the message, by appending each subsequent
227 // field-value to the first, each separated by a comma. The
228 // order in which header fields with the same field-name are
229 // received is therefore significant to the interpretation
230 // of the combined field value, and thus a proxy MUST NOT
231 // change the order of these field values when a message is
232 // forwarded.
233
234 // This implementation concatenates duplicate header values,
235 // with a comma separator. If the resulting value is invalid,
236 // that should be detected when the value is used.
237
238 Uint32 headerIndex = 0;
239 for (; headerIndex < headers.size(); headerIndex++)
240 {
241 if (headers[headerIndex].first == name)
242 {
243 break;
244 }
245 }
246 kumpf 1.36
247 if (headerIndex == headers.size())
248 {
249 headers.append(HTTPHeader(name, value));
250 PEG_LOGGER_TRACE((
251 Logger::STANDARD_LOG, System::CIMSERVER, 0,
252 "HTTP header name: $0, HTTP header value: $1",
253 name, value));
254 }
255 else
256 {
257 headers[headerIndex].second.append(", ").append(value);
258 PEG_LOGGER_TRACE((
259 Logger::STANDARD_LOG, System::CIMSERVER, 0,
260 "HTTP header name: $0, Updated HTTP header value: $1",
261 name, headers[headerIndex].second));
262 }
263 }
264 }
|
265 mike 1.2
|
266 kumpf 1.36 line = sep + ((*sep == '\r') ? 2 : 1);
267 firstTime = false;
|
268 mike 1.2 }
269 }
270
|
271 joyce.j 1.24
272 #ifdef PEGASUS_DEBUG
|
273 mike 1.2 void HTTPMessage::printAll(ostream& os) const
274 {
275 Message::print(os);
276
277 String startLine;
278 Array<HTTPHeader> headers;
279 Uint32 contentLength;
280 parse(startLine, headers, contentLength);
281
|
282 karl 1.13 // get pointer to start of data.
|
283 david.dillard 1.20 const char* content = message.getData() + message.size() - contentLength;
|
284 mike 1.2 // Print the first line:
285
|
286 karl 1.13 os << endl << startLine << endl;
|
287 mike 1.2
288 // Print the headers:
289
290 Boolean image = false;
291
292 for (Uint32 i = 0; i < headers.size(); i++)
293 {
|
294 kumpf 1.36 cout << headers[i].first << ": " << headers[i].second << endl;
|
295 david.dillard 1.22
|
296 kumpf 1.36 if (String::equalNoCase(headers[i].first, _HTTP_HEADER_CONTENT_TYPE))
297 {
298 if (headers[i].second.find("image/") == 0)
299 image = true;
300 }
|
301 mike 1.2 }
302
|
303 karl 1.13 os << endl;
|
304 mike 1.2
305 // Print the content:
306
307 for (Uint32 i = 0; i < contentLength; i++)
308 {
|
309 kumpf 1.36 //char c = content[i];
|
310 mike 1.2
|
311 kumpf 1.36 if (image)
312 {
313 if ((i % 60) == 0)
314 os << endl;
315
316 char c = content[i];
317
318 if (c >= ' ' && c < '~')
319 os << c;
320 else
321 os << '.';
322 }
323 else
324 cout << content[i];
|
325 mike 1.2 }
326
|
327 karl 1.13 os << endl;
|
328 mike 1.2 }
|
329 joyce.j 1.24 #endif
|
330 mike 1.2
|
331 brian.campbell 1.16 /*
332 * Find the header prefix (i.e 2-digit number in front of cim keyword) if any.
|
333 david.dillard 1.22 * If a fieldName is given it will use that, otherwise the FIRST field
334 * starting with the standard keyword will be used. Given field names that do
|
335 brian.campbell 1.16 * not start with the standard keyword will never match.
336 * if there is a keyword match, the prefix will be populated, else set to empty
337 */
338
339 void HTTPMessage::lookupHeaderPrefix(
|
340 mike 1.34 Array<HTTPHeader>& headers_,
|
341 brian.campbell 1.16 const String& fieldName,
342 String& prefix)
343 {
|
344 kumpf 1.36 ArrayIterator<HTTPHeader> headers(headers_);
|
345 mike 1.34
|
346 kumpf 1.36 static const char keyword[] = "CIM";
347 prefix.clear();
|
348 brian.campbell 1.16
|
349 kumpf 1.36 for (Uint32 i = 0, n = headers.size(); i < n; i++)
350 {
351 const String &h = headers[i].first;
|
352 brian.campbell 1.16
|
353 kumpf 1.25 if ((h.size() >= 3) &&
354 (h[0] >= '0') && (h[0] <= '9') &&
355 (h[1] >= '0') && (h[1] <= '9') &&
356 (h[2] == Char16('-')))
|
357 kumpf 1.36 {
358 String fieldNameCurrent = h.subString(3);
|
359 brian.campbell 1.16
|
360 kumpf 1.36 // ONLY fields starting with keyword can have prefixed according to spec
361 if (String::equalNoCase(fieldNameCurrent, keyword) == false)
362 continue;
363
364 prefix = h.subString(0,3);
365
366 // no field name given, just return the first prefix encountered
367 if (fieldName.size() == 0)
368 break;
369
370 if (String::equalNoCase(fieldNameCurrent, fieldName) == false)
371 prefix.clear();
372 else break;
373 }
374 }
|
375 brian.campbell 1.16 }
376
|
377 mike 1.2 Boolean HTTPMessage::lookupHeader(
|
378 mike 1.34 Array<HTTPHeader>& headers_,
|
379 mike 1.2 const String& fieldName,
380 String& fieldValue,
|
381 kumpf 1.5 Boolean allowNamespacePrefix)
|
382 mike 1.2 {
|
383 mike 1.34 ArrayIterator<HTTPHeader> headers(headers_);
384
|
385 mike 1.2 for (Uint32 i = 0, n = headers.size(); i < n; i++)
386 {
|
387 kumpf 1.5 if (String::equalNoCase(headers[i].first, fieldName) ||
388 (allowNamespacePrefix && (headers[i].first.size() >= 3) &&
|
389 kumpf 1.25 (headers[i].first[0] >= '0') && (headers[i].first[0] <= '9') &&
390 (headers[i].first[1] >= '0') && (headers[i].first[1] <= '9') &&
|
391 kumpf 1.5 (headers[i].first[2] == Char16('-')) &&
392 String::equalNoCase(headers[i].first.subString(3), fieldName)))
|
393 kumpf 1.36 {
394 fieldValue = headers[i].second;
395 return true;
396 }
|
397 mike 1.2 }
398
399 // Not found:
400 return false;
401 }
402
403 Boolean HTTPMessage::parseRequestLine(
404 const String& startLine,
405 String& methodName,
406 String& requestUri,
407 String& httpVersion)
408 {
409 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
410
411 // Extract the method-name:
412
413 Uint32 space1 = startLine.find(' ');
414
|
415 kumpf 1.10 if (space1 == PEG_NOT_FOUND)
|
416 kumpf 1.36 return false;
|
417 mike 1.2
418 methodName = startLine.subString(0, space1);
419
420 // Extrat the request-URI:
421
422 Uint32 space2 = startLine.find(space1 + 1, ' ');
423
|
424 kumpf 1.10 if (space2 == PEG_NOT_FOUND)
|
425 kumpf 1.36 return false;
|
426 mike 1.2
427 Uint32 uriPos = space1 + 1;
428
429 requestUri = startLine.subString(uriPos, space2 - uriPos);
430
431 // Extract the HTTP version:
432
433 httpVersion = startLine.subString(space2 + 1);
|
434 kumpf 1.4
435 return true;
436 }
437
438 Boolean HTTPMessage::parseStatusLine(
439 const String& statusLine,
440 String& httpVersion,
441 Uint32& statusCode,
442 String& reasonPhrase)
443 {
444 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
445 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
446
447 // Extract the HTTP version:
448
449 Uint32 space1 = statusLine.find(' ');
450
|
451 kumpf 1.10 if (space1 == PEG_NOT_FOUND)
|
452 kumpf 1.36 return false;
|
453 kumpf 1.4
454 httpVersion = statusLine.subString(0, space1);
455
456 // Extract the status code:
457
458 Uint32 space2 = statusLine.find(space1 + 1, ' ');
459
|
460 kumpf 1.10 if (space2 == PEG_NOT_FOUND)
|
461 kumpf 1.36 return false;
|
462 kumpf 1.4
463 Uint32 statusCodePos = space1 + 1;
464 String statusCodeStr;
465 statusCodeStr = statusLine.subString(statusCodePos, space2 - statusCodePos);
|
466 kumpf 1.11 if (!sscanf(statusCodeStr.getCString(), "%u", &statusCode))
|
467 kumpf 1.4 return false;
468
469 // Extract the reason phrase:
470
471 reasonPhrase = statusLine.subString(space2 + 1);
|
472 mike 1.2
473 return true;
474 }
475
476 PEGASUS_NAMESPACE_END
|