/// <summary> /// Parses url encoded form data /// </summary> /// <returns>An awaitable task.</returns> /// <param name="reader">The stream to read from.</param> /// <param name="config">The server configuration.</param> /// <param name="idletime">The maximum idle time.</param> /// <param name="timeouttask">A task that signals request timeout.</param> /// <param name="stoptask">A task that signals server stop.</param> internal async Task ParseFormData(BufferedStreamReader reader, ServerConfig config, TimeSpan idletime, Task timeouttask, Task stoptask) { if (this.IsContentType("application/x-www-form-urlencoded")) { if (this.ContentLength != 0) { if (this.ContentLength > config.MaxUrlEncodedFormSize) { throw new HttpException(HttpStatusCode.PayloadTooLarge); } var enc = this.GetEncodingForContentType(); ParseQueryString( enc.GetString( this.ContentLength > 0 ? await reader.RepeatReadAsync(this.ContentLength, idletime, timeouttask, stoptask) : await reader.ReadUntilCrlfAsync(config.MaxRequestLineSize, config.MaxUrlEncodedFormSize, idletime, timeouttask, stoptask) ), this.Form); } this.Body = new LimitedBodyStream(reader, 0, idletime, timeouttask, stoptask); } else if (RequestUtility.IsMultipartRequest(this.ContentType) && this.ContentLength > 0 && this.ContentLength < config.MaxUrlEncodedFormSize && config.AutoParseMultipartFormData) { await ParseMultiPart( async (headers, stream) => { var dispositionItems = RequestUtility.SplitHeaderLine(headers["Content-Disposition"]); if (!string.Equals(dispositionItems.FirstOrDefault().Key, "form-data", StringComparison.OrdinalIgnoreCase)) { throw new HttpException(HttpStatusCode.BadRequest); } var name = RequestUtility.GetHeaderComponent(headers["Content-Disposition"], "name"); if (string.IsNullOrWhiteSpace("name")) { throw new HttpException(HttpStatusCode.BadRequest); } var filename = RequestUtility.GetHeaderComponent(headers["Content-Disposition"], "filename"); var charset = RequestUtility.GetHeaderComponent(headers["Content-Type"], "charset") ?? "ascii"; if (string.IsNullOrWhiteSpace(filename)) { using (var sr = new StreamReader(stream, RequestUtility.GetEncodingForCharset(charset))) { var rtask = sr.ReadToEndAsync(); var rt = await Task.WhenAny(timeouttask, stoptask, rtask); if (rt != rtask) { if (rt == stoptask) { throw new TaskCanceledException(); } else { throw new HttpException(HttpStatusCode.RequestTimeout); } } this.Form[name] = rtask.Result; } } else { var me = new MultipartItem(headers) { Name = name, Filename = filename, Data = new MemoryStream() }; Task rtask; Task rt; using (var cs = new CancellationTokenSource(idletime)) { rtask = stream.CopyToAsync(me.Data, 8 * 1024, cs.Token); rt = await Task.WhenAny(timeouttask, stoptask, rtask); } if (rt != rtask) { if (rt == stoptask) { throw new TaskCanceledException(); } else { throw new HttpException(HttpStatusCode.RequestTimeout); } } rtask.GetAwaiter().GetResult(); me.Data.Position = 0; this.Files.Add(me); } }, reader, config, idletime, timeouttask, stoptask ); this.Body = new LimitedBodyStream(reader, 0, idletime, timeouttask, stoptask); } else { this.Body = new LimitedBodyStream(reader, this.ContentLength, idletime, timeouttask, stoptask); } }
/// <summary> /// Parses the stream items, if sent as multipart encoded /// </summary> /// <param name="itemparser">The parser method</param> /// <param name="reader">The stream to read from.</param> /// <param name="config">The server configuration.</param> /// <param name="idletime">The maximum idle time.</param> /// <param name="timeouttask">A task that signals request timeout.</param> /// <param name="stoptask">A task that signals server stop.</param> /// <returns>An awaitable task</returns> private async Task ParseMultiPart(Func <IDictionary <string, string>, Stream, Task> itemparser, BufferedStreamReader reader, ServerConfig config, TimeSpan idletime, Task timeouttask, Task stoptask) { if ((this.ContentType ?? "").StartsWith("multipart/form-data", StringComparison.OrdinalIgnoreCase)) { if (this.ContentLength > config.MaxPostSize) { throw new HttpException(HttpStatusCode.PayloadTooLarge); } var startpos = reader.Position; var trail = new byte[2]; var parts = this.ContentType.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); var bndpart = parts.FirstOrDefault(x => x.Trim().StartsWith("boundary", StringComparison.OrdinalIgnoreCase)) ?? string.Empty; var boundary = bndpart.Split(new char[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries).LastOrDefault(); if (string.IsNullOrWhiteSpace(boundary)) { throw new HttpException(HttpStatusCode.BadRequest); } // Since we have read the headers, we have consumed the initial CRLF // so we adjust the initial boundary reading to skip the CRLF var itemboundary = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary); var tmp = await reader.RepeatReadAsync(itemboundary.Length - 2, idletime, timeouttask, stoptask); if (!Enumerable.SequenceEqual(itemboundary.Skip(2), tmp)) { throw new HttpException(HttpStatusCode.BadRequest); } await reader.RepeatReadAsync(trail, 0, 2, idletime, timeouttask, stoptask); if (trail[0] != '\r' || trail[1] != '\n') { throw new HttpException(HttpStatusCode.BadRequest); } do { var headers = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase).WithDefaultValue(null); await reader.ReadHeaders( config.MaxRequestLineSize, config.MaxRequestHeaderSize, idletime, line => { var components = line.Split(new char[] { ':' }, 2); if (components.Length != 2 || string.IsNullOrWhiteSpace(components[0])) { throw new HttpException(HttpStatusCode.BadRequest); } headers[components[0].Trim()] = (components[1] ?? string.Empty).Trim(); }, timeouttask, stoptask ); await itemparser(headers, reader.GetDelimitedSubStream(itemboundary, idletime, timeouttask, stoptask)); await reader.RepeatReadAsync(trail, 0, 2, idletime, timeouttask, stoptask); }while (trail[0] == '\r' && trail[1] == '\n'); if (trail[0] != '-' || trail[1] != '-') { throw new HttpException(HttpStatusCode.BadRequest); } await reader.RepeatReadAsync(trail, 0, 2, idletime, timeouttask, stoptask); if (trail[0] != '\r' || trail[1] != '\n') { throw new HttpException(HttpStatusCode.BadRequest); } if (this.ContentLength > 0 && this.ContentLength != (reader.Position - startpos)) { throw new HttpException(HttpStatusCode.BadRequest); } } }