source: TFP-WebServer/SpaceWizards.HttpListener/src/System/Net/Managed/ChunkedInputStream.cs

Last change on this file was 377, checked in by alloc, 2 years ago

Made SpaceWizards.HttpListener compilable against .NET 4.8 with preprocessor define UNITY_NETFRAMEWORK

File size: 6.5 KB
Line 
1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3
4//
5// System.Net.ChunkedInputStream
6//
7// Authors:
8// Gonzalo Paniagua Javier (gonzalo@novell.com)
9//
10// Copyright (c) 2005 Novell, Inc (http://www.novell.com)
11//
12// Permission is hereby granted, free of charge, to any person obtaining
13// a copy of this software and associated documentation files (the
14// "Software"), to deal in the Software without restriction, including
15// without limitation the rights to use, copy, modify, merge, publish,
16// distribute, sublicense, and/or sell copies of the Software, and to
17// permit persons to whom the Software is furnished to do so, subject to
18// the following conditions:
19//
20// The above copyright notice and this permission notice shall be
21// included in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
31// ReSharper disable RedundantUsingDirective
32
33using System;
34using System.IO;
35using System.Net;
36using System.Runtime.InteropServices;
37
38namespace SpaceWizards.HttpListener
39{
40 internal sealed class ChunkedInputStream : HttpRequestStream
41 {
42 private ChunkStream _decoder;
43 private readonly HttpListenerContext _context;
44 private bool _no_more_data;
45
46 private sealed class ReadBufferState
47 {
48 public byte[] Buffer;
49 public int Offset;
50 public int Count;
51 public int InitialCount;
52 public HttpStreamAsyncResult Ares;
53 public ReadBufferState(byte[] buffer, int offset, int count, HttpStreamAsyncResult ares)
54 {
55 Buffer = buffer;
56 Offset = offset;
57 Count = count;
58 InitialCount = count;
59 Ares = ares;
60 }
61 }
62
63 public ChunkedInputStream(HttpListenerContext context, Stream stream, byte[] buffer, int offset, int length)
64 : base(stream, buffer, offset, length)
65 {
66 _context = context;
67 WebHeaderCollection coll = (WebHeaderCollection)context.Request.Headers;
68 _decoder = new ChunkStream(coll);
69 }
70
71 public ChunkStream Decoder
72 {
73 get { return _decoder; }
74 set { _decoder = value; }
75 }
76
77 protected override int ReadCore(byte[] buffer, int offset, int count)
78 {
79 IAsyncResult ares = BeginReadCore(buffer, offset, count, null, null);
80 return EndRead(ares);
81 }
82
83 protected override IAsyncResult BeginReadCore(byte[] buffer, int offset, int size, AsyncCallback? cback, object? state)
84 {
85 HttpStreamAsyncResult ares = new HttpStreamAsyncResult(this);
86 ares._callback = cback;
87 ares._state = state;
88 if (_no_more_data || size == 0 || _closed)
89 {
90 ares.Complete();
91 return ares;
92 }
93 int nread = _decoder.Read(buffer, offset, size);
94 offset += nread;
95 size -= nread;
96 if (size == 0)
97 {
98 // got all we wanted, no need to bother the decoder yet
99 ares._count = nread;
100 ares.Complete();
101 return ares;
102 }
103 if (!_decoder.WantMore)
104 {
105 _no_more_data = nread == 0;
106 ares._count = nread;
107 ares.Complete();
108 return ares;
109 }
110 ares._buffer = new byte[8192];
111 ares._offset = 0;
112 ares._count = 8192;
113 ReadBufferState rb = new ReadBufferState(buffer, offset, size, ares);
114 rb.InitialCount += nread;
115 base.BeginReadCore(ares._buffer, ares._offset, ares._count, OnRead, rb);
116 return ares;
117 }
118
119 private void OnRead(IAsyncResult base_ares)
120 {
121 ReadBufferState rb = (ReadBufferState)base_ares.AsyncState!;
122 HttpStreamAsyncResult ares = rb.Ares;
123 try
124 {
125 int nread = base.EndRead(base_ares);
126 if (nread == 0)
127 {
128 _no_more_data = true;
129 ares._count = rb.InitialCount - rb.Count;
130 ares.Complete();
131 return;
132 }
133
134 _decoder.Write(ares._buffer!, ares._offset, nread);
135 nread = _decoder.Read(rb.Buffer, rb.Offset, rb.Count);
136 rb.Offset += nread;
137 rb.Count -= nread;
138 if (rb.Count == 0 || !_decoder.WantMore)
139 {
140 _no_more_data = !_decoder.WantMore && nread == 0;
141 ares._count = rb.InitialCount - rb.Count;
142 ares.Complete();
143 return;
144 }
145 ares._offset = 0;
146 ares._count = Math.Min(8192, _decoder.ChunkLeft + 6);
147 base.BeginReadCore(ares._buffer!, ares._offset, ares._count, OnRead, rb);
148 }
149 catch (Exception e)
150 {
151 _context.Connection.SendError(e.Message, 400);
152 ares.Complete(e);
153 }
154 }
155
156 public override int EndRead(IAsyncResult asyncResult)
157 {
158 if (asyncResult == null)
159 throw new ArgumentNullException(nameof(asyncResult));
160
161 HttpStreamAsyncResult? ares = asyncResult as HttpStreamAsyncResult;
162 if (ares == null || !ReferenceEquals(this, ares._parent))
163 {
164 throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult));
165 }
166 if (ares._endCalled)
167 {
168 throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, nameof(EndRead)));
169 }
170 ares._endCalled = true;
171
172 if (!asyncResult.IsCompleted)
173 asyncResult.AsyncWaitHandle.WaitOne();
174
175 if (ares._error != null)
176 throw new HttpListenerException((int)HttpStatusCode.BadRequest, SR.Format(SR.net_io_operation_aborted, ares._error.Message));
177
178 return ares._count;
179 }
180 }
181}
Note: See TracBrowser for help on using the repository browser.