1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 #pragma warning disable 1634, 1691
5 namespace System.ServiceModel.Dispatcher
6 {
7     using System;
8     using System.Collections;
9     using System.Collections.Generic;
10     using System.Collections.Specialized;
11     using System.Globalization;
12     using System.Reflection;
13     using System.ServiceModel;
14     using System.ServiceModel.Channels;
15     using System.ServiceModel.Description;
16     using System.Text;
17     using System.Xml;
18     using System.ServiceModel.Web;
19 
20     class UriTemplateClientFormatter : IClientMessageFormatter
21     {
22         internal Dictionary<int, string> pathMapping;
23         internal Dictionary<int, KeyValuePair<string, Type>> queryMapping;
24         Uri baseUri;
25         IClientMessageFormatter inner;
26         bool innerIsUntypedMessage;
27         bool isGet;
28         string method;
29         QueryStringConverter qsc;
30         int totalNumUTVars;
31         UriTemplate uriTemplate;
32 
UriTemplateClientFormatter(OperationDescription operationDescription, IClientMessageFormatter inner, QueryStringConverter qsc, Uri baseUri, bool innerIsUntypedMessage, string contractName)33         public UriTemplateClientFormatter(OperationDescription operationDescription, IClientMessageFormatter inner, QueryStringConverter qsc, Uri baseUri, bool innerIsUntypedMessage, string contractName)
34         {
35             this.inner = inner;
36             this.qsc = qsc;
37             this.baseUri = baseUri;
38             this.innerIsUntypedMessage = innerIsUntypedMessage;
39             Populate(out this.pathMapping,
40                 out this.queryMapping,
41                 out this.totalNumUTVars,
42                 out this.uriTemplate,
43                 operationDescription,
44                 qsc,
45                 contractName);
46             this.method = WebHttpBehavior.GetWebMethod(operationDescription);
47             isGet = this.method == WebHttpBehavior.GET;
48         }
49 
DeserializeReply(Message message, object[] parameters)50         public object DeserializeReply(Message message, object[] parameters)
51         {
52             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR2.GetString(SR2.QueryStringFormatterOperationNotSupportedClientSide)));
53         }
54 
SerializeRequest(MessageVersion messageVersion, object[] parameters)55         public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
56         {
57             object[] innerParameters = new object[parameters.Length - this.totalNumUTVars];
58             NameValueCollection nvc = new NameValueCollection();
59             int j = 0;
60             for (int i = 0; i < parameters.Length; ++i)
61             {
62                 if (this.pathMapping.ContainsKey(i))
63                 {
64                     nvc[this.pathMapping[i]] = parameters[i] as string;
65                 }
66                 else if (this.queryMapping.ContainsKey(i))
67                 {
68                     if (parameters[i] != null)
69                     {
70                         nvc[this.queryMapping[i].Key] = this.qsc.ConvertValueToString(parameters[i], this.queryMapping[i].Value);
71                     }
72                 }
73                 else
74                 {
75                     innerParameters[j] = parameters[i];
76                     ++j;
77                 }
78             }
79             Message m = inner.SerializeRequest(messageVersion, innerParameters);
80             bool userSetTheToOnMessage = (this.innerIsUntypedMessage && m.Headers.To != null);
81             bool userSetTheToOnOutgoingHeaders = (OperationContext.Current != null && OperationContext.Current.OutgoingMessageHeaders.To != null);
82             if (!userSetTheToOnMessage && !userSetTheToOnOutgoingHeaders)
83             {
84                 m.Headers.To = this.uriTemplate.BindByName(this.baseUri, nvc);
85             }
86             if (WebOperationContext.Current != null)
87             {
88                 if (isGet)
89                 {
90                     WebOperationContext.Current.OutgoingRequest.SuppressEntityBody = true;
91                 }
92                 if (this.method != WebHttpBehavior.WildcardMethod && WebOperationContext.Current.OutgoingRequest.Method != null)
93                 {
94                     WebOperationContext.Current.OutgoingRequest.Method = this.method;
95                 }
96             }
97             else
98             {
99                 HttpRequestMessageProperty hrmp;
100                 if (m.Properties.ContainsKey(HttpRequestMessageProperty.Name))
101                 {
102                     hrmp = m.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
103                 }
104                 else
105                 {
106                     hrmp = new HttpRequestMessageProperty();
107                     m.Properties.Add(HttpRequestMessageProperty.Name, hrmp);
108                 }
109                 if (isGet)
110                 {
111                     hrmp.SuppressEntityBody = true;
112                 }
113                 if (this.method != WebHttpBehavior.WildcardMethod)
114                 {
115                     hrmp.Method = this.method;
116                 }
117             }
118             return m;
119         }
120 
GetUTStringOrDefault(OperationDescription operationDescription)121         internal static string GetUTStringOrDefault(OperationDescription operationDescription)
122         {
123             string utString = WebHttpBehavior.GetWebUriTemplate(operationDescription);
124             if (utString == null && WebHttpBehavior.GetWebMethod(operationDescription) == WebHttpBehavior.GET)
125             {
126                 utString = MakeDefaultGetUTString(operationDescription);
127             }
128             if (utString == null)
129             {
130                 utString = operationDescription.Name; // note: not + "/*", see 8988 and 9653
131             }
132             return utString;
133         }
134 
Populate(out Dictionary<int, string> pathMapping, out Dictionary<int, KeyValuePair<string, Type>> queryMapping, out int totalNumUTVars, out UriTemplate uriTemplate, OperationDescription operationDescription, QueryStringConverter qsc, string contractName)135         internal static void Populate(out Dictionary<int, string> pathMapping,
136             out Dictionary<int, KeyValuePair<string, Type>> queryMapping,
137             out int totalNumUTVars,
138             out UriTemplate uriTemplate,
139             OperationDescription operationDescription,
140             QueryStringConverter qsc,
141             string contractName)
142         {
143             pathMapping = new Dictionary<int, string>();
144             queryMapping = new Dictionary<int, KeyValuePair<string, Type>>();
145             string utString = GetUTStringOrDefault(operationDescription);
146             uriTemplate = new UriTemplate(utString);
147             List<string> neededPathVars = new List<string>(uriTemplate.PathSegmentVariableNames);
148             List<string> neededQueryVars = new List<string>(uriTemplate.QueryValueVariableNames);
149             Dictionary<string, byte> alreadyGotVars = new Dictionary<string, byte>(StringComparer.OrdinalIgnoreCase);
150             totalNumUTVars = neededPathVars.Count + neededQueryVars.Count;
151             for (int i = 0; i < operationDescription.Messages[0].Body.Parts.Count; ++i)
152             {
153                 MessagePartDescription mpd = operationDescription.Messages[0].Body.Parts[i];
154                 string parameterName = mpd.XmlName.DecodedName;
155                 if (alreadyGotVars.ContainsKey(parameterName))
156                 {
157                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
158                         SR2.GetString(SR2.UriTemplateVarCaseDistinction, operationDescription.XmlName.DecodedName, contractName, parameterName)));
159                 }
160                 List<string> neededPathCopy = new List<string>(neededPathVars);
161                 foreach (string pathVar in neededPathCopy)
162                 {
163                     if (string.Compare(parameterName, pathVar, StringComparison.OrdinalIgnoreCase) == 0)
164                     {
165                         if (mpd.Type != typeof(string))
166                         {
167                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
168                                 SR2.GetString(SR2.UriTemplatePathVarMustBeString, operationDescription.XmlName.DecodedName, contractName, parameterName)));
169                         }
170                         pathMapping.Add(i, parameterName);
171                         alreadyGotVars.Add(parameterName, 0);
172                         neededPathVars.Remove(pathVar);
173                     }
174                 }
175                 List<string> neededQueryCopy = new List<string>(neededQueryVars);
176                 foreach (string queryVar in neededQueryCopy)
177                 {
178                     if (string.Compare(parameterName, queryVar, StringComparison.OrdinalIgnoreCase) == 0)
179                     {
180                         if (!qsc.CanConvert(mpd.Type))
181                         {
182                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
183                                 SR2.GetString(SR2.UriTemplateQueryVarMustBeConvertible, operationDescription.XmlName.DecodedName, contractName, parameterName, mpd.Type, qsc.GetType().Name)));
184                         }
185                         queryMapping.Add(i, new KeyValuePair<string, Type>(parameterName, mpd.Type));
186                         alreadyGotVars.Add(parameterName, 0);
187                         neededQueryVars.Remove(queryVar);
188                     }
189                 }
190             }
191             if (neededPathVars.Count != 0)
192             {
193                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(
194                     SR2.UriTemplateMissingVar, operationDescription.XmlName.DecodedName, contractName, neededPathVars[0])));
195             }
196             if (neededQueryVars.Count != 0)
197             {
198                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString
199                     (SR2.UriTemplateMissingVar, operationDescription.XmlName.DecodedName, contractName, neededQueryVars[0])));
200             }
201         }
202 
MakeDefaultGetUTString(OperationDescription od)203         static string MakeDefaultGetUTString(OperationDescription od)
204         {
205             StringBuilder sb = new StringBuilder(od.XmlName.DecodedName);
206             //sb.Append("/*"); // note: not + "/*", see 8988 and 9653
207             if (!WebHttpBehavior.IsUntypedMessage(od.Messages[0]))
208             {
209                 sb.Append("?");
210                 foreach (MessagePartDescription mpd in od.Messages[0].Body.Parts)
211                 {
212                     string parameterName = mpd.XmlName.DecodedName;
213                     sb.Append(parameterName);
214                     sb.Append("={");
215                     sb.Append(parameterName);
216                     sb.Append("}&");
217                 }
218                 sb.Remove(sb.Length - 1, 1);
219             }
220             return sb.ToString();
221         }
222     }
223 }
224