HttpResponse.cs 25 KB

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