| | 140 | public class Response : IDisposable |
| | 141 | { |
| | 142 | private Context _ctx; |
| | 143 | private HttpStatusCode _statusCode; |
| | 144 | private Dictionary<String, String> _headers; |
| | 145 | private String _contentType; |
| | 146 | private String _charSet; |
| | 147 | private HttpStream _httpStream; |
| | 148 | |
| | 149 | public Response(Context ctx) |
| | 150 | { |
| | 151 | _ctx = ctx; |
| | 152 | _contentType = "text/html"; |
| | 153 | _charSet = "UTF-8"; |
| | 154 | _statusCode = HttpStatusCode.OK; |
| | 155 | _headers = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); |
| | 156 | _httpStream = new HttpStream(_ctx.Stream, HttpStream_OnBeforeWrite); |
| | 157 | } |
| | 158 | |
| | 159 | private void HttpStream_OnBeforeWrite(Object sender, EventArgs e) |
| | 160 | { |
| | 161 | WriteHeaders(); |
| | 162 | } |
| | 163 | |
| | 164 | public Stream OutputStream |
| | 165 | { |
| | 166 | get { return _httpStream; } |
| | 167 | } |
| | 168 | public Dictionary<String, String> Headers |
| | 169 | { |
| | 170 | get { return _headers; } |
| | 171 | } |
| | 172 | public HttpStatusCode StatusCode |
| | 173 | { |
| | 174 | set { _statusCode = value; } |
| | 175 | get { return _statusCode; } |
| | 176 | } |
| | 177 | public String ContentType |
| | 178 | { |
| | 179 | set { _contentType = value.Replace('\n', ' '); } |
| | 180 | get { return _contentType; } |
| | 181 | } |
| | 182 | public String CharSet |
| | 183 | { |
| | 184 | set { _charSet = value.Replace('\n', ' '); } |
| | 185 | get { return _charSet; } |
| | 186 | } |
| | 187 | |
| | 188 | private Boolean _alreadySentHeaders = false; |
| | 189 | public void WriteHeaders() |
| | 190 | { |
| | 191 | if (_alreadySentHeaders) |
| | 192 | return; |
| | 193 | |
| | 194 | StreamWriter sw = new StreamWriter(_ctx.Stream); |
| | 195 | sw.WriteLine("HTTP/1.0 {0} {1}", (Int32)_statusCode, _statusCode.ToString()); |
| | 196 | foreach (KeyValuePair<String, String> pair in _headers) |
| | 197 | { |
| | 198 | sw.WriteLine("{0}: {1}", pair.Key, pair.Value); |
| | 199 | } |
| | 200 | if (!_headers.ContainsKey("Server")) |
| | 201 | { |
| | 202 | sw.WriteLine("Server: {0}", _ctx.Server.VersionString); |
| | 203 | } |
| | 204 | if (!_headers.ContainsKey("Content-Type")) |
| | 205 | { |
| | 206 | if (ContentType.StartsWith("text/")) |
| | 207 | { |
| | 208 | sw.WriteLine("Conetent-Type: {0}; CharSet={1}", ContentType, CharSet); |
| | 209 | } |
| | 210 | else |
| | 211 | { |
| | 212 | sw.WriteLine("Conetent-Type: {0}", ContentType); |
| | 213 | } |
| | 214 | } |
| | 215 | sw.WriteLine("Connection: close"); |
| | 216 | sw.WriteLine(); |
| | 217 | sw.Flush(); |
| | 218 | |
| | 219 | _alreadySentHeaders = true; |
| | 220 | } |
| | 221 | |
| | 222 | private Boolean _responseEnded = false; |
| | 223 | public void End() |
| | 224 | { |
| | 225 | WriteHeaders(); |
| | 226 | _ctx.Stream.Flush(); |
| | 227 | _ctx.Stream.Close(); |
| | 228 | |
| | 229 | _responseEnded = true; |
| | 230 | } |
| | 231 | |
| | 232 | #region IDisposable �����o |
| | 233 | |
| | 234 | public void Dispose() |
| | 235 | { |
| | 236 | if (!_responseEnded) |
| | 237 | { |
| | 238 | End(); |
| | 239 | } |
| | 240 | } |
| | 241 | |
| | 242 | #endregion |
| | 243 | } |
| | 244 | |
| | 245 | #region HTTP �T�[�o�N���X |
| 177 | | if (Request != null) |
| 178 | | { |
| 179 | | Request(ctx); |
| 180 | | } |
| 181 | | else |
| 182 | | { |
| 183 | | // not handled |
| 184 | | Trace.WriteLineIf(_loggingEnabled, "Request Not Handled", "error"); |
| 185 | | throw new HttpException(501, "Not Implemented"); |
| | 288 | try |
| | 289 | { |
| | 290 | if (Request != null) |
| | 291 | { |
| | 292 | Request(ctx); |
| | 293 | ctx.Response.End(); |
| | 294 | } |
| | 295 | else |
| | 296 | { |
| | 297 | // not handled |
| | 298 | Trace.WriteLineIf(_loggingEnabled, "Request Not Handled", "error"); |
| | 299 | throw new HttpException(501, "Not Implemented"); |
| | 300 | } |
| | 301 | } |
| | 302 | catch (Exception ex) |
| | 303 | { |
| | 304 | if (ex is HttpException || (ex is IOException && ex.InnerException is SocketException)) |
| | 305 | throw; |
| | 306 | else |
| | 307 | throw new HttpException(500, "Internal Server Error", ex); |
| 239 | | Trace.WriteLineIf(_loggingEnabled, e.Message, "error"); |
| 240 | | sw.WriteLine("HTTP/1.0 500 Internal Server Error"); |
| 241 | | sw.WriteLine("Content-Type: text/html; charset=utf-8"); |
| 242 | | sw.WriteLine("Server: {0}", VersionString); |
| 243 | | sw.WriteLine("Connection: close"); |
| 244 | | sw.WriteLine(""); |
| 245 | | sw.WriteLine("<title>500 Internal Server Error</title>"); |
| 246 | | sw.WriteLine("<h1>500 Internal Server Error</h1>"); |
| 247 | | sw.WriteLine("<p>�T�[�o�ŏ�����s���ɃG���[���������܂����B</p>"); |
| 248 | | if (IPAddress.IsLoopback(((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address)) |
| 249 | | { |
| 250 | | sw.WriteLine("<h2>��</h2>"); |
| 251 | | sw.WriteLine("<pre>{0}</pre>", HttpUtility.HtmlEncode(e.ToString())); |
| 252 | | } |
| 253 | | sw.Flush(); |
| | 369 | Trace.WriteLineIf(_loggingEnabled, e.ToString(), "error"); |
| | 370 | if (stream.CanWrite) |
| | 371 | { |
| | 372 | sw.WriteLine("HTTP/1.0 500 Internal Server Error"); |
| | 373 | sw.WriteLine("Content-Type: text/html; charset=utf-8"); |
| | 374 | sw.WriteLine("Server: {0}", VersionString); |
| | 375 | sw.WriteLine("Connection: close"); |
| | 376 | sw.WriteLine(""); |
| | 377 | sw.WriteLine("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">"); |
| | 378 | sw.WriteLine("<title>500 Internal Server Error</title>"); |
| | 379 | sw.WriteLine("<h1>500 Internal Server Error</h1>"); |
| | 380 | sw.WriteLine("<p>�T�[�o�ŏ�����s���ɃG���[���������܂����B</p>"); |
| | 381 | if (IPAddress.IsLoopback(((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address)) |
| | 382 | { |
| | 383 | sw.WriteLine("<h2>��</h2>"); |
| | 384 | sw.WriteLine("<pre>{0}</pre>", HttpUtility.HtmlEncode(e.ToString())); |
| | 385 | } |
| | 386 | sw.Flush(); |
| | 387 | } |
| 304 | | HandleRequest(tcpClient); |
| 305 | | } |
| 306 | | catch (Exception e) |
| 307 | | { |
| 308 | | Trace.WriteLineIf(_loggingEnabled, e.Message, "error"); |
| | 437 | _tcpListener.BeginAcceptTcpClient(_AcceptTcpClientCallback, _tcpListener); |
| | 438 | TcpClient tcpClient = _tcpListener.EndAcceptTcpClient(ar); |
| | 439 | |
| | 440 | try |
| | 441 | { |
| | 442 | HandleRequest(tcpClient); |
| | 443 | } |
| | 444 | catch (Exception e) |
| | 445 | { |
| | 446 | Trace.WriteLineIf(_loggingEnabled, e.Message, "error"); |
| | 447 | if (!(e.InnerException is SocketException)) |
| | 448 | { |
| | 449 | throw; |
| | 450 | } |
| | 451 | } |
| | 452 | } |
| | 453 | catch (SocketException se) |
| | 454 | { |
| | 455 | Trace.WriteLineIf(_loggingEnabled, se.Message, "error"); |
| | 478 | #endregion |
| | 479 | |
| | 480 | #region HttpStream �N���X |
| | 481 | public class HttpStream : Stream |
| | 482 | { |
| | 483 | private Stream _baseStream; |
| | 484 | private Boolean _contentWritten = false; |
| | 485 | public event EventHandler BeforeWrite; |
| | 486 | |
| | 487 | public HttpStream(Stream stream, EventHandler beforeWriteHandler) |
| | 488 | { |
| | 489 | _baseStream = stream; |
| | 490 | BeforeWrite += beforeWriteHandler; |
| | 491 | } |
| | 492 | public Stream BaseStream |
| | 493 | { |
| | 494 | get { return _baseStream; } |
| | 495 | } |
| | 496 | public override bool CanRead |
| | 497 | { |
| | 498 | get { return _baseStream.CanRead; } |
| | 499 | } |
| | 500 | |
| | 501 | public override bool CanSeek |
| | 502 | { |
| | 503 | get { return _baseStream.CanSeek; } |
| | 504 | } |
| | 505 | |
| | 506 | public override bool CanWrite |
| | 507 | { |
| | 508 | get { return _baseStream.CanWrite; } |
| | 509 | } |
| | 510 | |
| | 511 | public override void Flush() |
| | 512 | { |
| | 513 | _baseStream.Flush(); |
| | 514 | } |
| | 515 | |
| | 516 | public override long Length |
| | 517 | { |
| | 518 | get { return _baseStream.Length; } |
| | 519 | } |
| | 520 | |
| | 521 | public override long Position |
| | 522 | { |
| | 523 | get |
| | 524 | { |
| | 525 | return _baseStream.Position; |
| | 526 | } |
| | 527 | set |
| | 528 | { |
| | 529 | _baseStream.Position = value; |
| | 530 | } |
| | 531 | } |
| | 532 | |
| | 533 | public override int Read(byte[] buffer, int offset, int count) |
| | 534 | { |
| | 535 | return _baseStream.Read(buffer, offset, count); |
| | 536 | } |
| | 537 | |
| | 538 | public override long Seek(long offset, SeekOrigin origin) |
| | 539 | { |
| | 540 | return _baseStream.Seek(offset, origin); |
| | 541 | } |
| | 542 | |
| | 543 | public override void SetLength(long value) |
| | 544 | { |
| | 545 | _baseStream.SetLength(value); |
| | 546 | } |
| | 547 | |
| | 548 | public override void Write(byte[] buffer, int offset, int count) |
| | 549 | { |
| | 550 | if (BeforeWrite != null && !_contentWritten) |
| | 551 | { |
| | 552 | _contentWritten = true; |
| | 553 | BeforeWrite(this, EventArgs.Empty); |
| | 554 | } |
| | 555 | |
| | 556 | _baseStream.Write(buffer, offset, count); |
| | 557 | } |
| | 558 | } |
| | 559 | #endregion |