<commit_before>// Copyright (c) 2014 SIL International
// This software is licensed under the MIT License (http://opensource.org/licenses/MIT)

using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using SIL.IO;
using SIL.Reporting;


namespace Bloom.Api
{
	/// <summary>
	/// this makes it easier to test without actually going through the http listener
	/// </summary>
	public class RequestInfo : IRequestInfo
	{
		private readonly HttpListenerContext _actualContext;
		private NameValueCollection _queryStringList;
		private NameValueCollection _postData;

		public string LocalPathWithoutQuery
		{
			get
			{
				var queryStart = RawUrl.IndexOf("?", StringComparison.Ordinal);
				var urlToDecode = queryStart == -1 ? RawUrl : RawUrl.Substring(0, queryStart);
				// this (done in fix for BL-3750) by itself caused us to lose + signs, which according to http://stackoverflow.com/a/1006074/723299 are *not* to be
				// replaced by space if they are in the "path component" of the url (hence the approach comment out below). In the "query" component,
				// plus signs do have a special meaning. So if this is correct, UrlDecode appears to be WRONG in its
				// handling of + signs. They should be treated literally, not turned into spaces.
				//no:	return HttpUtility.UrlDecode(urlToDecode);

				// So let's workaround that problem with UrlDecode and still do decoding on the path component:
				var pathWithoutLiteralPlusSigns = urlToDecode.Replace("+","%2B");
				return HttpUtility.UrlDecode(pathWithoutLiteralPlusSigns);

				// This uses the wrong encoding to decode the LocalPath. (BL-3750)
				// See unit test LocalPathWithoutQuery_SpecialCharactersDecodedCorrectly for example.
				//var uri = _actualContext.Request.Url;
				//return uri.LocalPath + HttpUtility.UrlDecode(uri.Fragment);
			}
		}

		public string ContentType
		{
			set { _actualContext.Response.ContentType = value; }
		}

		public string HttpMethod
		{
			get { return _actualContext.Request.HttpMethod; }
		}

		public RequestInfo(HttpListenerContext actualContext)
		{
			_actualContext = actualContext;
		}

		//used when an anchor has given us info, but we don't actually want the browser to navigate
		public void SucceededDoNotNavigate()
		{
			_actualContext.Response.StatusCode = 202; //Accepted. Request accepted but not completed yet, it will continue asynchronously.
			HaveOutput = true;
			return;
		}

		public void WriteCompleteOutput(string s)
		{
			WriteOutput(Encoding.UTF8.GetBytes(s), _actualContext.Response);
		}

		private void WriteOutput(byte[] buffer, HttpListenerResponse response)
		{
			response.ContentLength64 += buffer.Length;
			Stream output = response.OutputStream;
			output.Write(buffer, 0, buffer.Length);
			output.Close();
			HaveOutput = true;
		}

		public bool HaveOutput { get; private set; }

		public void ReplyWithFileContent(string path)
		{
			//Deal with BL-3153, where the file was still open in another thread
			FileStream fs;
			if(!RobustFile.Exists(path))
			{
				//for audio, at least, this is not really an error. We constantly are asking if audio already exists for the current segment
				//enhance: maybe audio should go through a different path, e.g. "/bloom/audio/somefile.wav"
				//then this path COULD write and error
				//Logger.WriteError("Server could not find" + path);
				_actualContext.Response.StatusCode = 404;
				return;
			}

			try
			{
				fs = RobustFile.OpenRead(path);
			}
			catch(Exception error)
			{<commit_msg>BL-4191 Catch exception when sending data to JS fails<commit_after>// Copyright (c) 2014 SIL International
// This software is licensed under the MIT License (http://opensource.org/licenses/MIT)

using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using SIL.IO;
using SIL.Reporting;


namespace Bloom.Api
{
	/// <summary>
	/// this makes it easier to test without actually going through the http listener
	/// </summary>
	public class RequestInfo : IRequestInfo
	{
		private readonly HttpListenerContext _actualContext;
		private NameValueCollection _queryStringList;
		private NameValueCollection _postData;

		public string LocalPathWithoutQuery
		{
			get
			{
				var queryStart = RawUrl.IndexOf("?", StringComparison.Ordinal);
				var urlToDecode = queryStart == -1 ? RawUrl : RawUrl.Substring(0, queryStart);
				// this (done in fix for BL-3750) by itself caused us to lose + signs, which according to http://stackoverflow.com/a/1006074/723299 are *not* to be
				// replaced by space if they are in the "path component" of the url (hence the approach comment out below). In the "query" component,
				// plus signs do have a special meaning. So if this is correct, UrlDecode appears to be WRONG in its
				// handling of + signs. They should be treated literally, not turned into spaces.
				//no:	return HttpUtility.UrlDecode(urlToDecode);

				// So let's workaround that problem with UrlDecode and still do decoding on the path component:
				var pathWithoutLiteralPlusSigns = urlToDecode.Replace("+","%2B");
				return HttpUtility.UrlDecode(pathWithoutLiteralPlusSigns);

				// This uses the wrong encoding to decode the LocalPath. (BL-3750)
				// See unit test LocalPathWithoutQuery_SpecialCharactersDecodedCorrectly for example.
				//var uri = _actualContext.Request.Url;
				//return uri.LocalPath + HttpUtility.UrlDecode(uri.Fragment);
			}
		}

		public string ContentType
		{
			set { _actualContext.Response.ContentType = value; }
		}

		public string HttpMethod
		{
			get { return _actualContext.Request.HttpMethod; }
		}

		public RequestInfo(HttpListenerContext actualContext)
		{
			_actualContext = actualContext;
		}

		//used when an anchor has given us info, but we don't actually want the browser to navigate
		public void SucceededDoNotNavigate()
		{
			_actualContext.Response.StatusCode = 202; //Accepted. Request accepted but not completed yet, it will continue asynchronously.
			HaveOutput = true;
			return;
		}

		public void WriteCompleteOutput(string s)
		{
			WriteOutput(Encoding.UTF8.GetBytes(s), _actualContext.Response);
		}

		private void WriteOutput(byte[] buffer, HttpListenerResponse response)
		{
			response.ContentLength64 += buffer.Length;
			Stream output = response.OutputStream;
			try
			{
				output.Write(buffer, 0, buffer.Length);
				output.Close();
			}
			catch (HttpListenerException e)
			{
				// We may well be unable to write if, while we were gathering the data, the user switched
				// pages or something similar so that the page that requested the data is gone. This seems
				// to produce this particular exception type.
				Logger.WriteEvent("Could not write requested data to JavaScript: " + e.Message + e.StackTrace);
				Debug.WriteLine(e.Message);
			}
			HaveOutput = true;
		}

		public bool HaveOutput { get; private set; }

		public void ReplyWithFileContent(string path)
		{
			//Deal with BL-3153, where the file was still open in another thread
			FileStream fs;
			if(!RobustFile.Exists(path))
			{
				//for audio, at least, this is not really an error. We constantly are asking if audio already exists for the current segment
				//enhance: maybe audio should go through a different path, e.g. "/bloom/audio/somefile.wav"
				//then this path COULD write and error
				//Logger.WriteError("Server could not find" + path);
				_actualContext.Response.StatusCode = 404;
				return;
			}

			try
			{
				fs = RobustFile.OpenRead(path);
			}
			catch(Exception error)
			{
{'fraction_non_alphanumeric': 0.08622268742640324, 'fraction_numerical': 0.01151380348030878, 'mean_word_length': 3.5992779783393503, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6936792', 'n_tokens_mistral': 2539, 'n_tokens_neox': 2139, 'n_words': 767}