public void HeadersList() { var collection = new HeadersList(new[] { new KeyValuePair <string, string>("myKey1", "myValue1"), new KeyValuePair <string, string>("myKey2", "myValue2"), new KeyValuePair <string, string>("myKey3", "myValue3"), new KeyValuePair <string, string>("myKey4", "myValue4"), new KeyValuePair <string, string>("myKey5", "myValue5"), new KeyValuePair <string, string>("myKey6", "myValue6"), new KeyValuePair <string, string>("myKey7", "myValue7"), new KeyValuePair <string, string>("myKey8", "myValue8"), new KeyValuePair <string, string>("myKey9", "myValue9"), new KeyValuePair <string, string>("myKey0", "myValue0") }); Assert.Equal(collection.Count, 10); Assert.Equal(collection.StoredHeadersSize, 60 + 80 + 32 * collection.Count); collection.Add(new KeyValuePair <string, string>("someAddKey1", "someAddValue1")); collection.Add(new KeyValuePair <string, string>("someAddKey2", "someAddValue2")); Assert.Equal(collection.Count, 12); Assert.Equal(collection.StoredHeadersSize, 60 + 80 + 32 * collection.Count + 22 + 26); int headersSize = collection.Sum(header => header.Key.Length + header.Value.Length + 32); Assert.Equal(collection.StoredHeadersSize, headersSize); }
/// <summary> /// Process cookies. /// </summary> /// <param name="toProcess">The header list containing the cookies.</param> private void ProcessCookie(HeadersList toProcess) { /* 12 -> 8.1.3.4. * If there are multiple Cookie header fields after * decompression, these MUST be concatenated into a single octet string * using the two octet delimiter of 0x3B, 0x20 (the ASCII string "; "). */ const string delimiter = "; "; var cookie = new StringBuilder(String.Empty); for (int i = 0; i < toProcess.Count; i++) { if (!toProcess[i].Key.Equals(CommonHeaders.Cookie)) { continue; } cookie.Append(toProcess[i].Value); cookie.Append(delimiter); toProcess.RemoveAt(i--); } if (cookie.Length > 0) { // Add without last delimeter toProcess.Add(new KeyValuePair <string, string>(CommonHeaders.Cookie, cookie.ToString(cookie.Length - 2, 2))); } }
/// <summary> /// Compress the header list. /// </summary> /// <param name="headers">The headers.</param> /// <returns>The compressed headers.</returns> public byte[] Compress(HeadersList headers) { var toSend = new HeadersList(); var toDelete = new HeadersList(_remoteRefSet); ClearStream(_serializerStream, (int)_serializerStream.Position); foreach (var header in headers) { if (header.Key == null || header.Value == null) { throw new InvalidHeaderException(header); } if (!_remoteRefSet.Contains(header)) { // Not there, Will send toSend.Add(header); } else { // Already there, don't delete toDelete.Remove(header); } } foreach (var header in toDelete) { // Anything left in toDelete, should send, so it is deleted from ref set. CompressIndexed(header); _remoteRefSet.Remove(header); // Update our copy } foreach (var header in toSend) { // Send whatever was left in headersCopy if (_remoteHeadersTable.Contains(header) || Constants.StaticTable.Contains(header)) { CompressIndexed(header); } else { CompressIncremental(header); } _remoteRefSet.Add(header); } _serializerStream.Flush(); var result = new byte[_serializerStream.Position]; var streamBuffer = _serializerStream.GetBuffer(); Buffer.BlockCopy(streamBuffer, 0, result, 0, (int)_serializerStream.Position); return(result); }
public byte[] Compress(HeadersList headers) { var toSend = new HeadersList(); var toDelete = new HeadersList(_remoteRefSet); ClearStream(_serializerStream, (int) _serializerStream.Position); foreach (var header in headers) { if (header.Key == null || header.Value == null) { throw new InvalidHeaderException(header); } if (!_remoteRefSet.Contains(header)) { //Not there, Will send toSend.Add(header); } else { //Already there, don't delete toDelete.Remove(header); } } foreach (var header in toDelete) { //Anything left in toDelete, should send, so it is deleted from ref set. CompressIndexed(header); _remoteRefSet.Remove(header); //Update our copy } foreach (var header in toSend) { //Send whatever was left in headersCopy if (_remoteHeaderTable.Contains(header)) { CompressIndexed(header); } else { CompressHeader(header, new Indexation(IndexationType.Incremental)); } _remoteRefSet.Add(header); //Update our copy } _serializerStream.Flush(); var result = new byte[_serializerStream.Position]; var streamBuffer = _serializerStream.GetBuffer(); Buffer.BlockCopy(streamBuffer, 0, result, 0, (int)_serializerStream.Position); return result; }
//localPath should be provided only for post and put cmds //serverPostAct should be provided only for post cmd private void SubmitRequest(Uri request, string method, string localPath = null, string serverPostAct = null) { var headers = new HeadersList { new KeyValuePair <string, string>(":method", method), new KeyValuePair <string, string>(":path", request.PathAndQuery), new KeyValuePair <string, string>(":version", _version), new KeyValuePair <string, string>(":host", _host), new KeyValuePair <string, string>(":scheme", _scheme), }; if (!String.IsNullOrEmpty(localPath)) { headers.Add(new KeyValuePair <string, string>(":localPath".ToLower(), localPath)); } if (!String.IsNullOrEmpty(serverPostAct)) { headers.Add(new KeyValuePair <string, string>(":serverPostAct".ToLower(), serverPostAct)); } //Sending request with average priority _clientSession.SendRequest(headers, Priority.None, false); }
/// <summary> /// Decompress the headers. /// </summary> /// <param name="serializedHeaders">The serialised headers.</param> /// <returns>The header list.</returns> public HeadersList Decompress(byte[] serializedHeaders) { try { _currentOffset = 0; var unindexedHeadersList = new HeadersList(); while (_currentOffset != serializedHeaders.Length) { var entry = ParseHeader(serializedHeaders); // parsed indexed header which was already in the refSet if (entry == null) { continue; } var header = new KeyValuePair <string, string>(entry.Item1, entry.Item2); if (entry.Item3 == IndexationType.WithoutIndexation || entry.Item3 == IndexationType.NeverIndexed) { unindexedHeadersList.Add(header); } } // Base result on already modified reference set var result = new HeadersList(_localRefSet); // Add to result Without indexation and Never Indexed // They were not added into reference set result.AddRange(unindexedHeadersList); ProcessCookie(result); return(result); } catch (Exception e) { throw new Exception(e.Message); } }
/// <summary> /// Dispatches the incoming frame. /// </summary> /// <param name="frame">The frame.</param> /// <exception cref="System.NotImplementedException"></exception> private void DispatchIncomingFrame(Frame frame) { Http2Stream stream = null; //Spec 03 tells that frame with continues flag MUST be followed by a frame with the same type //and the same stread id. if (_toBeContinuedHeaders.Count != 0) { if (_toBeContinuedFrame.FrameType != frame.FrameType || _toBeContinuedFrame.StreamId != frame.StreamId) { //If not, we must close the session. Dispose(); return; } } try { switch (frame.FrameType) { case FrameType.Headers: Http2Logger.LogDebug("New headers with id = " + frame.StreamId); var headersFrame = (HeadersFrame)frame; var serializedHeaders = new byte[headersFrame.CompressedHeaders.Count]; Buffer.BlockCopy(headersFrame.CompressedHeaders.Array, headersFrame.CompressedHeaders.Offset, serializedHeaders, 0, serializedHeaders.Length); var decompressedHeaders = _comprProc.Decompress(serializedHeaders); var headers = new HeadersList(decompressedHeaders); if (!headersFrame.IsEndHeaders) { _toBeContinuedHeaders.AddRange(decompressedHeaders); _toBeContinuedFrame = headersFrame; break; } headers.AddRange(_toBeContinuedHeaders); _toBeContinuedHeaders.Clear(); _toBeContinuedFrame = null; headersFrame.Headers.AddRange(headers); foreach (var header in headers) { Http2Logger.LogDebug("Stream {0} header: {1}={2}", frame.StreamId, header.Key, header.Value); } stream = GetStream(headersFrame.StreamId); if (stream == null) { if (_ourEnd == ConnectionEnd.Server) { string path = headers.GetValue(":path"); if (path == null) { path = _handshakeHeaders.ContainsKey(":path") ? _handshakeHeaders[":path"] : @"\index.html"; headers.Add(new KeyValuePair<string, string>(":path", path)); } } else { headers.AddRange(_handshakeHeaders); } stream = CreateStream(headers, frame.StreamId); } break; case FrameType.Priority: var priorityFrame = (PriorityFrame)frame; Http2Logger.LogDebug("Priority frame. StreamId: {0} Priority: {1}", priorityFrame.StreamId, priorityFrame.Priority); stream = GetStream(priorityFrame.StreamId); if (_usePriorities) { stream.Priority = priorityFrame.Priority; } break; case FrameType.RstStream: var resetFrame = (RstStreamFrame)frame; stream = GetStream(resetFrame.StreamId); if (stream != null) { Http2Logger.LogDebug("RST frame with code " + resetFrame.StatusCode); stream.Dispose(); } break; case FrameType.Data: var dataFrame = (DataFrame)frame; Http2Logger.LogDebug("Data frame. StreamId:{0} Length:{1}", dataFrame.StreamId, dataFrame.FrameLength); stream = GetStream(dataFrame.StreamId); //Aggressive window update if (stream != null && stream.IsFlowControlEnabled) { stream.WriteWindowUpdate(InitialWindowSize); } break; case FrameType.Ping: var pingFrame = (PingFrame)frame; Http2Logger.LogDebug("Ping frame with StreamId:{0} Payload:{1}", pingFrame.StreamId, pingFrame.Payload.Count); if (pingFrame.FrameLength != PingFrame.PayloadLength) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Ping payload size is not equal to 8"); } if (pingFrame.IsPong) { _wasPingReceived = true; _pingReceived.Set(); } else { var pongFrame = new PingFrame(true, pingFrame.Payload.ToArray()); _writeQueue.WriteFrame(pongFrame); } break; case FrameType.Settings: //Not first frame in the session. //Client initiates connection and sends settings before request. //It means that if server sent settings before it will not be a first frame, //because client initiates connection. if (_ourEnd == ConnectionEnd.Server && !_wasSettingsReceived && (ActiveStreams.Count > 0)) { Dispose(); return; } var settingFrame = (SettingsFrame)frame; Http2Logger.LogDebug("Settings frame. Entry count: {0} StreamId: {1}", settingFrame.EntryCount, settingFrame.StreamId); _wasSettingsReceived = true; _settingsManager.ProcessSettings(settingFrame, this, _flowControlManager); if (_ourEnd == ConnectionEnd.Server && _sessionSocket.SecureProtocol == SecureProtocol.None) { //The HTTP/1.1 request that is sent prior to upgrade is associated with //stream 1 and is assigned the highest possible priority. Stream 1 is //implicitly half closed from the client toward the server, since the //request is completed as an HTTP/1.1 request. After commencing the //HTTP/2.0 connection, stream 1 is used for the response. stream = CreateStream(Priority.Pri0); stream.EndStreamReceived = true; stream.Headers.Add(new KeyValuePair<string, string>(":method", _handshakeHeaders[":method"])); stream.Headers.Add(new KeyValuePair<string, string>(":path", _handshakeHeaders[":path"])); OnFrameReceived(this, new FrameReceivedEventArgs(stream, new HeadersFrame(stream.Id, false))); } break; case FrameType.WindowUpdate: if (_useFlowControl) { var windowFrame = (WindowUpdateFrame)frame; Http2Logger.LogDebug("WindowUpdate frame. Delta: {0} StreamId: {1}", windowFrame.Delta, windowFrame.StreamId); stream = GetStream(windowFrame.StreamId); if (stream != null) { stream.UpdateWindowSize(windowFrame.Delta); stream.PumpUnshippedFrames(); } } break; case FrameType.GoAway: _goAwayReceived = true; Http2Logger.LogDebug("GoAway frame received"); Dispose(); break; default: throw new NotImplementedException(frame.FrameType.ToString()); } if (stream != null && frame is IEndStreamFrame && ((IEndStreamFrame)frame).IsEndStream) { //Tell the stream that it was the last frame Http2Logger.LogDebug("Final frame received for stream with id = " + stream.Id); stream.EndStreamReceived = true; } if (stream != null && OnFrameReceived != null) { OnFrameReceived(this, new FrameReceivedEventArgs(stream, frame)); } } //Frame came for already closed stream. Ignore it. //Spec: //An endpoint that sends RST_STREAM MUST ignore //frames that it receives on closed streams if it sends RST_STREAM. // //An endpoint MUST NOT send frames on a closed stream. An endpoint //that receives a frame after receiving a RST_STREAM or a frame //containing a END_STREAM flag on that stream MUST treat that as a //stream error (Section 5.4.2) of type PROTOCOL_ERROR. catch (Http2StreamNotFoundException) { if (stream != null) { stream.WriteRst(ResetStatusCode.ProtocolError); } else { //GoAway? } } catch (CompressionError ex) { //The endpoint is unable to maintain the compression context for the connection. Http2Logger.LogError("Compression error occurred: " + ex.Message); Close(ResetStatusCode.CompressionError); } catch (ProtocolError pEx) { Http2Logger.LogError("Protocol error occurred: " + pEx.Message); Close(pEx.Code); } }
//localPath should be provided only for post and put cmds //serverPostAct should be provided only for post cmd private void SubmitRequest(Uri request, string method, string localPath = null, string serverPostAct = null) { var headers = new HeadersList { new KeyValuePair<string, string>(":method", method), new KeyValuePair<string, string>(":path", request.PathAndQuery), new KeyValuePair<string, string>(":version", _version), new KeyValuePair<string, string>(":host", _host), new KeyValuePair<string, string>(":scheme", _scheme), }; if (!String.IsNullOrEmpty(localPath)) { headers.Add(new KeyValuePair<string, string>(":localPath".ToLower(), localPath)); } if (!String.IsNullOrEmpty(serverPostAct)) { headers.Add(new KeyValuePair<string, string>(":serverPostAct".ToLower(), serverPostAct)); } //Sending request with average priority _clientSession.SendRequest(headers, Priority.None, false); }
/// <summary> /// Modifies the table. /// </summary> /// <param name="headerName">Name of the header.</param> /// <param name="headerValue">The header value.</param> /// <param name="headerType">Type of the header.</param> /// <param name="useHeadersTable">The use headers table.</param> /// <param name="index">The index.</param> private void ModifyTable(string headerName, string headerValue, IndexationType headerType, HeadersList useHeadersTable, int index) { int headerLen = headerName.Length + headerValue.Length + sizeof(Int32); //spec 04: 3.2.3. Header Table Management // The header table can be modified by either adding a new entry to it //or by replacing an existing one. Before doing such a modification, //it has to be ensured that the header table size will stay lower than //or equal to the SETTINGS_MAX_BUFFER_SIZE limit (see Section 5). To //achieve this, repeatedly, the first entry of the header table is //removed, until enough space is available for the modification. //A consequence of removing one or more entries at the beginning of the //header table is that the remaining entries are renumbered. The first //entry of the header table is always associated to the index 0. //When the modification of the header table is the replacement of an //existing entry, the replaced entry is the one indicated in the //literal representation before any entry is removed from the header //table. If the entry to be replaced is removed from the header table //when performing the size adjustment, the replacement entry is //inserted at the beginning of the header table. //The addition of a new entry with a size greater than the //SETTINGS_MAX_BUFFER_SIZE limit causes all the entries from the header //table to be dropped and the new entry not to be added to the header //table. The replacement of an existing entry with a new entry with a //size greater than the SETTINGS_MAX_BUFFER_SIZE has the same //consequences. switch (headerType) { case IndexationType.Incremental: while (useHeadersTable.StoredHeadersSize + headerLen > MaxHeaderByteSize && useHeadersTable.Count > 0) { useHeadersTable.RemoveAt(0); } useHeadersTable.Add(new KeyValuePair<string, string>(headerName, headerValue)); break; case IndexationType.Substitution: if (index != -1) { var header = useHeadersTable[index]; int substHeaderLen = header.Key.Length + header.Value.Length + sizeof(Int32); int deletedHeadersCounter = 0; while (useHeadersTable.StoredHeadersSize + headerLen - substHeaderLen > MaxHeaderByteSize) { if (useHeadersTable.Count > 0) { useHeadersTable.RemoveAt(0); deletedHeadersCounter++; } } if (index >= deletedHeadersCounter) { useHeadersTable[index - deletedHeadersCounter] = new KeyValuePair<string, string>(headerName, headerValue); } else { useHeadersTable.Insert(0, new KeyValuePair<string, string>(headerName, headerValue)); } } //If header wasn't found then add it to the table else { while (useHeadersTable.StoredHeadersSize + headerLen > MaxHeaderByteSize && useHeadersTable.Count > 0) { useHeadersTable.RemoveAt(0); } useHeadersTable.Add(new KeyValuePair<string, string>(headerName, headerValue)); } break; default: return; } }
public HeadersList Decompress(byte[] serializedHeaders) { try { var workingSet = new HeadersList(_localRefSet); var unindexedHeadersList = new HeadersList(); _currentOffset = 0; while (_currentOffset != serializedHeaders.Length) { var entry = ParseHeader(serializedHeaders); var header = new KeyValuePair<string, string>(entry.Item1, entry.Item2); if (entry.Item3 == IndexationType.Indexed) { if (workingSet.Contains(header)) workingSet.RemoveAll(h => h.Equals(header)); else workingSet.Add(header); } else if (entry.Item3 == IndexationType.WithoutIndexation) { unindexedHeadersList.Add(header); } else { workingSet.Add(header); } } _localRefSet = new HeadersList(workingSet); for (int i = _localRefSet.Count - 1; i >= 0; --i) { var header = _localRefSet[i]; if (!_localHeaderTable.Contains(header)) _localRefSet.RemoveAll(h => h.Equals(header)); } workingSet.AddRange(unindexedHeadersList); return workingSet; } catch (Exception e) { throw new CompressionError(e); } }
public HeadersList Decompress(byte[] serializedHeaders) { try { _currentOffset = 0; var unindexedHeadersList = new HeadersList(); while (_currentOffset != serializedHeaders.Length) { var entry = ParseHeader(serializedHeaders); // parsed indexed header which was already in the refSet if (entry == null) continue; var header = new KeyValuePair<string, string>(entry.Item1, entry.Item2); if (entry.Item3 == IndexationType.WithoutIndexation || entry.Item3 == IndexationType.NeverIndexed) { unindexedHeadersList.Add(header); } } // Base result on already modified reference set var result = new HeadersList(_localRefSet); // Add to result Without indexation and Never Indexed // They were not added into reference set result.AddRange(unindexedHeadersList); ProcessCookie(result); return result; } catch (Exception e) { throw new CompressionError(e.Message); } }
private void ProcessCookie(HeadersList toProcess) { /* 12 -> 8.1.3.4. If there are multiple Cookie header fields after decompression, these MUST be concatenated into a single octet string using the two octet delimiter of 0x3B, 0x20 (the ASCII string "; "). */ const string delimiter = "; "; var cookie = new StringBuilder(String.Empty); for (int i = 0; i < toProcess.Count; i++) { if (!toProcess[i].Key.Equals(CommonHeaders.Cookie)) continue; cookie.Append(toProcess[i].Value); cookie.Append(delimiter); toProcess.RemoveAt(i--); } if (cookie.Length > 0) { // Add without last delimeter toProcess.Add(new KeyValuePair<string, string>(CommonHeaders.Cookie, cookie.ToString(cookie.Length - 2, 2))); } }
//09 -> 8.1.3.4. Compressing the Cookie Header Field private void ProcessCookie(HeadersList toProcess) { //The Cookie header field [COOKIE] can carry a significant amount of //redundant data. //The Cookie header field uses a semi-colon (";") to delimit cookie- //pairs (or "crumbs"). This header field doesn't follow the list //construction rules in HTTP (see [HTTP-p1], Section 3.2.2), which //prevents cookie-pairs from being separated into different name-value //pairs. This can significantly reduce compression efficiency as //individual cookie-pairs are updated. //To allow for better compression efficiency, the Cookie header field //MAY be split into separate header fields, each with one or more //cookie-pairs. If there are multiple Cookie header fields after //decompression, these MUST be concatenated into a single octet string //using the two octet delimiter of 0x3B, 0x20 (the ASCII string "; "). const string delimiter = "; "; var cookie = new StringBuilder(String.Empty); for (int i = 0; i < toProcess.Count; i++) { if (!toProcess[i].Key.Equals(CommonHeaders.Cookie)) continue; cookie.Append(toProcess[i].Value); cookie.Append(delimiter); toProcess.RemoveAt(i--); } if (cookie.Length > 0) { //Add without last delimeter toProcess.Add(new KeyValuePair<string, string>(CommonHeaders.Cookie, cookie.ToString(cookie.Length - 2, 2))); } }
/// <summary> /// Modifies the table. /// </summary> /// <param name="headerName">Name of the header.</param> /// <param name="headerValue">The header value.</param> /// <param name="headerType">Type of the header.</param> /// <param name="useHeadersTable">The use headers table.</param> /// <param name="index">The index.</param> private void ModifyTable(string headerName, string headerValue, IndexationType headerType, HeadersList useHeadersTable, int index) { int headerLen = headerName.Length + headerValue.Length; switch (headerType) { case IndexationType.Incremental: if (useHeadersTable.Count > HeadersLimit - 1) { useHeadersTable.RemoveAt(0); } while (useHeadersTable.StoredHeadersSize + headerLen > MaxHeaderByteSize) { useHeadersTable.RemoveAt(0); } useHeadersTable.Add(new KeyValuePair<string, string>(headerName, headerValue)); break; case IndexationType.Substitution: if (index != -1) { useHeadersTable[index] = new KeyValuePair<string, string>(headerName, headerValue); } else { if (useHeadersTable.Count > HeadersLimit - 1) { useHeadersTable.RemoveAt(0); } while (useHeadersTable.StoredHeadersSize + headerLen > MaxHeaderByteSize) { useHeadersTable.RemoveAt(0); } //If header wasn't found then add it to the table useHeadersTable.Add(new KeyValuePair<string, string>(headerName, headerValue)); } break; default: return; } }
/// <summary> /// Process indexed. /// </summary> /// <param name="index">The index.</param> /// <returns>The indexation.</returns> private Tuple <string, string, IndexationType> ProcessIndexed(int index) { /* 07 -> 4.2 * The index value of 0 is not used. It MUST be treated as a decoding * error if found in an indexed header field representation. */ if (index == 0) { throw new Exception("indexed representation with zero value"); } var header = default(KeyValuePair <string, string>); bool isInStatic = index > _localHeadersTable.Count & index <= _localHeadersTable.Count + Constants.StaticTable.Count; bool isInHeaders = index <= _localHeadersTable.Count; if (isInStatic) { header = Constants.StaticTable[index - _localHeadersTable.Count - 1]; } else if (isInHeaders) { header = _localHeadersTable[index - 1]; } else { throw new IndexOutOfRangeException("no such index nor in static neither in headers tables"); } /* 07 -> 3.2.1 * An _indexed representation_ corresponding to an entry _present_ in * the reference set entails the following actions: * * o The entry is removed from the reference set. */ if (_localRefSet.Contains(header)) { _localRefSet.Remove(header); return(null); } /* 07 -> 3.2.1 * An _indexed representation_ corresponding to an entry _not present_ * in the reference set entails the following actions: * * o If referencing an element of the static table: * * The header field corresponding to the referenced entry is * emitted. * * The referenced static entry is inserted at the beginning of the * header table. * * A reference to this new header table entry is added to the * reference set, except if this new entry didn't fit in the * header table. * * o If referencing an element of the header table: * * The header field corresponding to the referenced entry is * emitted. * * The referenced header table entry is added to the reference * set. */ if (isInStatic) { _localHeadersTable.Insert(0, header); } _localRefSet.Add(header); return(new Tuple <string, string, IndexationType>(header.Key, header.Value, IndexationType.Indexed)); }
/// <summary> /// Write the response status and compressed header. /// </summary> /// <param name="writeEndOfHeaders">Write the end of the header bytes, carrige return line feed.</param> /// <param name="writeResponseStatus">Write the response status (:status = 200).</param> /// <param name="compressed">Compress the headers.</param> /// <param name="headerFrame">Only header frame types are supported.</param> public void WriteResponseHeaders(bool writeEndOfHeaders = true, bool writeResponseStatus = true, bool compressed = true, Protocol.OpCodeFrame headerFrame = Protocol.OpCodeFrame.Headers) { byte[] buffer = null; string data = ""; HeadersList headers = new HeadersList(); // If chunked is used. if (SendChunked) { AddHeader("Transfer-Encoding", "Chunked"); } // If the server has been specified. if (!String.IsNullOrEmpty(Server)) { AddHeader("Server", Server); } // If content length has been specified. if (ContentLength > 0) { AddHeader("Content-Length", ContentLength.ToString()); } // If the allow has been specified. if (!String.IsNullOrEmpty(Allow)) { AddHeader("Allow", Allow); } // If the content type has been specified. if (!String.IsNullOrEmpty(ContentType)) { AddHeader("Content-Type", ContentType); } // If the Upgrade has been specified. if (!String.IsNullOrEmpty(Upgrade)) { // If an upgrade is required // then set the connection to upgrade // and set the upgrade to the protocol (e.g. WebSocket, HTTP/2.0 .. etc). AddHeader("Connection", "Upgrade"); AddHeader("Upgrade", Upgrade); } else { // If the connection is open. if (KeepAlive) { AddHeader("Connection", "Keep-Alive"); } } // If the content encoding has been specified. if (!String.IsNullOrEmpty(ContentEncoding)) { AddHeader("Content-Encoding", ContentEncoding); } // If the content lanaguage has been specified. if (!String.IsNullOrEmpty(ContentLanguage)) { AddHeader("Content-Language", ContentLanguage); } // If authenticate type is other than none. if (AuthorizationType != Nequeo.Security.AuthenticationType.None) { AddHeader("WWW-Authenticate", AuthorizationType.ToString()); } // Write response status. if (writeResponseStatus) { // Compress the headers if (compressed) { // Set the response status. headers.Add(new KeyValuePair <string, string>(":status", StatusCode.ToString() + (StatusSubcode > 0 ? "." + StatusSubcode.ToString() : ""))); } else { // Send the http response status. data = ":status = " + StatusCode.ToString() + (StatusSubcode > 0 ? "." + StatusSubcode.ToString() : "") + _deli; buffer = Encoding.Default.GetBytes(data); Write(buffer, 0, buffer.Length); } } // If headers exists. if (Headers != null) { // For each header found. foreach (string header in Headers.AllKeys) { // Compress the headers if (compressed) { // Add each header. headers.Add(new KeyValuePair <string, string>(header.ToLower(), Headers[header])); } else { // Add each header. data = header.ToLower() + " = " + Headers[header] + _deli; buffer = Encoding.Default.GetBytes(data); Write(buffer, 0, buffer.Length); } } } // If cookies exists. if (Cookies != null) { // For each cookie found. foreach (Cookie cookie in Cookies) { // Make shore the cookie has been set. if (!String.IsNullOrEmpty(cookie.Name) && !String.IsNullOrEmpty(cookie.Value)) { // Compress the headers if (compressed) { // Get the cookie details. headers.Add(new KeyValuePair <string, string>("set-cookie", cookie.Name + "=" + cookie.Value + (cookie.Expires != null ? "; Expires=" + cookie.Expires.ToUniversalTime().ToLongDateString() + " " + cookie.Expires.ToUniversalTime().ToLongTimeString() + " GMT" : "") + (!String.IsNullOrEmpty(cookie.Path) ? "; Path=" + cookie.Path : "") + (!String.IsNullOrEmpty(cookie.Domain) ? "; Domain=" + cookie.Domain : "") + (cookie.Version > 0 ? "; Version=" + cookie.Version : "") + (cookie.Secure ? "; Secure" : "") + (cookie.HttpOnly ? "; HttpOnly" : "") )); } else { // Get the cookie details. data = "Set-Cookie" + ": " + cookie.Name + "=" + cookie.Value + (cookie.Expires != null ? "; Expires=" + cookie.Expires.ToUniversalTime().ToLongDateString() + " " + cookie.Expires.ToUniversalTime().ToLongTimeString() + " GMT" : "") + (!String.IsNullOrEmpty(cookie.Path) ? "; Path=" + cookie.Path : "") + (!String.IsNullOrEmpty(cookie.Domain) ? "; Domain=" + cookie.Domain : "") + (cookie.Version > 0 ? "; Version=" + cookie.Version : "") + (cookie.Secure ? "; Secure" : "") + (cookie.HttpOnly ? "; HttpOnly" : "") + _deli; // Write to the stream. buffer = Encoding.Default.GetBytes(data); Write(buffer, 0, buffer.Length); } } } } // Compress the headers if (compressed) { // Compress the headers. buffer = Utility.CompressHeaders(headers); Write(buffer, 0, buffer.Length); } else { // Write the end of the headers. if (writeEndOfHeaders) { // Send the header end space. data = _deli; buffer = Encoding.Default.GetBytes(data); Write(buffer, 0, buffer.Length); } } }