public EndRequestRecord GetEndRequestRecord(int appStatus, byte protocolStatus)
        {
            FastCGIHeader header = new FastCGIHeader();
            header.RequestId = Request.BeginRequestRecord.Header.RequestId;
            header.Type = FastCGIHeader.TYPE.END_REQUEST;
            header.Version = Request.BeginRequestRecord.Header.Version;
            header.ContentLength = EndRequestBody.LENGTH;
            header.PaddingLength = 0;
            header.Reserved = 0;

            EndRequestBody body = new EndRequestBody();
            body.AppStatus = appStatus;
            body.ProtocolStatus = protocolStatus;

            return new EndRequestRecord()
            {
                Headr = header,
                Body = body
            };
        }
        private int TranslateRemainingData(byte[] buffer, int offset, int length, int replaceOffset)
        {
            List<byte> stdinData = new List<byte> ();

            int endOffset = offset + length;
            while (offset < endOffset)
            {
                if (!_stdinHeader.HasValue)
                {
                    FastCGIHeader header = new FastCGIHeader (buffer, offset);
                    offset += FastCGIHeader.LENGTH;
                    if (header.RequestId != 0)
                    {
                        if (header.Type == FastCGIHeader.TYPE.STDIN)
                        {
                            _stdinHeader = header;
                        } else if (header.Type == FastCGIHeader.TYPE.ABORT_REQUEST)
                        {
                            AbortRequest = true;
                        }
                    }
                }

                if (_stdinHeader.HasValue)
                {
                    if (_stdinHeader.Value.ContentLength == 0)
                    {
                        // empty stdin = last stdin = endofrequest
                        EndOfRequest = true;
                        break;
                    } else if (_stdinHeader.Value.RequestId != 0 && endOffset > offset)
                    {
                        int contentLength = Math.Min (_stdinHeader.Value.ContentLength - _stdinDataReceivedLength, endOffset - offset);
                        if (contentLength > 0)
                        {
                            for (int i = offset; i < offset + contentLength; i++)
                            {
                                buffer [replaceOffset] = buffer [i];
                                replaceOffset += 1;
                            }

                            offset += contentLength;

                            _stdinDataReceivedLength += contentLength;
                        } else
                        {
                            // empty stdin = last stdin = endofrequest
                            EndOfRequest = true;
                            break;
                        }

                        if (_stdinDataReceivedLength == _stdinHeader.Value.ContentLength)
                        {
                            offset += _stdinHeader.Value.PaddingLength;
                            _stdinHeader = null;
                            _stdinDataReceivedLength = 0;
                        }
                    }
                }
            }
            return replaceOffset;
        }
        public int ParseRequest(byte[] buffer, int offset, int length)
        {
            int endOffset = offset + length;

            while (offset < endOffset)
            {
                FastCGIHeader header = new FastCGIHeader(buffer, offset);
                if (header.RequestId != 0)
                {
                    if (Request == null)
                        Request = new FastCGIRequest();

                    offset += FastCGIHeader.LENGTH;

                    if (header.Type == FastCGIHeader.TYPE.BEGIN_REQUEST)
                    {
                        Request.BeginRequestRecord = new BeginRequestRecord()
                        {
                            Header = header,
                            Body = new BeginRequestBody(buffer, offset)
                        };
                        offset += header.ContentLength;
                    }
                    else if (header.Type == FastCGIHeader.TYPE.PARAMS)
                    {
                        offset = Request.ParseParams(header, buffer, offset, endOffset);
                    }
                    else
                    {
                        offset -= FastCGIHeader.LENGTH;
                        break;
                    }

                    offset += header.PaddingLength;
                }
            }

            #if DEBUG
            Log();
            #endif

            return offset;
        }
        public byte[] TranslateToFCGIResponse(byte[] responseData)
        {
            //            int paddingLength = (int)Math.Ceiling(responseData.Length / 8.0) * 8 - responseData.Length;
            //            byte[] fcgiRespData = new byte[FastCGIHeader.LENGTH + responseData.Length + paddingLength + FastCGIHeader.LENGTH + EndRequestBody.LENGTH];

            int maxChunkLength = ushort.MaxValue - 8;
            int lastChunkLength;

            int chuncksCount = Math.DivRem(responseData.Length, maxChunkLength, out lastChunkLength) + 1;
            int padding = 8 - (lastChunkLength % 8);
            if (padding == 8)
                padding = 0;

            byte[] fcgiRespData = new byte[(chuncksCount - 1) * (maxChunkLength + FastCGIHeader.LENGTH)
                                           + FastCGIHeader.LENGTH + lastChunkLength + padding
                                           + FastCGIHeader.LENGTH + EndRequestBody.LENGTH];

            for (int i=0; i<chuncksCount; i++)
            {
                int contentLength = i == chuncksCount - 1 ? lastChunkLength : maxChunkLength;
                int paddingLength = i == chuncksCount - 1 ? padding : 0;

                FastCGIHeader stdoutHeader = new FastCGIHeader ();
                stdoutHeader.RequestId = Request.BeginRequestRecord.Header.RequestId;
                stdoutHeader.Type = FastCGIHeader.TYPE.STDOUT;
                stdoutHeader.Version = Request.BeginRequestRecord.Header.Version;
                stdoutHeader.ContentLength = (ushort)contentLength;
                stdoutHeader.PaddingLength = (byte)paddingLength;
                stdoutHeader.Reserved = 0;

                // copy stdout header
                stdoutHeader.CopyBytesTo (fcgiRespData, i * (FastCGIHeader.LENGTH + maxChunkLength));

                // copy stdout body
                Array.Copy (responseData, i * maxChunkLength, fcgiRespData, i * (FastCGIHeader.LENGTH + maxChunkLength) + FastCGIHeader.LENGTH, contentLength);
            }

            // copy endrequest record header & body
            int endReqRecOffset = fcgiRespData.Length - (FastCGIHeader.LENGTH + EndRequestBody.LENGTH);
            GetEndRequestRecord(0, EndRequestBody.PROTOCOL_STATUS.FCGI_REQUEST_COMPLETE)
                               .CopyBytesTo(fcgiRespData, endReqRecOffset);

            return fcgiRespData;
        }
        public int ParseParams(FastCGIHeader paramsHeader, byte[] buffer, int offset, int endOffset)
        {
            if (paramsHeader.ContentLength > 0)
            {
                int paramsEndOffset = Math.Min(offset + paramsHeader.ContentLength, endOffset);
                while (offset < paramsEndOffset)
                {
                    FastCGIParam param = new FastCGIParam(buffer, offset);
                    Params.Add(param.Name, param);

                    offset = param.ParamEndOffset;
                }
            }

            return offset;
        }