HttpResponse.cs 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. //
  2. // System.Web.HttpResponse.cs
  3. //
  4. //
  5. // Author:
  6. // Miguel de Icaza ([email protected])
  7. // Gonzalo Paniagua Javier ([email protected])
  8. //
  9. // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System.Text;
  31. using System.Web.UI;
  32. using System.Collections;
  33. using System.Collections.Specialized;
  34. using System.IO;
  35. using System.Web.Caching;
  36. using System.Threading;
  37. using System.Web.Util;
  38. using System.Web.Configuration;
  39. using System.Globalization;
  40. using System.Security.Permissions;
  41. #if TARGET_J2EE
  42. using [email protected];
  43. #endif
  44. namespace System.Web {
  45. // CAS - no InheritanceDemand here as the class is sealed
  46. [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  47. public sealed class HttpResponse {
  48. internal HttpWorkerRequest WorkerRequest;
  49. internal HttpResponseStream output_stream;
  50. internal bool buffer = true;
  51. HttpContext context;
  52. TextWriter writer;
  53. HttpCachePolicy cache_policy;
  54. Encoding encoding;
  55. HttpCookieCollection cookies;
  56. int status_code = 200;
  57. string status_description = "OK";
  58. string content_type = "text/html";
  59. string charset;
  60. bool charset_set;
  61. CachedRawResponse cached_response;
  62. string user_cache_control = "private";
  63. string redirect_location;
  64. //
  65. // Negative Content-Length means we auto-compute the size of content-length
  66. // can be overwritten with AppendHeader ("Content-Length", value)
  67. //
  68. long content_length = -1;
  69. //
  70. // The list of the headers that we will send back to the client, except
  71. // the headers that we compute here.
  72. //
  73. ArrayList headers = new ArrayList ();
  74. bool headers_sent;
  75. ArrayList cached_headers;
  76. //
  77. // Transfer encoding state
  78. //
  79. string transfer_encoding;
  80. internal bool use_chunked;
  81. bool closed;
  82. internal bool suppress_content;
  83. //
  84. // Session State
  85. //
  86. string app_path_mod;
  87. //
  88. // Passed as flags
  89. //
  90. internal object FlagEnd = new object ();
  91. #if NET_2_0
  92. bool is_request_being_redirected;
  93. Encoding headerEncoding;
  94. #endif
  95. internal HttpResponse ()
  96. {
  97. output_stream = new HttpResponseStream (this);
  98. }
  99. public HttpResponse (TextWriter writer) : this ()
  100. {
  101. this.writer = writer;
  102. }
  103. internal HttpResponse (HttpWorkerRequest worker_request, HttpContext context) : this ()
  104. {
  105. WorkerRequest = worker_request;
  106. this.context = context;
  107. if (worker_request != null)
  108. use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
  109. }
  110. internal TextWriter SetTextWriter (TextWriter writer)
  111. {
  112. TextWriter prev = this.writer;
  113. this.writer = writer;
  114. return prev;
  115. }
  116. public bool Buffer {
  117. get {
  118. return buffer;
  119. }
  120. set {
  121. buffer = value;
  122. }
  123. }
  124. public bool BufferOutput {
  125. get {
  126. return buffer;
  127. }
  128. set {
  129. buffer = value;
  130. }
  131. }
  132. //
  133. // Use the default from <globalization> section if the client has not set the encoding
  134. //
  135. public Encoding ContentEncoding {
  136. get {
  137. if (encoding == null) {
  138. if (context != null) {
  139. string client_content_type = context.Request.ContentType;
  140. string parameter = HttpRequest.GetParameter (client_content_type, "; charset=");
  141. if (parameter != null) {
  142. try {
  143. // Do what the #1 web server does
  144. encoding = Encoding.GetEncoding (parameter);
  145. } catch {
  146. }
  147. }
  148. }
  149. if (encoding == null)
  150. encoding = WebEncoding.ResponseEncoding;
  151. }
  152. return encoding;
  153. }
  154. set {
  155. if (value == null)
  156. throw new ArgumentException ("ContentEncoding can not be null");
  157. encoding = value;
  158. HttpWriter http_writer = writer as HttpWriter;
  159. if (http_writer != null)
  160. http_writer.SetEncoding (encoding);
  161. }
  162. }
  163. public string ContentType {
  164. get {
  165. return content_type;
  166. }
  167. set {
  168. content_type = value;
  169. }
  170. }
  171. public string Charset {
  172. get {
  173. if (charset == null)
  174. charset = ContentEncoding.WebName;
  175. return charset;
  176. }
  177. set {
  178. charset_set = true;
  179. charset = value;
  180. }
  181. }
  182. public HttpCookieCollection Cookies {
  183. get {
  184. if (cookies == null)
  185. cookies = new HttpCookieCollection (true, false);
  186. return cookies;
  187. }
  188. }
  189. public int Expires {
  190. get {
  191. if (cache_policy == null)
  192. return 0;
  193. return cache_policy.ExpireMinutes ();
  194. }
  195. set {
  196. Cache.SetExpires (DateTime.Now + new TimeSpan (0, value, 0));
  197. }
  198. }
  199. public DateTime ExpiresAbsolute {
  200. get {
  201. return Cache.Expires;
  202. }
  203. set {
  204. Cache.SetExpires (value);
  205. }
  206. }
  207. public Stream Filter {
  208. get {
  209. if (WorkerRequest == null)
  210. return null;
  211. return output_stream.Filter;
  212. }
  213. set {
  214. output_stream.Filter = value;
  215. }
  216. }
  217. #if NET_2_0
  218. public Encoding HeaderEncoding {
  219. get {
  220. if (headerEncoding == null) {
  221. GlobalizationSection gs = WebConfigurationManager.SafeGetSection ("system.web/globalization", typeof (GlobalizationSection)) as GlobalizationSection;
  222. if (gs == null)
  223. headerEncoding = Encoding.UTF8;
  224. else {
  225. headerEncoding = gs.ResponseHeaderEncoding;
  226. if (headerEncoding == Encoding.Unicode)
  227. throw new HttpException ("HeaderEncoding must not be Unicode");
  228. }
  229. }
  230. return headerEncoding;
  231. }
  232. set {
  233. if (headers_sent)
  234. throw new HttpException ("headers have already been sent");
  235. if (value == null)
  236. throw new ArgumentNullException ("HeaderEncoding");
  237. if (value == Encoding.Unicode)
  238. throw new HttpException ("HeaderEncoding must not be Unicode");
  239. headerEncoding = value;
  240. }
  241. }
  242. #endif
  243. public bool IsClientConnected {
  244. get {
  245. if (WorkerRequest == null)
  246. return true; // yep that's true
  247. return WorkerRequest.IsClientConnected ();
  248. }
  249. }
  250. #if NET_2_0
  251. public bool IsRequestBeingRedirected {
  252. get { return is_request_being_redirected; }
  253. }
  254. #endif
  255. public TextWriter Output {
  256. get {
  257. if (writer == null)
  258. writer = new HttpWriter (this);
  259. return writer;
  260. }
  261. }
  262. public Stream OutputStream {
  263. get {
  264. return output_stream;
  265. }
  266. }
  267. public string RedirectLocation {
  268. get {
  269. return redirect_location;
  270. }
  271. set {
  272. redirect_location = value;
  273. }
  274. }
  275. public string Status {
  276. get {
  277. return String.Format ("{0} {1}", status_code, StatusDescription);
  278. }
  279. set {
  280. int p = value.IndexOf (' ');
  281. if (p == -1)
  282. throw new HttpException ("Invalid format for the Status property");
  283. string s = value.Substring (0, p);
  284. #if NET_2_0
  285. if (!Int32.TryParse (s, out status_code))
  286. throw new HttpException ("Invalid format for the Status property");
  287. #else
  288. try {
  289. status_code = Int32.Parse (s);
  290. } catch {
  291. throw new HttpException ("Invalid format for the Status property");
  292. }
  293. #endif
  294. status_description = value.Substring (p+1);
  295. }
  296. }
  297. public int StatusCode {
  298. get {
  299. return status_code;
  300. }
  301. set {
  302. if (headers_sent)
  303. throw new HttpException ("headers have already been sent");
  304. status_code = value;
  305. status_description = null;
  306. }
  307. }
  308. public string StatusDescription {
  309. get {
  310. if (status_description == null)
  311. status_description = HttpWorkerRequest.GetStatusDescription (status_code);
  312. return status_description;
  313. }
  314. set {
  315. if (headers_sent)
  316. throw new HttpException ("headers have already been sent");
  317. status_description = value;
  318. }
  319. }
  320. public bool SuppressContent {
  321. get {
  322. return suppress_content;
  323. }
  324. set {
  325. suppress_content = value;
  326. }
  327. }
  328. #if NET_2_0
  329. [MonoTODO ("Not implemented")]
  330. public void AddCacheDependency (CacheDependency[] dependencies)
  331. {
  332. throw new NotImplementedException ();
  333. }
  334. [MonoTODO ("Not implemented")]
  335. public void AddCacheItemDependencies (string[] cacheKeys)
  336. {
  337. throw new NotImplementedException ();
  338. }
  339. #endif
  340. [MonoTODO("Currently does nothing")]
  341. public void AddCacheItemDependencies (ArrayList cacheKeys)
  342. {
  343. // TODO: talk to jackson about the cache
  344. }
  345. [MonoTODO("Currently does nothing")]
  346. public void AddCacheItemDependency (string cacheKey)
  347. {
  348. // TODO: talk to jackson about the cache
  349. }
  350. [MonoTODO("Currently does nothing")]
  351. public void AddFileDependencies (ArrayList filenames)
  352. {
  353. // TODO: talk to jackson about the cache
  354. }
  355. #if NET_2_0
  356. [MonoTODO ("Not implemented")]
  357. public void AddFileDependencies (string[] filenames)
  358. {
  359. throw new NotImplementedException ();
  360. }
  361. #endif
  362. [MonoTODO ("Currently does nothing")]
  363. public void AddFileDependency (string filename)
  364. {
  365. // TODO: talk to jackson about the cache
  366. }
  367. public void AddHeader (string name, string value)
  368. {
  369. AppendHeader (name, value);
  370. }
  371. public void AppendCookie (HttpCookie cookie)
  372. {
  373. Cookies.Add (cookie);
  374. }
  375. //
  376. // AppendHeader:
  377. // Special case for Content-Length, Content-Type, Transfer-Encoding and Cache-Control
  378. //
  379. //
  380. public void AppendHeader (string name, string value)
  381. {
  382. if (headers_sent)
  383. throw new HttpException ("headers have been already sent");
  384. if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){
  385. content_length = (long) UInt64.Parse (value);
  386. use_chunked = false;
  387. return;
  388. }
  389. if (String.Compare (name, "content-type", true, CultureInfo.InvariantCulture) == 0){
  390. ContentType = value;
  391. return;
  392. }
  393. if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){
  394. transfer_encoding = value;
  395. use_chunked = false;
  396. return;
  397. }
  398. if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){
  399. user_cache_control = value;
  400. return;
  401. }
  402. headers.Add (new UnknownResponseHeader (name, value));
  403. }
  404. [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
  405. public void AppendToLog (string param)
  406. {
  407. Console.Write ("System.Web: ");
  408. Console.WriteLine (param);
  409. }
  410. public string ApplyAppPathModifier (string virtualPath)
  411. {
  412. if (virtualPath == null)
  413. return null;
  414. if (virtualPath == "")
  415. return context.Request.RootVirtualDir;
  416. if (UrlUtils.IsRelativeUrl (virtualPath)) {
  417. virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
  418. } else if (UrlUtils.IsRooted (virtualPath)) {
  419. virtualPath = UrlUtils.Canonic (virtualPath);
  420. }
  421. if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
  422. string rvd = context.Request.RootVirtualDir;
  423. string basevd = rvd.Replace (app_path_mod, "");
  424. if (!StrUtils.StartsWith (virtualPath, basevd))
  425. return virtualPath;
  426. virtualPath = UrlUtils.Combine (rvd, virtualPath.Substring (basevd.Length));
  427. }
  428. return virtualPath;
  429. }
  430. public void BinaryWrite (byte [] buffer)
  431. {
  432. output_stream.Write (buffer, 0, buffer.Length);
  433. }
  434. internal void BinaryWrite (byte [] buffer, int start, int len)
  435. {
  436. output_stream.Write (buffer, start, len);
  437. }
  438. public void Clear ()
  439. {
  440. ClearContent ();
  441. }
  442. public void ClearContent ()
  443. {
  444. output_stream.Clear ();
  445. }
  446. public void ClearHeaders ()
  447. {
  448. if (headers_sent)
  449. throw new HttpException ("headers have been already sent");
  450. // Reset the special case headers.
  451. content_length = -1;
  452. content_type = "text/html";
  453. transfer_encoding = null;
  454. user_cache_control = null;
  455. headers.Clear ();
  456. }
  457. internal bool HeadersSent {
  458. get {
  459. return headers_sent;
  460. }
  461. }
  462. public void Close ()
  463. {
  464. if (closed)
  465. return;
  466. if (WorkerRequest != null)
  467. WorkerRequest.CloseConnection ();
  468. closed = true;
  469. }
  470. public void End ()
  471. {
  472. if (context.TimeoutPossible) {
  473. Thread.CurrentThread.Abort (FlagEnd);
  474. } else {
  475. // If this is called from an async event, signal the completion
  476. // but don't throw.
  477. context.ApplicationInstance.CompleteRequest ();
  478. }
  479. }
  480. // Generate:
  481. // Content-Length
  482. // Content-Type
  483. // Transfer-Encoding (chunked)
  484. // Cache-Control
  485. void AddHeadersNoCache (ArrayList write_headers, bool final_flush)
  486. {
  487. //
  488. // Transfer-Encoding
  489. //
  490. if (use_chunked)
  491. write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", "chunked"));
  492. else if (transfer_encoding != null)
  493. write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", transfer_encoding));
  494. UnknownResponseHeader date_header = new UnknownResponseHeader ("Date",
  495. DateTime.UtcNow.ToString ("r", CultureInfo.InvariantCulture));
  496. write_headers.Add (date_header);
  497. if (IsCached)
  498. cached_response.DateHeader = date_header;
  499. if (redirect_location != null)
  500. write_headers.Add (new UnknownResponseHeader ("Location", redirect_location));
  501. //
  502. // If Content-Length is set.
  503. //
  504. if (content_length >= 0){
  505. write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
  506. content_length.ToString (CultureInfo.InvariantCulture)));
  507. } else if (Buffer){
  508. if (final_flush){
  509. //
  510. // If we are buffering and this is the last flush, not a middle-flush,
  511. // we know the content-length.
  512. //
  513. content_length = output_stream.total;
  514. write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
  515. content_length.ToString (CultureInfo.InvariantCulture)));
  516. } else {
  517. //
  518. // We are buffering, and this is a flush in the middle.
  519. // If we are not chunked, we need to set "Connection: close".
  520. //
  521. if (use_chunked){
  522. #if DEBUG
  523. Console.WriteLine ("Setting to close2");
  524. #endif
  525. write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
  526. }
  527. }
  528. } else {
  529. //
  530. // If the content-length is not set, and we are not buffering, we must
  531. // close at the end.
  532. //
  533. if (use_chunked){
  534. #if DEBUG
  535. Console.WriteLine ("Setting to close");
  536. #endif
  537. write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
  538. }
  539. }
  540. //
  541. // Cache Control, the cache policy takes precedence over the cache_control property.
  542. //
  543. if (cache_policy != null)
  544. cache_policy.SetHeaders (this, headers);
  545. else
  546. write_headers.Add (new UnknownResponseHeader ("Cache-Control", CacheControl));
  547. //
  548. // Content-Type
  549. //
  550. if (content_type != null){
  551. string header = content_type;
  552. if (charset_set || header == "text/plain" || header == "text/html") {
  553. if (header.IndexOf ("charset=") == -1) {
  554. if (charset == null || charset == "")
  555. charset = ContentEncoding.HeaderName;
  556. header += "; charset=" + charset;
  557. }
  558. }
  559. write_headers.Add (new UnknownResponseHeader ("Content-Type", header));
  560. }
  561. if (cookies != null && cookies.Count != 0){
  562. int n = cookies.Count;
  563. for (int i = 0; i < n; i++)
  564. write_headers.Add (cookies.Get (i).GetCookieHeader ());
  565. #if TARGET_J2EE
  566. // For J2EE Portal support emulate cookies by storing them in the session.
  567. context.Request.SetSessionCookiesForPortal (cookies);
  568. #endif
  569. }
  570. }
  571. internal void WriteHeaders (bool final_flush)
  572. {
  573. if (headers_sent)
  574. return;
  575. if (WorkerRequest != null)
  576. WorkerRequest.SendStatus (status_code, StatusDescription);
  577. if (cached_response != null)
  578. cached_response.SetHeaders (headers);
  579. // If this page is cached use the cached headers
  580. // instead of the standard headers
  581. ArrayList write_headers = headers;
  582. if (cached_headers != null)
  583. write_headers = cached_headers;
  584. else
  585. AddHeadersNoCache (write_headers, final_flush);
  586. //
  587. // Flush
  588. //
  589. if (context != null) {
  590. HttpApplication app_instance = context.ApplicationInstance;
  591. if (app_instance != null)
  592. app_instance.TriggerPreSendRequestHeaders ();
  593. }
  594. if (WorkerRequest != null) {
  595. foreach (BaseResponseHeader header in write_headers){
  596. header.SendContent (WorkerRequest);
  597. }
  598. }
  599. headers_sent = true;
  600. }
  601. internal void DoFilter (bool close)
  602. {
  603. if (output_stream.HaveFilter && context != null && context.Error == null)
  604. output_stream.ApplyFilter (close);
  605. }
  606. internal void Flush (bool final_flush)
  607. {
  608. DoFilter (final_flush);
  609. if (!headers_sent){
  610. if (final_flush || status_code != 200)
  611. use_chunked = false;
  612. }
  613. bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
  614. if (suppress_content || head) {
  615. if (!headers_sent)
  616. WriteHeaders (true);
  617. output_stream.Clear ();
  618. if (WorkerRequest != null)
  619. output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
  620. return;
  621. }
  622. if (!headers_sent)
  623. WriteHeaders (final_flush);
  624. if (context != null) {
  625. HttpApplication app_instance = context.ApplicationInstance;
  626. if (app_instance != null)
  627. app_instance.TriggerPreSendRequestContent ();
  628. }
  629. if (IsCached) {
  630. MemoryStream ms = output_stream.GetData ();
  631. cached_response.ContentLength = (int) ms.Length;
  632. cached_response.SetData (ms.GetBuffer ());
  633. }
  634. if (WorkerRequest != null)
  635. output_stream.Flush (WorkerRequest, final_flush);
  636. }
  637. public void Flush ()
  638. {
  639. Flush (false);
  640. }
  641. public void Pics (string value)
  642. {
  643. AppendHeader ("PICS-Label", value);
  644. }
  645. public void Redirect (string url)
  646. {
  647. Redirect (url, true);
  648. }
  649. public void Redirect (string url, bool endResponse)
  650. {
  651. if (headers_sent)
  652. throw new HttpException ("header have been already sent");
  653. #if TARGET_J2EE
  654. // In J2EE portal we need to handle Redirect at the processAction phase
  655. // using the portlet ActionResponse that will send for us the redirect header.
  656. IPortletActionResponse resp = context.ServletResponse as IPortletActionResponse;
  657. if (resp != null) {
  658. resp.sendRedirect (ApplyAppPathModifier (url));
  659. if (endResponse)
  660. End ();
  661. return;
  662. }
  663. #endif
  664. #if NET_2_0
  665. is_request_being_redirected = true;
  666. #endif
  667. ClearHeaders ();
  668. ClearContent ();
  669. StatusCode = 302;
  670. url = ApplyAppPathModifier (url);
  671. headers.Add (new UnknownResponseHeader ("Location", url));
  672. // Text for browsers that can't handle location header
  673. Write ("<html><head><title>Object moved</title></head><body>\r\n");
  674. Write ("<h2>Object moved to <a href='" + url + "'>here</a></h2>\r\n");
  675. Write ("</body><html>\r\n");
  676. if (endResponse)
  677. End ();
  678. #if NET_2_0
  679. is_request_being_redirected = false;
  680. #endif
  681. }
  682. public static void RemoveOutputCacheItem (string path)
  683. {
  684. if (path == null)
  685. throw new ArgumentNullException ("path");
  686. if (path.Length == 0)
  687. return;
  688. if (path [0] != '/')
  689. throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
  690. HttpRuntime.Cache.Remove (path);
  691. }
  692. public void SetCookie (HttpCookie cookie)
  693. {
  694. AppendCookie (cookie);
  695. }
  696. public void Write (char ch)
  697. {
  698. Output.Write (ch);
  699. }
  700. public void Write (object obj)
  701. {
  702. if (obj == null)
  703. return;
  704. Output.Write (obj.ToString ());
  705. }
  706. public void Write (string s)
  707. {
  708. Output.Write (s);
  709. }
  710. public void Write (char [] buffer, int index, int count)
  711. {
  712. Output.Write (buffer, index, count);
  713. }
  714. internal void WriteFile (FileStream fs, long offset, long size)
  715. {
  716. byte [] buffer = new byte [32*1024];
  717. if (offset != 0)
  718. fs.Position = offset;
  719. long remain = size;
  720. int n;
  721. while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
  722. remain -= n;
  723. output_stream.Write (buffer, 0, n);
  724. }
  725. }
  726. public void WriteFile (string filename)
  727. {
  728. WriteFile (filename, false);
  729. }
  730. public void WriteFile (string filename, bool readIntoMemory)
  731. {
  732. if (filename == null)
  733. throw new ArgumentNullException ("filename");
  734. if (readIntoMemory){
  735. using (FileStream fs = File.OpenRead (filename))
  736. WriteFile (fs, 0, fs.Length);
  737. } else {
  738. FileInfo fi = new FileInfo (filename);
  739. output_stream.WriteFile (filename, 0, fi.Length);
  740. }
  741. if (buffer)
  742. return;
  743. output_stream.ApplyFilter (false);
  744. Flush ();
  745. }
  746. #if TARGET_JVM
  747. public void WriteFile (IntPtr fileHandle, long offset, long size) {
  748. throw new NotSupportedException("IntPtr not supported");
  749. }
  750. #else
  751. public void WriteFile (IntPtr fileHandle, long offset, long size)
  752. {
  753. if (offset < 0)
  754. throw new ArgumentNullException ("offset can not be negative");
  755. if (size < 0)
  756. throw new ArgumentNullException ("size can not be negative");
  757. if (size == 0)
  758. return;
  759. // Note: this .ctor will throw a SecurityException if the caller
  760. // doesn't have the UnmanagedCode permission
  761. using (FileStream fs = new FileStream (fileHandle, FileAccess.Read))
  762. WriteFile (fs, offset, size);
  763. if (buffer)
  764. return;
  765. output_stream.ApplyFilter (false);
  766. Flush ();
  767. }
  768. #endif
  769. public void WriteFile (string filename, long offset, long size)
  770. {
  771. if (filename == null)
  772. throw new ArgumentNullException ("filename");
  773. if (offset < 0)
  774. throw new ArgumentNullException ("offset can not be negative");
  775. if (size < 0)
  776. throw new ArgumentNullException ("size can not be negative");
  777. if (size == 0)
  778. return;
  779. FileStream fs = File.OpenRead (filename);
  780. WriteFile (fs, offset, size);
  781. if (buffer)
  782. return;
  783. output_stream.ApplyFilter (false);
  784. Flush ();
  785. }
  786. #if NET_2_0
  787. [MonoTODO ("Not implemented")]
  788. public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
  789. {
  790. throw new NotImplementedException ();
  791. }
  792. #endif
  793. //
  794. // Like WriteFile, but never buffers, so we manually Flush here
  795. //
  796. public void TransmitFile (string filename)
  797. {
  798. if (filename == null)
  799. throw new ArgumentNullException ("filename");
  800. TransmitFile (filename, false);
  801. }
  802. internal void TransmitFile (string filename, bool final_flush)
  803. {
  804. FileInfo fi = new FileInfo (filename);
  805. using (Stream s = fi.OpenRead ()); // Just check if we can read.
  806. output_stream.WriteFile (filename, 0, fi.Length);
  807. output_stream.ApplyFilter (final_flush);
  808. Flush (final_flush);
  809. }
  810. #region Session state support
  811. internal void SetAppPathModifier (string app_modifier)
  812. {
  813. app_path_mod = app_modifier;
  814. }
  815. #endregion
  816. #region Cache Support
  817. internal void SetCachedHeaders (ArrayList headers)
  818. {
  819. cached_headers = headers;
  820. }
  821. internal bool IsCached {
  822. get {
  823. return cached_response != null;
  824. }
  825. }
  826. public HttpCachePolicy Cache {
  827. get {
  828. if (cache_policy == null) {
  829. cache_policy = new HttpCachePolicy ();
  830. cache_policy.CacheabilityUpdated += new CacheabilityUpdatedCallback (OnCacheabilityUpdated);
  831. }
  832. return cache_policy;
  833. }
  834. }
  835. private void OnCacheabilityUpdated (object sender, CacheabilityUpdatedEventArgs e)
  836. {
  837. if (e.Cacheability >= HttpCacheability.Server && !IsCached)
  838. cached_response = new CachedRawResponse (cache_policy);
  839. else if (e.Cacheability <= HttpCacheability.Private)
  840. cached_response = null;
  841. }
  842. internal CachedRawResponse GetCachedResponse ()
  843. {
  844. cached_response.StatusCode = StatusCode;
  845. cached_response.StatusDescription = StatusDescription;
  846. return cached_response;
  847. }
  848. //
  849. // This is one of the old ASP compatibility methods, the real cache
  850. // control is in the Cache property, and this is a second class citizen
  851. //
  852. public string CacheControl {
  853. set {
  854. if (value == null || value == "") {
  855. Cache.SetCacheability (HttpCacheability.NoCache);
  856. user_cache_control = null;
  857. } else if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0) {
  858. Cache.SetCacheability (HttpCacheability.Public);
  859. user_cache_control = "public";
  860. } else if (String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0) {
  861. Cache.SetCacheability (HttpCacheability.Private);
  862. user_cache_control = "private";
  863. } else if (String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0) {
  864. Cache.SetCacheability (HttpCacheability.NoCache);
  865. user_cache_control = "no-cache";
  866. } else
  867. throw new ArgumentException ("CacheControl property only allows `public', " +
  868. "`private' or no-cache, for different uses, use " +
  869. "Response.AppendHeader");
  870. }
  871. get { return (user_cache_control != null) ? user_cache_control : "private"; }
  872. }
  873. #endregion
  874. internal int GetOutputByteCount ()
  875. {
  876. return output_stream.GetTotalLength ();
  877. }
  878. internal void ReleaseResources ()
  879. {
  880. output_stream.ReleaseResources (true);
  881. output_stream = null;
  882. }
  883. }
  884. }