1 #define EMBEDDED_IN_1_0
4 // System.Net.EndPointListener
7 // Gonzalo Paniagua Javier (gonzalo@novell.com)
9 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // 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.
34 using System.Net.Sockets;
35 using System.Collections;
36 using System.Security.Cryptography;
37 using System.Security.Cryptography.X509Certificates;
38 using Mono.Security.Authenticode;
40 using System; using System.Net; namespace MonoHttp {
41 sealed class EndPointListener : IHttpListenerContextBinder
45 Hashtable prefixes; // Dictionary <ListenerPrefix, HttpListener>
46 ArrayList unhandled; // List<ListenerPrefix> unhandled; host = '*'
47 ArrayList all; // List<ListenerPrefix> all; host = '+'
49 AsymmetricAlgorithm key;
52 public EndPointListener (IPAddress addr, int port, bool secure)
57 LoadCertificateAndKey (addr, port);
61 endpoint = new IPEndPoint (addr, port);
62 sock = new Socket (addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
65 sock.BeginAccept (OnAccept, this);
66 prefixes = new Hashtable ();
70 void LoadCertificateAndKey (IPAddress addr, int port)
72 // Actually load the certificate
74 string dirname = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
75 string path = Path.Combine (dirname, ".mono");
76 path = Path.Combine (path, "httplistener");
77 string cert_file = Path.Combine (path, String.Format ("{0}.cer", port));
78 string pvk_file = Path.Combine (path, String.Format ("{0}.pvk", port));
79 cert = new X509Certificate2 (cert_file);
80 key = PrivateKey.CreateFromFile (pvk_file).RSA;
87 static void OnAccept (IAsyncResult ares)
89 EndPointListener epl = (EndPointListener) ares.AsyncState;
90 Socket accepted = null;
92 accepted = epl.sock.EndAccept (ares);
94 // Anything to do here?
97 epl.sock.BeginAccept (OnAccept, epl);
99 if (accepted != null) {
108 if (accepted == null)
111 if (epl.secure && (epl.key == null)) {
115 HttpConnection conn = new HttpConnection (accepted, epl);
116 conn.BeginReadRequest ();
119 public bool BindContext (HttpListenerContext context)
121 HttpListenerRequest req = context.Request;
122 ListenerPrefix prefix;
123 HttpListener listener = SearchListener (req.UserHostName, req.RawUrl, out prefix);
124 if (listener == null)
127 context.Listener = listener;
128 context.Connection.Prefix = prefix;
129 listener.RegisterContext (context);
133 public void UnbindContext (HttpListenerContext context)
135 if (context == null || context.Request == null)
138 HttpListenerRequest req = context.Request;
139 ListenerPrefix prefix;
140 HttpListener listener = SearchListener (req.UserHostName, req.RawUrl, out prefix);
141 if (listener != null)
142 listener.UnregisterContext (context);
145 HttpListener SearchListener (string host, string raw_url, out ListenerPrefix prefix)
151 //TODO: We should use a ReaderWriterLock between this and the add/remove operations.
153 int colon = host.IndexOf (':');
155 host = host.Substring (0, colon);
161 if (MonoHttp.Utility.MaybeUri (raw_url) && Uri.TryCreate (raw_url, UriKind.Absolute, out raw_uri))
163 try { raw_uri = new Uri (raw_url); } catch { raw_uri = null; } if (raw_uri != null)
166 path = raw_uri.PathAndQuery;
168 path = HttpUtility.UrlDecode (raw_url);
170 string path_slash = path [path.Length - 1] == '/' ? path : path + "/";
172 HttpListener best_match = null;
173 int best_length = -1;
176 if (host != null && host != "") {
177 foreach (ListenerPrefix p in prefixes.Keys) {
178 string ppath = p.Path;
179 if (ppath.Length < best_length)
182 if (p.Host == host && (path.StartsWith (ppath) || path_slash.StartsWith (ppath))) {
183 best_length = ppath.Length;
184 best_match = (HttpListener) prefixes [p];
188 if (best_length != -1)
192 best_match = MatchFromList (host, path, unhandled, out prefix);
193 if (best_match != null)
196 best_match = MatchFromList (host, path, all, out prefix);
197 if (best_match != null)
203 HttpListener MatchFromList (string host, string path, ArrayList list, out ListenerPrefix prefix)
209 HttpListener best_match = null;
210 int best_length = -1;
212 foreach (ListenerPrefix p in list) {
213 string ppath = p.Path;
214 if (ppath.Length < best_length)
217 if (path.StartsWith (ppath)) {
218 best_length = ppath.Length;
219 best_match = p.Listener;
227 void AddSpecial (ArrayList coll, ListenerPrefix prefix)
232 foreach (ListenerPrefix p in coll) {
233 if (p.Path == prefix.Path) //TODO: code
234 throw new HttpListenerException (400, "Prefix already in use.");
240 void RemoveSpecial (ArrayList coll, ListenerPrefix prefix)
246 for (int i = 0; i < c; i++) {
247 ListenerPrefix p = (ListenerPrefix) coll [i];
248 if (p.Path == prefix.Path) {
256 void CheckIfRemove ()
258 if (prefixes.Count > 0)
261 if (unhandled != null && unhandled.Count > 0)
264 if (all != null && all.Count > 0)
267 EndPointManager.RemoveEndPoint (this, endpoint);
275 public void AddPrefix (ListenerPrefix prefix, HttpListener listener)
278 if (prefix.Host == "*") {
279 if (unhandled == null)
280 unhandled = new ArrayList ();
282 prefix.Listener = listener;
283 AddSpecial (unhandled, prefix);
287 if (prefix.Host == "+") {
289 all = new ArrayList ();
290 prefix.Listener = listener;
291 AddSpecial (all, prefix);
295 if (prefixes.ContainsKey (prefix)) {
296 HttpListener other = (HttpListener) prefixes [prefix];
297 if (other != listener) // TODO: code.
298 throw new HttpListenerException (400, "There's another listener for " + prefix);
302 prefixes [prefix] = listener;
306 public void RemovePrefix (ListenerPrefix prefix, HttpListener listener)
309 if (prefix.Host == "*") {
310 RemoveSpecial (unhandled, prefix);
314 if (prefix.Host == "+") {
315 RemoveSpecial (all, prefix);
319 if (prefixes.ContainsKey (prefix)) {
320 prefixes.Remove (prefix);