Subversion Repositories general

Rev

Rev 1092 | Rev 1125 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1092 dev 1
using System;
2
using System.Collections;
3
using System.Net;
4
using System.Net.Sockets;
5
using System.Threading;
6
using System.Text;
7
using System.Text.RegularExpressions;
8
using System.Xml;
9
 
10
// FIXME deny write access to all public properties
11
// FIXME for text/xml get encoding from body if not specified in header
12
// FIXME option to not store parsed data, just raw packets and reparse on demand
13
namespace TCPproxy
14
{
15
	public enum LogLevel
16
	{
17
		Critical,
18
		Error,
19
		Warning,
20
		Important,
21
		Info,
22
		Debug
23
	}
24
 
25
	public enum SocketState
26
	{
27
		None,
28
		Connecting,
29
		Connected,
30
		ShutdownSend,
1122 dev 31
		ShutdownReceived,
1092 dev 32
		Closed
33
	}
34
 
35
	public enum TcpMessageDirection
36
	{
37
		Local,
38
		Remote
39
	}
40
 
41
	public enum HttpVersion
42
	{
43
		V0_9,
44
		V1_0,
45
		V1_1
46
	}
47
 
48
	public enum HttpEncoding
49
	{
50
		Identify,
51
		Gzip,
52
		Compress,
53
		Deflate,
54
		Unknown
55
	}
56
 
57
	public class TcpLogEventArgs : EventArgs
58
	{
59
		private readonly LogLevel  level;
60
		private readonly string    message;
61
		private readonly Exception exception;
62
 
63
		public LogLevel Level
64
		{
65
			get { return level; }
66
		}
67
 
68
		public string Message
69
		{
70
			get { return message; }
71
		}
72
 
73
		public Exception Exception
74
		{
75
			get { return exception; }
76
		}
77
 
78
		internal TcpLogEventArgs(LogLevel level, string message, Exception exception)
79
		{
80
			this.level     = level;
81
			this.message   = message;
82
			this.exception = exception;
83
		}
84
	}
85
 
86
	public class TcpEventArgs : EventArgs
87
	{
88
		internal TcpEventArgs()
89
		{
90
		}
91
	}
92
 
93
	public class TcpConnectionEventArgs : EventArgs
94
	{
95
		private readonly TcpConnection tcp;
96
 
97
		public TcpConnection Tcp
98
		{
99
			get { return tcp; }
100
		}
101
 
102
		internal TcpConnectionEventArgs(TcpConnection tcp)
103
		{
104
			this.tcp = tcp;
105
		}
106
	}
107
 
108
	public class TcpHttpEventArgs : EventArgs
109
	{
110
		private readonly HttpMessage http;
111
 
112
		public HttpMessage Http
113
		{
114
			get { return http; }
115
		}
116
 
117
		internal TcpHttpEventArgs(HttpMessage http)
118
		{
119
			this.http = http;
120
		}
121
	}
122
 
123
	public delegate void TcpLogEventHandler(object sender, TcpLogEventArgs e);
124
 
125
	public delegate void TcpEventHandler(object sender, TcpEventArgs e);
126
 
127
	public delegate void TcpConnectionEventHandler(object sender, TcpConnectionEventArgs e);
128
 
129
	public delegate void TcpHttpEventHandler(object sender, TcpHttpEventArgs e);
130
 
131
	public class TcpListener
132
	{
133
		private int       listenPort     = -1;
134
		private IPAddress resendHost;
135
		private int       resendPort     = -1;
136
		private Socket    socket;
137
		private int       tcpId          = 0;
138
		private ArrayList tcpConnections = new ArrayList();
139
 
140
		public TcpListener(int listenPort, IPAddress resendHost, int resendPort)
141
		{
142
			this.listenPort = listenPort;
143
			this.resendHost = resendHost;
144
			this.resendPort = resendPort;
145
		}
146
 
147
		public void StartListening()
148
		{
149
			socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
150
			socket.Bind(new IPEndPoint(IPAddress.Any, listenPort));
151
			socket.Listen(100);
152
			socket.BeginAccept(new AsyncCallback(OnClientConnect), null);
153
			SendLog(null, LogLevel.Important, "Listen on " + socket.LocalEndPoint);
154
		}
1122 dev 155
 
1092 dev 156
		public void StopListening()
157
		{
1122 dev 158
			try
1092 dev 159
			{
160
				if(socket != null)
161
				{
162
					SendLog(null, LogLevel.Important, "Stop listening " + socket.LocalEndPoint);
163
					socket.Close();
164
				}
165
			}
166
			catch(ObjectDisposedException) // socket is already closed
167
			{
1122 dev 168
			}
169
			catch(Exception ex)
170
			{
1092 dev 171
				Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
172
			}
173
		}
174
 
175
		public void CancelAll()
176
		{
177
			ArrayList tcpConnectionsCopy;
178
 
179
			lock(tcpConnections)
180
			{
181
				tcpConnectionsCopy = new ArrayList(tcpConnections);
182
			}
183
 
184
			foreach(TcpConnection tcp in tcpConnectionsCopy)
185
			{
186
				tcp.Cancel();
187
			}
188
		}
189
 
190
		protected virtual void OnClientConnect(IAsyncResult asyn)
191
		{
1122 dev 192
			try
1092 dev 193
			{
194
				Socket worker = socket.EndAccept(asyn);
195
				socket.BeginAccept(new AsyncCallback(OnClientConnect), null); // wait for next client
196
 
197
				TcpConnection tcp = new TcpConnection(string.Format("{0:0000}", tcpId++));
198
				tcp.Close += new TcpEventHandler(TcpConnectionClosed);
199
				OnNewTcp(new TcpConnectionEventArgs(tcp));
200
 
1122 dev 201
				lock(tcpConnections)
1092 dev 202
				{
203
					tcpConnections.Add(tcp);
204
				}
205
 
206
				tcp.Continue(resendHost, resendPort, worker);
207
			}
208
			catch(ObjectDisposedException) {}  // socket is closed
1122 dev 209
			catch(Exception ex)
1092 dev 210
			{
211
				Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
212
			}
213
		}
214
 
215
		protected virtual void TcpConnectionClosed(object sender, TcpEventArgs e)
216
		{
217
			lock(tcpConnections)
218
			{
219
				tcpConnections.Remove((TcpConnection)sender);
220
			}
221
		}
222
 
223
		public event TcpConnectionEventHandler NewTcp;
224
 
225
		protected virtual void OnNewTcp(TcpConnectionEventArgs e)
226
		{
1122 dev 227
			if(NewTcp != null)
1092 dev 228
			{
1122 dev 229
				NewTcp(this, e);
1092 dev 230
			}
231
		}
232
 
233
		public event TcpLogEventHandler Log;
234
 
235
		protected virtual void OnLog(TcpLogEventArgs e)
236
		{
1122 dev 237
			if(Log != null)
1092 dev 238
			{
1122 dev 239
				Log(this, e);
1092 dev 240
			}
241
		}
242
 
243
		protected virtual void SendLog(TcpConnection tcp, LogLevel level, string message)
244
		{
245
			TcpLogEventArgs e = new TcpLogEventArgs(level, message, null);
246
			OnLog(e);
247
		}
248
 
249
		protected virtual void SendLog(TcpConnection tcp, LogLevel level, Exception ex)
250
		{
251
			TcpLogEventArgs e = new TcpLogEventArgs(level, null, ex);
252
			OnLog(e);
253
		}
254
	}
255
 
256
	public class TcpConnection
257
	{
258
		private string       id;
259
		private DateTime     startTimestamp     = DateTime.MinValue;
260
		private DateTime     localEndTimestamp  = DateTime.MinValue;
261
		private DateTime     remoteEndTimestamp = DateTime.MinValue;
262
		private IPEndPoint   localPoint;
263
		private IPEndPoint   remotePoint;
264
		private LinkedList   messages    = new LinkedList();
265
		private SocketState  localState  = SocketState.None;
266
		private SocketState  remoteState = SocketState.None;
267
		private SocketWorker worker;
268
		private LinkedList   https       = new LinkedList();
269
		private HttpParser   httpParser;
270
 
271
		public string Id
272
		{
273
			get { return id; }
274
		}
275
 
276
		public DateTime StartTimestamp
277
		{
278
			get { return startTimestamp; }
279
		}
280
 
281
		public DateTime LocalEndTimestamp
282
		{
283
			get { return localEndTimestamp; }
284
		}
285
 
286
		public DateTime RemoteEndTimestamp
287
		{
288
			get { return remoteEndTimestamp; }
289
		}
290
 
291
		public IPEndPoint LocalPoint
292
		{
293
			get { return localPoint; }
294
		}
295
 
296
		public IPEndPoint RemotePoint
297
		{
298
			get { return remotePoint; }
299
		}
300
 
301
		public LinkedList Messages
302
		{
303
			get { return messages; }	// FIXME return read-only object
304
		}
305
 
306
		public SocketState LocalState
307
		{
308
			get { return localState; }
309
		}
310
 
311
		public SocketState RemoteState
312
		{
313
			get { return remoteState; }
314
		}
315
 
316
		private int CountBytes(TcpMessageDirection direction)
317
		{
318
			int count = 0;
319
 
320
			foreach(TcpMessage message in messages)
321
			{
322
				if(message.Direction == direction)
323
					count += message.Length;
324
			}
325
			return count;
326
		}
327
 
328
		public int SentBytes
329
		{
330
			get { return CountBytes(TcpMessageDirection.Local); }
331
		}
332
 
333
		public int ReceivedBytes
334
		{
335
			get { return CountBytes(TcpMessageDirection.Remote); }
336
		}
337
 
338
		internal TcpConnection(string id)
339
		{
340
			this.id         = id;
341
			this.httpParser = HttpParser.Parse(this);
342
		}
343
 
344
		internal void Continue(IPAddress resendHost, int resendPort, Socket localSocket)
345
		{
346
			SendLog(LogLevel.Important, "Client connected from " + ((IPEndPoint)localSocket.RemoteEndPoint).ToString());
347
			SetLocalState(SocketState.Connected);
348
			this.worker = new SocketWorker(resendHost, resendPort, localSocket, this);
349
		}
350
 
351
		public void Cancel()
352
		{
353
			worker.Cancel();
354
		}
355
 
356
		protected TcpMessage Append(TcpMessageDirection direction, byte[] newBytes)
357
		{
358
			return Append(direction, newBytes, newBytes.Length);
359
		}
360
 
361
		protected TcpMessage Append(TcpMessageDirection direction, byte[] newBytes, int length)
362
		{
363
			if(newBytes == null) return null;
364
 
365
			TcpMessage message;
366
 
1122 dev 367
			lock(this)
1092 dev 368
			{
369
				message = new TcpMessage();
370
				message.Direction = direction;
371
				messages.Add(message);
372
				message.Append(newBytes, length);
373
			}
374
 
375
			httpParser.NewMessageArived();
376
			OnUpdate(new TcpEventArgs());
377
 
378
			return message;
379
		}
380
 
381
		internal void AddHttpMessage(HttpMessage http)
382
		{
383
			lock(this)
384
			{
385
				https.Add(http);
386
				TcpHttpEventArgs e = new TcpHttpEventArgs(http);
387
				OnNewHttp(e);
388
			}
389
		}
390
 
391
 
392
		public override string ToString()	 // FIXME delete the method
393
		{
394
			return id + " " + startTimestamp.ToString("HH:mm:ss.ffff");
395
		}
396
 
397
 
398
		protected void SetLocalPoint(IPEndPoint localPoint)
399
		{
1122 dev 400
			this.localPoint = localPoint;
1092 dev 401
			OnUpdate(new TcpEventArgs());
402
		}
403
 
404
		protected void SetRemotePoint(IPEndPoint remotePoint)
405
		{
1122 dev 406
			this.remotePoint = remotePoint;
1092 dev 407
			OnUpdate(new TcpEventArgs());
408
		}
409
 
410
		protected void SetLocalState(SocketState localState)
411
		{
1122 dev 412
			if(this.localState == SocketState.None && localState == SocketState.Connecting)
1092 dev 413
			{
414
				startTimestamp = DateTime.Now;
415
			}
1122 dev 416
			else if(this.localState == SocketState.None && localState == SocketState.Connected)
1092 dev 417
			{
418
				startTimestamp = DateTime.Now;
419
			}
1122 dev 420
			else if(this.localState == SocketState.None && localState == SocketState.Closed)
1092 dev 421
			{
422
			}
1122 dev 423
			else if(this.localState == SocketState.Connecting && localState == SocketState.Connected)
1092 dev 424
			{
425
			}
1122 dev 426
			else if(this.localState == SocketState.Connecting && localState == SocketState.Closed)
1092 dev 427
			{
428
				if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
429
			}
1122 dev 430
			else if(this.localState == SocketState.Connected && localState == SocketState.ShutdownSend)
1092 dev 431
			{
432
			}
1122 dev 433
			else if(this.localState == SocketState.Connected && localState == SocketState.ShutdownReceived)
1092 dev 434
			{
435
				if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
436
			}
1122 dev 437
			else if(this.localState == SocketState.Connected && localState == SocketState.Closed)
1092 dev 438
			{
439
				if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
440
			}
1122 dev 441
			else if(this.localState == SocketState.ShutdownSend && localState == SocketState.Closed)
1092 dev 442
			{
443
				if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
444
			}
1122 dev 445
			else if(this.localState == SocketState.ShutdownSend && localState == SocketState.ShutdownReceived)
1092 dev 446
			{
447
				if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
448
			}
1122 dev 449
			else if(this.localState == SocketState.ShutdownReceived && localState == SocketState.ShutdownSend)
1092 dev 450
			{
451
			}
1122 dev 452
			else if(this.localState == SocketState.ShutdownReceived && localState == SocketState.Closed)
1092 dev 453
			{
454
				if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
455
			}
1122 dev 456
			else if(this.localState == SocketState.Closed && localState == SocketState.Closed)
1092 dev 457
			{
458
				if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
459
			}
1122 dev 460
			else
1092 dev 461
			{
462
				throw new Exception("Wrong local socket state change: from " + this.localState + " to " + localState);
463
			}
1122 dev 464
			this.localState = localState;
1092 dev 465
			if(this.localState == SocketState.Closed) httpParser.NewMessageArived();
466
			OnUpdate(new TcpEventArgs());
467
		}
468
 
469
		protected void SetRemoteState(SocketState remoteState)
470
		{
1122 dev 471
			if(this.remoteState == SocketState.None && remoteState == SocketState.Connecting)
1092 dev 472
			{
473
			}
1122 dev 474
			else if(this.remoteState == SocketState.None && remoteState == SocketState.Connected)
1092 dev 475
			{
476
			}
1122 dev 477
			else if(this.remoteState == SocketState.None && remoteState == SocketState.Closed)
1092 dev 478
			{
479
			}
1122 dev 480
			else if(this.remoteState == SocketState.Connecting && remoteState == SocketState.Connected)
1092 dev 481
			{
482
			}
1122 dev 483
			else if(this.remoteState == SocketState.Connecting && remoteState == SocketState.Closed)
1092 dev 484
			{
485
				if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
486
			}
1122 dev 487
			else if(this.remoteState == SocketState.Connected && remoteState == SocketState.ShutdownSend)
1092 dev 488
			{
489
			}
1122 dev 490
			else if(this.remoteState == SocketState.Connected && remoteState == SocketState.ShutdownReceived)
1092 dev 491
			{
492
				if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
493
			}
1122 dev 494
			else if(this.remoteState == SocketState.Connected && remoteState == SocketState.Closed)
1092 dev 495
			{
496
				if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
497
			}
1122 dev 498
			else if(this.remoteState == SocketState.ShutdownSend && remoteState == SocketState.Closed)
1092 dev 499
			{
500
				if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
501
			}
1122 dev 502
			else if(this.remoteState == SocketState.ShutdownSend && remoteState == SocketState.ShutdownReceived)
1092 dev 503
			{
504
				if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
505
			}
1122 dev 506
			else if(this.remoteState == SocketState.ShutdownReceived && remoteState == SocketState.ShutdownSend)
1092 dev 507
			{
508
			}
1122 dev 509
			else if(this.remoteState == SocketState.ShutdownReceived && remoteState == SocketState.Closed)
1092 dev 510
			{
511
				if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
512
			}
1122 dev 513
			else if(this.remoteState == SocketState.Closed && remoteState == SocketState.Closed)
1092 dev 514
			{
515
				if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
516
			}
1122 dev 517
			else
1092 dev 518
			{
519
				throw new Exception("Wrong remote socket state change: from " + this.remoteState + " to " + remoteState);
520
			}
1122 dev 521
			this.remoteState = remoteState;
1092 dev 522
			if(this.remoteState == SocketState.Closed) httpParser.NewMessageArived();
523
			OnUpdate(new TcpEventArgs());
524
		}
525
 
526
		public event TcpEventHandler Update;
527
 
528
		protected virtual void OnUpdate(TcpEventArgs e)
529
		{
1122 dev 530
			if(Update != null)
1092 dev 531
			{
1122 dev 532
				Update(this, e);
1092 dev 533
			}
534
		}
535
 
536
		public event TcpHttpEventHandler NewHttp;
537
 
538
		protected virtual void OnNewHttp(TcpHttpEventArgs e)
539
		{
1122 dev 540
			if(NewHttp != null)
1092 dev 541
			{
1122 dev 542
				NewHttp(this, e);
1092 dev 543
			}
544
		}
545
 
546
		public event TcpEventHandler Close;
547
 
548
		protected virtual void OnClose(TcpEventArgs e)
549
		{
1122 dev 550
			if(Close != null)
1092 dev 551
			{
1122 dev 552
				Close(this, e);
1092 dev 553
			}
554
		}
555
 
556
		public event TcpLogEventHandler Log;
557
 
558
		protected virtual void OnLog(TcpLogEventArgs e)
559
		{
1122 dev 560
			if(Log != null)
1092 dev 561
			{
1122 dev 562
				Log(this, e);
1092 dev 563
			}
564
		}
565
 
566
		protected virtual void SendLog(LogLevel level, string message)
567
		{
568
			TcpLogEventArgs e = new TcpLogEventArgs(level, message, null);
569
			OnLog(e);
570
		}
571
 
572
		protected virtual void SendLog(LogLevel level, Exception ex)
573
		{
574
			TcpLogEventArgs e = new TcpLogEventArgs(level, null, ex);
575
			OnLog(e);
576
		}
577
 
578
		protected class SocketWorker
579
		{
580
			private enum SendCommandType
581
			{
582
				Send,
583
				Shutdown,
584
				Reset
585
			}
586
 
587
			private class SendCommand
588
			{
589
				public byte[]          buffer  = null;
590
				public int             length  = 0;
1122 dev 591
				public SendCommandType cmdType = SendCommandType.Send;
1092 dev 592
			}
593
 
594
			private static int BUF_SIZE = 2048;
595
 
596
			private TcpConnection    tcp;
597
			private Socket           localSocket;
598
			private Socket           remoteSocket;
599
			private byte[]           localDataBuffer;
600
			private byte[]           remoteDataBuffer;
601
			private AsyncCallback    receiveLocalMethod;
602
			private AsyncCallback    receiveRemoteMethod;
603
			private Queue            localSendQueue              = new Queue();
604
			private Queue            remoteSendQueue             = new Queue();
605
			private AutoResetEvent   localSendEvent              = new AutoResetEvent(false);
606
			private AutoResetEvent   remoteSendEvent             = new AutoResetEvent(false);
607
			private bool             localSocketSendShutdown     = false;
608
			private bool             localSocketReceiveShutdown  = false;
609
			private bool             remoteSocketSendShutdown    = false;
610
			private bool             remoteSocketReceiveShutdown = false;
611
			private ManualResetEvent remoteSocketEvent           = new ManualResetEvent(false);
612
			private Thread           localSendThread;
613
			private Thread           remoteSendThread;
614
 
615
			public SocketWorker(IPAddress resendHost, int resendPort, Socket localSocket, TcpConnection tcp)
616
			{
1122 dev 617
				try
1092 dev 618
				{
1122 dev 619
					tcp.SendLog(LogLevel.Debug, string.Format("Local socket: {0}:{1} <-> {2}:{3}",
620
						((IPEndPoint)localSocket.LocalEndPoint).Address,
1092 dev 621
						((IPEndPoint)localSocket.LocalEndPoint).Port,
1122 dev 622
						((IPEndPoint)localSocket.RemoteEndPoint).Address,
1092 dev 623
						((IPEndPoint)localSocket.RemoteEndPoint).Port));
624
 
625
					this.localSocket    = localSocket;
626
					this.tcp            = tcp;
627
					receiveLocalMethod  = new AsyncCallback(OnLocalReceived);
628
					receiveRemoteMethod = new AsyncCallback(OnRemoteReceived);
629
 
630
					tcp.SetLocalPoint((IPEndPoint)localSocket.RemoteEndPoint);
631
					this.localSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1);
1122 dev 632
 
1092 dev 633
					localSendThread = new Thread(new ThreadStart(LocalSendProc));
634
					localSendThread.Name = "SocketWorker.LocalSendProc";
635
					localSendThread.Start();
636
 
637
					ContinueLocalReceive();
1122 dev 638
 
1092 dev 639
					if(resendHost == null)
640
					{
641
						remoteSocket = null;
642
					}
643
					else
644
					{
645
						tcp.SetRemoteState(SocketState.Connecting);
646
 
647
						IPEndPoint point = new IPEndPoint(resendHost, resendPort);
648
						remoteSocket = new Socket(point.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
649
						remoteSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1);
650
						remoteSocket.Connect(point);
651
						tcp.SetRemoteState(SocketState.Connected);
652
						tcp.SetRemotePoint((IPEndPoint)remoteSocket.RemoteEndPoint);
653
 
654
						remoteSendThread = new Thread(new ThreadStart(RemoteSendProc));
655
						remoteSendThread.Name = "SocketWorker.RemoteSendProc";
656
						remoteSendThread.Start();
657
 
658
						ContinueRemoteReceive();
659
						remoteSocketEvent.Set(); // remote socket ready to send data
660
						tcp.SendLog(LogLevel.Info, "Connected to server " + tcp.RemotePoint.ToString());
661
 
1122 dev 662
						tcp.SendLog(LogLevel.Debug, string.Format("Remote socket: {0}:{1} <-> {2}:{3}",
663
							((IPEndPoint)remoteSocket.LocalEndPoint).Address,
1092 dev 664
							((IPEndPoint)remoteSocket.LocalEndPoint).Port,
1122 dev 665
							((IPEndPoint)remoteSocket.RemoteEndPoint).Address,
1092 dev 666
							((IPEndPoint)remoteSocket.RemoteEndPoint).Port));
667
					}
668
				}
669
				catch(Exception ex)
670
				{
671
					tcp.SendLog(LogLevel.Warning, ex);
672
					Cancel();
673
				}
674
			}
675
 
676
			private void ContinueLocalReceive()
677
			{
1122 dev 678
				try
1092 dev 679
				{
680
					localDataBuffer = new byte[BUF_SIZE];
681
					localSocket.BeginReceive(localDataBuffer, 0, BUF_SIZE, SocketFlags.None, receiveLocalMethod, this);
682
				}
683
				catch(ObjectDisposedException ex) // the socket is closed
684
				{
685
					tcp.SendLog(LogLevel.Info, ex);
686
					Cancel();
687
				}
688
				catch(Exception ex)
689
				{
690
					tcp.SendLog(LogLevel.Warning, ex);
691
					Cancel();
692
				}
693
			}
694
 
695
			private void ContinueRemoteReceive()
696
			{
1122 dev 697
				try
1092 dev 698
				{
699
					remoteDataBuffer = new byte[BUF_SIZE];
700
					remoteSocket.BeginReceive(remoteDataBuffer, 0, BUF_SIZE, SocketFlags.None, receiveRemoteMethod, this);
701
				}
702
				catch(ObjectDisposedException ex) // the socket is closed
703
				{
704
					tcp.SendLog(LogLevel.Info, ex);
705
					Cancel();
706
				}
707
				catch(Exception ex)
708
				{
709
					tcp.SendLog(LogLevel.Warning, ex);
710
					Cancel();
711
				}
712
			}
713
 
714
			private void CheckLocalSocket()
715
			{
1122 dev 716
				lock(localSocket)
1092 dev 717
				{
1122 dev 718
					try
1092 dev 719
					{
720
						if(localSocketReceiveShutdown && localSocketSendShutdown)
721
						{
722
							if(localSocket.Connected) localSocket.Close();
723
							tcp.SetLocalState(SocketState.Closed);
724
						}
725
					}
726
					catch(Exception ex) // in any case we want to close the socket
727
					{
728
						tcp.SendLog(LogLevel.Warning, ex);
729
					}
730
				}
731
			}
732
 
733
			private void CheckRemoteSocket()
734
			{
1122 dev 735
				lock(remoteSocket)
1092 dev 736
				{
1122 dev 737
					try
1092 dev 738
					{
1122 dev 739
						if(remoteSocketReceiveShutdown && remoteSocketSendShutdown)
1092 dev 740
						{
741
							if(remoteSocket.Connected) remoteSocket.Close();
742
							tcp.SetRemoteState(SocketState.Closed);
743
						}
744
					}
745
					catch(Exception ex) // in any case we want to close the socket
746
					{
747
						tcp.SendLog(LogLevel.Warning, ex);
748
					}
749
				}
750
			}
751
 
752
			private void OnLocalReceived(IAsyncResult asyn)
753
			{
1122 dev 754
				try
1092 dev 755
				{
756
					int  bytesReceived = 0;
757
					bool reset         = false;
758
 
759
					try
760
					{
761
						bytesReceived = localSocket.EndReceive(asyn);
762
					}
763
					catch(ObjectDisposedException)
764
					{
765
						reset = true;
766
					}
767
					catch(SocketException ex)
768
					{
1122 dev 769
						if(ex.ErrorCode == 10054)
1092 dev 770
							reset = true;
1122 dev 771
						else
1092 dev 772
							throw ex;
773
					}
774
 
775
					if(reset)
776
					{
777
						tcp.SendLog(LogLevel.Info, "Got reset from local end");
778
 
779
						lock(localSocket)
780
						{
781
							if(localSocket.Connected) localSocket.Close();
782
							tcp.SetLocalState(SocketState.Closed);
783
						}
784
 
785
						SendCommand cmd = new SendCommand();
786
						cmd.cmdType = SendCommandType.Reset;
787
						lock(localSendQueue)
788
						{
789
							localSendQueue.Enqueue(cmd);
790
							localSendEvent.Set();
791
						}
792
					}
793
					else if(bytesReceived <= 0)
794
					{
795
						tcp.SendLog(LogLevel.Info, "Got showdown from local end");
796
 
797
						localSocket.Shutdown(SocketShutdown.Receive);
1122 dev 798
						tcp.SetLocalState(SocketState.ShutdownReceived);
1092 dev 799
						localSocketReceiveShutdown = true;
800
						CheckLocalSocket();
801
 
802
						SendCommand cmd = new SendCommand();
803
						cmd.cmdType = SendCommandType.Shutdown;
804
						lock(localSendQueue)
805
						{
806
							localSendQueue.Enqueue(cmd);
807
							localSendEvent.Set();
808
						}
809
					}
810
					else
811
					{
812
						tcp.SendLog(LogLevel.Debug, string.Format("Local received {0} bytes", bytesReceived));
813
 
814
						SendCommand cmd = new SendCommand();
815
						cmd.buffer = localDataBuffer;
816
						cmd.length = bytesReceived;
817
						lock(localSendQueue)
818
						{
819
							localSendQueue.Enqueue(cmd);
820
							localSendEvent.Set();
821
						}
822
						ContinueLocalReceive();
823
					}
824
				}
825
				catch(Exception ex)
826
				{
827
					tcp.SendLog(LogLevel.Warning, ex);
828
					Cancel();
829
				}
830
			}
831
 
832
			private void LocalSendProc()
833
			{
834
				try
835
				{
1122 dev 836
					while(true)
1092 dev 837
					{
838
						SendCommand cmd;
839
 
1122 dev 840
						if(localSendQueue.Count == 0)
1092 dev 841
						{
842
							localSendEvent.WaitOne();
843
						}
844
 
1122 dev 845
						lock(localSendQueue)
1092 dev 846
						{
847
							localSendEvent.Reset();
848
							cmd = (SendCommand)localSendQueue.Dequeue();
849
						}
850
 
851
						if(cmd.cmdType == SendCommandType.Reset) // reset marker
852
						{
853
							if(remoteSocket == null || !remoteSocket.Connected) remoteSocketEvent.WaitOne();
854
							tcp.SendLog(LogLevel.Debug, string.Format("Send reset to remote end"));
855
							lock(remoteSocket)
856
							{
857
								if(!remoteSocket.Connected) remoteSocket.Close();
858
								tcp.SetRemoteState(SocketState.Closed);
859
							}
860
 
861
							break; // no more send allowed
862
						}
863
						else if(cmd.cmdType == SendCommandType.Shutdown) // shutdown marker
864
						{
865
							if(remoteSocket == null || !remoteSocket.Connected) remoteSocketEvent.WaitOne();
866
							tcp.SendLog(LogLevel.Debug, string.Format("Send shutdown to remote end"));
867
							remoteSocket.Shutdown(SocketShutdown.Send);
868
							tcp.SetRemoteState(SocketState.ShutdownSend);
869
							remoteSocketSendShutdown = true;
870
							CheckRemoteSocket();
871
 
872
							break; // no more send allowed
873
						}
874
						else
875
						{
876
							// store received bytes
877
							tcp.Append(TcpMessageDirection.Local, cmd.buffer, cmd.length);
878
 
879
							// forward it
880
							if(remoteSocket == null || !remoteSocket.Connected) remoteSocketEvent.WaitOne();
881
							tcp.SendLog(LogLevel.Debug, string.Format("Send {0} bytes to remote end", cmd.length));
882
							remoteSocket.Send(cmd.buffer, cmd.length, SocketFlags.None);
883
						}
884
					}
885
				}
886
				catch(ThreadAbortException)
887
				{
888
				}
889
				catch(Exception ex)
890
				{
891
					tcp.SendLog(LogLevel.Warning, ex);
892
					Cancel();
893
				}
894
			}
895
 
896
			private void OnRemoteReceived(IAsyncResult asyn)
897
			{
1122 dev 898
				try
1092 dev 899
				{
900
					int  bytesReceived = 0;
901
					bool reset         = false;
902
 
903
					try
904
					{
905
						bytesReceived = remoteSocket.EndReceive(asyn);
906
					}
907
					catch(ObjectDisposedException)
908
					{
909
						reset = true;
910
					}
911
					catch(SocketException ex)
912
					{
1122 dev 913
						if(ex.ErrorCode == 10054)
1092 dev 914
							reset = true;
1122 dev 915
						else
1092 dev 916
							throw ex;
917
					}
918
 
919
					if(reset)
920
					{
921
						tcp.SendLog(LogLevel.Info, "Got reset from remote end");
922
 
923
						lock(remoteSocket)
924
						{
925
							if(remoteSocket.Connected) remoteSocket.Close();
926
							tcp.SetRemoteState(SocketState.Closed);
927
						}
928
 
929
						SendCommand cmd = new SendCommand();
930
						cmd.cmdType = SendCommandType.Reset;
931
						lock(remoteSendQueue)
932
						{
933
							remoteSendQueue.Enqueue(cmd);
934
							remoteSendEvent.Set();
935
						}
936
					}
937
					else if(bytesReceived <= 0)
938
					{
939
						tcp.SendLog(LogLevel.Info, "Got showdown from remote end");
940
 
941
						remoteSocket.Shutdown(SocketShutdown.Receive);
1122 dev 942
						tcp.SetRemoteState(SocketState.ShutdownReceived);
1092 dev 943
						remoteSocketReceiveShutdown = true;
944
						CheckRemoteSocket();
945
 
946
						SendCommand cmd = new SendCommand();
947
						cmd.cmdType = SendCommandType.Shutdown;
948
						lock(remoteSendQueue)
949
						{
950
							remoteSendQueue.Enqueue(cmd);
951
							remoteSendEvent.Set();
952
						}
953
					}
954
					else
955
					{
956
						tcp.SendLog(LogLevel.Debug, string.Format("Remote received {0} bytes", bytesReceived));
957
 
958
						SendCommand cmd = new SendCommand();
959
						cmd.buffer = remoteDataBuffer;
960
						cmd.length = bytesReceived;
961
						lock(remoteSendQueue)
962
						{
963
							remoteSendQueue.Enqueue(cmd);
964
							remoteSendEvent.Set();
965
						}
966
						ContinueRemoteReceive();
967
					}
968
				}
969
				catch(Exception ex)
970
				{
971
					tcp.SendLog(LogLevel.Warning, ex);
972
					Cancel();
973
				}
974
			}
975
 
976
			private void RemoteSendProc()
977
			{
1122 dev 978
				try
1092 dev 979
				{
1122 dev 980
					while(true)
1092 dev 981
					{
982
						SendCommand cmd;
983
 
1122 dev 984
						if(remoteSendQueue.Count == 0)
1092 dev 985
						{
986
							remoteSendEvent.WaitOne();
987
						}
988
 
1122 dev 989
						lock(remoteSendQueue)
1092 dev 990
						{
991
							remoteSendEvent.Reset();
992
							cmd = (SendCommand)remoteSendQueue.Dequeue();
993
						}
994
 
995
						if(cmd.cmdType == SendCommandType.Reset) // reset marker
996
						{
997
							tcp.SendLog(LogLevel.Debug, string.Format("Send reset to local end"));
998
							lock(localSocket)
999
							{
1000
								if(localSocket.Connected) localSocket.Close();
1001
								tcp.SetLocalState(SocketState.Closed);
1002
							}
1003
 
1004
							break; // no more send allowed
1005
						}
1006
						else if(cmd.cmdType == SendCommandType.Shutdown) // shutdown marker
1007
						{
1008
							tcp.SendLog(LogLevel.Debug, string.Format("Send shutdown to local end"));
1009
							localSocket.Shutdown(SocketShutdown.Send);
1010
							tcp.SetLocalState(SocketState.ShutdownSend);
1011
							localSocketSendShutdown = true;
1012
							CheckLocalSocket();
1013
 
1014
							break; // no more send allowed
1015
						}
1016
						else
1017
						{
1018
							// store received bytes
1019
							tcp.Append(TcpMessageDirection.Remote, cmd.buffer, cmd.length);
1020
 
1021
							// forward it
1022
							tcp.SendLog(LogLevel.Debug, string.Format("Send {0} bytes to local end", cmd.length));
1023
							localSocket.Send(cmd.buffer, cmd.length, SocketFlags.None);
1024
						}
1025
					}
1026
				}
1027
				catch(ThreadAbortException)
1028
				{
1029
				}
1030
				catch(Exception ex)
1031
				{
1032
					tcp.SendLog(LogLevel.Warning, ex);
1033
					Cancel();
1034
				}
1035
			}
1036
 
1037
			public void Cancel()
1038
			{
1039
				tcp.SendLog(LogLevel.Important, "Connection canceled");
1040
 
1122 dev 1041
				try
1092 dev 1042
				{
1043
					if(localSendThread != null  && localSendThread.IsAlive)  localSendThread.Abort();
1044
					if(remoteSendThread != null && remoteSendThread.IsAlive) remoteSendThread.Abort();
1045
 
1046
					// close sockets
1122 dev 1047
					try
1092 dev 1048
					{
1122 dev 1049
						if(localSocket != null)
1092 dev 1050
						{
1051
							lock(localSocket)
1052
							{
1053
								if(localSocket.Connected) localSocket.Close();
1054
							}
1055
						}
1056
					}
1057
					catch(Exception ex) // in any case we want to close the socket
1058
					{
1059
						tcp.SendLog(LogLevel.Warning, ex);
1060
					}
1061
					tcp.SetLocalState(SocketState.Closed);
1062
 
1122 dev 1063
					try
1092 dev 1064
					{
1065
						if(remoteSocket != null)
1066
						{
1067
							lock(remoteSocket)
1068
							{
1069
								if(remoteSocket.Connected) remoteSocket.Close();
1070
							}
1071
						}
1072
					}
1073
					catch(Exception ex) // in any case we want to close the socket
1074
					{
1075
						tcp.SendLog(LogLevel.Warning, ex);
1076
					}
1077
					tcp.SetRemoteState(SocketState.Closed);
1078
 
1079
					// return
1080
					tcp.OnClose(new TcpEventArgs());
1081
				}
1082
				catch(Exception ex)
1083
				{
1084
					tcp.SendLog(LogLevel.Warning, ex);
1085
				}
1086
			}
1087
		}
1088
	}
