3 using System.Collections.Generic;
10 public enum StatusCodes
13 SwitchingProtocols = 101,
17 NonAuthoritativeInformation = 203,
21 MultipleChoices = 300,
22 MovedPermanently = 301,
27 TemporaryRedirect = 307,
30 PaymentRequired = 402,
33 MethodNotAllowed = 405,
35 ProxyAuthenticationRequired = 407,
40 PreconditionFailed = 412,
41 RequestEntityTooLarge = 413,
42 RequestURITooLarge = 414,
43 UnsupportedMediaType = 415,
44 RequestRangeNotSatisfiable = 416,
45 ExpectationFailed = 417,
46 InternalServerError = 500,
49 ServiceUnavailable = 503,
51 HTTPVersionNotSupported = 505
53 public enum MethodEnum
65 public enum GeneralHeadersEnum
77 public enum RequestHeadersEnum
91 IfUnmodifiedSince = 12,
93 ProxyAuthorization = 14,
99 public enum ResponseHeadersEnum
105 ProxyAuthentication = 4,
111 public enum EntityHeadersEnum
124 private static string[] methods =
new string[] {
"OPTIONS",
"GET",
"HEAD",
"POST",
"PUT",
"DELETE",
"TRACE",
"CONNECT",
"extension-method" };
125 private static string[] generalHeaders =
new string[] {
"Cache-Control",
"Connection",
"Date",
"Pragma",
"Trailer",
"Transfer-Encoding",
"Upgrade",
"Via",
"Warning" };
126 private static string[] requestHeaders =
new string[] {
"Accept",
"Accept-Charset",
"Accept-Encoding",
"Accept-Language",
"Authorization",
"Expect",
"From",
"Host",
"If-Match",
127 "If-Modified-Since",
"If-None-Match",
"If-Range",
"If-Unmodified-Since",
"Max-Forwards",
"Proxy-Authorizatio",
"Range",
"Referer",
"TE",
"User-Agent"};
128 private static string[] responseHeaders =
new string[] {
"Accept-Ranges",
"Age",
"ETag",
"Location",
"Retry-After",
"Server",
"Vary",
"WWW-Authenticate" };
129 private static string[] entityHeaders =
new string[] {
"Allow",
"Content-Encoding",
"Content-Language",
"Content-Length",
"Content-Location",
"Content-MD5",
"Content-Range",
"Content-Type",
130 "Expires",
"Last-Modified" };
132 private static char SP =
' ';
133 private static char HT =
'\t';
134 private static string CRLF =
"\r\n";
135 private static string HTTPVER =
"HTTP/1.1";
136 private static string RequestCookieHeader =
"Cookie";
137 private static string ResponseCookieHeader =
"Set-Cookie";
139 private Dictionary<string, string> headers =
new Dictionary<string, string>();
140 private List<Cookie> cookies =
new List<Cookie>();
143 public MethodEnum Method {
get;
set; }
144 public string RequestURI {
get;
set; }
145 public string RequestQuery {
get;
set; }
146 public byte[] Body {
get;
set; }
147 public StatusCodes StatusCode {
get;
set; }
152 RequestQuery =
string.Empty;
154 Method = MethodEnum.Get;
157 #region Public Methods 158 public string this[
string headerKey] {
get => headers[headerKey];
set => headers[headerKey] = value; }
169 public string this[GeneralHeadersEnum headerKey] {
get => headers[generalHeaders[(int)headerKey]];
set => headers[generalHeaders[(int)headerKey]] = value; }
175 public string this[ResponseHeadersEnum headerKey] {
get => headers[responseHeaders[(int)headerKey]];
set => headers[responseHeaders[(int)headerKey]] = value; }
181 public string this[RequestHeadersEnum headerKey] {
get => headers[requestHeaders[(int)headerKey]];
set => headers[requestHeaders[(int)headerKey]] = value; }
187 public string this[EntityHeadersEnum headerKey] {
get => headers[entityHeaders[(int)headerKey]];
set => headers[entityHeaders[(int)headerKey]] = value; }
191 public List<Cookie>
Cookies {
get => cookies; }
199 headers[generalHeaders[(int)header]] = value;
208 headers[requestHeaders[(int)requestHeader]] = value;
217 headers[responseHeaders[(int)responseHeader]] = value;
226 headers[entityHeaders[(int)entityHeader]] = value;
234 StringBuilder requestString =
new StringBuilder();
235 requestString.Append(methods[(
int)Method]).Append(SP).Append(RequestURI).Append(
'?').Append(RequestQuery).Append(SP).Append(HTTPVER).Append(CRLF);
236 foreach(KeyValuePair<string, string> header
in headers)
238 requestString.Append(header.Key).Append(
':').Append(SP).Append(header.Value).Append(CRLF);
240 requestString.Append(CRLF);
243 requestString.Append(RequestCookieHeader).Append(
':').Append(SP);
244 for(
int i = 0; i <
Cookies.Count; i++)
250 requestString.Append(
';');
254 byte[] request =
new byte[requestString.Length + (Body ==
null ? 0 : Body.Length)];
255 byte[] requestBytes = ASCIIEncoding.ASCII.GetBytes(requestString.ToString());
256 Array.Copy(requestBytes, 0, request, 0, requestBytes.Length);
259 Array.Copy(Body, 0, request, requestBytes.Length, Body.Length);
272 StringBuilder parseBuffer =
new StringBuilder();
279 string headerKey =
string.Empty;
280 string headerValue =
string.Empty;
282 for(
int i = 0; i < requestBytes.Length; i++)
286 Method = ParseMethod(requestBytes, ref i);
291 RequestURI = ParseRequestURI(requestBytes, ref i);
292 if(requestBytes[i] ==
'?')
303 RequestQuery = ParseRequestQuery(requestBytes, ref i);
308 ParseHTTPVer(requestBytes, ref i);
313 if(requestBytes[i] == CRLF[0])
317 else if(requestBytes[i] == CRLF[1])
324 headerKey = ParseHeaderKey(requestBytes, ref i);
330 if (requestBytes[i] == CRLF[0])
334 else if (requestBytes[i] == CRLF[1])
341 headerValue = ParseHeaderValue(requestBytes, ref i);
342 if (headerKey == RequestCookieHeader)
348 headers.Add(headerKey, headerValue);
354 if(requestBytes.Length - bodyIndex > 1)
360 this.Body =
new byte[requestBytes.Length - bodyIndex];
361 Array.Copy(requestBytes, bodyIndex, this.Body, 0, this.Body.Length);
374 if (includeContentLengthHeader)
380 this[EntityHeadersEnum.ContentLength] = Body ==
null ?
"0" : Body.Length.ToString();
382 StringBuilder responseString =
new StringBuilder();
383 responseString.Append(HTTPVER).Append(SP).Append((
int)this.StatusCode).Append(SP).Append(this.StatusCode.ToString()).Append(CRLF);
384 foreach (KeyValuePair<string, string> header
in headers)
386 responseString.Append(header.Key).Append(
':').Append(SP).Append(header.Value).Append(CRLF);
388 responseString.Append(CRLF);
391 responseString.Append(ResponseCookieHeader).Append(
':').Append(SP).Append(cookie.
BuildCookieString()).Append(CRLF);
393 byte[] response =
new byte[responseString.Length + (Body ==
null ? 0 : Body.Length)];
394 byte[] responseBytes = ASCIIEncoding.ASCII.GetBytes(responseString.ToString());
395 Array.Copy(responseBytes, 0, response, 0, responseBytes.Length);
398 Array.Copy(Body, 0, response, responseBytes.Length, Body.Length);
411 StringBuilder parseBuffer =
new StringBuilder();
418 string headerKey =
string.Empty;
419 string headerValue =
string.Empty;
421 for (
int i = 0; i < responseBytes.Length; i++)
425 ParseHTTPVer(responseBytes, ref i);
430 StatusCode = (StatusCodes)ParseResponseCode(responseBytes, ref i);
435 if(StatusCode != ParseResponseCodeString(responseBytes, ref i))
443 if (responseBytes[i] == CRLF[0])
447 else if (responseBytes[i] == CRLF[1])
454 headerKey = ParseHeaderKey(responseBytes, ref i);
460 if (responseBytes[i] == CRLF[0])
464 else if (responseBytes[i] == CRLF[1])
471 headerValue = ParseHeaderValue(responseBytes, ref i);
472 if (headerKey == ResponseCookieHeader)
478 headers.Add(headerKey, headerValue);
484 if (responseBytes.Length - bodyIndex > 1)
490 this.Body =
new byte[responseBytes.Length - bodyIndex - 1];
491 Array.Copy(responseBytes, bodyIndex, this.Body, 0, this.Body.Length);
538 return headers.ContainsKey(header);
548 if(
this[
"Content-Type"] ==
"application/x-www-form-urlencoded")
550 Dictionary<string, string> returnDictionary =
new Dictionary<string, string>();
555 string formKey =
string.Empty;
557 for(
int i = 0; i < Body.Length; i++)
561 formKey = GetField(Body, ref i);
566 returnDictionary[formKey] = GetValue(Body, ref i);
570 return returnDictionary;
572 else if (
this[
"Content-Type"].Contains(
"multipart/form-data"))
574 throw new NotImplementedException(
"Multipart posting not implemented");
587 #region Private Methods 588 private MethodEnum GetMethod(
string methodString)
590 int index = Array.IndexOf(methods, methodString.ToUpper());
591 return (MethodEnum)index;
594 private MethodEnum ParseMethod(
byte[] buffer, ref
int index)
600 StringBuilder parseBuffer =
new StringBuilder();
601 for (; index < buffer.Length; index++)
605 if (buffer[index] == (
byte)SP)
607 string methodString = parseBuffer.ToString();
608 return GetMethod(methodString);
612 parseBuffer.Append((
char)buffer[index]);
623 private string ParseRequestURI(
byte[] buffer, ref
int index)
628 StringBuilder parseBuffer =
new StringBuilder();
629 for (; index < buffer.Length; index++)
633 if (buffer[index] == (
byte)SP)
635 return parseBuffer.ToString();
637 if (buffer[index] ==
'?')
639 return parseBuffer.ToString();
643 parseBuffer.Append((
char)buffer[index]);
654 private string ParseRequestQuery(
byte[] buffer, ref
int index)
659 StringBuilder parseBuffer =
new StringBuilder();
660 for (; index < buffer.Length; index++)
664 if (buffer[index] == (
byte)SP)
666 return parseBuffer.ToString();
670 parseBuffer.Append((
char)buffer[index]);
681 private void ParseHTTPVer(
byte[] buffer, ref
int index)
688 StringBuilder parseBuffer =
new StringBuilder();
689 for (; index < buffer.Length; index++)
693 if (buffer[index] == CRLF[1] || buffer[index] == SP)
695 string httpVer = parseBuffer.ToString();
696 if (httpVer != HTTPVER)
702 else if(buffer[index] == CRLF[0])
711 parseBuffer.Append((
char)buffer[index]);
721 private string ParseHeaderKey(
byte[] buffer, ref
int index)
726 StringBuilder parseBuffer =
new StringBuilder();
727 for (; index < buffer.Length; index++)
731 if (buffer[index] ==
':')
733 return parseBuffer.ToString();
737 parseBuffer.Append((
char)buffer[index]);
748 private string ParseHeaderValue(
byte[] buffer, ref
int index)
753 StringBuilder parseBuffer =
new StringBuilder();
754 for (; index < buffer.Length; index++)
758 if (buffer[index] == CRLF[1])
760 return parseBuffer.ToString().Trim();
762 else if (buffer[index] == CRLF[0])
771 parseBuffer.Append((
char)buffer[index]);
782 private int ParseResponseCode(
byte[] buffer, ref
int index)
787 StringBuilder parseBuffer =
new StringBuilder();
788 for (; index < buffer.Length; index++)
792 if (buffer[index] == SP)
794 return int.Parse(parseBuffer.ToString());
798 parseBuffer.Append((
char)buffer[index]);
809 private StatusCodes ParseResponseCodeString(
byte[] buffer, ref
int index)
814 StringBuilder parseBuffer =
new StringBuilder();
815 for (; index < buffer.Length; index++)
819 if (buffer[index] == CRLF[1])
821 return (StatusCodes)Enum.Parse(typeof(StatusCodes), parseBuffer.ToString().Trim());
823 else if (buffer[index] == CRLF[0])
832 parseBuffer.Append((
char)buffer[index]);
843 private string GetField(
byte[] buffer, ref
int index)
848 StringBuilder parseBuffer =
new StringBuilder();
849 for (; index < buffer.Length; index++)
853 if (buffer[index] ==
'=')
855 return parseBuffer.ToString().Trim();
859 parseBuffer.Append((
char)buffer[index]);
870 private string GetValue(
byte[] buffer, ref
int index)
875 StringBuilder parseBuffer =
new StringBuilder();
876 for (; index < buffer.Length; index++)
880 if (buffer[index] ==
'&')
882 return parseBuffer.ToString().Trim();
886 parseBuffer.Append((
char)buffer[index]);
894 return parseBuffer.ToString().Trim();
bool ContainsHeader(GeneralHeadersEnum header)
Check if the message contains a header.
bool ContainsHeader(RequestHeadersEnum header)
Check if the message contains a header.
void AddEntityHeaders(EntityHeadersEnum entityHeader, string value)
Add an entity header to the message.
Dictionary< string, string > GetPostForm()
Parse the body into a posted from respecting the reference manual.
bool ContainsHeader(EntityHeadersEnum header)
Check if the message contains a header.
byte [] BuildRequest()
Build the request bytes based on the message contents.
string BuildCookieString()
Build the string containing the cookie definition.
Exception for invalid response status code.
bool ContainsHeader(string header)
Check if the message contains a header.
byte [] BuildResponse(bool includeContentLengthHeader)
Build the response bytes based on the message contents.
Exception for invalid post forms.
void AddResponseHeader(ResponseHeadersEnum responseHeader, string value)
Add a response header to the message.
bool ContainsHeader(ResponseHeadersEnum header)
Check if the message contains a header.
void AddGeneralHeader(GeneralHeadersEnum header, string value)
Add a general header to the message.
List< Cookie > Cookies
List of cookies.
Exception in case of Invalid HTTP Version.
void AddRequestHeader(RequestHeadersEnum requestHeader, string value)
Add a request header to the message.
Exception cause by an invalid request URI.
void ParseResponse(byte[] responseBytes)
Parse the received bytes and populate the message contents.
Invalid HTTP Method Exception.
void ParseRequest(byte[] requestBytes)
Parse the received bytes and populate the message contents.