HttpResponse.cs 27 KB

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