1089
 
1090
	internal class HttpParser
1091
	{
1092
		private enum HttpCharType
1093
		{
1094
			None,
1095
			Control,
1096
			Digit,
1097
			UpAlpha,
1098
			LoAlpha,
1099
			NonChar,
1100
			Separator,
1101
			CrLf,
1102
		}
1103
 
1104
		private static HttpCharType[] charTypes  = null;
1105
		private static bool[]         tokenChars = null;
1122 dev 1106
		private static char[]         charValues = {
1107
																								 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
1108
																								 '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
1109
																								 ' ',  '!',  '"',  '#',  '$',  '%',  '&',  '\'', '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
1110
																								 '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
1092 dev 1111
																								 '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
1122 dev 1112
																								 'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',  'Z',  '[',  '\\', ']',  '^',  '_',
1113
																								 '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
1092 dev 1114
																								 'p',  'q',  'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z',  '{',  '|',  '}',  '~',  '\0'
1115
																							 };
1116
 
1117
		private static void InitTables()
1118
		{
1119
			if(charTypes != null) return;
1120
 
1121
			// main table
1122
			charTypes = new HttpCharType[256];
1123
 
1124
			for(int i = 0; i < charTypes.Length; i++) charTypes[i] = HttpCharType.None;
1125
 
1126
			for(int i = 0; i <= 31; i++) charTypes[i] = HttpCharType.Control;
1127
			charTypes[127] = HttpCharType.Control;   // <del>
1128
 
1129
			for(int i = 48; i <= 57; i++) charTypes[i] = HttpCharType.Digit;
1130
 
1131
			for(int i = 65; i <=  90; i++) charTypes[i] = HttpCharType.UpAlpha;
1132
			for(int i = 97; i <= 122; i++) charTypes[i] = HttpCharType.LoAlpha;
1133
 
1134
			for(int i = 128; i < charTypes.Length; i++) charTypes[i] = HttpCharType.NonChar;
1135
 
1136
			charTypes[ 40] = HttpCharType.Separator;   // (
1137
			charTypes[ 41] = HttpCharType.Separator;   // )
1138
			charTypes[ 60] = HttpCharType.Separator;   // <
1139
			charTypes[ 62] = HttpCharType.Separator;   // >
1140
			charTypes[ 64] = HttpCharType.Separator;   // @
1141
			charTypes[ 44] = HttpCharType.Separator;   // ,
1142
			charTypes[ 59] = HttpCharType.Separator;   // ;
1143
			charTypes[ 58] = HttpCharType.Separator;   // :
1144
			charTypes[ 92] = HttpCharType.Separator;   // \
1145
			charTypes[ 34] = HttpCharType.Separator;   // "
1146
			charTypes[ 47] = HttpCharType.Separator;   // /
1147
			charTypes[ 91] = HttpCharType.Separator;   // [
1148
			charTypes[ 93] = HttpCharType.Separator;   // ]
1149
			charTypes[ 63] = HttpCharType.Separator;   // ?
1150
			charTypes[ 61] = HttpCharType.Separator;   // =
1151
			charTypes[123] = HttpCharType.Separator;   // {
1152
			charTypes[125] = HttpCharType.Separator;   // }
1153
			charTypes[ 32] = HttpCharType.Separator;   // <space>
1154
			charTypes[  9] = HttpCharType.Separator;   // <tab>
1155
 
1156
			charTypes[ 13] = HttpCharType.CrLf;        // <CR>
1157
			charTypes[ 10] = HttpCharType.CrLf;        // <LF>
1158
 
1159
			// token table
1160
			tokenChars = new bool[256];
1122 dev 1161
			for(int i = 0; i < tokenChars.Length; i++)
1092 dev 1162
			{
1122 dev 1163
				tokenChars[i] = !(charTypes[i] == HttpCharType.NonChar
1092 dev 1164
					|| charTypes[i] == HttpCharType.Control || charTypes[i] == HttpCharType.Separator
1165
					|| charTypes[i] == HttpCharType.CrLf);
1166
			}
1167
		}
1168
 
1169
		private class ParsePosition
1170
		{
1171
			private TcpConnection  messages;
1172
			private IEnumerator    messagesEnum;
1173
			private TcpMessage     tcp    = null;
1174
			private int            tcpPos;
1175
			private int            tcpLen;
1176
			private bool           tcpEnd = false;
1177
			private AutoResetEvent newMessageEvent;
1178
			private AutoResetEvent nextMessageEvent;
1179
 
1180
			public ParsePosition(TcpConnection messages, AutoResetEvent nextMessageEvent)
1181
			{
1182
				this.messages         = messages;
1183
				this.messagesEnum     = messages.Messages.GetEnumerator();
1184
				this.newMessageEvent  = new AutoResetEvent(false);
1185
				this.nextMessageEvent = nextMessageEvent;
1186
			}
1187
 
1188
			public AutoResetEvent NewMessageEvent
1189
			{
1190
				get { return newMessageEvent; }
1191
			}
1192
 
1193
			public bool IsEnd
1194
			{
1195
				get { return tcpEnd; }
1196
			}
1197
 
1198
			public TcpMessage CurrentMessage
1199
			{
1200
				get { return tcp; }
1201
			}
1202
 
1203
			private bool MoveNext()
1204
			{
1122 dev 1205
				for(bool moved = false; !moved; )
1092 dev 1206
				{
1207
					lock(messages)
1208
					{
1209
						newMessageEvent.Reset();
1210
						moved = messagesEnum.MoveNext();
1211
					}
1212
 
1213
					if(moved) break;
1214
 
1122 dev 1215
					if(!newMessageEvent.WaitOne())
1092 dev 1216
						throw new Exception("Cannot get next TCP message");
1217
 
1218
					lock(messages)
1219
					{
1220
						if(messages.LocalState == SocketState.Closed && messages.RemoteState == SocketState.Closed)
1221
							return false;
1222
 
1223
						moved = messagesEnum.MoveNext();
1224
					}
1225
				}
1226
 
1227
				return true;
1228
			}
1229
 
1230
			private void NextTcp()
1231
			{
1232
				if(tcpEnd) return;
1233
 
1234
				TcpMessage newTcp = null;
1235
 
1122 dev 1236
				do
1092 dev 1237
				{
1238
					if(!MoveNext())
1239
					{
1240
						tcpEnd = true;
1241
						break;
1242
					}
1243
					newTcp = (TcpMessage)messagesEnum.Current;
1244
				}
1245
				while(tcp != null && tcp.Direction != newTcp.Direction);
1246
 
1247
				if(!tcpEnd)
1248
				{
1249
					tcp    = newTcp;
1250
					tcpLen = tcp.Length;
1251
					tcpPos = 0;
1252
					if(nextMessageEvent != null) nextMessageEvent.Set();
1253
				}
1254
			}
1255
 
1256
			public byte CurrentOctet()
1257
			{
1258
				if(tcp == null || tcpPos >= tcpLen) NextTcp();
1259
				if(tcpEnd) return 0;
1260
 
1261
				return tcp.Bytes[tcpPos];
1262
			}
1263
 
1264
			public byte NextOctet()
1265
			{
1266
				tcpPos++;
1267
 
1268
				if(tcp == null || tcpPos >= tcpLen) NextTcp();
1269
				if(tcpEnd) return 0;
1270
 
1271
				return tcp.Bytes[tcpPos];
1272
			}
1273
 
1274
			public void SetDirection(TcpMessageDirection direction)
1275
			{
1122 dev 1276
				do
1092 dev 1277
				{
1278
					if(!MoveNext())
1279
					{
1280
						tcp    = null;
1281
						tcpEnd = true;
1282
						break;
1283
					}
1284
					tcp = (TcpMessage)messagesEnum.Current;
1285
				}
1286
				while(tcp.Direction != direction);
1287
 
1288
				if(!tcpEnd)
1289
				{
1290
					tcpLen = tcp.Length;
1291
					tcpPos = 0;
1292
				}
1293
			}
1294
		}
1295
 
1296
		private TcpConnection  messages;
1297
		private AutoResetEvent requestEvent     = new AutoResetEvent(false); // new request found
1298
		private AutoResetEvent nextMessageEvent = new AutoResetEvent(false); // request goes to next TCP message
1299
		private LinkedList     https            = new LinkedList();
1300
		private ParsePosition  requestPos;
1301
		private ParsePosition  responsePos;
1302
		private AutoResetEvent newMessageEvent  = new AutoResetEvent(false); // new TCP message available
1303
		private Thread         runThread;
1304
 
1305
		public static HttpParser Parse(TcpConnection messages)
1306
		{
1307
			HttpParser parser = new HttpParser(messages);
1308
			parser.runThread = new Thread(new ThreadStart(parser.Run));
1309
			parser.RunThread.Name = "HttpParser.Run";
1310
			parser.runThread.Start();
1311
 
1312
			return parser;
1313
		}
1314
 
1315
		public void NewMessageArived()
1316
		{
1317
			requestPos.NewMessageEvent.Set();
1318
			responsePos.NewMessageEvent.Set();
1319
		}
1320
 
1321
		public Thread RunThread
1322
		{
1323
			get { return runThread; }
1324
		}
1325
 
1326
		private HttpParser(TcpConnection messages)
1327
		{
1328
			this.messages = messages;
1329
			InitTables();
1330
		}
1331
 
1332
		/// <summary>
1333
		/// Try to recognize the stored TCP packets as sequence of HTTP messages (request-response)
1334
		/// </summary>
1335
		private void Run()
1336
		{
1337
			Thread responseThread = null;
1338
 
1122 dev 1339
			try
1092 dev 1340
			{
1341
				requestPos  = new ParsePosition(messages, nextMessageEvent);
1342
				responsePos = new ParsePosition(messages, null);
1343
 
1344
				responseThread = new Thread(new ThreadStart(MatchResponses));
1345
				responseThread.Name = "HttpParser.MatchResponses";
1346
				responseThread.Start();
1347
 
1348
				// find requests
1122 dev 1349
				while(!requestPos.IsEnd)
1092 dev 1350
				{
1351
					HttpMessage http = new HttpMessage();
1122 dev 1352
					lock(https)
1092 dev 1353
					{
1354
						https.Add(http);
1355
						requestEvent.Set(); // new request available
1356
					}
1357
 
1358
					messages.AddHttpMessage(http);
1359
					SkipEmptyLines(requestPos);
1360
 
1361
					ParseRequestLine(requestPos, http);
1362
					http.UpdateHttpMessage();
1363
 
1364
					ParseHeaders(requestPos, http, true);
1365
					SetRequestProperties(http);
1366
					http.UpdateHttpMessage();
1122 dev 1367
 
1092 dev 1368
					bool fullLength = ParseBody(requestPos, http, true);
1369
					if("text" == http.RequestContentType && "xml" == http.RequestContentSubtype)
1370
					{
1371
						http.RequestXml = new XmlMessage(http.RequestText);
1372
					}
1373
					http.UpdateHttpMessage();
1374
 
1375
					if(fullLength) requestPos.NextOctet();
1376
					http.RequestComplete = true;
1377
					http.UpdateHttpMessage();
1378
 
1379
					SkipEmptyLines(requestPos);
1380
				}
1381
 
1382
				responseThread.Join();
1383
			}
1122 dev 1384
			catch(Exception ex)
1092 dev 1385
			{
1386
				Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
1387
				if(responseThread != null) responseThread.Abort();
1388
			}
1389
		}
1390
 
1391
		private void MatchResponses()
1392
		{
1122 dev 1393
			try
1092 dev 1394
			{
1395
				IEnumerator httpEnum = https.GetEnumerator();
1396
 
1397
				if(!nextMessageEvent.WaitOne()) throw new Exception("Cannot get first message of request");
1398
 
1122 dev 1399
				responsePos.SetDirection(requestPos.CurrentMessage.Direction == TcpMessageDirection.Local
1092 dev 1400
					? TcpMessageDirection.Remote : TcpMessageDirection.Local);
1122 dev 1401
 
1402
				while(!responsePos.IsEnd)
1092 dev 1403
				{
1404
					bool moved;
1405
 
1406
					lock(https)
1407
					{
1408
						requestEvent.Reset();
1409
						moved = httpEnum.MoveNext();
1410
					}
1411
 
1122 dev 1412
					if(!moved)
1092 dev 1413
					{
1122 dev 1414
						if(!requestEvent.WaitOne())
1092 dev 1415
							throw new Exception("Cannot get next request");
1416
 
1417
						lock(https)
1418
						{
1122 dev 1419
							if(!httpEnum.MoveNext())
1092 dev 1420
								throw new Exception("Tried to find response by no HTTP message available");
1421
						}
1422
					}
1423
 
1424
					HttpMessage http = (HttpMessage)httpEnum.Current;
1425
 
1426
					ParseResponseLine(responsePos, http);
1427
					http.UpdateHttpMessage();
1428
 
1429
					ParseHeaders(responsePos, http, false);
1430
					SetResponseProperties(http);
1431
					http.UpdateHttpMessage();
1432
 
1433
					bool fullLength = ParseBody(responsePos, http, false);
1434
					if("text" == http.ResponseContentType && "xml" == http.ResponseContentSubtype)
1435
					{
1436
						http.ResponseXml = new XmlMessage(http.ResponseText);
1437
					}
1438
					http.UpdateHttpMessage();
1439
 
1440
					if(fullLength) responsePos.NextOctet();
1441
					http.ResponseComplete = true;
1442
					http.UpdateHttpMessage();
1443
				}
1444
			}
1122 dev 1445
			catch(Exception ex)
1092 dev 1446
			{
1447
				Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
1448
			}
1449
		}
1450
 
1451
		private string GetToken(ParsePosition pos, int limit)
1452
		{
1453
			StringBuilder res = new StringBuilder(100);
1454
			int           len = 0;
1455
 
1456
			for(byte b = pos.CurrentOctet(); !pos.IsEnd && tokenChars[b]; b = pos.NextOctet())
1457
			{
1458
				res.Append(charValues[b]);
1459
				if(limit > 0 && limit < ++len) return null; // length limit
1460
			}
1461
 
1462
			return res.ToString();
1463
		}
1464
 
1465
		private string GetUntilSpace(ParsePosition pos, int limit)
1466
		{
1467
			StringBuilder res = new StringBuilder(1024);
1468
			int           len = 0;
1469
 
1470
			for(byte b = pos.CurrentOctet(); !pos.IsEnd && b != 32 && b != 13 && b != 10; b = pos.NextOctet())
1471
			{                                     //     <space>    <cr>       <lf>
1472
				res.Append(charValues[b]);
1473
				if(limit > 0 && limit < ++len) return null; // length limit
1474
			}
1475
 
1122 dev 1476
			return res.ToString();
1092 dev 1477
		}
1478
 
1479
		private string GetUntilEoL(ParsePosition pos, int limit)
1480
		{
1481
			StringBuilder res = new StringBuilder(1024);
1482
			int           len = 0;
1483
 
1484
			for(byte b = pos.CurrentOctet(); !pos.IsEnd && b != 13; b = pos.NextOctet())
1485
			{                                     //     <cr>
1486
				res.Append(charValues[b]);
1487
				if(limit > 0 && limit < ++len) return null; // length limit
1488
			}
1489
 
1122 dev 1490
			return res.ToString();
1092 dev 1491
		}
1492
 
1493
		private void ExpectSpace(ParsePosition pos)
1494
		{
1495
			if(pos.IsEnd || pos.CurrentOctet() != 32)
1496
				throw new HttpParseException("Space expected");
1497
 
1498
			pos.NextOctet();
1499
		}
1500
 
1501
		private void ExpectCRLF(ParsePosition pos)
1502
		{
1503
			if(pos.IsEnd || pos.CurrentOctet() != 13)
1504
				throw new HttpParseException("Carriage return expected");
1505
			if(pos.IsEnd || pos.NextOctet() != 10)
1506
				throw new HttpParseException("Linefeed expected");
1507
 
1508
			pos.NextOctet();
1509
		}
1510
 
1511
		private void SkipEmptyLines(ParsePosition pos)
1512
		{
1122 dev 1513
			while(pos.CurrentOctet() == 13)
1092 dev 1514
				ExpectCRLF(pos);
1515
		}
1516
 
1517
		private void ParseRequestLine(ParsePosition pos, HttpMessage http)
1518
		{
1519
			// method
1520
			http.RequestMethod = GetToken(pos, 1024);
1521
			if(http.RequestMethod == null || http.RequestMethod.Length == 0)
1522
				throw new HttpParseException("Request method name expected");
1523
			ExpectSpace(pos);
1524
 
1525
			// URI
1526
			http.RequestUri = GetUntilSpace(pos, 1024);
1527
			if(http.RequestUri == null || http.RequestUri.Length == 0)
1528
				throw new HttpParseException("Request URI expected");
1529
 
1530
			if(pos.IsEnd)
1531
				throw new HttpParseException("Unexpected end of message");
1532
 
1533
			// EoL or version
1534
			byte b = pos.CurrentOctet();
1122 dev 1535
			if(b == 13)
1092 dev 1536
			{
1122 dev 1537
				if(pos.IsEnd || pos.NextOctet() != 10)
1092 dev 1538
				{
1539
					throw new HttpParseException("Linefeed expected");
1540
				}
1122 dev 1541
				else
1092 dev 1542
				{
1543
					if(!pos.IsEnd) ExpectCRLF(pos);
1544
					http.RequestVersion = HttpVersion.V0_9;
1545
					return;
1546
				}
1547
			}
1122 dev 1548
			else if(b != 32)
1092 dev 1549
			{
1550
				throw new HttpParseException("HTTP version expected");
1551
			}
1552
			pos.NextOctet();
1553
 
1554
			// check version
1555
			string versionStr = GetUntilEoL(pos, 20);
1556
			if(pos.IsEnd || versionStr == null || versionStr.Length == 0)
1557
				throw new HttpParseException("HTTP version expected");
1558
 
1122 dev 1559
			if(versionStr == "HTTP/1.0")
1092 dev 1560
			{
1561
				http.RequestVersion = HttpVersion.V1_0;
1562
			}
1122 dev 1563
			else if(versionStr == "HTTP/1.1")
1092 dev 1564
			{
1565
				http.RequestVersion = HttpVersion.V1_1;
1566
			}
1122 dev 1567
			else
1092 dev 1568
			{
1569
				throw new HttpParseException("Unknown HTTP version: " + versionStr);
1570
			}
1571
 
1572
			ExpectCRLF(pos);
1573
		}
1574
 
1575
		private void ParseHeaders(ParsePosition pos, HttpMessage http, bool request)
1576
		{
1577
			if(pos.IsEnd) return;  // end of TCP messages
1578
 
1122 dev 1579
			while(true)
1092 dev 1580
			{
1581
				if(pos.IsEnd)
1582
					throw new HttpParseException("Unexpected end of message");
1583
 
1122 dev 1584
				if(pos.CurrentOctet() == 13)
1092 dev 1585
				{
1122 dev 1586
					if(pos.IsEnd || pos.NextOctet() != 10)
1092 dev 1587
					{
1588
						throw new HttpParseException("Linefeed expected");
1589
					}
1122 dev 1590
					else
1092 dev 1591
					{
1592
						pos.NextOctet(); // end of header, move to body
1593
						return;
1594
					}
1595
				}
1596
				if(pos.IsEnd) return;  // end of TCP messages
1597
 
1598
				string name = GetToken(pos, 0);
1599
				if(name == null || name.Length == 0)
1600
					throw new HttpParseException("Request header name expected");
1601
 
1602
				if(pos.IsEnd || pos.CurrentOctet() != 58)   // :
1603
					throw new HttpParseException("Request header value expected");
1604
 
1605
				pos.NextOctet();
1606
				string s = TrimHeaderValue(GetUntilEoL(pos, 0));
1607
 
1608
				ExpectCRLF(pos);
1609
 
1610
				if(request)
1611
					http.AddRequestHeader(name, s);
1612
				else
1613
					http.AddResponseHeader(name, s);
1614
			}
1615
		}
1616
 
1617
		enum HeaderValueState
1618
		{
1619
			Space,
1620
			Token,
1621
			Quoted
1622
		}
1623
 
1624
		private string TrimHeaderValue(string s)
1625
		{
1626
			if(s == null) return null;
1627
 
1628
			HeaderValueState state = HeaderValueState.Space;
1629
			StringBuilder    buf   = new StringBuilder();
1630
 
1122 dev 1631
			for(int i = 0, l = s.Length; i < l; i++)
1092 dev 1632
			{
1633
				char c = s[i];
1122 dev 1634
				switch(state)
1092 dev 1635
				{
1636
					case HeaderValueState.Space:
1637
						if(c != ' ' && c != '\t')
1638
						{
1122 dev 1639
							if(c == '"')
1092 dev 1640
							{
1641
								if(buf.Length > 0) buf.Append(' ');
1642
								buf.Append(c);
1643
								state =	HeaderValueState.Quoted;
1644
							}
1122 dev 1645
							else
1092 dev 1646
							{
1647
								if(buf.Length > 0) buf.Append(' ');
1648
								buf.Append(c);
1649
								state =	HeaderValueState.Token;
1650
							}
1651
						}
1652
						break;
1653
 
1654
					case HeaderValueState.Token:
1122 dev 1655
						if(c == ' ' || c == '\t')
1092 dev 1656
						{
1657
							state =	HeaderValueState.Space;
1658
						}
1122 dev 1659
						else if(c == '"')
1092 dev 1660
						{
1661
							buf.Append(c);
1662
							state =	HeaderValueState.Quoted;
1663
						}
1122 dev 1664
						else
1092 dev 1665
						{
1666
							buf.Append(c);
1667
						}
1668
						break;
1669
 
1670
					case HeaderValueState.Quoted:
1122 dev 1671
						if(c == '"')
1092 dev 1672
						{
1673
							buf.Append(c);
1674
							i++;
1675
							if(i < l)
1122 dev 1676
							{
1092 dev 1677
								c = s[i];
1122 dev 1678
								if(c == ' ' || c == '\t')
1092 dev 1679
								{
1680
									state =	HeaderValueState.Space;
1681
								}
1122 dev 1682
								else if(c == '"')
1092 dev 1683
								{
1684
									buf.Append(c);
1685
									state =	HeaderValueState.Quoted;
1686
								}
1687
								else
1688
								{
1689
									buf.Append(c);
1690
									state =	HeaderValueState.Token;
1691
								}
1692
							}
1693
						}
1122 dev 1694
						else
1092 dev 1695
						{
1696
							buf.Append(c);
1697
						}
1698
						break;
1699
				}
1700
			}
1701
 
1702
			return buf.ToString();
1703
		}
1704
 
1705
		private HttpEncoding ParseEncoding(string encoding)
1706
		{
1122 dev 1707
			if(encoding == null || encoding == "identity")
1092 dev 1708
				return HttpEncoding.Identify;
1122 dev 1709
			else if(encoding == "gzip")
1092 dev 1710
				return HttpEncoding.Gzip;
1122 dev 1711
			else if(encoding == "compress")
1092 dev 1712
				return HttpEncoding.Compress;
1122 dev 1713
			else if(encoding == "deflate")
1092 dev 1714
				return HttpEncoding.Deflate;
1122 dev 1715
			else
1092 dev 1716
				return HttpEncoding.Unknown;
1717
		}
1718
 
1719
		private void SetRequestProperties(HttpMessage http)
1720
		{
1721
			// length
1722
			string contentLength = (string)http.RequestHeadersHash["Content-Length"];
1122 dev 1723
			if(contentLength != null)
1092 dev 1724
			{
1725
				http.RequestLength = int.Parse(contentLength);
1726
			}
1727
 
1728
			// encoding
1729
			http.RequestEncoding = ParseEncoding((string)http.RequestHeadersHash["Content-Encoding"]);
1730
 
1731
			// type & charset
1732
			string contentType = (string)http.RequestHeadersHash["Content-Type"];
1122 dev 1733
			if(contentType != null)
1092 dev 1734
			{
1735
				Match match = Regex.Match(contentType, @"^\s*(\S+)/(\S+)\s*($|;\s*(charset=""?(\S+)""?)?)");
1122 dev 1736
				if(match.Success)
1092 dev 1737
				{
1738
					http.RequestContentType    = match.Groups[1].Captures[0].Value;
1739
					http.RequestContentSubtype = match.Groups[2].Captures[0].Value;
1740
					if(match.Groups.Count >= 6) http.RequestCharset = match.Groups[5].Captures[0].Value.Trim('"');
1741
				}
1742
			}
1743
 
1744
			// soap action
1745
			string soapAction = (string)http.RequestHeadersHash["soapaction"];
1122 dev 1746
			if(soapAction != null)
1092 dev 1747
			{
1748
				http.SoapAction = soapAction.Trim('"');
1749
			}
1750
		}
1751
 
1752
		private bool ParseBody(ParsePosition pos, HttpMessage http, bool request)
1753
		{
1754
			if(request && http.RequestMethod != "POST") return false;
1755
 
1756
			// FIXME parse and save on-the-fly, dont wait util end of message
1757
 
1758
			byte[]       bin            = new byte[8*1024];
1759
			int          len            = 0;
1760
			int          limit          = (request ? http.RequestLength         : http.ResponseLength);
1761
			HttpEncoding encoding       = (request ? http.RequestEncoding       : http.ResponseEncoding);
1762
			string       contentType    = (request ? http.RequestContentType    : http.ResponseContentType);
1763
			string       contentSubtype = (request ? http.RequestContentSubtype : http.ResponseContentSubtype);
1764
			string       charset        = (request ? http.RequestCharset        : http.ResponseCharset);
1765
 
1766
			for(byte b = pos.CurrentOctet(); !pos.IsEnd; b = pos.NextOctet())
1767
			{
1122 dev 1768
				if(len >= bin.Length)
1092 dev 1769
				{
1770
					byte[] newBin = new byte[bin.Length*2];
1771
					Array.Copy(bin, newBin, len);
1772
					bin = newBin;
1773
				}
1774
 
1775
				bin[len++] = b;
1776
				if(limit > 0 && limit <= len)  // full length
1777
				{
1778
					break;
1779
				}
1780
			}
1781
 
1782
			string text = null;
1122 dev 1783
			if(encoding == HttpEncoding.Identify && contentType == "text")
1092 dev 1784
			{
1122 dev 1785
				try
1092 dev 1786
				{
1787
					Encoding enc = Encoding.GetEncoding(charset == null ? (contentSubtype == "xml" ? "UTF-8" : "ASCII") : charset);
1788
					text = enc.GetString(bin, 0, len);
1789
				}
1122 dev 1790
				catch(NotSupportedException)
1092 dev 1791
				{
1792
					Console.WriteLine("Unsupported encoding: " + charset);
1793
				}
1794
			}
1795
 
1122 dev 1796
			if(request)
1092 dev 1797
			{
1798
				http.RequestLength = len;
1799
				http.RequestBody   = bin;
1800
				http.RequestText   = text;
1801
			}
1122 dev 1802
			else
1092 dev 1803
			{
1804
				http.ResponseLength = len;
1805
				http.ResponseBody   = bin;
1806
				http.ResponseText   = text;
1807
			}
1808
 
1809
			return (limit > 0 && limit <= len);  // full length reached, need to go to next octet
1810
		}
1811
 
1812
		private void ParseResponseLine(ParsePosition pos, HttpMessage http)
1813
		{
1814
			// version
1815
			string versionStr = GetUntilSpace(pos, 20);
1816
			if(pos.IsEnd || versionStr == null || versionStr.Length == 0)
1817
				throw new HttpParseException("HTTP version expected");
1818
 
1122 dev 1819
			if(versionStr == "HTTP/1.0")
1092 dev 1820
			{
1821
				http.ResponseVersion = HttpVersion.V1_0;
1822
			}
1122 dev 1823
			else if(versionStr == "HTTP/1.1")
1092 dev 1824
			{
1825
				http.ResponseVersion = HttpVersion.V1_1;
1826
			}
1122 dev 1827
			else
1092 dev 1828
			{
1829
				throw new HttpParseException("Unknown HTTP version: " + versionStr);
1830
			}
1831
			ExpectSpace(pos);
1832
 
1833
			// status code
1834
			string code = GetToken(pos, 3);
1835
			if(code == null || code.Length != 3)
1836
				throw new HttpParseException("Status code expected");
1837
 
1122 dev 1838
			try
1092 dev 1839
			{
1840
				int c = int.Parse(code);
1841
				if(c < 100 || c >= 1000) throw new HttpParseException("Status code expected");
1842
				http.ResponseStatusCode = c;
1843
			}
1122 dev 1844
			catch(FormatException)
1092 dev 1845
			{
1846
				throw new HttpParseException("Status code expected");
1847
			}
1848
			ExpectSpace(pos);
1849
 
1850
			// status message
1851
			http.ResponseStatusMessage = GetUntilEoL(pos, 0);
1122 dev 1852
 
1092 dev 1853
			if(pos.IsEnd)
1854
				throw new HttpParseException("Unexpected end of message");
1855
 
1856
			ExpectCRLF(pos);
1857
		}
1858
 
1859
		private void SetResponseProperties(HttpMessage http)
1860
		{
1861
			// length
1862
			HttpHeader contentLength = (HttpHeader)http.ResponseHeadersHash["Content-Length"];
1122 dev 1863
			if(contentLength != null)
1092 dev 1864
			{
1865
				http.ResponseLength = int.Parse(contentLength.Values[0]);
1866
			}
1867
 
1868
			// encoding
1869
			HttpHeader contentEncoding = (HttpHeader)http.ResponseHeadersHash["Content-Encoding"];
1870
			http.ResponseEncoding = ParseEncoding((contentEncoding == null) ? null : contentEncoding.Values[0]);
1871
 
1872
			// type & charset
1873
			HttpHeader contentType = (HttpHeader)http.ResponseHeadersHash["Content-Type"];
1122 dev 1874
			if(contentType != null)
1092 dev 1875
			{
1876
				Match match = Regex.Match(contentType.Values[0], @"^\s*(\S+)/(\S+)\s*($|;\s*(charset=""?(\S+)""?)?)");
1122 dev 1877
				if(match.Success)
1092 dev 1878
				{
1879
					http.ResponseContentType    = match.Groups[1].Captures[0].Value;
1880
					http.ResponseContentSubtype = match.Groups[2].Captures[0].Value;
1122 dev 1881
					if(match.Groups.Count >= 6 && match.Groups[5].Captures.Count > 0)
1092 dev 1882
						http.ResponseCharset = match.Groups[5].Captures[0].Value.Trim('"');
1883
				}
1884
			}
1885
		}
1886
	}
