1 martin 1.9 //%LICENSE////////////////////////////////////////////////////////////////
|
2 martin 1.10 //
|
3 martin 1.9 // 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.10 //
|
10 martin 1.9 // 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.10 //
|
17 martin 1.9 // The above copyright notice and this permission notice shall be included
18 // in all copies or substantial portions of the Software.
|
19 martin 1.10 //
|
20 martin 1.9 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
21 martin 1.10 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22 martin 1.9 // 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.10 //
|
28 martin 1.9 //////////////////////////////////////////////////////////////////////////
|
29 carolann.graves 1.1 //
30 //%/////////////////////////////////////////////////////////////////////////////
31
32 #include <Pegasus/Common/Config.h>
33 #include <Pegasus/Common/Constants.h>
34 #include <Pegasus/Common/XmlReader.h>
35 #include <Pegasus/Common/System.h>
36 #include <Pegasus/Common/CIMMessage.h>
37 #include <Pegasus/Common/MessageLoader.h>
38 #include <Pegasus/Common/Tracer.h>
39 #include <Pegasus/Client/CIMClientException.h>
40 #include "HTTPExportResponseDecoder.h"
|
41 anusha.kandepu 1.12 #ifdef PEGASUS_ENABLE_PROTOCOL_WSMAN
42 #include <Pegasus/WsmServer/WsmConstants.h>
43 #include <Pegasus/WsmServer/WsmReader.h>
44 #include <Pegasus/WsmServer/WsmFault.h>
45 #include <Pegasus/WsmServer/WsmRequestDecoder.h>
46 #include <Pegasus/WsmServer/WsmEndpointReference.h>
47 #endif
|
48 carolann.graves 1.1
49 PEGASUS_USING_STD;
50 PEGASUS_NAMESPACE_BEGIN
51
52 void HTTPExportResponseDecoder::parseHTTPHeaders(
53 HTTPMessage* httpMessage,
54 ClientExceptionMessage*& exceptionMessage,
55 Array<HTTPHeader>& headers,
56 Uint32& contentLength,
57 Uint32& statusCode,
58 String& reasonPhrase,
59 Boolean& cimReconnect,
60 Boolean& valid)
61 {
62 PEG_METHOD_ENTER(TRC_EXPORT_CLIENT,
63 "HTTPExportResponseDecoder::parseHTTPHeaders()");
64
65 exceptionMessage = 0;
66 headers.clear();
67 contentLength = 0;
68 statusCode = 0;
69 carolann.graves 1.1 reasonPhrase = String::EMPTY;
70 cimReconnect = false;
71 valid = true;
72
73 String startLine;
74
|
75 kumpf 1.5 //
76 // Check for empty HTTP response message
77 //
|
78 carolann.graves 1.1 if (httpMessage->message.size() == 0)
79 {
80 MessageLoaderParms mlParms(
|
81 anusha.kandepu 1.12 "ExportClient.HTTPExportResponseDecoder.EMPTY_RESPONSE",
|
82 dave.sudlik 1.6 "Connection closed by CIM Server.");
|
83 carolann.graves 1.1 String mlString(MessageLoader::getMessage(mlParms));
84 AutoPtr<CIMClientMalformedHTTPException> malformedHTTPException(
85 new CIMClientMalformedHTTPException(mlString));
86 AutoPtr<ClientExceptionMessage> response(
87 new ClientExceptionMessage(malformedHTTPException.get()));
88 malformedHTTPException.release();
89 exceptionMessage = response.release();
90 valid = false;
91
92 PEG_METHOD_EXIT();
93 return;
94 }
95
96 //
97 // Parse the HTTP message headers and get content length
98 //
99 httpMessage->parse(startLine, headers, contentLength);
100
101 //
102 // Check for Connection: Close
103 //
|
104 kumpf 1.11 const char* connectClose;
|
105 carolann.graves 1.1 if (HTTPMessage::lookupHeader(headers, "Connection", connectClose, false))
106 {
|
107 kumpf 1.11 if (System::strcasecmp(connectClose, "Close") == 0)
|
108 carolann.graves 1.1 {
109 // reconnect and then resend next request.
110 cimReconnect=true;
111 }
|
112 kumpf 1.2 }
|
113 carolann.graves 1.1
|
114 marek 1.7 PEG_TRACE_CSTRING(TRC_XML_IO, Tracer::LEVEL4,
|
115 marek 1.4 httpMessage->message.getData());
|
116 carolann.graves 1.1
117 //
118 // Parse HTTP message status line
119 //
120 String httpVersion;
121
122 Boolean parsableMessage = HTTPMessage::parseStatusLine(
123 startLine, httpVersion, statusCode, reasonPhrase);
124 if (!parsableMessage)
125 {
126 MessageLoaderParms mlParms(
|
127 anusha.kandepu 1.12 "ExportClient.HTTPExportResponseDecoder.MALFORMED_RESPONSE",
|
128 carolann.graves 1.1 "Malformed HTTP response message.");
129 String mlString(MessageLoader::getMessage(mlParms));
130 AutoPtr<CIMClientMalformedHTTPException> malformedHTTPException(
131 new CIMClientMalformedHTTPException(mlString));
132 AutoPtr<ClientExceptionMessage> response(
133 new ClientExceptionMessage(malformedHTTPException.get()));
134 malformedHTTPException.release();
135 response->setCloseConnect(cimReconnect);
136 exceptionMessage = response.release();
137 valid = false;
138
139 PEG_METHOD_EXIT();
140 return;
141 }
142
143 PEG_METHOD_EXIT();
144 }
145
146 void HTTPExportResponseDecoder::validateHTTPHeaders(
147 HTTPMessage* httpMessage,
148 Array<HTTPHeader>& headers,
149 carolann.graves 1.1 Uint32 contentLength,
150 Uint32 statusCode,
151 Boolean cimReconnect,
152 const String& reasonPhrase,
153 char*& content,
154 ClientExceptionMessage*& exceptionMessage,
|
155 anusha.kandepu 1.12 Boolean& valid,
156 Boolean wsmanFlag)
|
157 carolann.graves 1.1 {
158 PEG_METHOD_ENTER(TRC_EXPORT_CLIENT,
159 "HTTPExportResponseDecoder::validateHTTPHeaders()");
160
161 content = 0;
162 exceptionMessage = 0;
163 valid = true;
164
165 //
166 // If authentication failed, a CIMClientHTTPErrorException will be
167 // generated with the "401 Unauthorized" status in the (re-challenge)
168 // response
169 //
170 // Check for a non-success (200 OK) response
171 //
172 if (statusCode != HTTP_STATUSCODE_OK)
173 {
174 String cimError;
175 String pegasusError;
176
177 HTTPMessage::lookupHeader(headers, "CIMError", cimError);
178 carolann.graves 1.1 HTTPMessage::lookupHeader(headers, PEGASUS_HTTPHEADERTAG_ERRORDETAIL,
179 pegasusError);
180 try
181 {
182 pegasusError = XmlReader::decodeURICharacters(pegasusError);
183 }
184 catch (const ParseError&)
185 {
186 // Ignore this exception. We're more interested in having the
187 // message in encoded form than knowing that the format is invalid.
188 }
189
190 AutoPtr<CIMClientHTTPErrorException> httpError(
191 new CIMClientHTTPErrorException(statusCode, reasonPhrase, cimError,
192 pegasusError));
193 AutoPtr<ClientExceptionMessage> response(
194 new ClientExceptionMessage(httpError.get()));
195
196 httpError.release();
197 response->setCloseConnect(cimReconnect);
198 exceptionMessage = response.release();
199 carolann.graves 1.1 valid = false;
200
201 PEG_METHOD_EXIT();
202 return;
203 }
|
204 anusha.kandepu 1.12 const char* cimExport;
|
205 carolann.graves 1.1 //
206 // Check for missing "CIMExport" header
207 //
|
208 anusha.kandepu 1.12 if (!wsmanFlag &&
209 !HTTPMessage::lookupHeader(headers,"CIMExport",cimExport,true))
|
210 carolann.graves 1.1 {
211 MessageLoaderParms mlParms(
212 "ExportClient.CIMExportResponseDecoder.MISSING_CIMEXP_HEADER",
213 "Missing CIMExport HTTP header");
214 String mlString(MessageLoader::getMessage(mlParms));
215
216 AutoPtr<CIMClientMalformedHTTPException> malformedHTTPException(new
217 CIMClientMalformedHTTPException(mlString));
218
219 AutoPtr<ClientExceptionMessage> response(
220 new ClientExceptionMessage(malformedHTTPException.get()));
221
222 malformedHTTPException.release();
223 response->setCloseConnect(cimReconnect);
224 exceptionMessage = response.release();
225 valid = false;
226
227 PEG_METHOD_EXIT();
228 return;
229 }
230
231 carolann.graves 1.1 //
232 // Check for missing "Content-Type" header
233 //
|
234 kumpf 1.11 const char* cimContentType;
|
235 carolann.graves 1.1 if (!HTTPMessage::lookupHeader(
|
236 kumpf 1.11 headers, "Content-Type", cimContentType, true))
|
237 carolann.graves 1.1 {
238 AutoPtr<CIMClientMalformedHTTPException> malformedHTTPException(new
239 CIMClientMalformedHTTPException(
240 "Missing CIMContentType HTTP header"));
241 AutoPtr<ClientExceptionMessage> response(
242 new ClientExceptionMessage(malformedHTTPException.get()));
243
244 malformedHTTPException.release();
245 response->setCloseConnect(cimReconnect);
246 exceptionMessage = response.release();
247 valid = false;
248
249 PEG_METHOD_EXIT();
250 return;
251 }
252
253 //
254 // Calculate the beginning of the content from the message size and
255 // the content length
256 //
257 content = (char *) httpMessage->message.getData() +
258 carolann.graves 1.1 httpMessage->message.size() - contentLength;
259
260 //
261 // Expect CIMExport HTTP header value MethodResponse
262 //
|
263 anusha.kandepu 1.12
264 if ( !wsmanFlag && System::strcasecmp(cimExport, "MethodResponse") != 0)
|
265 carolann.graves 1.1 {
|
266 anusha.kandepu 1.12
|
267 carolann.graves 1.1 MessageLoaderParms mlParms(
268 "ExportClient.CIMExportResponseDecoder.EXPECTED_METHODRESPONSE",
269 "Received CIMExport HTTP header value \"$0\", "
270 "expected \"MethodResponse\"", cimExport);
271 String mlString(MessageLoader::getMessage(mlParms));
272
273 AutoPtr<CIMClientMalformedHTTPException> malformedHTTPException(
274 new CIMClientMalformedHTTPException(mlString));
275
276 AutoPtr<ClientExceptionMessage> response(
277 new ClientExceptionMessage(malformedHTTPException.get()));
278
279 malformedHTTPException.release();
280 response->setCloseConnect(cimReconnect);
281 exceptionMessage = response.release();
282 valid = false;
283
284 PEG_METHOD_EXIT();
|
285 kumpf 1.5 return;
|
286 anusha.kandepu 1.12 }
|
287 carolann.graves 1.1
288 PEG_METHOD_EXIT();
289 }
290
291 void HTTPExportResponseDecoder::decodeExportResponse(
292 char* content,
293 Boolean cimReconnect,
294 Message*& responseMessage)
295 {
296 PEG_METHOD_ENTER (TRC_EXPORT_CLIENT,
297 "HTTPExportResponseDecoder::decodeExportResponse()");
298
299 AutoPtr<Message> response;
300
301 //
302 // Create and initialize XML parser:
303 //
304 XmlParser parser((char*)content);
305 XmlEntry entry;
306
307 try
308 carolann.graves 1.1 {
309 //
310 // Process <?xml ... >
311 //
312 const char* xmlVersion = 0;
313 const char* xmlEncoding = 0;
314 XmlReader::getXmlDeclaration(parser, xmlVersion, xmlEncoding);
315
316 //
317 // Process <CIM ... >
318 //
319 const char* cimVersion = 0;
320 const char* dtdVersion = 0;
321 XmlReader::getCimStartTag(parser, cimVersion, dtdVersion);
322
323 //
324 // Expect <MESSAGE ... >
325 //
326 String messageId;
327 String protocolVersion;
328 if (!XmlReader::getMessageStartTag(parser, messageId, protocolVersion))
329 carolann.graves 1.1 {
330 MessageLoaderParms mlParms(
331 "ExportClient.CIMExportResponseDecoder."
332 "EXPECTED_MESSAGE_ELEMENT",
333 "expected MESSAGE element");
334 String mlString(MessageLoader::getMessage(mlParms));
335
336 PEG_METHOD_EXIT();
337 throw XmlValidationError(parser.getLine(), mlString);
338 }
339
340 //
341 // Check for unsupported protocol version
342 //
|
343 karl 1.3
344 if (!XmlReader::isSupportedProtocolVersion(protocolVersion))
|
345 carolann.graves 1.1 {
346 MessageLoaderParms mlParms(
347 "ExportClient.CIMExportResponseDecoder.UNSUPPORTED_PROTOCOL",
348 "Received unsupported protocol version \"$0\", expected \"$1\"",
349 protocolVersion, "1.0");
350 String mlString(MessageLoader::getMessage(mlParms));
351
352 AutoPtr<CIMClientResponseException> responseException(
353 new CIMClientResponseException(mlString));
354
|
355 kumpf 1.8 AutoPtr<ClientExceptionMessage> clientExceptionMessage(
|
356 carolann.graves 1.1 new ClientExceptionMessage(responseException.get()));
357
358 responseException.release();
|
359 kumpf 1.8 clientExceptionMessage->setCloseConnect(cimReconnect);
360 responseMessage = clientExceptionMessage.release();
|
361 carolann.graves 1.1
362 PEG_METHOD_EXIT();
363 return;
364 }
365
366 //
367 // Expect <SIMPLEEXPRSP ... >
368 //
369 XmlReader::expectStartTag(parser, entry, "SIMPLEEXPRSP");
370
371 //
372 // Expect <EXPMETHODRESPONSE ... >
373 //
374 const char* expMethodResponseName = 0;
375 Boolean isEmptyTag = false;
376
377 if (XmlReader::getEMethodResponseStartTag(
378 parser, expMethodResponseName, isEmptyTag))
379 {
380 if (System::strcasecmp(expMethodResponseName, "ExportIndication")
381 == 0)
382 carolann.graves 1.1 {
383 response.reset(_decodeExportIndicationResponse(
384 parser, messageId, isEmptyTag));
385 }
386 else
387 {
388 //
389 // Unrecognized ExpMethodResponse name attribute
390 //
391 MessageLoaderParms mlParms(
392 "ExportClient.CIMExportResponseDecoder."
393 "UNRECOGNIZED_EXPMETHRSP",
394 "Unrecognized ExpMethodResponse name \"$0\"",
395 expMethodResponseName);
396 String mlString(MessageLoader::getMessage(mlParms));
|
397 kumpf 1.5
|
398 carolann.graves 1.1 PEG_METHOD_EXIT();
399 throw XmlValidationError(parser.getLine(), mlString);
400 }
401
402 //
403 // Handle end tag:
404 //
405 if (!isEmptyTag)
406 {
407 XmlReader::expectEndTag(parser, "EXPMETHODRESPONSE");
408 }
409 }
410 else
411 {
412 //
413 // Expected ExpMethodResponse element
414 //
415 MessageLoaderParms mlParms(
416 "ExportClient.CIMExportResponseDecoder."
417 "EXPECTED_EXPMETHODRESPONSE_ELEMENT",
418 "expected EXPMETHODRESPONSE element");
419 carolann.graves 1.1 String mlString(MessageLoader::getMessage(mlParms));
|
420 kumpf 1.5
|
421 carolann.graves 1.1 PEG_METHOD_EXIT();
422 throw XmlValidationError(parser.getLine(), mlString);
423 }
424
425 //
426 // Handle end tags:
427 //
428 XmlReader::expectEndTag(parser, "SIMPLEEXPRSP");
429 XmlReader::expectEndTag(parser, "MESSAGE");
430 XmlReader::expectEndTag(parser, "CIM");
431 }
432 catch (XmlException& x)
433 {
434 response.reset(new ClientExceptionMessage(
435 new CIMClientXmlException(x.getMessage())));
436 }
437 catch (Exception& x)
438 {
439 response.reset(new ClientExceptionMessage(
440 new CIMClientResponseException(x.getMessage())));
441 }
442 carolann.graves 1.1
443 //
444 // Note: Ignore any ContentLanguage set in the export response
445 //
446
447 response->setCloseConnect(cimReconnect);
448 responseMessage = response.release();
449
450 PEG_METHOD_EXIT();
451 }
452
453 CIMExportIndicationResponseMessage*
454 HTTPExportResponseDecoder::_decodeExportIndicationResponse(
455 XmlParser& parser,
456 const String& messageId,
457 Boolean isEmptyExpMethodResponseTag)
458 {
459 PEG_METHOD_ENTER (TRC_EXPORT_CLIENT,
460 "HTTPExportResponseDecoder::_decodeExportIndicationResponse()");
|
461 anusha.kandepu 1.12
|
462 carolann.graves 1.1 XmlEntry entry;
463 CIMException cimException;
464
465 if (!isEmptyExpMethodResponseTag)
466 {
467 if (XmlReader::getErrorElement(parser, cimException))
468 {
469 PEG_METHOD_EXIT();
470 return(new CIMExportIndicationResponseMessage(
471 messageId,
472 cimException,
473 QueueIdStack()));
474 }
475
476 if (XmlReader::testStartTagOrEmptyTag(parser, entry, "IRETURNVALUE"))
477 {
478 if (entry.type != XmlEntry::EMPTY_TAG)
479 {
480 XmlReader::expectEndTag(parser, "IRETURNVALUE");
481 }
482 }
483 carolann.graves 1.1 }
484
485 PEG_METHOD_EXIT();
486 return(new CIMExportIndicationResponseMessage(
487 messageId,
488 cimException,
489 QueueIdStack()));
490 }
491
|
492 anusha.kandepu 1.12 #ifdef PEGASUS_ENABLE_PROTOCOL_WSMAN
493 inline void checkRequiredHeader(
494 const char* headerName,
495 Boolean headerSpecified)
496 {
497 if (!headerSpecified)
498 {
499 throw WsmFault(
500 WsmFault::wsa_MessageInformationHeaderRequired,
501 MessageLoaderParms(
502 "ExportClient.HTTPExportResponseDecoder.MISSING_HEADER",
503 "Required SOAP header \"$0\" was not specified.",
504 headerName));
505 }
506 }
507
508
509 void HTTPExportResponseDecoder::decodeWSMANExportResponse(
510 char* content,
511 Boolean reconnect,
512 Message*& responseMessage,
513 anusha.kandepu 1.12 ContentLanguageList & contentLanguages,
514 WsmRequest * request)
515 {
516 PEG_METHOD_ENTER (TRC_EXPORT_CLIENT,
517 "HTTPExportResponseDecoder::decodeWSMANExportResponse()");
518 AutoPtr<Message> response;
519 // Create and initialize XML parser:
520 XmlParser parser((char*)content);
521 WsmReader wsmReader(content);
522 XmlEntry entry;
523 String wsaAction;
524 String wsaTo;
525 String wsaRelatesTo,messageId;
526 WsmEndpointReference epr;
527 try
528 {
529 // Process <?xml ... >
530 const char* xmlVersion = 0;
531 const char* xmlEncoding = 0;
532 wsmReader.getXmlDeclaration(xmlVersion, xmlEncoding);
533 // Decode the SOAP envelope
534 anusha.kandepu 1.12 wsmReader.expectStartTag(
535 entry, WsmNamespaces::SOAP_ENVELOPE, "Envelope");
536 Boolean gotEntry;
537 String wsaMessageId;
538 wsmReader.setHideEmptyTags(true);
539 wsmReader.expectStartTag(entry, WsmNamespaces::SOAP_ENVELOPE, "Header");
540 wsmReader.setHideEmptyTags(false);
541
542 while ((gotEntry = wsmReader.next(entry)) &&
543 ((entry.type == XmlEntry::START_TAG) ||
544 (entry.type == XmlEntry::EMPTY_TAG)))
545 {
546 int nsType = entry.nsType;
547 const char* elementName = entry.localName;
548 Boolean needEndTag = (entry.type == XmlEntry::START_TAG);
549
550 if ((nsType == WsmNamespaces::WS_ADDRESSING) &&
551 (strcmp(elementName, "To") == 0))
552 {
553 wsmReader.checkDuplicateHeader(entry.text, wsaTo.size());
554 wsaTo = wsmReader.getElementContent(entry);
555 anusha.kandepu 1.12 }
556 else if ((nsType == WsmNamespaces::WS_ADDRESSING) &&
557 (strcmp(elementName, "Action") == 0))
558 {
559 wsmReader.checkDuplicateHeader(entry.text, wsaAction.size());
560 wsaAction = wsmReader.getElementContent(entry);
561 }
562 else if ((nsType == WsmNamespaces::WS_ADDRESSING) &&
563 (strcmp(elementName, "MessageID") == 0))
564 {
565 wsmReader.checkDuplicateHeader(entry.text, messageId.size());
566 messageId = wsmReader.getElementContent(entry);
567 }
568 else if ((nsType == WsmNamespaces::WS_ADDRESSING) &&
569 (strcmp(elementName, "RelatesTo") == 0))
570 {
571 wsmReader.checkDuplicateHeader(entry.text, wsaMessageId.size());
572 wsaRelatesTo = wsmReader.getElementContent(entry);
573 }
574 else
575 {
576 anusha.kandepu 1.12 wsmReader.skipElement(entry);
577 // The end tag, if any, has already been consumed.
578 needEndTag = false;
579 }
580
581 if (needEndTag)
582 {
583 wsmReader.expectEndTag(nsType, elementName);
584 }
585 }
586 if (gotEntry)
587 {
588 wsmReader.getParser().putBack(entry);
589 }
590 wsmReader.expectEndTag(WsmNamespaces::SOAP_ENVELOPE, "Header");
591 }
592 catch (XmlException&)
593 {
594 // Do not treat this as an InvalidMessageInformationHeader fault.
595 throw;
596 }
597 anusha.kandepu 1.12 catch (Exception& e)
598 {
599 throw WsmFault(
600 WsmFault::wsa_InvalidMessageInformationHeader,
601 e.getMessage(),
602 e.getContentLanguages());
603 }
604 checkRequiredHeader("wsa:To", wsaTo.size());
605 checkRequiredHeader("wsa:RelatesTo", wsaRelatesTo.size());
606 checkRequiredHeader("wsa:Action", wsaAction.size());
607 if (wsaAction == WSM_ACTION_ACK)
608 {
609 response.reset(new WSMANExportIndicationResponseMessage(
610 messageId,
611 request,
612 contentLanguages));
613 }
614 response->setCloseConnect(reconnect);
615 responseMessage = response.release();
616 PEG_METHOD_EXIT();
617
618 anusha.kandepu 1.12 }
619 #endif
|
620 carolann.graves 1.1 PEGASUS_NAMESPACE_END
|