private HttpResponse buildOptionsResponse(HttpRequest request)
        {
            HttpResponse answer = new HttpResponse(HttpStatus.NO_CONTENT_204);

            // vvv http://www.w3.org/TR/cors/#access-control-allow-methods-response-header
            answer.putHeader("Access-Control-Allow-Methods", "OPTIONS, POST");
            // ^^^ http://www.w3.org/TR/cors/#access-control-allow-methods-response-header


            String accessControlAllowOrigin = request.getHttpHeader("origin");
            if (null == accessControlAllowOrigin)
            {
                accessControlAllowOrigin = "*";
            }
            answer.putHeader("Access-Control-Allow-Origin", accessControlAllowOrigin);

            String accessControlRequestHeaders = request.getHttpHeader("access-control-request-headers");
            if (null != accessControlRequestHeaders)
            {
                answer.putHeader("Access-Control-Allow-Headers", accessControlRequestHeaders);
            }
            
            return answer;
        }
        public HttpResponse processRequest(HttpRequest request)
        {

            String requestUri = request.RequestUri;

            if (requestUri.EndsWith("/"))
            {
                requestUri = requestUri + "index.html";
            }

            { // some validation
                validateRequestUri(requestUri);
                validateMimeTypeForRequestUri(requestUri);
            }

			FileInfo absoluteFileInfo = toAbsoluteFileInfo(requestUri);


			String eTag = getETag(absoluteFileInfo);

			HttpResponse answer;

			String ifNoneMatch = request.getHttpHeader("if-none-match");
			if (null != ifNoneMatch && ifNoneMatch.Equals(eTag))
			{
				answer = new HttpResponse(HttpStatus.NOT_MODIFIED_304);
			}
			else {

				Entity body = readFile(absoluteFileInfo);
				answer = new HttpResponse(HttpStatus.OK_200, body);
				String contentType = MimeTypes.getMimeTypeForPath(requestUri);
				answer.setContentType(contentType);

			}

			if (null != _cacheControl)
			{
				answer.putHeader(_cacheControl.getName(), _cacheControl.getValue());
			}
				

			{
				answer.putHeader("Date", DateTime.UtcNow.ToString("R"));
			}

			{
				var lastModified = absoluteFileInfo.LastWriteTimeUtc;
				answer.putHeader("Last-Modified", lastModified.ToString("R"));
			}


			answer.putHeader("ETag", eTag);
			return answer;

        }
        public static HttpResponse toHttpResponse( Exception e ) 
        {
            		
            int statusCode = HttpStatus.INTERNAL_SERVER_ERROR_500;
		
		    if( e is BaseException ) {
			    BaseException baseException = (BaseException)e;

                int faultCode = baseException.FaultCode;

                // does BaseException have what looks like a HTTP CODE ?
                if (0 < faultCode && faultCode < 1000)
                {
                    statusCode = faultCode;
                }
            }

			Entity entity = toEntity( statusCode );

            HttpResponse answer = new HttpResponse( statusCode, entity );
            return answer;
		}
        private bool writeResponse(HttpResponse response)
        {
            // socket closed while processing the request ...
            if (!_socket.Connected)
            {
                log.warn("!_socket.Connected");
                return false;
            }

            try
            {
                // write the response ... 
                HttpResponseWriter.writeResponse(response, _networkStream);
            }
            catch (BaseException e)
            {
                if (e.FaultCode == StreamHelper.IOEXCEPTION_ON_STREAM_WRITE)
                {
                    log.warn("IOException raised while writing response (socket closed ?)");
                    return false;
                }
                else
                {
                    log.warn(e);
                    return false;
                }
            }
            catch (Exception e)
            {
                log.warn(e);
                return false;
            }

            return true;
        }
        private void logRequestResponse(HttpRequest request, HttpResponse response, bool writeResponseSucceded)
        {
            int statusCode = response.Status;

            String requestUri = request.RequestUri;

            long contentLength = 0;
            if (null != response.Entity)
            {
                contentLength = response.Entity.getContentLength();
                if (null != response.Range)
                {
                    contentLength = response.Range.getContentLength(contentLength);
                }
            }


            long timeTaken = DateTime.Now.Ticks - request.Created;
            // DateTime .Ticks Property ... 
            //A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.
            timeTaken /= 10 * 1000;

            String completed;
            if (writeResponseSucceded)
            {
                completed = "true";
            } else 
            {
                completed = "false";
            }

            String rangeString;
            {
                if (null == response.Range)
                {
                    rangeString = "bytes";
                }
                else
                {
                    rangeString = response.Range.ToContentRange(response.Entity.getContentLength());
                }
            }

            log.infoFormat("status:{0} uri:{1} content-length:{2} time-taken:{3} completed:{4} range:{5}", statusCode, requestUri, contentLength, timeTaken, completed, rangeString);

        }
        internal static HttpResponse processPostRequest(ServicesRegistery servicesRegistery, HttpRequest request)
        {

            if (HttpMethod.POST != request.Method)
            {
                log.errorFormat("unsupported method; request.Method = '{0}'", request.Method);
                throw HttpErrorHelper.badRequest400FromOriginator(typeof(ServicesRequestHandler));
            }


            Entity entity = request.Entity;
            if (_MAXIMUM_REQUEST_ENTITY_LENGTH < entity.getContentLength())
            {
                log.errorFormat("_MAXIMUM_REQUEST_ENTITY_LENGTH < entity.getContentLength(); _MAXIMUM_REQUEST_ENTITY_LENGTH = {0}; entity.getContentLength() = {1}", _MAXIMUM_REQUEST_ENTITY_LENGTH, entity.getContentLength());
                throw HttpErrorHelper.requestEntityTooLarge413FromOriginator(typeof(ServicesRequestHandler));
            }

            Data data = GetData(entity);

            BrokerMessage call = Serializer.deserialize(data);
            BrokerMessage response = process(servicesRegistery, call);

            HttpResponse answer;
            {
                if (BrokerMessageType.ONEWAY == call.getMessageType())
                {
                    answer = new HttpResponse(HttpStatus.NO_CONTENT_204);
                }
                else
                {
                    Data responseData = Serializer.Serialize(response);
                    Entity responseBody = new DataEntity(responseData);
                    answer = new HttpResponse(HttpStatus.OK_200, responseBody);
                }
            }

            return answer;


        }
        public static void tryWriteResponse(HttpResponse response, Stream outputStream)
        {
            log.enteredMethod();

            int statusCode = response.Status;
            String statusString = HttpStatus.getReason(statusCode);

            StringBuilder statusLineAndHeaders = new StringBuilder();
            statusLineAndHeaders.AppendFormat("HTTP/1.1 {0} {1}\r\n", statusCode, statusString);

            Dictionary<String, String> headers = response.headers;
            foreach (KeyValuePair<String, String> dictionaryEntry in headers)
            {
                statusLineAndHeaders.AppendFormat("{0}: {1}\r\n", dictionaryEntry.Key, dictionaryEntry.Value);
            }

            Entity entity = response.Entity;


            //////////////////////////////////////////////////////////////////
            // no entity

            if (null == entity)
            {
                if (204 != statusCode)
                {
                    log.warnFormat("null == entity && 204 != statusCode; statusCode = {0}", statusCode);
                    statusLineAndHeaders.Append("Content-Length: 0\r\n");
                }
                else
                {
                    // from ...
                    // http://stackoverflow.com/questions/912863/is-an-http-application-that-sends-a-content-length-or-transfer-encoding-with-a-2
                    // ... it would 'appear' safest to not include 'Content-Length' on a 204
                }

                statusLineAndHeaders.Append("Accept-Ranges: bytes\r\n\r\n");

                byte[] utfBytes = StringHelper.ToUtfBytes(statusLineAndHeaders.ToString());
                outputStream.Write(utfBytes, 0, utfBytes.Length);

                return; // our work is done
            }

            //////////////////////////////////////////////////////////////////
            // has entity 

            long entityContentLength = entity.getContentLength();
            long seekPosition = 0;
            long amountToWrite = entityContentLength;

            //////////////////////////////////////////////////////////////////
            // headers relevant to range support
            Range range = response.Range;

            if (null == range)
            {
                statusLineAndHeaders.Append("Accept-Ranges: bytes\r\n");

                if (HttpStatus.PARTIAL_CONTENT_206 == statusCode)
                {
                    log.warn("null == range && HttpStatus.PARTIAL_CONTENT_206 == statusCode");
                }
            }
            else
            {
                String contentRangeHeader = String.Format("Content-Range: {0}\r\n", range.ToContentRange(entityContentLength));
                statusLineAndHeaders.Append(contentRangeHeader);

                amountToWrite = range.getContentLength(entityContentLength);
                seekPosition = range.getSeekPosition(entityContentLength);

                if (HttpStatus.PARTIAL_CONTENT_206 != statusCode)
                {
                    log.warn("null != range && HttpStatus.PARTIAL_CONTENT_206 != statusCode");
                }
            }


            //////////////////////////////////////////////////////////////////
            // content-length and final newline

            statusLineAndHeaders.Append(String.Format("Content-Length: {0}\r\n\r\n", amountToWrite));


            //////////////////////////////////////////////////////////////////
            // write the headers

            byte[] headersUtf8Bytes = StringHelper.ToUtfBytes(statusLineAndHeaders.ToString()); // C# compiler does not like reuse of the name 'utfBytes'
            outputStream.Write(headersUtf8Bytes, 0, headersUtf8Bytes.Length);

            //////////////////////////////////////////////////////////////////
            // write the entity

            entity.WriteTo(outputStream, seekPosition, amountToWrite);
            StreamHelper.flush(outputStream, false, typeof(HttpResponseWriter));
        }
 public static void writeResponse(HttpResponse response, Stream outputStream)
 {
     tryWriteResponse(response, outputStream);
 }