1887
 
1888
	public class HttpHeader
1889
	{
1890
		private string   name;
1891
		private string[] headerValues;
1892
 
1893
		public string Name
1894
		{
1895
			get { return name; }
1896
		}
1897
 
1898
		public string[] Values
1899
		{
1900
			get { return headerValues; }
1901
		}
1902
 
1903
		internal HttpHeader()
1904
		{
1905
		}
1906
 
1907
		internal HttpHeader(string name, string headerValue)
1908
		{
1909
			this.name = name;
1910
			AddValue(headerValue);
1911
		}
1912
 
1913
		internal void AddValue(string value)
1914
		{
1915
			if(headerValues == null)
1916
			{
1917
				headerValues = new string[1];
1918
			}
1122 dev 1919
			else
1092 dev 1920
			{
1921
				string[] newValues = new string[headerValues.Length + 1];
1922
				Array.Copy(headerValues, 0, newValues, 0, headerValues.Length);
1923
				headerValues = newValues;
1924
			}
1925
 
1926
			headerValues[headerValues.Length-1] = value;
1927
		}
1928
	}
1929
 
1930
	public class HttpMessage
1931
	{
1932
		private bool         requestComplete     = false;
1933
		private HttpVersion  requestVersion;
1934
		private string       requestMethod;
1935
		private string       requestUri;
1936
		private LinkedList   requestHeaders      = new LinkedList();
1937
		private Hashtable    requestHeadersHash  = new Hashtable();
1938
		private int          requestLength       = -1; // -1 == unknown
1939
		private HttpEncoding requestEncoding     = HttpEncoding.Identify;
1940
		private string       requestContentType;
1941
		private string       requestContentSubtype;
1942
		private string       requestCharset;
1943
		private string       soapAction;
1944
		private byte[]       requestBody;
1945
		private string       requestText;
1946
		private XmlMessage   requestXml;
1947
 
1948
		private bool         responseComplete    = false;
1949
		private HttpVersion  responseVersion;
1950
		private int          responseStatusCode;
1951
		private string       responseStatusMessage;
1952
		private LinkedList   responseHeaders     = new LinkedList();
1953
		private Hashtable    responseHeadersHash = new Hashtable();
1954
		private int          responseLength      = -1; // -1 == unknown
1955
		private HttpEncoding responseEncoding    = HttpEncoding.Identify;
1956
		private string       responseContentType;
1957
		private string       responseContentSubtype;
1958
		private string       responseCharset;
1959
		private byte[]       responseBody;
1960
		private string       responseText;
1961
		private XmlMessage   responseXml;
1962
 
1963
		public bool RequestComplete
1964
		{
1965
			get { return requestComplete; }
1966
			set { requestComplete = value; }
1967
		}
1968
 
1969
		public HttpVersion RequestVersion
1970
		{
1971
			get { return requestVersion; }
1972
			set { requestVersion = value; }
1973
		}
1974
 
1975
		public string RequestMethod
1976
		{
1977
			get { return requestMethod; }
1978
			set { requestMethod = value; }
1979
		}
1980
 
1981
		public string RequestUri
1982
		{
1983
			get { return requestUri; }
1984
			set { requestUri = value; }
1985
		}
1986
 
1987
		public LinkedList RequestHeaders
1988
		{
1989
			get { return requestHeaders; }
1990
		}
1991
 
1992
		public IDictionary RequestHeadersHash
1993
		{
1994
			get { return requestHeadersHash; }
1995
		}
1996
 
1997
		public int RequestLength
1998
		{
1999
			get { return requestLength; }
2000
			set { requestLength = value; }
2001
		}
2002
 
2003
		public HttpEncoding RequestEncoding
2004
		{
2005
			get { return requestEncoding; }
2006
			set { requestEncoding = value; }
2007
		}
2008
 
2009
		public string RequestContentType
2010
		{
2011
			get { return requestContentType; }
2012
			set { requestContentType = value; }
2013
		}
2014
 
2015
		public string RequestContentSubtype
2016
		{
2017
			get { return requestContentSubtype; }
2018
			set { requestContentSubtype = value; }
2019
		}
2020
 
2021
		public string RequestCharset
2022
		{
2023
			get { return requestCharset; }
2024
			set { requestCharset = value; }
2025
		}
2026
 
2027
		public string SoapAction
2028
		{
2029
			get { return soapAction; }
2030
			set { soapAction = value; }
2031
		}
2032
 
2033
		public byte[] RequestBody
2034
		{
2035
			get { return requestBody; }
2036
			set { requestBody = value; }
2037
		}
2038
 
2039
		public string RequestText
2040
		{
2041
			get { return requestText; }
2042
			set { requestText = value; }
2043
		}
2044
 
2045
		public XmlMessage RequestXml
2046
		{
2047
			get { return requestXml; }
2048
			set { requestXml = value; }
2049
		}
2050
 
2051
		public bool ResponseComplete
2052
		{
2053
			get { return responseComplete; }
2054
			set { responseComplete = value; }
2055
		}
2056
 
2057
		public HttpVersion ResponseVersion
2058
		{
2059
			get { return responseVersion; }
2060
			set { responseVersion = value; }
2061
		}
2062
 
2063
		public int ResponseStatusCode
2064
		{
2065
			get { return responseStatusCode; }
2066
			set { responseStatusCode = value; }
2067
		}
2068
 
2069
		public string ResponseStatusMessage
2070
		{
2071
			get { return responseStatusMessage; }
2072
			set { responseStatusMessage = value; }
2073
		}
2074
 
2075
		public LinkedList ResponseHeaders
2076
		{
2077
			get { return responseHeaders; }
2078
		}
2079
 
2080
		public IDictionary ResponseHeadersHash
2081
		{
2082
			get { return responseHeadersHash; }
2083
		}
2084
 
2085
		public int ResponseLength
2086
		{
2087
			get { return responseLength; }
2088
			set { responseLength = value; }
2089
		}
2090
 
2091
		public HttpEncoding ResponseEncoding
2092
		{
2093
			get { return responseEncoding; }
2094
			set { responseEncoding = value; }
2095
		}
2096
 
2097
		public string ResponseContentType
2098
		{
2099
			get { return responseContentType; }
2100
			set { responseContentType = value; }
2101
		}
2102
 
2103
		public string ResponseContentSubtype
2104
		{
2105
			get { return responseContentSubtype; }
2106
			set { responseContentSubtype = value; }
2107
		}
2108
 
2109
		public string ResponseCharset
2110
		{
2111
			get { return responseCharset; }
2112
			set { responseCharset = value; }
2113
		}
2114
 
2115
		public byte[] ResponseBody
2116
		{
2117
			get { return responseBody; }
2118
			set { responseBody = value; }
2119
		}
2120
 
2121
		public string ResponseText
2122
		{
2123
			get { return responseText; }
2124
			set { responseText = value; }
2125
		}
2126
 
2127
		public XmlMessage ResponseXml
2128
		{
2129
			get { return responseXml; }
2130
			set { responseXml = value; }
2131
		}
2132
 
2133
		public void AddRequestHeader(string name, string headerValue)
2134
		{
2135
			requestHeaders.Add(new HttpHeader(name, headerValue));
2136
			requestHeadersHash.Add(name, headerValue);
2137
		}
2138
 
2139
		public void AddResponseHeader(string name, string headerValue)
2140
		{
2141
			HttpHeader header = (HttpHeader)responseHeadersHash[name];
2142
			if(header == null)
2143
			{
2144
				header = new HttpHeader(name, headerValue);
2145
				responseHeaders.Add(header);
2146
				responseHeadersHash.Add(name, header);
2147
			}
2148
			else
2149
			{
2150
				header.AddValue(headerValue);
2151
			}
2152
		}
2153
 
2154
		public override string ToString()  // FIXME delete the method
2155
		{
1122 dev 2156
			return (soapAction != null ? soapAction
1092 dev 2157
				: (requestMethod == null ? "" : requestMethod) + " " + (requestUri == null ? "" : requestUri));
2158
		}
2159
 
2160
		public event TcpEventHandler Update;
2161
 
2162
		protected virtual void OnUpdate(TcpEventArgs e)
2163
		{
1122 dev 2164
			if(Update != null)
1092 dev 2165
			{
1122 dev 2166
				Update(this, e);
1092 dev 2167
			}
2168
		}
2169
 
2170
		internal void UpdateHttpMessage()
2171
		{
2172
			OnUpdate(new TcpEventArgs());
2173
		}
2174
	}
