81e1d6f67d4be3308554d97eb4f01a2ecc97bafa
[mono.git] / mcs / class / System / System.Net / ResponseStream.cs
1 //
2 // System.Net.ResponseStream
3 //
4 // Author:
5 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
6 //
7 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 #if SECURITY_DEP
30
31 using System.IO;
32 using System.Net.Sockets;
33 using System.Text;
34 using System.Runtime.InteropServices;
35 namespace System.Net {
36         // FIXME: Does this buffer the response until Close?
37         // Update: we send a single packet for the first non-chunked Write
38         // What happens when we set content-length to X and write X-1 bytes then close?
39         // what if we don't set content-length at all?
40         class ResponseStream : Stream
41         {
42                 HttpListenerResponse response;
43                 bool ignore_errors;
44                 bool disposed;
45                 bool trailer_sent;
46                 Stream stream;
47
48                 internal ResponseStream (Stream stream, HttpListenerResponse response, bool ignore_errors)
49                 {
50                         this.response = response;
51                         this.ignore_errors = ignore_errors;
52                         this.stream = stream;
53                 }
54
55                 public override bool CanRead {
56                         get { return false; }
57                 }
58
59                 public override bool CanSeek {
60                         get { return false; }
61                 }
62
63                 public override bool CanWrite {
64                         get { return true; }
65                 }
66
67                 public override long Length {
68                         get { throw new NotSupportedException (); }
69                 }
70
71                 public override long Position {
72                         get { throw new NotSupportedException (); }
73                         set { throw new NotSupportedException (); }
74                 }
75
76
77                 public override void Close ()
78                 {
79                         if (disposed == false) {
80                                 disposed = true;
81                                 byte [] bytes = null;
82                                 MemoryStream ms = GetHeaders (true);
83                                 bool chunked = response.SendChunked;
84                                 if (stream.CanWrite) {
85                                         try {
86                                                 if (ms != null) {
87                                                         long start = ms.Position;
88                                                         if (chunked && !trailer_sent) {
89                                                                 bytes = GetChunkSizeBytes (0, true);
90                                                                 ms.Position = ms.Length;
91                                                                 ms.Write (bytes, 0, bytes.Length);
92                                                         }
93                                                         InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
94                                                         trailer_sent = true;
95                                                 } else if (chunked && !trailer_sent) {
96                                                         bytes = GetChunkSizeBytes (0, true);
97                                                         InternalWrite (bytes, 0, bytes.Length);
98                                                         trailer_sent = true;
99                                                 }
100                                         } catch (IOException) {
101                                                 // Ignore error due to connection reset by peer
102                                         }
103                                 }
104                                 response.Close ();
105                         }
106                 }
107
108                 MemoryStream GetHeaders (bool closing)
109                 {
110                         // SendHeaders works on shared headers
111                         lock (response.headers_lock) {
112                                 if (response.HeadersSent)
113                                         return null;
114                                 MemoryStream ms = new MemoryStream ();
115                                 response.SendHeaders (closing, ms);
116                                 return ms;
117                         }
118                 }
119
120                 public override void Flush ()
121                 {
122                 }
123
124                 static byte [] crlf = new byte [] { 13, 10 };
125                 static byte [] GetChunkSizeBytes (int size, bool final)
126                 {
127                         string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "");
128                         return Encoding.ASCII.GetBytes (str);
129                 }
130
131                 internal void InternalWrite (byte [] buffer, int offset, int count)
132                 {
133                         if (ignore_errors) {
134                                 try {
135                                         stream.Write (buffer, offset, count);
136                                 } catch { }
137                         } else {
138                                 stream.Write (buffer, offset, count);
139                         }
140                 }
141
142                 public override void Write (byte [] buffer, int offset, int count)
143                 {
144                         if (disposed)
145                                 throw new ObjectDisposedException (GetType ().ToString ());
146                         if (count == 0)
147                                 return;
148
149                         byte [] bytes = null;
150                         MemoryStream ms = GetHeaders (false);
151                         bool chunked = response.SendChunked;
152                         if (ms != null) {
153                                 long start = ms.Position; // After the possible preamble for the encoding
154                                 ms.Position = ms.Length;
155                                 if (chunked) {
156                                         bytes = GetChunkSizeBytes (count, false);
157                                         ms.Write (bytes, 0, bytes.Length);
158                                 }
159
160                                 int new_count = Math.Min (count, 16384 - (int) ms.Position + (int) start);
161                                 ms.Write (buffer, offset, new_count);
162                                 count -= new_count;
163                                 offset += new_count;
164                                 InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
165                                 ms.SetLength (0);
166                                 ms.Capacity = 0; // 'dispose' the buffer in ms.
167                         } else if (chunked) {
168                                 bytes = GetChunkSizeBytes (count, false);
169                                 InternalWrite (bytes, 0, bytes.Length);
170                         }
171
172                         if (count > 0)
173                                 InternalWrite (buffer, offset, count);
174                         if (chunked)
175                                 InternalWrite (crlf, 0, 2);
176                 }
177
178                 public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
179                                                         AsyncCallback cback, object state)
180                 {
181                         if (disposed)
182                                 throw new ObjectDisposedException (GetType ().ToString ());
183
184                         byte [] bytes = null;
185                         MemoryStream ms = GetHeaders (false);
186                         bool chunked = response.SendChunked;
187                         if (ms != null) {
188                                 long start = ms.Position;
189                                 ms.Position = ms.Length;
190                                 if (chunked) {
191                                         bytes = GetChunkSizeBytes (count, false);
192                                         ms.Write (bytes, 0, bytes.Length);
193                                 }
194                                 ms.Write (buffer, offset, count);
195                                 buffer = ms.GetBuffer ();
196                                 offset = (int) start;
197                                 count = (int) (ms.Position - start);
198                         } else if (chunked) {
199                                 bytes = GetChunkSizeBytes (count, false);
200                                 InternalWrite (bytes, 0, bytes.Length);
201                         }
202
203                         return stream.BeginWrite (buffer, offset, count, cback, state);
204                 }
205
206                 public override void EndWrite (IAsyncResult ares)
207                 {
208                         if (disposed)
209                                 throw new ObjectDisposedException (GetType ().ToString ());
210
211                         if (ignore_errors) {
212                                 try {
213                                         stream.EndWrite (ares);
214                                         if (response.SendChunked)
215                                                 stream.Write (crlf, 0, 2);
216                                 } catch { }
217                         } else {
218                                 stream.EndWrite (ares);
219                                 if (response.SendChunked)
220                                         stream.Write (crlf, 0, 2);
221                         }
222                 }
223
224                 public override int Read ([In,Out] byte[] buffer, int offset, int count)
225                 {
226                         throw new NotSupportedException ();
227                 }
228
229                 public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
230                                                         AsyncCallback cback, object state)
231                 {
232                         throw new NotSupportedException ();
233                 }
234
235                 public override int EndRead (IAsyncResult ares)
236                 {
237                         throw new NotSupportedException ();
238                 }
239
240                 public override long Seek (long offset, SeekOrigin origin)
241                 {
242                         throw new NotSupportedException ();
243                 }
244
245                 public override void SetLength (long value)
246                 {
247                         throw new NotSupportedException ();
248                 }
249         }
250 }
251 #endif
252