2 // System.Net.HttpWebResponse
5 // Lawrence Pit (loz@cable.a2000.nl)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Daniel Nauck (dna(at)mono-project(dot)de)
9 // (c) 2002 Lawrence Pit
10 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
11 // (c) 2008 Daniel Nauck
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
37 using System.Globalization;
39 using System.IO.Compression;
40 using System.Net.Sockets;
41 using System.Runtime.Serialization;
47 internal class HttpWebResponse : WebResponse, ISerializable, IDisposable {
50 public class HttpWebResponse : WebResponse, ISerializable, IDisposable {
53 WebHeaderCollection webHeaders;
54 CookieCollection cookieCollection;
57 HttpStatusCode statusCode;
58 string statusDescription;
61 CookieContainer cookie_container;
68 internal HttpWebResponse (Uri uri, string method, WebConnectionData data, CookieContainer container)
72 webHeaders = data.Headers;
73 version = data.Version;
74 statusCode = (HttpStatusCode) data.StatusCode;
75 statusDescription = data.StatusDescription;
80 string cl = webHeaders ["Content-Length"];
81 if (String.IsNullOrEmpty (cl) || !Int64.TryParse (cl, out contentLength))
87 if (container != null) {
88 this.cookie_container = container;
92 string content_encoding = webHeaders ["Content-Encoding"];
93 if (content_encoding == "gzip" && (data.request.AutomaticDecompression & DecompressionMethods.GZip) != 0)
94 stream = new GZipStream (stream, CompressionMode.Decompress);
95 else if (content_encoding == "deflate" && (data.request.AutomaticDecompression & DecompressionMethods.Deflate) != 0)
96 stream = new DeflateStream (stream, CompressionMode.Decompress);
99 [Obsolete ("Serialization is obsoleted for this type", false)]
100 protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)
102 SerializationInfo info = serializationInfo;
104 uri = (Uri) info.GetValue ("uri", typeof (Uri));
105 contentLength = info.GetInt64 ("contentLength");
106 contentType = info.GetString ("contentType");
107 method = info.GetString ("method");
108 statusDescription = info.GetString ("statusDescription");
109 cookieCollection = (CookieCollection) info.GetValue ("cookieCollection", typeof (CookieCollection));
110 version = (Version) info.GetValue ("version", typeof (Version));
111 statusCode = (HttpStatusCode) info.GetValue ("statusCode", typeof (HttpStatusCode));
116 public string CharacterSet {
117 // Content-Type = "Content-Type" ":" media-type
118 // media-type = type "/" subtype *( ";" parameter )
119 // parameter = attribute "=" value
120 // 3.7.1. default is ISO-8859-1
122 string contentType = ContentType;
123 if (contentType == null)
125 string val = contentType.ToLower ();
126 int pos = val.IndexOf ("charset=");
130 int pos2 = val.IndexOf (';', pos);
132 ? contentType.Substring (pos)
133 : contentType.Substring (pos, pos2 - pos);
137 public string ContentEncoding {
140 string h = webHeaders ["Content-Encoding"];
141 return h != null ? h : "";
145 public override long ContentLength {
147 return contentLength;
151 public override string ContentType {
155 if (contentType == null)
156 contentType = webHeaders ["Content-Type"];
162 public CookieCollection Cookies {
165 if (cookieCollection == null)
166 cookieCollection = new CookieCollection ();
167 return cookieCollection;
171 cookieCollection = value;
175 public override WebHeaderCollection Headers {
181 static Exception GetMustImplement ()
183 return new NotImplementedException ();
187 public override bool IsMutuallyAuthenticated
190 throw GetMustImplement ();
194 public DateTime LastModified {
198 string dtStr = webHeaders ["Last-Modified"];
199 return MonoHttpDate.Parse (dtStr);
200 } catch (Exception) {
206 public string Method {
213 public Version ProtocolVersion {
220 public override Uri ResponseUri {
227 public string Server {
230 return webHeaders ["Server"];
234 public HttpStatusCode StatusCode {
240 public string StatusDescription {
243 return statusDescription;
249 public string GetResponseHeader (string headerName)
252 string value = webHeaders [headerName];
253 return (value != null) ? value : "";
256 internal void ReadAll ()
258 WebConnectionStream wce = stream as WebConnectionStream;
267 public override Stream GetResponseStream ()
272 if (0 == String.Compare (method, "HEAD", true)) // see par 4.3 & 9.4
278 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
279 StreamingContext streamingContext)
281 GetObjectData (serializationInfo, streamingContext);
284 protected override void GetObjectData (SerializationInfo serializationInfo,
285 StreamingContext streamingContext)
287 SerializationInfo info = serializationInfo;
289 info.AddValue ("uri", uri);
290 info.AddValue ("contentLength", contentLength);
291 info.AddValue ("contentType", contentType);
292 info.AddValue ("method", method);
293 info.AddValue ("statusDescription", statusDescription);
294 info.AddValue ("cookieCollection", cookieCollection);
295 info.AddValue ("version", version);
296 info.AddValue ("statusCode", statusCode);
301 public override void Close ()
303 ((IDisposable) this).Dispose ();
306 void IDisposable.Dispose ()
309 GC.SuppressFinalize (this);
312 void Dispose (bool disposing)
316 this.disposed = true;
319 // release managed resources
321 cookieCollection = null;
324 statusDescription = null;
327 // release unmanaged resources
334 private void CheckDisposed ()
337 throw new ObjectDisposedException (GetType ().FullName);
342 if (webHeaders == null)
345 string [] values = webHeaders.GetValues ("Set-Cookie");
346 if (values != null) {
347 foreach (string va in values)
351 values = webHeaders.GetValues ("Set-Cookie2");
352 if (values != null) {
353 foreach (string va in values)
358 void SetCookie (string header)
361 Cookie cookie = null;
362 CookieParser parser = new CookieParser (header);
364 while (parser.GetNextNameValue (out name, out val)) {
365 if ((name == null || name == "") && cookie == null)
368 if (cookie == null) {
369 cookie = new Cookie (name, val);
373 name = name.ToUpper ();
376 if (cookie.Comment == null)
377 cookie.Comment = val;
380 if (cookie.CommentUri == null)
381 cookie.CommentUri = new Uri (val);
384 cookie.Discard = true;
387 if (cookie.Domain == "")
391 cookie.HttpOnly = true;
393 case "MAX-AGE": // RFC Style Set-Cookie2
394 if (cookie.Expires == DateTime.MinValue) {
396 cookie.Expires = cookie.TimeStamp.AddSeconds (UInt32.Parse (val));
400 case "EXPIRES": // Netscape Style Set-Cookie
401 if (cookie.Expires != DateTime.MinValue)
404 cookie.Expires = TryParseCookieExpires (val);
410 if (cookie.Port == null)
414 cookie.Secure = true;
418 cookie.Version = (int) UInt32.Parse (val);
424 if (cookieCollection == null)
425 cookieCollection = new CookieCollection ();
427 if (cookie.Domain == "")
428 cookie.Domain = uri.Host;
430 cookieCollection.Add (cookie);
431 if (cookie_container != null)
432 cookie_container.Add (uri, cookie);
435 void SetCookie2 (string cookies_str)
437 string [] cookies = cookies_str.Split (',');
439 foreach (string cookie_str in cookies)
440 SetCookie (cookie_str);
443 string[] cookieExpiresFormats =
445 "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
446 "ddd, dd'-'MMM'-'yy HH':'mm':'ss 'GMT'" };
448 DateTime TryParseCookieExpires (string value)
450 if (value == null || value.Length == 0)
451 return DateTime.MinValue;
453 for (int i = 0; i < cookieExpiresFormats.Length; i++)
456 DateTime cookieExpiresUtc = DateTime.ParseExact (value, cookieExpiresFormats [i], CultureInfo.InvariantCulture);
458 //convert UTC/GMT time to local time
459 cookieExpiresUtc = DateTime.SpecifyKind (cookieExpiresUtc, DateTimeKind.Utc);
460 return TimeZone.CurrentTimeZone.ToLocalTime (cookieExpiresUtc);
464 //If we can't parse Expires, use cookie as session cookie (expires is DateTime.MinValue)
465 return DateTime.MinValue;
474 public CookieParser (string header) : this (header, 0)
478 public CookieParser (string header, int position)
480 this.header = header;
482 this.length = header.Length;
485 public bool GetNextNameValue (out string name, out string val)
493 name = GetCookieName ();
494 if (pos < header.Length && header [pos] == '=') {
496 val = GetCookieValue ();
499 if (pos < length && header [pos] == ';')
505 string GetCookieName ()
508 while (k < length && Char.IsWhiteSpace (header [k]))
512 while (k < length && header [k] != ';' && header [k] != '=')
516 return header.Substring (begin, k - begin).Trim ();
519 string GetCookieValue ()
525 while (k < length && Char.IsWhiteSpace (header [k]))
529 if (header [k] == '"'){
533 while (k < length && header [k] != '"')
536 for (j = k; j < length && header [j] != ';'; j++)
541 while (k < length && header [k] != ';')
546 return header.Substring (begin, k - begin).Trim ();