HttpListenerRequest.Managed.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. using System;
  2. using System.Text;
  3. using System.Collections.Specialized;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Security.Authentication.ExtendedProtection;
  7. using System.Security.Cryptography.X509Certificates;
  8. using MediaBrowser.Model.Services;
  9. using MediaBrowser.Model.Text;
  10. namespace SocketHttpListener.Net
  11. {
  12. public sealed partial class HttpListenerRequest
  13. {
  14. private long _contentLength;
  15. private bool _clSet;
  16. private WebHeaderCollection _headers;
  17. private string _method;
  18. private Stream _inputStream;
  19. private HttpListenerContext _context;
  20. private bool _isChunked;
  21. private static byte[] s_100continue = Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue\r\n\r\n");
  22. internal HttpListenerRequest(HttpListenerContext context)
  23. {
  24. _context = context;
  25. _headers = new WebHeaderCollection();
  26. _version = HttpVersion.Version10;
  27. }
  28. private static readonly char[] s_separators = new char[] { ' ' };
  29. internal void SetRequestLine(string req)
  30. {
  31. string[] parts = req.Split(s_separators, 3);
  32. if (parts.Length != 3)
  33. {
  34. _context.ErrorMessage = "Invalid request line (parts).";
  35. return;
  36. }
  37. _method = parts[0];
  38. foreach (char c in _method)
  39. {
  40. int ic = (int)c;
  41. if ((ic >= 'A' && ic <= 'Z') ||
  42. (ic > 32 && c < 127 && c != '(' && c != ')' && c != '<' &&
  43. c != '<' && c != '>' && c != '@' && c != ',' && c != ';' &&
  44. c != ':' && c != '\\' && c != '"' && c != '/' && c != '[' &&
  45. c != ']' && c != '?' && c != '=' && c != '{' && c != '}'))
  46. continue;
  47. _context.ErrorMessage = "(Invalid verb)";
  48. return;
  49. }
  50. _rawUrl = parts[1];
  51. if (parts[2].Length != 8 || !parts[2].StartsWith("HTTP/"))
  52. {
  53. _context.ErrorMessage = "Invalid request line (version).";
  54. return;
  55. }
  56. try
  57. {
  58. _version = new Version(parts[2].Substring(5));
  59. }
  60. catch
  61. {
  62. _context.ErrorMessage = "Invalid request line (version).";
  63. return;
  64. }
  65. if (_version.Major < 1)
  66. {
  67. _context.ErrorMessage = "Invalid request line (version).";
  68. return;
  69. }
  70. if (_version.Major > 1)
  71. {
  72. _context.ErrorStatus = (int)HttpStatusCode.HttpVersionNotSupported;
  73. _context.ErrorMessage = HttpStatusDescription.Get(HttpStatusCode.HttpVersionNotSupported);
  74. return;
  75. }
  76. }
  77. private static bool MaybeUri(string s)
  78. {
  79. int p = s.IndexOf(':');
  80. if (p == -1)
  81. return false;
  82. if (p >= 10)
  83. return false;
  84. return IsPredefinedScheme(s.Substring(0, p));
  85. }
  86. private static bool IsPredefinedScheme(string scheme)
  87. {
  88. if (scheme == null || scheme.Length < 3)
  89. return false;
  90. char c = scheme[0];
  91. if (c == 'h')
  92. return (scheme == UriScheme.Http || scheme == UriScheme.Https);
  93. if (c == 'f')
  94. return (scheme == UriScheme.File || scheme == UriScheme.Ftp);
  95. if (c == 'n')
  96. {
  97. c = scheme[1];
  98. if (c == 'e')
  99. return (scheme == UriScheme.News || scheme == UriScheme.NetPipe || scheme == UriScheme.NetTcp);
  100. if (scheme == UriScheme.Nntp)
  101. return true;
  102. return false;
  103. }
  104. if ((c == 'g' && scheme == UriScheme.Gopher) || (c == 'm' && scheme == UriScheme.Mailto))
  105. return true;
  106. return false;
  107. }
  108. internal void FinishInitialization()
  109. {
  110. string host = UserHostName;
  111. if (_version > HttpVersion.Version10 && (host == null || host.Length == 0))
  112. {
  113. _context.ErrorMessage = "Invalid host name";
  114. return;
  115. }
  116. string path;
  117. Uri raw_uri = null;
  118. if (MaybeUri(_rawUrl.ToLowerInvariant()) && Uri.TryCreate(_rawUrl, UriKind.Absolute, out raw_uri))
  119. path = raw_uri.PathAndQuery;
  120. else
  121. path = _rawUrl;
  122. if ((host == null || host.Length == 0))
  123. host = UserHostAddress;
  124. if (raw_uri != null)
  125. host = raw_uri.Host;
  126. int colon = host.IndexOf(']') == -1 ? host.IndexOf(':') : host.LastIndexOf(':');
  127. if (colon >= 0)
  128. host = host.Substring(0, colon);
  129. string base_uri = string.Format("{0}://{1}:{2}", RequestScheme, host, LocalEndPoint.Port);
  130. if (!Uri.TryCreate(base_uri + path, UriKind.Absolute, out _requestUri))
  131. {
  132. _context.ErrorMessage = System.Net.WebUtility.HtmlEncode("Invalid url: " + base_uri + path);
  133. return;
  134. }
  135. _requestUri = HttpListenerRequestUriBuilder.GetRequestUri(_rawUrl, _requestUri.Scheme,
  136. _requestUri.Authority, _requestUri.LocalPath, _requestUri.Query);
  137. if (_version >= HttpVersion.Version11)
  138. {
  139. string t_encoding = Headers[HttpKnownHeaderNames.TransferEncoding];
  140. _isChunked = (t_encoding != null && string.Equals(t_encoding, "chunked", StringComparison.OrdinalIgnoreCase));
  141. // 'identity' is not valid!
  142. if (t_encoding != null && !_isChunked)
  143. {
  144. _context.Connection.SendError(null, 501);
  145. return;
  146. }
  147. }
  148. if (!_isChunked && !_clSet)
  149. {
  150. if (string.Equals(_method, "POST", StringComparison.OrdinalIgnoreCase) ||
  151. string.Equals(_method, "PUT", StringComparison.OrdinalIgnoreCase))
  152. {
  153. _context.Connection.SendError(null, 411);
  154. return;
  155. }
  156. }
  157. if (String.Compare(Headers[HttpKnownHeaderNames.Expect], "100-continue", StringComparison.OrdinalIgnoreCase) == 0)
  158. {
  159. HttpResponseStream output = _context.Connection.GetResponseStream();
  160. output.InternalWrite(s_100continue, 0, s_100continue.Length);
  161. }
  162. }
  163. internal static string Unquote(String str)
  164. {
  165. int start = str.IndexOf('\"');
  166. int end = str.LastIndexOf('\"');
  167. if (start >= 0 && end >= 0)
  168. str = str.Substring(start + 1, end - 1);
  169. return str.Trim();
  170. }
  171. internal void AddHeader(string header)
  172. {
  173. int colon = header.IndexOf(':');
  174. if (colon == -1 || colon == 0)
  175. {
  176. _context.ErrorMessage = HttpStatusDescription.Get(400);
  177. _context.ErrorStatus = 400;
  178. return;
  179. }
  180. string name = header.Substring(0, colon).Trim();
  181. string val = header.Substring(colon + 1).Trim();
  182. if (name.Equals("content-length", StringComparison.OrdinalIgnoreCase))
  183. {
  184. // To match Windows behavior:
  185. // Content lengths >= 0 and <= long.MaxValue are accepted as is.
  186. // Content lengths > long.MaxValue and <= ulong.MaxValue are treated as 0.
  187. // Content lengths < 0 cause the requests to fail.
  188. // Other input is a failure, too.
  189. long parsedContentLength =
  190. ulong.TryParse(val, out ulong parsedUlongContentLength) ? (parsedUlongContentLength <= long.MaxValue ? (long)parsedUlongContentLength : 0) :
  191. long.Parse(val);
  192. if (parsedContentLength < 0 || (_clSet && parsedContentLength != _contentLength))
  193. {
  194. _context.ErrorMessage = "Invalid Content-Length.";
  195. }
  196. else
  197. {
  198. _contentLength = parsedContentLength;
  199. _clSet = true;
  200. }
  201. }
  202. else if (name.Equals("transfer-encoding", StringComparison.OrdinalIgnoreCase))
  203. {
  204. if (Headers[HttpKnownHeaderNames.TransferEncoding] != null)
  205. {
  206. _context.ErrorStatus = (int)HttpStatusCode.NotImplemented;
  207. _context.ErrorMessage = HttpStatusDescription.Get(HttpStatusCode.NotImplemented);
  208. }
  209. }
  210. if (_context.ErrorMessage == null)
  211. {
  212. _headers.Set(name, val);
  213. }
  214. }
  215. // returns true is the stream could be reused.
  216. internal bool FlushInput()
  217. {
  218. if (!HasEntityBody)
  219. return true;
  220. int length = 2048;
  221. if (_contentLength > 0)
  222. length = (int)Math.Min(_contentLength, (long)length);
  223. byte[] bytes = new byte[length];
  224. while (true)
  225. {
  226. try
  227. {
  228. IAsyncResult ares = InputStream.BeginRead(bytes, 0, length, null, null);
  229. if (!ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne(1000))
  230. return false;
  231. if (InputStream.EndRead(ares) <= 0)
  232. return true;
  233. }
  234. catch (ObjectDisposedException)
  235. {
  236. _inputStream = null;
  237. return true;
  238. }
  239. catch
  240. {
  241. return false;
  242. }
  243. }
  244. }
  245. public long ContentLength64
  246. {
  247. get
  248. {
  249. if (_isChunked)
  250. _contentLength = -1;
  251. return _contentLength;
  252. }
  253. }
  254. public bool HasEntityBody => (_contentLength > 0 || _isChunked);
  255. public QueryParamCollection Headers => _headers;
  256. public string HttpMethod => _method;
  257. public Stream InputStream
  258. {
  259. get
  260. {
  261. if (_inputStream == null)
  262. {
  263. if (_isChunked || _contentLength > 0)
  264. _inputStream = _context.Connection.GetRequestStream(_isChunked, _contentLength);
  265. else
  266. _inputStream = Stream.Null;
  267. }
  268. return _inputStream;
  269. }
  270. }
  271. public bool IsAuthenticated => false;
  272. public bool IsSecureConnection => _context.Connection.IsSecure;
  273. public System.Net.IPEndPoint LocalEndPoint => _context.Connection.LocalEndPoint;
  274. public System.Net.IPEndPoint RemoteEndPoint => _context.Connection.RemoteEndPoint;
  275. public Guid RequestTraceIdentifier { get; } = Guid.NewGuid();
  276. public string ServiceName => null;
  277. private Uri RequestUri => _requestUri;
  278. private bool SupportsWebSockets => true;
  279. }
  280. }