2 // Mono.ASPNET.InitialWorkerRequest
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2003 Ximian, Inc (http://www.ximian.com)
8 // (C) Copyright 2004 Novell, Inc
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System
.Collections
;
31 using System
.Configuration
;
34 using System
.Net
.Sockets
;
40 public class RequestData
44 public string PathInfo
;
45 public string QueryString
;
46 public string Protocol
;
47 public byte [] InputBuffer
;
49 public RequestData (string verb
, string path
, string queryString
, string protocol
)
53 this.QueryString
= queryString
;
54 this.Protocol
= protocol
;
57 public override string ToString ()
59 StringBuilder sb
= new StringBuilder ();
60 sb
.AppendFormat ("Verb: {0}\n", Verb
);
61 sb
.AppendFormat ("Path: {0}\n", Path
);
62 sb
.AppendFormat ("PathInfo: {0}\n", PathInfo
);
63 sb
.AppendFormat ("QueryString: {0}\n", QueryString
);
64 return sb
.ToString ();
68 class RequestLineException
: ApplicationException
{
69 public RequestLineException () : base ("Error reading request line")
74 public class InitialWorkerRequest
86 const int BSize
= 1024 * 32;
88 static Stack bufferStack
= new Stack ();
90 static byte [] AllocateBuffer ()
93 if (bufferStack
.Count
!= 0)
94 return (byte []) bufferStack
.Pop ();
96 return new byte [BSize
];
99 static void FreeBuffer (byte [] buf
)
102 bufferStack
.Push (buf
);
106 public InitialWorkerRequest (NetworkStream ns
)
109 throw new ArgumentNullException ("ns");
116 inputBuffer
= AllocateBuffer ();
117 inputLength
= stream
.Read (inputBuffer
, 0, BSize
);
118 if (inputLength
== 0) // Socket closed
119 throw new IOException ("socket closed");
127 if (inputBuffer
== null)
130 if (position
>= inputLength
)
131 return stream
.ReadByte ();
133 return (int) inputBuffer
[position
++];
138 bool foundCR
= false;
139 StringBuilder text
= new StringBuilder ();
142 int c
= ReadInputByte ();
144 if (c
== -1) { // end of stream
145 if (text
.Length
== 0)
154 if (c
== '\n') { // newline
155 if ((text
.Length
> 0) && (text
[text
.Length
- 1] == '\r'))
160 } else if (foundCR
) {
169 text
.Append ((char) c
);
170 if (text
.Length
> 8192)
171 throw new InvalidOperationException ("Input line too long.");
174 return text
.ToString ();
177 bool GetRequestLine ()
184 gotSomeInput
= false;
189 // Ignore empty lines before the actual request.
194 gotSomeInput
= false;
198 string [] s
= req
.Split (' ');
202 verb
= s
[0].Trim ();
203 path
= s
[1].Trim ();
206 verb
= s
[0].Trim ();
207 path
= s
[1].Trim ();
208 protocol
= s
[2].Trim ();
214 int qmark
= path
.IndexOf ('?');
216 queryString
= path
.Substring (qmark
+ 1);
217 path
= path
.Substring (0, qmark
);
220 path
= HttpUtility
.UrlDecode (path
);
221 path
= GetSafePath (path
);
222 if (path
.StartsWith ("/~/")) {
223 // Not sure about this. It makes request such us /~/dir/file work
224 path
= path
.Substring (2);
230 string GetSafePath (string path
)
233 if (path
.EndsWith ("/"))
236 path
= HttpUtility
.UrlDecode (path
);
237 path
= path
.Replace ('\\','/');
238 while (path
.IndexOf ("//") != -1)
239 path
= path
.Replace ("//", "/");
241 string [] parts
= path
.Split ('/');
242 ArrayList result
= new ArrayList (parts
.Length
);
244 int end
= parts
.Length
;
245 for (int i
= 0; i
< end
; i
++) {
246 string current
= parts
[i
];
247 if (current
== "" || current
== "." )
250 if (current
== "..") {
251 if (result
.Count
> 0)
252 result
.RemoveAt (result
.Count
- 1);
256 result
.Add (current
);
259 if (result
.Count
== 0)
262 result
.Insert (0, "");
263 return String
.Join ("/", (string []) result
.ToArray (typeof (string))) + trail
;
266 public void ReadRequestData ()
268 if (!GetRequestLine ())
269 throw new RequestLineException ();
271 if (protocol
== null) {
272 protocol
= "HTTP/1.0";
276 public bool GotSomeInput
{
277 get { return gotSomeInput; }
280 public RequestData RequestData
{
282 RequestData rd
= new RequestData (verb
, path
, queryString
, protocol
);
283 byte [] buffer
= new byte [inputLength
- position
];
284 Buffer
.BlockCopy (inputBuffer
, position
, buffer
, 0, inputLength
- position
);
285 rd
.InputBuffer
= buffer
;
286 FreeBuffer (inputBuffer
);