1 karl 1.21 //%2005////////////////////////////////////////////////////////////////////////
|
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 mike 1.2 //
12 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
13 kumpf 1.9 // of this software and associated documentation files (the "Software"), to
14 // deal in the Software without restriction, including without limitation the
15 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
16 mike 1.2 // sell copies of the Software, and to permit persons to whom the Software is
17 // furnished to do so, subject to the following conditions:
|
18 david.dillard 1.22 //
|
19 kumpf 1.9 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
|
20 mike 1.2 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
21 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
22 kumpf 1.9 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
23 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
25 mike 1.2 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 //==============================================================================
29 //
30 // Author: Mike Brasher (mbrasher@bmc.com)
31 //
|
32 kumpf 1.4 // Modified By: Roger Kumpf, Hewlett-Packard Company (roger_kumpf@hp.com)
|
33 david 1.12 // Dave Rosckes (rosckes@us.ibm.com)
|
34 brian.campbell 1.16 // Brian G. Campbell, EMC (campbell_brian@emc.com) - PEP140/phase1
|
35 a.arora 1.17 // Amit K Arora, IBM (amita@in.ibm.com) for PEP101
|
36 se.gupta 1.18 // Seema Gupta (gseema@in.ibm.com) for Bug#1096
|
37 david.dillard 1.20 // David Dillard, VERITAS Software Corp.
38 // (david.dillard@veritas.com)
|
39 mike 1.2 //
40 //%/////////////////////////////////////////////////////////////////////////////
41
42 #include <Pegasus/Common/Config.h>
43 #include <iostream>
|
44 mike 1.29 #include <cstring>
|
45 mike 1.2 #include "HTTPMessage.h"
46
47 PEGASUS_USING_STD;
48
49 PEGASUS_NAMESPACE_BEGIN
50
|
51 mike 1.32 static const String _CONTENT_TYPE = "content-type";
|
52 mike 1.2
53 //------------------------------------------------------------------------------
54 //
55 // Implementation notes:
56 //
|
57 david.dillard 1.22 // According to the HTTP specification:
|
58 mike 1.2 //
59 // 1. Method names are case-sensitive.
60 // 2. Field names are case-insensitive.
61 // 3. The first line of a message is known as the "start-line".
62 // 4. Subsequent lines are known as headers.
63 // 5. Headers have a field-name and field-value.
64 // 6. Start-lines may be request-lines or status-lines. Request lines
65 // have this form:
66 //
67 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
68 //
69 // Status-lines have this form:
70 //
71 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
72 //
73 //------------------------------------------------------------------------------
74
75 static char* _FindSeparator(const char* data, Uint32 size)
76 {
|
77 mike 1.31 const char* p = data;
|
78 mike 1.30
79 // Short-circuit by using memchr(). This will work whenever there is a
80 // "\r\n" sequence. Some clients may send incorrect headers which are
81 // only separated by "\n". In this case the code below this block
82 // will handle it.
83 {
84 const char* q = (char*)memchr(data, '\r', size);
85
86 if (q && q[1] == '\n')
87 return (char*)q;
88 }
|
89 mike 1.29
|
90 mike 1.2 const char* end = p + size;
91
92 while (p != end)
93 {
94 if (*p == '\r')
95 {
96 Uint32 n = end - p;
97
98 if (n >= 2 && p[1] == '\n')
99 return (char*)p;
100 }
101 else if (*p == '\n')
102 return (char*)p;
103
104 p++;
105 }
106
107 return 0;
108 }
109
110 HTTPMessage::HTTPMessage(
|
111 mike 1.28 const Buffer& message_,
|
112 brian.campbell 1.16 Uint32 queueId_, const CIMException *cimException_)
|
113 mike 1.2 :
|
114 david.dillard 1.22 Message(HTTP_MESSAGE),
115 message(message_),
|
116 humberto 1.15 queueId(queueId_),
|
117 kumpf 1.26 authInfo(0),
|
118 humberto 1.15 acceptLanguagesDecoded(false),
119 contentLanguagesDecoded(false)
|
120 mike 1.2 {
|
121 brian.campbell 1.16 if (cimException_)
122 cimException = *cimException_;
|
123 mike 1.2 }
124
|
125 mday 1.3
|
126 se.gupta 1.18 HTTPMessage::HTTPMessage(const HTTPMessage & msg)
|
127 mday 1.3 : Base(msg)
128 {
129 message = msg.message;
130 queueId = msg.queueId;
|
131 se.gupta 1.18 authInfo = msg.authInfo;
|
132 humberto 1.15 acceptLanguages = msg.acceptLanguages;
133 contentLanguages = msg.contentLanguages;
134 acceptLanguagesDecoded = msg.acceptLanguagesDecoded;
135 contentLanguagesDecoded = msg.contentLanguagesDecoded;
|
136 brian.campbell 1.16 cimException = msg.cimException;
|
137 se.gupta 1.18 }
|
138 mday 1.3
139
|
140 mike 1.2 void HTTPMessage::parse(
141 String& startLine,
142 Array<HTTPHeader>& headers,
143 Uint32& contentLength) const
144 {
145 startLine.clear();
146 headers.clear();
147 contentLength = 0;
148
149 char* data = (char*)message.getData();
150 Uint32 size = message.size();
|
151 david.dillard 1.20 char* line = data;
|
152 mike 1.2 char* sep;
153 Boolean firstTime = true;
154
155 while ((sep = _FindSeparator(line, size - (line - data))))
156 {
157 // Look for double separator which terminates the header?
158
159 if (line == sep)
160 {
161 // Establish pointer to content (account for "\n" and "\r\n").
162
|
163 david.dillard 1.20 char* content = line + ((*sep == '\r') ? 2 : 1);
|
164 mike 1.2
165 // Determine length of content:
166
167 contentLength = message.size() - (content - data);
168 break;
169 }
170
171 Uint32 lineLength = sep - line;
172
173 if (firstTime)
174 startLine.assign(line, lineLength);
175 else
176 {
177 // Find the colon:
178
|
179 david.dillard 1.20 char* colon = 0;
|
180 mike 1.2
181 for (Uint32 i = 0; i < lineLength; i++)
182 {
183 if (line[i] == ':')
184 {
185 colon = &line[i];
186 break;
187 }
188 }
189
190 // This should always be true:
191
192 if (colon)
193 {
194 // Get the name part:
195
|
196 david.dillard 1.20 char* end;
|
197 mike 1.2
198 for (end = colon - 1; end > line && isspace(*end); end--)
199 ;
200
201 end++;
202
203 String name(line, end - line);
204
205 // Get the value part:
206
|
207 david.dillard 1.20 char* start;
|
208 mike 1.2
209 for (start = colon + 1; start < sep && isspace(*start); start++)
210 ;
211
212 String value(start, sep - start);
213
214 headers.append(HTTPHeader(name, value));
|
215 david 1.12
|
216 mike 1.27 PEG_LOGGER_TRACE((Logger::STANDARD_LOG, System::CIMSERVER, 0,
217 "HTTPMessage - HTTP header name: $0 HTTP header value: $1",
218 name,value));
|
219 mike 1.2 }
220 }
221
222 line = sep + ((*sep == '\r') ? 2 : 1);
223 firstTime = false;
224 }
225 }
226
|
227 joyce.j 1.24
228 #ifdef PEGASUS_DEBUG
|
229 mike 1.2 void HTTPMessage::printAll(ostream& os) const
230 {
231 Message::print(os);
232
233 String startLine;
234 Array<HTTPHeader> headers;
235 Uint32 contentLength;
236 parse(startLine, headers, contentLength);
237
|
238 karl 1.13 // get pointer to start of data.
|
239 david.dillard 1.20 const char* content = message.getData() + message.size() - contentLength;
|
240 mike 1.2 // Print the first line:
241
|
242 karl 1.13 os << endl << startLine << endl;
|
243 mike 1.2
244 // Print the headers:
245
246 Boolean image = false;
247
248 for (Uint32 i = 0; i < headers.size(); i++)
249 {
|
250 karl 1.13 cout << headers[i].first << ": " << headers[i].second << endl;
|
251 david.dillard 1.22
|
252 mike 1.32 if (String::equalNoCase(headers[i].first, _CONTENT_TYPE))
|
253 karl 1.13 {
254 if (headers[i].second.find("image/") == 0)
255 image = true;
256 }
|
257 mike 1.2 }
258
|
259 karl 1.13 os << endl;
|
260 mike 1.2
261 // Print the content:
262
263 for (Uint32 i = 0; i < contentLength; i++)
264 {
|
265 david.dillard 1.20 //char c = content[i];
|
266 mike 1.2
267 if (image)
268 {
269 if ((i % 60) == 0)
|
270 karl 1.13 os << endl;
|
271 mike 1.2
|
272 david.dillard 1.20 char c = content[i];
|
273 mike 1.2
274 if (c >= ' ' && c < '~')
|
275 karl 1.13 os << c;
|
276 mike 1.2 else
|
277 karl 1.13 os << '.';
|
278 mike 1.2 }
279 else
|
280 karl 1.13 cout << content[i];
|
281 mike 1.2 }
282
|
283 karl 1.13 os << endl;
|
284 mike 1.2 }
|
285 joyce.j 1.24 #endif
|
286 mike 1.2
|
287 brian.campbell 1.16 /*
288 * Find the header prefix (i.e 2-digit number in front of cim keyword) if any.
|
289 david.dillard 1.22 * If a fieldName is given it will use that, otherwise the FIRST field
290 * starting with the standard keyword will be used. Given field names that do
|
291 brian.campbell 1.16 * not start with the standard keyword will never match.
292 * if there is a keyword match, the prefix will be populated, else set to empty
293 */
294
295 void HTTPMessage::lookupHeaderPrefix(
296 Array<HTTPHeader>& headers,
297 const String& fieldName,
298 String& prefix)
299 {
300 static const char keyword[] = "CIM";
301 prefix.clear();
302
303 for (Uint32 i = 0, n = headers.size(); i < n; i++)
304 {
305 const String &h = headers[i].first;
306
|
307 kumpf 1.25 if ((h.size() >= 3) &&
308 (h[0] >= '0') && (h[0] <= '9') &&
309 (h[1] >= '0') && (h[1] <= '9') &&
310 (h[2] == Char16('-')))
|
311 brian.campbell 1.16 {
312 String fieldNameCurrent = h.subString(3);
313
314 // ONLY fields starting with keyword can have prefixed according to spec
315 if (String::equalNoCase(fieldNameCurrent, keyword) == false)
316 continue;
317
318 prefix = h.subString(0,3);
319
320 // no field name given, just return the first prefix encountered
321 if (fieldName.size() == 0)
322 break;
323
324 if (String::equalNoCase(fieldNameCurrent, fieldName) == false)
325 prefix.clear();
|
326 david.dillard 1.22 else break;
|
327 brian.campbell 1.16 }
328 }
329 }
330
|
331 mike 1.2 Boolean HTTPMessage::lookupHeader(
332 Array<HTTPHeader>& headers,
333 const String& fieldName,
334 String& fieldValue,
|
335 kumpf 1.5 Boolean allowNamespacePrefix)
|
336 mike 1.2 {
337 for (Uint32 i = 0, n = headers.size(); i < n; i++)
338 {
|
339 kumpf 1.5 if (String::equalNoCase(headers[i].first, fieldName) ||
340 (allowNamespacePrefix && (headers[i].first.size() >= 3) &&
|
341 kumpf 1.25 (headers[i].first[0] >= '0') && (headers[i].first[0] <= '9') &&
342 (headers[i].first[1] >= '0') && (headers[i].first[1] <= '9') &&
|
343 kumpf 1.5 (headers[i].first[2] == Char16('-')) &&
344 String::equalNoCase(headers[i].first.subString(3), fieldName)))
|
345 mike 1.2 {
346 fieldValue = headers[i].second;
347 return true;
348 }
349 }
350
351 // Not found:
352 return false;
353 }
354
355 Boolean HTTPMessage::parseRequestLine(
356 const String& startLine,
357 String& methodName,
358 String& requestUri,
359 String& httpVersion)
360 {
361 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
362
363 // Extract the method-name:
364
365 Uint32 space1 = startLine.find(' ');
366 mike 1.2
|
367 kumpf 1.10 if (space1 == PEG_NOT_FOUND)
|
368 mike 1.2 return false;
369
370 methodName = startLine.subString(0, space1);
371
372 // Extrat the request-URI:
373
374 Uint32 space2 = startLine.find(space1 + 1, ' ');
375
|
376 kumpf 1.10 if (space2 == PEG_NOT_FOUND)
|
377 mike 1.2 return false;
378
379 Uint32 uriPos = space1 + 1;
380
381 requestUri = startLine.subString(uriPos, space2 - uriPos);
382
383 // Extract the HTTP version:
384
385 httpVersion = startLine.subString(space2 + 1);
|
386 kumpf 1.4
387 return true;
388 }
389
390 Boolean HTTPMessage::parseStatusLine(
391 const String& statusLine,
392 String& httpVersion,
393 Uint32& statusCode,
394 String& reasonPhrase)
395 {
396 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
397 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
398
399 // Extract the HTTP version:
400
401 Uint32 space1 = statusLine.find(' ');
402
|
403 kumpf 1.10 if (space1 == PEG_NOT_FOUND)
|
404 kumpf 1.4 return false;
405
406 httpVersion = statusLine.subString(0, space1);
407
408 // Extract the status code:
409
410 Uint32 space2 = statusLine.find(space1 + 1, ' ');
411
|
412 kumpf 1.10 if (space2 == PEG_NOT_FOUND)
|
413 kumpf 1.4 return false;
414
415 Uint32 statusCodePos = space1 + 1;
416 String statusCodeStr;
417 statusCodeStr = statusLine.subString(statusCodePos, space2 - statusCodePos);
|
418 kumpf 1.11 if (!sscanf(statusCodeStr.getCString(), "%u", &statusCode))
|
419 kumpf 1.4 return false;
420
421 // Extract the reason phrase:
422
423 reasonPhrase = statusLine.subString(space2 + 1);
|
424 mike 1.2
425 return true;
426 }
427
428 PEGASUS_NAMESPACE_END
|