1 /*
2  * Copyright 1999-2006 University of Chicago
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  * http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include "globus_i_soap_client.h"
19 #include "globus_service_registry.h"
20 #include "globus_i_service_engine.h"
21
22 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
23 static
24 void
25 globus_l_soap_client_request_done_callback(
26     globus_result_t                     result,
27     void *                              args);
28
29 static
30 void
31 globus_l_soap_client_request_callback(
32     void *                              args);
33
34 #endif
35
36 /**
37  * Nonblocking SOAP request invocation.
38  * @ingroup globus_soap_client_request
39  *
40  * Invokes the operation described by the @a operation parameter on the
41  * container running at the named @a endpoint. Once the message has been 
42  * sent by the client (and all applicable handlers have been invoked), the
43  * @a callback function will be invoked. Once that occurs, the application
44  * may call globus_soap_client_register_response() to begin to retrieve
45  * the result of the operation.
46  *
47  * @param client_handle
48  *     The SOAP client handle to use for state while processing this
49  *     request.
50  * @param endpoint
51  *     The URI of the endpoint to send the SOAP operation request to.
52  * @param operation
53  *     The description of the operation and its serializers and deserializers.
54  *     This must not be modified until the operation and its response have
55  *     been processed by the client handle.
56  * @param input
57  *     The operation's input element value. A copy of this value will be made
58  *     by this function, so this parameter's value may be freed or otherwise
59  *     modified by the caller after this function returns.
60  * @param callback
61  *     Callback function to invoke once the operation request has been sent.
62  * @param user_args
63  *     Application-specific argument to the callback function.
64  *
65  * @retval GLOBUS_SUCCESS
66  *     Processing of this operation has begun successfully.
67  * @retval GLOBUS_SOAP_MESSAGE_ERROR_TYPE_CLIENT_ALREADY_INVOKED
68  *     Processing the operation failed because the client is already actively
69  *     handling a request or response.
70  * @retval GLOBUS_SOAP_MESSAGE_ERROR_TYPE_INIT_FAILED
71  *     Processing the operation failed because the client could not initialize
72  *     some part of its data.
73  * @retval GLOBUS_SOAP_MESSAGE_ERROR_TYPE_CLIENT_REQUEST_FAILED
74  *     Processing the operation failed because the client could not initialize
75  *     some part of its data.
76  */
77 globus_result_t
78 globus_soap_client_register_request(
79     globus_soap_client_handle_t         client_handle,
80     const char *                        endpoint,
81     const globus_soap_client_operation_t *
82                                         operation,
83     void *                              input,
84     globus_soap_client_request_callback_func_t
85                                         callback,
86     void *                              user_args)
87 3720 {
88 3720     globus_handler_chain_t              chain = NULL;
89 3720     globus_result_t                     result = GLOBUS_SUCCESS;
90     globus_i_soap_client_request_handle_t *
91 3720                                         request = NULL;
92 3720     globus_service_engine_t             engine = NULL;
93
94 3720     if (client_handle->message != NULL)
95     {
96 0         result = GlobusSoapMessageErrorClientAlreadyInvoked();
97 0         goto exit;
98     }
99
100     /* If this handle was used previously, then the response will 
101      * contain a copy of the response if the operation was successful and
102      * there was a response and the call was not done with a blocking
103      * function.
104      */
105 3720     globus_i_soap_client_response_handle_destroy(&client_handle->response);
106
107 3720     result = globus_soap_message_handle_init(&client_handle->message, NULL);
108 3720     if (result != GLOBUS_SUCCESS)
109     {
110 0         result = GlobusSoapMessageErrorFailedClientInit(
111             result, operation->request_action);
112 0         goto error_exit;
113     }
114
115 3720     globus_soap_message_handle_set_registry(
116         client_handle->message,
117         operation->registry);
118
119 3720     chain = client_handle->handler_chain;
120
121 3720     if (chain != NULL)
122     {
123 3720         globus_soap_message_set_handler_chain(client_handle->message, chain);
124     }
125
126 3720     request = &client_handle->request;
127     
128 3720     globus_assert_string(endpoint, "NULL endpoint");
129
130 3720     request->endpoint = globus_libc_strdup(endpoint);
131 3720     request->callback = callback;
132 3720     request->args = user_args;
133 3720     operation->input_type_info->copy(&request->input, input);
134 3720     request->state = GLOBUS_SOAP_CLIENT_REQUEST_INIT;
135 3720     client_handle->operation = operation;
136
137 3720     result = globus_soap_message_attr_set(
138         client_handle->attrs, 
139         WSADDR_DESTINATION_KEY, 
140         globus_soap_message_attr_copy_string,
141         globus_libc_free,
142         (void *)endpoint);
143 3720     if(result != GLOBUS_SUCCESS)
144     {
145 0         result = GlobusSoapMessageErrorClientRequestFailed(
146             result, operation->request_action);
147 0         goto error_exit;
148     }
149
150 3720     if (operation->request_action != NULL)
151     {
152 3720         result = globus_soap_message_attr_set(
153             client_handle->attrs, 
154             WSADDR_ACTION_REQUEST_KEY, 
155             NULL,
156             NULL, 
157             (void *) operation->request_action);
158 3720         if(result != GLOBUS_SUCCESS)
159         {
160 0             result = GlobusSoapMessageErrorClientRequestFailed(
161                 result, operation->request_action);
162 0             goto error_exit;
163         }
164
165 3720         result = globus_soap_message_attr_set(
166             client_handle->attrs, 
167             GLOBUS_SOAP_MESSAGE_SOAP_ACTION_KEY, 
168             NULL,
169             NULL, 
170             (void *) operation->request_action);
171
172 3720         if(result != GLOBUS_SUCCESS)
173         {
174 0             result = GlobusSoapMessageErrorClientRequestFailed(
175                 result, operation->request_action);
176 0             goto error_exit;
177         }
178     }
179
180 3720     result = globus_soap_message_handle_set_attrs(
181         client_handle->message, client_handle->attrs);
182 3720     if(result != GLOBUS_SUCCESS)
183     {
184 0         result = GlobusSoapMessageErrorClientRequestFailed(
185             result, operation->request_action);
186     }
187
188 3720     result = globus_service_engine_lookup(
189             endpoint, &engine);
190
191 3720     if (engine != NULL)
192     {
193         /* Local invocation mode */
194 453         result = globus_soap_message_attr_set(
195             client_handle->attrs, 
196             GLOBUS_SOAP_CLIENT_LOCAL_INVOCATION_KEY, 
197             NULL,
198             NULL,
199             engine);
200
201 453         if (result != GLOBUS_SUCCESS)
202         {
203 0             goto exit;
204         }
205 453         request->state = GLOBUS_SOAP_CLIENT_REQUEST_LOCAL_INVOCATION;
206     }
207     
208
209 3720     result = globus_callback_register_oneshot(
210         &request->callback_handle,
211         NULL,
212         globus_l_soap_client_request_callback,
213         request);
214 3720     if(result != GLOBUS_SUCCESS)
215     {
216 0         result = GlobusSoapMessageErrorClientRequestFailed(
217             result, operation->request_action);
218         goto error_exit;
219     }
220
221 0     goto exit;
222
223  error_exit:
224
225 0     if(request->input && operation->input_type_info)
226     {
227 0         operation->input_type_info->destroy(request->input);
228     }
229 0     if(request->endpoint)
230     {
231 0         globus_free(request->endpoint);
232     }
233     
234 0     if(client_handle->message)
235     {
236 0         globus_soap_message_handle_destroy(client_handle->message);
237 0         client_handle->message = NULL;
238     }
239
240  exit:
241
242 3720     return result;
243 }
244 /* globus_soap_client_register_request() */
245
246 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
247 static
248 void
249 globus_l_soap_client_request_done_callback(
250     globus_result_t                     result,
251     void *                              args)
252 16335 {
253 16335     globus_i_soap_client_request_handle_t * request;
254
255 16335     request = args;
256 16335     globus_assert_string(request, "request handle in callback is NULL");
257
258 16335     globus_mutex_lock(&request->client_handle->mutex);
259 16335     request->result = result;
260 16335     request->done = 1;
261 16335     globus_mutex_unlock(&request->client_handle->mutex);
262
263 16335     globus_l_soap_client_request_callback(request);
264 }
265 /* globus_l_soap_client_request_done_callback() */
266
267 extern
268 void
269 globus_i_soap_client_request_handle_destroy(
270     globus_i_soap_client_request_handle_t * 
271                                         request)
272 3827 {
273 3827     globus_bool_t                       active;
274
275 3827     globus_assert_string(request, "request handle in callback is NULL");
276     
277 3827     globus_mutex_lock(&request->client_handle->mutex);
278
279 3827     if (request->callback_handle)
280     {
281 0         globus_callback_unregister(
282                 request->callback_handle,
283                 NULL,
284                 NULL,
285                 &active);
286
287 0         if (active)
288         {
289 0             request->state = GLOBUS_SOAP_CLIENT_REQUEST_DESTROY_HANDLERS;
290 0             request->done = 0;
291
292 0             goto out;
293         }
294 0         request->callback_handle = GLOBUS_NULL_HANDLE;
295     }
296 3827     if(request->input)
297     {
298 3720         if (request->client_handle->operation->input_type_info)
299         {
300 3720             request->client_handle->operation->input_type_info->destroy(request->input);
301         }
302
303 3720         request->input = NULL;
304     }
305
306 3827     if(request->endpoint)
307     {
308 3720         globus_free(request->endpoint);
309 3720         request->endpoint = NULL;
310     }
311 out:
312 3827     globus_mutex_unlock(&request->client_handle->mutex);
313 }
314 /* globus_i_soap_client_request_handle_destroy() */
315
316 static
317 void
318 globus_l_soap_client_request_callback(
319     void *                              args)
320 20055 {
321 20055     globus_soap_message_handle_t        message_handle = NULL;
322 20055     globus_result_t                     result = GLOBUS_SUCCESS;
323 20055     globus_handler_chain_t              chain = NULL;
324     globus_i_soap_client_request_handle_t *
325 20055                                         request;
326     const globus_soap_client_operation_t *
327 20055                                         operation;
328 20055     xsd_QName                           subelement;
329 20055     globus_service_descriptor_t *       service_desc;
330 20055     globus_service_engine_t             engine;
331 20055     int                                 rc;
332 20055     globus_url_t                        url;
333     globus_service_operation_descriptor_t *
334 20055                                         op_desc = NULL;
335 20055     globus_result_t                     (*invoke_function)();
336 20055     char *                              fault_name;
337 20055     void *                              fault;
338
339 20055     request = args;
340 20055     globus_assert_string(request, "request handle is NULL");
341
342 20055     operation = request->client_handle->operation;
343
344 20055     if (request->callback_handle != GLOBUS_NULL_HANDLE)
345     {
346 3720         globus_callback_unregister(
347                 request->callback_handle,
348                 NULL,
349                 NULL,
350                 NULL);
351 3720         request->callback_handle = GLOBUS_NULL_HANDLE;
352     }
353
354 20055     switch(request->state)
355     {
356     case GLOBUS_SOAP_CLIENT_REQUEST_INIT:
357
358 3267         globus_mutex_lock(&request->client_handle->mutex);
359 3267         request->done = 0;
360 3267         request->state = GLOBUS_SOAP_CLIENT_REQUEST_INIT_HANDLERS;
361 3267         globus_mutex_unlock(&request->client_handle->mutex);
362
363 3267         result = globus_soap_message_get_handler_chain(
364             request->client_handle->message, &chain);
365 3267         if(result != GLOBUS_SUCCESS)
366         {
367 0             goto error_exit;
368         }
369
370 3267         if(chain)
371         {
372 3267             globus_handler_chain_register_invoke(
373                 chain, 
374                 GLOBUS_HANDLER_TYPE_REQUEST_INIT,
375                 request->client_handle->message,
376                 globus_l_soap_client_request_done_callback,
377                 request);
378         }
379
380 3267         break;
381
382     case GLOBUS_SOAP_CLIENT_REQUEST_INIT_HANDLERS:
383
384 3267         globus_mutex_lock(&request->client_handle->mutex);
385 3267         request->done = 0;
386 3267         request->state = GLOBUS_SOAP_CLIENT_REQUEST_OPENING;
387 3267         globus_mutex_unlock(&request->client_handle->mutex);
388
389 3267         result = globus_soap_message_register_open(
390             request->client_handle->message, 
391             request->endpoint,
392             globus_l_soap_client_request_done_callback,
393             request);
394 3267         if(result != GLOBUS_SUCCESS)
395         {
396 0             goto error_exit;
397         }
398
399 3267         break;
400
401     case GLOBUS_SOAP_CLIENT_REQUEST_OPENING:
402
403 3267         if(request->done)
404         {
405 3267             if(request->result != GLOBUS_SUCCESS)
406             {
407 0                 result = request->result;
408 0                 goto error_exit;
409             }
410
411 3267             globus_mutex_lock(&request->client_handle->mutex);
412 3267             request->done = 0;
413 3267             request->state = GLOBUS_SOAP_CLIENT_REQUEST_INVOKING_HANDLERS;
414 3267             globus_mutex_unlock(&request->client_handle->mutex);
415             
416 3267             result = globus_soap_message_serialize_envelope(
417                 request->client_handle->message);
418 3267             if(result != GLOBUS_SUCCESS)
419             {
420 0                 goto error_exit;
421             }
422
423 3267             result = globus_soap_message_serialize_header(
424                 request->client_handle->message);
425 3267             if(result != GLOBUS_SUCCESS)
426             {
427 0                 goto error_exit;
428             }
429
430 3267             result = globus_soap_message_serialize_header_begin_close(
431                 request->client_handle->message);
432 3267             if(result != GLOBUS_SUCCESS)
433             {
434 0                 goto error_exit;
435             }
436            
437 3267     result = globus_soap_message_set_marker(
438 request->client_handle->message, 
439 GLOBUS_SOAP_MESSAGE_MARKER_HEADER_CONTENT);
440 3267     if(result != GLOBUS_SUCCESS)
441     {   
442 0 goto error_exit;
443     }
444     
445 3267             result = globus_soap_message_serialize_header_end(
446                 request->client_handle->message);
447 3267             if(result != GLOBUS_SUCCESS)
448             {
449 0                 goto error_exit;
450             }
451
452 3267             result = globus_soap_message_serialize_body(
453                 request->client_handle->message);
454 3267             if(result != GLOBUS_SUCCESS)
455             {
456 0                 goto error_exit;
457             }
458
459 3267             result = globus_soap_message_serialize_body_begin_close(
460                 request->client_handle->message);
461 3267             if(result != GLOBUS_SUCCESS)
462             {
463 0                 goto error_exit;
464             }
465
466 3267             subelement.Namespace = operation->input_element.Namespace;
467 3267             subelement.local = operation->input_element.local;
468
469 3267             result = operation->input_type_info->serialize(
470                 &subelement,
471                 request->input,
472                 request->client_handle->message,
473                 0);
474 3267             if(result != GLOBUS_SUCCESS)
475             {
476 0                 goto error_exit;
477             }
478
479 3267             result = globus_soap_message_serialize_body_end(
480                 request->client_handle->message);
481 3267             if(result != GLOBUS_SUCCESS)
482             {
483 0                 goto error_exit;
484             }
485
486 3267             result = globus_soap_message_serialize_envelope_end(
487                 request->client_handle->message);
488 3267             if(result != GLOBUS_SUCCESS)
489             {
490 0                 goto error_exit;
491             }
492
493 3267             result = globus_soap_message_set_write_position_to_marker(
494                 request->client_handle->message, 
495                 GLOBUS_SOAP_MESSAGE_MARKER_HEADER_CONTENT);
496 3267             if(result != GLOBUS_SUCCESS)
497             {
498 0                 goto error_exit;
499             }
500
501 3267             result = globus_soap_message_get_handler_chain(
502                 request->client_handle->message, &chain);
503 3267             if(result != GLOBUS_SUCCESS)
504             {
505 0                 goto error_exit;
506             }
507
508 3267             if(chain)
509             {
510 3267                 globus_handler_chain_register_invoke(
511                     chain, 
512                     GLOBUS_HANDLER_TYPE_REQUEST,
513                     request->client_handle->message,
514                     globus_l_soap_client_request_done_callback,
515                     request);
516             }
517         }
518
519 3267         break;
520
521     case GLOBUS_SOAP_CLIENT_REQUEST_INVOKING_HANDLERS:
522
523 3267         if(request->done)
524         {
525 3267             if(request->result != GLOBUS_SUCCESS)
526             {
527 0                 result = request->result;
528 0                 goto error_exit;
529             }
530
531 3267             globus_mutex_lock(&request->client_handle->mutex);
532 3267             request->done = 0;
533 3267             request->state = GLOBUS_SOAP_CLIENT_REQUEST_SENDING;
534 3267             globus_mutex_unlock(&request->client_handle->mutex);
535
536 3267             result = globus_soap_message_register_write_request(
537                 request->client_handle->message,
538                 globus_l_soap_client_request_done_callback,
539                 request,
540                 operation->output_type_info != NULL);
541 3267             if(result != GLOBUS_SUCCESS)
542             {
543 0                 goto error_exit;
544             }
545         }
546
547 3267         break;
548
549     case GLOBUS_SOAP_CLIENT_REQUEST_SENDING:
550
551 3267         if(request->done)
552         {
553 3267             if(request->result != GLOBUS_SUCCESS)
554             {
555 0                 result = request->result;
556 0                 goto error_exit;
557             }
558
559 3267             globus_mutex_lock(&request->client_handle->mutex);
560 3267             request->done = 0;
561 3267             request->state = GLOBUS_SOAP_CLIENT_REQUEST_DESTROY_HANDLERS;
562 3267             globus_mutex_unlock(&request->client_handle->mutex);
563
564 3267             result = globus_soap_message_get_handler_chain(
565                 request->client_handle->message, &chain);
566 3267             if(result != GLOBUS_SUCCESS)
567             {
568 0                 goto error_exit;
569             }
570
571 3267             if(chain)
572             {
573 3267                 globus_handler_chain_register_invoke(
574                     chain, 
575                     GLOBUS_HANDLER_TYPE_REQUEST_DESTROY,
576                     request->client_handle->message,
577                     globus_l_soap_client_request_done_callback,
578                     request);
579             }
580         }
581
582 3267         break;
583
584     case GLOBUS_SOAP_CLIENT_REQUEST_DESTROY_HANDLERS:
585
586 3267         if(request->done)
587         {
588 3267             if(request->result != GLOBUS_SUCCESS)
589             {
590 0                 result = request->result;
591 0                 goto error_exit;
592             }
593
594 3267             globus_mutex_lock(&request->client_handle->mutex);
595 3267             request->done = 0;
596 3267             globus_mutex_unlock(&request->client_handle->mutex);
597
598 3267             request->callback(
599                 request->client_handle, 
600                 request->args, GLOBUS_SUCCESS);
601
602 3267             globus_i_soap_client_request_handle_destroy(request);
603         }
604
605 3267         break;
606
607     case GLOBUS_SOAP_CLIENT_REQUEST_LOCAL_INVOCATION:
608 453         rc = globus_url_parse(request->endpoint, &url);
609
610 453         if (rc != 0)
611         {
612 0             goto error_exit;
613         }
614 453         result = globus_i_service_engine_get_descriptor(
615                 url.url_path,
616                 request->client_handle->message,
617                 &service_desc);
618 453         globus_url_destroy(&url);
619 453         if (result != GLOBUS_SUCCESS || service_desc == NULL)
620         {
621 0             goto error_exit;
622         }
623
624 453         if(service_desc->op_mapper)
625         {
626 453             op_desc = globus_hashtable_lookup(
627                     &service_desc->op_mapper,
628                     (void *) &request->client_handle->operation->input_element);
629
630 453             result = globus_operation_table_get_operation(
631                     service_desc->operations,
632                     request->client_handle->operation->input_element.local,
633                     (void **)&invoke_function);
634         }
635 453         engine = globus_soap_message_attr_get(
636             request->client_handle->attrs, 
637             GLOBUS_SOAP_CLIENT_LOCAL_INVOCATION_KEY);
638
639         /* No-response operations will have a null output_type_info */
640 453         if (request->client_handle->operation->output_type_info)
641         {
642 453             request->client_handle->operation->output_type_info->initialize(
643                     &request->client_handle->response.output);
644         }
645
646 453         if (op_desc->fault_type != NULL)
647         {
648 453             fault_name = NULL;
649 453             fault = NULL;
650
651 453             result = invoke_function(
652                 engine,
653                 request->client_handle->message,
654                 service_desc,
655                 request->input,
656                 request->client_handle->response.output,
657                 &fault_name,
658                 &fault);
659
660 453             if (result == GLOBUS_SUCCESS && fault_name != NULL)
661             {
662 0                 xsd_any *               fault_any = NULL;
663 0                 int                     fault_type;
664
665 0                 xsd_any_init(&fault_any);
666
667 0                 result = op_desc->fault_type(
668                         fault_name,
669                         &fault_type,
670                         &fault_any->any_info);
671
672 0                 fault_any->value = fault;
673
674 0                 request->client_handle->response.fault = fault;
675 0                 request->client_handle->response.fault_type = fault_type;
676             }
677         }
678         else
679         {
680 0             request->client_handle->response.result = result = invoke_function(
681                 engine,
682                 request->client_handle->message,
683                 service_desc,
684                 request->input);
685         }
686 453         request->client_handle->response.result  = result;
687 453         if (request->client_handle->operation->output_type_info)
688         {
689 453             request->callback(
690                 request->client_handle, 
691                 request->args, GLOBUS_SUCCESS);
692
693 453             globus_i_soap_client_request_handle_destroy(request);
694         }
695         else
696         {
697 0             request->callback(
698                 request->client_handle, 
699                 request->args, GLOBUS_SUCCESS);
700         }
701 0         break;
702
703     default:
704
705 0         globus_assert_string(NULL, "Unkown request state");
706 0         break;
707     }
708
709 0     return;
710
711  error_exit:
712
713 0     result = GlobusSoapMessageErrorClientRequestFailed(
714         result, operation->request_action);
715     
716 0     globus_mutex_lock(&request->client_handle->mutex);
717 0     message_handle = request->client_handle->message;
718 0     request->client_handle->message = NULL;
719 0     globus_mutex_unlock(&request->client_handle->mutex);
720
721 0     request->callback(request->client_handle, request->args, result);
722
723 0     globus_soap_message_handle_destroy(message_handle);
724 0     globus_i_soap_client_request_handle_destroy(request);
725 }
726 /* globus_l_soap_client_request_callback() */
727