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.Net.Sockets;
40 using System.Runtime.Serialization;
46 public class HttpWebResponse : WebResponse, ISerializable, IDisposable
49 WebHeaderCollection webHeaders;
50 CookieCollection cookieCollection;
53 HttpStatusCode statusCode;
54 string statusDescription;
55 long contentLength = -1;
57 CookieContainer cookie_container;
59 bool disposed = false;
64 internal HttpWebResponse (Uri uri, string method, WebConnectionData data, CookieContainer container)
68 webHeaders = data.Headers;
69 version = data.Version;
70 statusCode = (HttpStatusCode) data.StatusCode;
71 statusDescription = data.StatusDescription;
73 if (container != null) {
74 this.cookie_container = container;
80 [Obsolete ("Serialization is obsoleted for this type", false)]
82 protected HttpWebResponse (SerializationInfo serializationInfo, StreamingContext streamingContext)
84 SerializationInfo info = serializationInfo;
86 uri = (Uri) info.GetValue ("uri", typeof (Uri));
87 contentLength = info.GetInt64 ("contentLength");
88 contentType = info.GetString ("contentType");
89 method = info.GetString ("method");
90 statusDescription = info.GetString ("statusDescription");
91 cookieCollection = (CookieCollection) info.GetValue ("cookieCollection", typeof (CookieCollection));
92 version = (Version) info.GetValue ("version", typeof (Version));
93 statusCode = (HttpStatusCode) info.GetValue ("statusCode", typeof (HttpStatusCode));
98 public string CharacterSet {
99 // Content-Type = "Content-Type" ":" media-type
100 // media-type = type "/" subtype *( ";" parameter )
101 // parameter = attribute "=" value
102 // 3.7.1. default is ISO-8859-1
105 string contentType = ContentType;
106 if (contentType == null)
108 string val = contentType.ToLower ();
109 int pos = val.IndexOf ("charset=");
113 int pos2 = val.IndexOf (';', pos);
115 ? contentType.Substring (pos)
116 : contentType.Substring (pos, pos2 - pos);
120 public string ContentEncoding {
123 string h = webHeaders ["Content-Encoding"];
124 return h != null ? h : "";
128 public override long ContentLength {
131 if (contentLength != -1)
132 return contentLength;
135 contentLength = (long) UInt64.Parse (webHeaders ["Content-Length"]);
136 } catch (Exception) {
140 return contentLength;
144 public override string ContentType {
147 if (contentType == null)
148 contentType = webHeaders ["Content-Type"];
154 public CookieCollection Cookies {
158 if (cookieCollection == null)
159 cookieCollection = new CookieCollection ();
160 return cookieCollection;
164 cookieCollection = value;
168 public override WebHeaderCollection Headers {
176 static Exception GetMustImplement ()
178 return new NotImplementedException ();
182 public override bool IsMutuallyAuthenticated
185 throw GetMustImplement ();
190 public DateTime LastModified {
194 string dtStr = webHeaders ["Last-Modified"];
195 return MonoHttpDate.Parse (dtStr);
196 } catch (Exception) {
202 public string Method {
209 public Version ProtocolVersion {
216 public override Uri ResponseUri {
223 public string Server {
226 return webHeaders ["Server"];
230 public HttpStatusCode StatusCode {
237 public string StatusDescription {
240 return statusDescription;
246 public override int GetHashCode ()
249 return base.GetHashCode ();
253 public string GetResponseHeader (string headerName)
256 string value = webHeaders [headerName];
257 return (value != null) ? value : "";
260 internal void ReadAll ()
262 WebConnectionStream wce = stream as WebConnectionStream;
271 public override Stream GetResponseStream ()
276 if (0 == String.Compare (method, "HEAD", true)) // see par 4.3 & 9.4
282 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
283 StreamingContext streamingContext)
285 GetObjectData (serializationInfo, streamingContext);
291 void GetObjectData (SerializationInfo serializationInfo,
292 StreamingContext streamingContext)
294 SerializationInfo info = serializationInfo;
296 info.AddValue ("uri", uri);
297 info.AddValue ("contentLength", contentLength);
298 info.AddValue ("contentType", contentType);
299 info.AddValue ("method", method);
300 info.AddValue ("statusDescription", statusDescription);
301 info.AddValue ("cookieCollection", cookieCollection);
302 info.AddValue ("version", version);
303 info.AddValue ("statusCode", statusCode);
308 public override void Close ()
310 ((IDisposable) this).Dispose ();
313 void IDisposable.Dispose ()
316 GC.SuppressFinalize (this);
322 void Dispose (bool disposing)
326 this.disposed = true;
329 // release managed resources
332 cookieCollection = null;
335 statusDescription = null;
338 // release unmanaged resources
345 private void CheckDisposed ()
348 throw new ObjectDisposedException (GetType ().FullName);
353 if (webHeaders == null)
356 string [] values = webHeaders.GetValues ("Set-Cookie");
357 if (values != null) {
358 foreach (string va in values)
362 values = webHeaders.GetValues ("Set-Cookie2");
363 if (values != null) {
364 foreach (string va in values)
369 void SetCookie (string header)
372 Cookie cookie = null;
373 CookieParser parser = new CookieParser (header);
375 while (parser.GetNextNameValue (out name, out val)) {
376 if ((name == null || name == "") && cookie == null)
379 if (cookie == null) {
380 cookie = new Cookie (name, val);
384 name = name.ToUpper ();
387 if (cookie.Comment == null)
388 cookie.Comment = val;
391 if (cookie.CommentUri == null)
392 cookie.CommentUri = new Uri (val);
395 cookie.Discard = true;
398 if (cookie.Domain == "")
403 cookie.HttpOnly = true;
406 case "MAX-AGE": // RFC Style Set-Cookie2
407 if (cookie.Expires == DateTime.MinValue) {
409 cookie.Expires = cookie.TimeStamp.AddSeconds (UInt32.Parse (val));
413 case "EXPIRES": // Netscape Style Set-Cookie
414 if (cookie.Expires != DateTime.MinValue)
417 cookie.Expires = TryParseCookieExpires (val);
423 if (cookie.Port == null)
427 cookie.Secure = true;
431 cookie.Version = (int) UInt32.Parse (val);
437 if (cookieCollection == null)
438 cookieCollection = new CookieCollection ();
440 if (cookie.Domain == "")
441 cookie.Domain = uri.Host;
443 cookieCollection.Add (cookie);
444 if (cookie_container != null)
445 cookie_container.Add (uri, cookie);
448 void SetCookie2 (string cookies_str)
450 string [] cookies = cookies_str.Split (',');
452 foreach (string cookie_str in cookies)
453 SetCookie (cookie_str);
456 string[] cookieExpiresFormats =
458 "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'",
459 "ddd, dd'-'MMM'-'yy HH':'mm':'ss 'GMT'" };
461 DateTime TryParseCookieExpires (string value)
463 if (value == null || value.Length == 0)
464 return DateTime.MinValue;
466 for (int i = 0; i <= cookieExpiresFormats.Length; i++)
469 DateTime cookieExpiresUtc = DateTime.ParseExact (value, cookieExpiresFormats [i], CultureInfo.InvariantCulture);
471 //convert UTC/GMT time to local time
473 cookieExpiresUtc = DateTime.SpecifyKind (cookieExpiresUtc, DateTimeKind.Utc);
474 return TimeZone.CurrentTimeZone.ToLocalTime (cookieExpiresUtc);
476 //DateTime.Kind is only available on .NET 2.0, so do some calculation
477 TimeSpan localOffset = TimeZone.CurrentTimeZone.GetUtcOffset (cookieExpiresUtc.Date);
478 return cookieExpiresUtc.Add (localOffset);
483 //If we can't parse Expires, use cookie as session cookie (expires is DateTime.MinValue)
484 return DateTime.MinValue;
493 public CookieParser (string header) : this (header, 0)
497 public CookieParser (string header, int position)
499 this.header = header;
501 this.length = header.Length;
504 public bool GetNextNameValue (out string name, out string val)
512 name = GetCookieName ();
513 if (pos < header.Length && header [pos] == '=') {
515 val = GetCookieValue ();
518 if (pos < length && header [pos] == ';')
524 string GetCookieName ()
527 while (k < length && Char.IsWhiteSpace (header [k]))
531 while (k < length && header [k] != ';' && header [k] != '=')
535 return header.Substring (begin, k - begin).Trim ();
538 string GetCookieValue ()
544 while (k < length && Char.IsWhiteSpace (header [k]))
548 if (header [k] == '"'){
552 while (k < length && header [k] != '"')
555 for (j = k; j < length && header [j] != ';'; j++)
560 while (k < length && header [k] != ';')
565 return header.Substring (begin, k - begin).Trim ();