2175
 
2176
	internal class HttpParseException : Exception
2177
	{
2178
		public HttpParseException() : base()
2179
		{
2180
		}
2181
 
2182
		public HttpParseException(string message) : base(message)
2183
		{
2184
		}
2185
 
2186
		public HttpParseException(System.Runtime.Serialization.SerializationInfo info,
2187
			System.Runtime.Serialization.StreamingContext context) : base(info, context)
2188
		{
2189
		}
2190
 
2191
		public HttpParseException(string message, Exception innerException) : base(message, innerException)
2192
		{
2193
		}
2194
	}
2195
 
2196
	public class XmlMessage
2197
	{
2198
		private XmlDocument xml;
2199
		private XmlException parseException;
2200
 
2201
		public XmlDocument Xml
2202
		{
2203
			get { return xml; }
2204
		}
2205
 
2206
		public XmlException ParseException
2207
		{
2208
			get { return parseException; }
2209
		}
2210
 
2211
		internal XmlMessage(string text)
2212
		{
1122 dev 2213
			try
1092 dev 2214
			{
2215
				this.xml = new XmlDocument();
2216
				this.xml.LoadXml(text);
2217
			}
2218
			catch(XmlException ex)
2219
			{
2220
				parseException = ex;
2221
			}
2222
		}
2223
	}
