Statistics
| Branch: | Tag: | Revision:

root / Assets / Plugins / UnityHTTP / Response.cs @ 9:37719e88568d

History | View | Annotate | Download (8.4 kB)

1
using System;
2
using System.Collections;
3
using System.Collections.Generic;
4
using System.IO;
5
using System.Text;
6
using System.Globalization;
7
using Ionic.Zlib;
8
9
namespace HTTP
10
{
11
	public class Response
12
	{
13
		public Request request;
14
		public int status = 200;
15
		public string message = "OK";
16
		public byte[] bytes;
17
18
		List<byte[]> chunks;
19
		Dictionary<string, List<string>> headers = new Dictionary<string, List<string>> ();
20
21
		public string Text {
22
			get {
23
				if (bytes == null)
24
					return "";
25
				return System.Text.UTF8Encoding.UTF8.GetString (bytes);
26
			}
27
		}
28
29
		public string Asset {
30
			get {
31
				throw new NotSupportedException ("This can't be done, yet.");
32
			}
33
		}
34
35
        public Hashtable Object {
36
            get {
37
                if ( bytes == null )
38
                {
39
                    return null;
40
                }
41
                
42
                bool result = false;
43
                Hashtable obj = (Hashtable)JSON.JsonDecode( this.Text, ref result );
44
                if ( !result )
45
                {
46
                    obj = null;
47
                }
48
                
49
                return obj;
50
            }
51
        }
52
        
53
        public ArrayList Array {
54
            get {
55
                if ( bytes == null )
56
                {
57
                    return null;
58
                }
59
                
60
                bool result = false;
61
                ArrayList array = (ArrayList)JSON.JsonDecode( this.Text, ref result );
62
                if ( !result )
63
                {
64
                    array = null;
65
                }
66
                
67
                return array;
68
            }
69
        }
70
        
71
		void AddHeader (string name, string value)
72
		{
73
			name = name.ToLower ().Trim ();
74
			value = value.Trim ();
75
			if (!headers.ContainsKey (name))
76
				headers[name] = new List<string> ();
77
			headers[name].Add (value);
78
		}
79
80
        public List< string > GetHeaders()
81
        {
82
            List< string > result = new List< string >();
83
            foreach (string name in headers.Keys) {
84
                foreach (string value in headers[name]) {
85
                    result.Add( name + ": " + value );
86
                }
87
            }
88
89
            return result;
90
        }
91
92
		public List<string> GetHeaders (string name)
93
		{
94
			name = name.ToLower ().Trim ();
95
			if (!headers.ContainsKey (name))
96
				return new List<string> ();
97
			return headers[name];
98
		}
99
100
		public string GetHeader (string name)
101
		{
102
			name = name.ToLower ().Trim ();
103
			if (!headers.ContainsKey (name))
104
				return string.Empty;
105
			return headers[name][headers[name].Count - 1];
106
		}
107
108
		public Response ()
109
		{
110
			//ReadFromStream (stream);
111
		}
112
113
		string ReadLine (Stream stream)
114
		{
115
			var line = new List<byte> ();
116
			while (true) {
117
				int c = stream.ReadByte ();
118
                if (c == -1) {
119
                    throw new HTTPException("Unterminated Stream Encountered.");
120
                }
121
				if ((byte)c == Request.EOL[1])
122
					break;
123
				line.Add ((byte)c);
124
			}
125
			var s = ASCIIEncoding.ASCII.GetString (line.ToArray ()).Trim ();
126
			return s;
127
		}
128
129
		string[] ReadKeyValue (Stream stream)
130
		{
131
			string line = ReadLine (stream);
132
			if (line == "")
133
				return null;
134
			else {
135
				var split = line.IndexOf (':');
136
				if (split == -1)
137
					return null;
138
				var parts = new string[2];
139
				parts[0] = line.Substring (0, split).Trim ();
140
				parts[1] = line.Substring (split + 1).Trim ();
141
				return parts;
142
			}
143
144
		}
145
146
		public byte[] TakeChunk() {
147
			byte[] b = null;
148
			lock(chunks) {
149
				if(chunks.Count > 0) {
150
					b = chunks[0];
151
					chunks.RemoveAt(0);
152
					return b;
153
				}
154
			}
155
			return b;
156
		}
157
158
		public void ReadFromStream (Stream inputStream)
159
		{
160
			//var inputStream = new BinaryReader(inputStream);
161
			var top = ReadLine (inputStream).Split (new char[] { ' ' });
162
163
			if (!int.TryParse (top[1], out status))
164
				throw new HTTPException ("Bad Status Code");
165
166
            // MemoryStream is a disposable
167
            // http://stackoverflow.com/questions/234059/is-a-memory-leak-created-if-a-memorystream-in-net-is-not-closed
168
            using (var output = new MemoryStream ()) {
169
                message = string.Join (" ", top, 2, top.Length - 2);
170
                headers.Clear ();
171
172
                while (true) {
173
                    // Collect Headers
174
                    string[] parts = ReadKeyValue (inputStream);
175
                    if (parts == null)
176
                        break;
177
                    AddHeader (parts[0], parts[1]);
178
                }
179
180
                if ( request.cookieJar != null )
181
                {
182
                    List< string > cookies = GetHeaders( "set-cookie" );
183
                    for ( int cookieIndex = 0; cookieIndex < cookies.Count; ++cookieIndex )
184
                    {
185
                        string cookieString = cookies[ cookieIndex ];
186
                        if ( cookieString.IndexOf( "domain=", StringComparison.CurrentCultureIgnoreCase ) == -1 )
187
                        {
188
                            cookieString += "; domain=" + request.uri.Host;
189
                        }
190
191
                        if ( cookieString.IndexOf( "path=", StringComparison.CurrentCultureIgnoreCase ) == -1 )
192
                        {
193
                            cookieString += "; path=" + request.uri.AbsolutePath;
194
                        }
195
196
                        request.cookieJar.SetCookie( new Cookie( cookieString ) );
197
                    }
198
                }
199
200
                if (GetHeader ("transfer-encoding") == "chunked") {
201
                    chunks = new List<byte[]> ();
202
                    while (true) {
203
                        // Collect Body
204
                        string hexLength = ReadLine (inputStream);
205
                        //Console.WriteLine("HexLength:" + hexLength);
206
                        if (hexLength == "0") {
207
                            lock(chunks) {
208
                                chunks.Add(new byte[] {});
209
                            }
210
                            break;
211
                        }
212
                        int length = int.Parse (hexLength, NumberStyles.AllowHexSpecifier);
213
                        for (int i = 0; i < length; i++)
214
                            output.WriteByte ((byte)inputStream.ReadByte ());
215
                        lock(chunks) {
216
                            if (GetHeader ("content-encoding").Contains ("gzip"))
217
                                chunks.Add (UnZip(output));
218
                            else
219
                                chunks.Add (output.ToArray ());
220
                        }
221
                        output.SetLength (0);
222
                        //forget the CRLF.
223
                        inputStream.ReadByte ();
224
                        inputStream.ReadByte ();
225
                    }
226
227
                    while (true) {
228
                        //Collect Trailers
229
                        string[] parts = ReadKeyValue (inputStream);
230
                        if (parts == null)
231
                            break;
232
                        AddHeader (parts[0], parts[1]);
233
                    }
234
                    var unchunked = new List<byte>();
235
                    foreach(var i in chunks) {
236
                        unchunked.AddRange(i);
237
                    }
238
                    bytes = unchunked.ToArray();
239
240
                } else {
241
                    // Read Body
242
                    int contentLength = 0;
243
244
                    try {
245
                        contentLength = int.Parse (GetHeader ("content-length"));
246
                    } catch {
247
                        contentLength = 0;
248
                    }
249
250
                    int _b;
251
                    while( ( contentLength == 0 || output.Length < contentLength )
252
                    	      && (_b = inputStream.ReadByte()) != -1 ) {
253
                        output.WriteByte((byte)_b);
254
                    }
255
256
                    if( contentLength > 0 && output.Length != contentLength ) {
257
                        throw new HTTPException ("Response length does not match content length");
258
                    }
259
260
                    if (GetHeader ("content-encoding").Contains ("gzip")) {
261
                        bytes = UnZip(output);
262
                    } else {
263
                        bytes = output.ToArray ();
264
                    }
265
                }
266
            }
267
		}
268
269
270
		byte[] UnZip(MemoryStream output) {
271
			var cms = new MemoryStream ();
272
			output.Seek (0, SeekOrigin.Begin);
273
			using (var gz = new GZipStream (output, CompressionMode.Decompress)) {
274
				var buf = new byte[1024];
275
				int byteCount = 0;
276
				while ((byteCount = gz.Read (buf, 0, buf.Length)) > 0) {
277
					cms.Write (buf, 0, byteCount);
278
				}
279
			}
280
			return cms.ToArray ();
281
		}
282
283
	}
284
}