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";
137 private Dictionary<string, string> headers =
new Dictionary<string, string>();
140 public MethodEnum Method {
get;
set; }
141 public string RequestURI {
get;
set; }
142 public string RequestQuery {
get;
set; }
143 public byte[] Body {
get;
set; }
144 public StatusCodes StatusCode {
get;
set; }
149 RequestQuery =
string.Empty;
151 Method = MethodEnum.Get;
154 #region Public Methods 155 public string this[
string headerKey] {
get => headers[headerKey];
set => headers[headerKey] = value; }
166 public string this[GeneralHeadersEnum headerKey] {
get => headers[generalHeaders[(int)headerKey]];
set => headers[generalHeaders[(int)headerKey]] = value; }
172 public string this[ResponseHeadersEnum headerKey] {
get => headers[responseHeaders[(int)headerKey]];
set => headers[responseHeaders[(int)headerKey]] = value; }
178 public string this[RequestHeadersEnum headerKey] {
get => headers[requestHeaders[(int)headerKey]];
set => headers[requestHeaders[(int)headerKey]] = value; }
184 public string this[EntityHeadersEnum headerKey] {
get => headers[entityHeaders[(int)headerKey]];
set => headers[entityHeaders[(int)headerKey]] = value; }
192 headers[generalHeaders[(int)header]] = value;
201 headers[requestHeaders[(int)requestHeader]] = value;
210 headers[responseHeaders[(int)responseHeader]] = value;
219 headers[entityHeaders[(int)entityHeader]] = value;
227 StringBuilder requestString =
new StringBuilder();
228 requestString.Append(methods[(
int)Method]).Append(SP).Append(RequestURI).Append(
'?').Append(RequestQuery).Append(SP).Append(HTTPVER).Append(CRLF);
229 foreach(KeyValuePair<string, string> header
in headers)
231 requestString.Append(header.Key).Append(
':').Append(SP).Append(header.Value).Append(CRLF);
233 requestString.Append(CRLF);
234 byte[] request =
new byte[requestString.Length + (Body ==
null ? 0 : Body.Length)];
235 byte[] requestBytes = ASCIIEncoding.ASCII.GetBytes(requestString.ToString());
236 Array.Copy(requestBytes, 0, request, 0, requestBytes.Length);
239 Array.Copy(Body, 0, request, requestBytes.Length, Body.Length);
252 StringBuilder parseBuffer =
new StringBuilder();
259 string headerKey =
string.Empty;
260 string headerValue =
string.Empty;
262 for(
int i = 0; i < requestBytes.Length; i++)
266 Method = ParseMethod(requestBytes, ref i);
271 RequestURI = ParseRequestURI(requestBytes, ref i);
272 if(requestBytes[i] ==
'?')
283 RequestQuery = ParseRequestQuery(requestBytes, ref i);
288 ParseHTTPVer(requestBytes, ref i);
293 if(requestBytes[i] == CRLF[0])
297 else if(requestBytes[i] == CRLF[1])
304 headerKey = ParseHeaderKey(requestBytes, ref i);
310 if (requestBytes[i] == CRLF[0])
314 else if (requestBytes[i] == CRLF[1])
321 headerValue = ParseHeaderValue(requestBytes, ref i);
322 headers.Add(headerKey, headerValue);
327 if(requestBytes.Length - bodyIndex > 1)
333 this.Body =
new byte[requestBytes.Length - bodyIndex];
334 Array.Copy(requestBytes, bodyIndex, this.Body, 0, this.Body.Length);
347 if (includeContentLengthHeader)
353 this[EntityHeadersEnum.ContentLength] = Body ==
null ?
"0" : Body.Length.ToString();
355 StringBuilder responseString =
new StringBuilder();
356 responseString.Append(HTTPVER).Append(SP).Append((
int)this.StatusCode).Append(SP).Append(this.StatusCode.ToString()).Append(CRLF);
357 foreach (KeyValuePair<string, string> header
in headers)
359 responseString.Append(header.Key).Append(
':').Append(SP).Append(header.Value).Append(CRLF);
361 responseString.Append(CRLF);
362 byte[] response =
new byte[responseString.Length + (Body ==
null ? 0 : Body.Length)];
363 byte[] responseBytes = ASCIIEncoding.ASCII.GetBytes(responseString.ToString());
364 Array.Copy(responseBytes, 0, response, 0, responseBytes.Length);
367 Array.Copy(Body, 0, response, responseBytes.Length, Body.Length);
380 StringBuilder parseBuffer =
new StringBuilder();
387 string headerKey =
string.Empty;
388 string headerValue =
string.Empty;
390 for (
int i = 0; i < responseBytes.Length; i++)
394 ParseHTTPVer(responseBytes, ref i);
399 StatusCode = (StatusCodes)ParseResponseCode(responseBytes, ref i);
404 if(StatusCode != ParseResponseCodeString(responseBytes, ref i))
412 if (responseBytes[i] == CRLF[0])
416 else if (responseBytes[i] == CRLF[1])
423 headerKey = ParseHeaderKey(responseBytes, ref i);
429 if (responseBytes[i] == CRLF[0])
433 else if (responseBytes[i] == CRLF[1])
440 headerValue = ParseHeaderValue(responseBytes, ref i);
441 headers.Add(headerKey, headerValue);
446 if (responseBytes.Length - bodyIndex > 1)
452 this.Body =
new byte[responseBytes.Length - bodyIndex - 1];
453 Array.Copy(responseBytes, bodyIndex, this.Body, 0, this.Body.Length);
491 return headers.ContainsKey(header);
501 if(
this[
"Content-Type"] ==
"application/x-www-form-urlencoded")
503 Dictionary<string, string> returnDictionary =
new Dictionary<string, string>();
508 string formKey =
string.Empty;
510 for(
int i = 0; i < Body.Length; i++)
514 formKey = GetField(Body, ref i);
519 returnDictionary[formKey] = GetValue(Body, ref i);
523 return returnDictionary;
525 else if (
this[
"Content-Type"].Contains(
"multipart/form-data"))
527 throw new NotImplementedException(
"Multipart posting not implemented");
540 #region Private Methods 541 private MethodEnum GetMethod(
string methodString)
543 int index = Array.IndexOf(methods, methodString.ToUpper());
544 return (MethodEnum)index;
547 private MethodEnum ParseMethod(
byte[] buffer, ref
int index)
553 StringBuilder parseBuffer =
new StringBuilder();
554 for (; index < buffer.Length; index++)
558 if (buffer[index] == (
byte)SP)
560 string methodString = parseBuffer.ToString();
561 return GetMethod(methodString);
565 parseBuffer.Append((
char)buffer[index]);
576 private string ParseRequestURI(
byte[] buffer, ref
int index)
581 StringBuilder parseBuffer =
new StringBuilder();
582 for (; index < buffer.Length; index++)
586 if (buffer[index] == (
byte)SP)
588 return parseBuffer.ToString();
590 if (buffer[index] ==
'?')
592 return parseBuffer.ToString();
596 parseBuffer.Append((
char)buffer[index]);
607 private string ParseRequestQuery(
byte[] buffer, ref
int index)
612 StringBuilder parseBuffer =
new StringBuilder();
613 for (; index < buffer.Length; index++)
617 if (buffer[index] == (
byte)SP)
619 return parseBuffer.ToString();
623 parseBuffer.Append((
char)buffer[index]);
634 private void ParseHTTPVer(
byte[] buffer, ref
int index)
641 StringBuilder parseBuffer =
new StringBuilder();
642 for (; index < buffer.Length; index++)
646 if (buffer[index] == CRLF[1] || buffer[index] == SP)
648 string httpVer = parseBuffer.ToString();
649 if (httpVer != HTTPVER)
655 else if(buffer[index] == CRLF[0])
664 parseBuffer.Append((
char)buffer[index]);
674 private string ParseHeaderKey(
byte[] buffer, ref
int index)
679 StringBuilder parseBuffer =
new StringBuilder();
680 for (; index < buffer.Length; index++)
684 if (buffer[index] ==
':')
686 return parseBuffer.ToString();
690 parseBuffer.Append((
char)buffer[index]);
701 private string ParseHeaderValue(
byte[] buffer, ref
int index)
706 StringBuilder parseBuffer =
new StringBuilder();
707 for (; index < buffer.Length; index++)
711 if (buffer[index] == CRLF[1])
713 return parseBuffer.ToString().Trim();
715 else if (buffer[index] == CRLF[0])
724 parseBuffer.Append((
char)buffer[index]);
735 private int ParseResponseCode(
byte[] buffer, ref
int index)
740 StringBuilder parseBuffer =
new StringBuilder();
741 for (; index < buffer.Length; index++)
745 if (buffer[index] == SP)
747 return int.Parse(parseBuffer.ToString());
751 parseBuffer.Append((
char)buffer[index]);
762 private StatusCodes ParseResponseCodeString(
byte[] buffer, ref
int index)
767 StringBuilder parseBuffer =
new StringBuilder();
768 for (; index < buffer.Length; index++)
772 if (buffer[index] == CRLF[1])
774 return (StatusCodes)Enum.Parse(typeof(StatusCodes), parseBuffer.ToString().Trim());
776 else if (buffer[index] == CRLF[0])
785 parseBuffer.Append((
char)buffer[index]);
796 private string GetField(
byte[] buffer, ref
int index)
801 StringBuilder parseBuffer =
new StringBuilder();
802 for (; index < buffer.Length; index++)
806 if (buffer[index] ==
'=')
808 return parseBuffer.ToString().Trim();
812 parseBuffer.Append((
char)buffer[index]);
823 private string GetValue(
byte[] buffer, ref
int index)
828 StringBuilder parseBuffer =
new StringBuilder();
829 for (; index < buffer.Length; index++)
833 if (buffer[index] ==
'&')
835 return parseBuffer.ToString().Trim();
839 parseBuffer.Append((
char)buffer[index]);
847 return parseBuffer.ToString().Trim();
bool ContainsHeader(GeneralHeadersEnum header)
Check if the message contains a header.
byte [] GetResponse(bool includeContentLengthHeader)
Build the response bytes based on the message contents.
bool ContainsHeader(RequestHeadersEnum header)
Check if the message contains a header.
void AddEntityHeaders(EntityHeadersEnum entityHeader, string value)
Add an entity header to the message.
byte [] GetRequest()
Build the request bytes based on the message contents.
Dictionary< string, string > GetPostForm()
Parse the body into a posted from respecting the reference manual.
Exception for invalid response status code.
bool ContainsHeader(string header)
Check if the message contains a header.
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.
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.