2224
 
2225
	public class TcpMessage
2226
	{
2227
		private TcpMessageDirection direction;
2228
		private byte[]              bytes;
2229
		private int                 length = 0;
2230
		private DateTime            timestamp;
2231
 
2232
		public TcpMessageDirection Direction
2233
		{
2234
			get { return direction; }
2235
			set { direction = value; }
2236
		}
2237
 
2238
		public int Length
2239
		{
2240
			get { return length; }
2241
		}
2242
 
2243
		public byte[] Bytes
2244
		{
1122 dev 2245
			get
1092 dev 2246
			{
2247
				return bytes;
2248
			}
2249
			set
2250
			{
2251
				length = 0;
2252
				Append(value);
2253
			}
2254
		}
2255
 
2256
		public DateTime Timestamp
2257
		{
2258
			get { return timestamp; }
2259
		}
2260
 
2261
		internal TcpMessage()
2262
		{
2263
			this.timestamp = DateTime.Now;
2264
			this.bytes  = new byte[1024];
2265
		}
2266
 
2267
		internal TcpMessage(byte[] bytes, int length)
2268
		{
2269
			this.timestamp = DateTime.Now;
2270
			this.bytes     = new byte[length];
2271
			this.length   = length;
2272
			Array.Copy(this.bytes, bytes, length);
2273
		}
2274
 
2275
		internal TcpMessage Append(byte[] newBytes)
2276
		{
2277
			if(newBytes == null) return this;
2278
 
2279
			return Append(newBytes, newBytes.Length);
2280
		}
2281
 
2282
		internal TcpMessage Append(byte[] newBytes, int length)
2283
		{
2284
			if(newBytes == null) return this;
2285
 
1122 dev 2286
			lock(this)
1092 dev 2287
			{
2288
				// grow array
1122 dev 2289
				if(this.length + length > bytes.Length)
1092 dev 2290
				{
2291
					int newLength = bytes.Length;
2292
					while(this.length + length > newLength) newLength *= 2;
2293
					byte[] newArray = new byte[newLength];
2294
 
2295
					Array.Copy(bytes, newArray, this.length);
2296
					bytes = newArray;
2297
				}
2298
 
2299
				// store received bytes
2300
				Array.Copy(newBytes, 0, bytes, this.length, length);
2301
				this.length += length;
2302
 
2303
				return this;
2304
			}
2305
		}
2306
	}
2307
 
2308
}