Subversion Repositories general

Rev

Rev 1122 | Rev 1199 | 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 = {
1125 dev 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',  ':',  ';',  '<',  '=',  '>',  '?',
1111
			 '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
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',
1114
			 'p',  'q',  'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z',  '{',  '|',  '}',  '~',  '\0'
1115
		 };
1092 dev 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);
1125 dev 1360
                    http.RequestStartTimestamp = requestPos.CurrentMessage.Timestamp;
1092 dev 1361
 
1362
					ParseRequestLine(requestPos, http);
1363
					http.UpdateHttpMessage();
1364
 
1365
					ParseHeaders(requestPos, http, true);
1366
					SetRequestProperties(http);
1367
					http.UpdateHttpMessage();
1122 dev 1368
 
1092 dev 1369
					bool fullLength = ParseBody(requestPos, http, true);
1370
					if("text" == http.RequestContentType && "xml" == http.RequestContentSubtype)
1371
					{
1372
						http.RequestXml = new XmlMessage(http.RequestText);
1373
					}
1374
					http.UpdateHttpMessage();
1375
 
1376
					if(fullLength) requestPos.NextOctet();
1377
					http.RequestComplete = true;
1378
					http.UpdateHttpMessage();
1379
 
1380
					SkipEmptyLines(requestPos);
1381
				}
1382
 
1383
				responseThread.Join();
1384
			}
1122 dev 1385
			catch(Exception ex)
1092 dev 1386
			{
1387
				Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
1388
				if(responseThread != null) responseThread.Abort();
1389
			}
1390
		}
1391
 
1392
		private void MatchResponses()
1393
		{
1122 dev 1394
			try
1092 dev 1395
			{
1396
				IEnumerator httpEnum = https.GetEnumerator();
1397
 
1398
				if(!nextMessageEvent.WaitOne()) throw new Exception("Cannot get first message of request");
1399
 
1122 dev 1400
				responsePos.SetDirection(requestPos.CurrentMessage.Direction == TcpMessageDirection.Local
1092 dev 1401
					? TcpMessageDirection.Remote : TcpMessageDirection.Local);
1122 dev 1402
 
1403
				while(!responsePos.IsEnd)
1092 dev 1404
				{
1405
					bool moved;
1406
 
1407
					lock(https)
1408
					{
1409
						requestEvent.Reset();
1410
						moved = httpEnum.MoveNext();
1411
					}
1412
 
1122 dev 1413
					if(!moved)
1092 dev 1414
					{
1122 dev 1415
						if(!requestEvent.WaitOne())
1092 dev 1416
							throw new Exception("Cannot get next request");
1417
 
1418
						lock(https)
1419
						{
1122 dev 1420
							if(!httpEnum.MoveNext())
1092 dev 1421
								throw new Exception("Tried to find response by no HTTP message available");
1422
						}
1423
					}
1424
 
1425
					HttpMessage http = (HttpMessage)httpEnum.Current;
1426
 
1427
					ParseResponseLine(responsePos, http);
1125 dev 1428
                    http.ResponseStartTimestamp = responsePos.CurrentMessage.Timestamp;
1092 dev 1429
					http.UpdateHttpMessage();
1430
 
1431
					ParseHeaders(responsePos, http, false);
1432
					SetResponseProperties(http);
1433
					http.UpdateHttpMessage();
1434
 
1435
					bool fullLength = ParseBody(responsePos, http, false);
1436
					if("text" == http.ResponseContentType && "xml" == http.ResponseContentSubtype)
1437
					{
1438
						http.ResponseXml = new XmlMessage(http.ResponseText);
1439
					}
1440
					http.UpdateHttpMessage();
1441
 
1442
					if(fullLength) responsePos.NextOctet();
1443
					http.ResponseComplete = true;
1444
					http.UpdateHttpMessage();
1445
				}
1446
			}
1122 dev 1447
			catch(Exception ex)
1092 dev 1448
			{
1449
				Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
1450
			}
1451
		}
1452
 
1453
		private string GetToken(ParsePosition pos, int limit)
1454
		{
1455
			StringBuilder res = new StringBuilder(100);
1456
			int           len = 0;
1457
 
1458
			for(byte b = pos.CurrentOctet(); !pos.IsEnd && tokenChars[b]; b = pos.NextOctet())
1459
			{
1460
				res.Append(charValues[b]);
1461
				if(limit > 0 && limit < ++len) return null; // length limit
1462
			}
1463
 
1464
			return res.ToString();
1465
		}
1466
 
1467
		private string GetUntilSpace(ParsePosition pos, int limit)
1468
		{
1469
			StringBuilder res = new StringBuilder(1024);
1470
			int           len = 0;
1471
 
1472
			for(byte b = pos.CurrentOctet(); !pos.IsEnd && b != 32 && b != 13 && b != 10; b = pos.NextOctet())
1473
			{                                     //     <space>    <cr>       <lf>
1474
				res.Append(charValues[b]);
1475
				if(limit > 0 && limit < ++len) return null; // length limit
1476
			}
1477
 
1122 dev 1478
			return res.ToString();
1092 dev 1479
		}
1480
 
1481
		private string GetUntilEoL(ParsePosition pos, int limit)
1482
		{
1483
			StringBuilder res = new StringBuilder(1024);
1484
			int           len = 0;
1485
 
1486
			for(byte b = pos.CurrentOctet(); !pos.IsEnd && b != 13; b = pos.NextOctet())
1487
			{                                     //     <cr>
1488
				res.Append(charValues[b]);
1489
				if(limit > 0 && limit < ++len) return null; // length limit
1490
			}
1491
 
1122 dev 1492
			return res.ToString();
1092 dev 1493
		}
1494
 
1495
		private void ExpectSpace(ParsePosition pos)
1496
		{
1497
			if(pos.IsEnd || pos.CurrentOctet() != 32)
1498
				throw new HttpParseException("Space expected");
1499
 
1500
			pos.NextOctet();
1501
		}
1502
 
1503
		private void ExpectCRLF(ParsePosition pos)
1504
		{
1505
			if(pos.IsEnd || pos.CurrentOctet() != 13)
1506
				throw new HttpParseException("Carriage return expected");
1507
			if(pos.IsEnd || pos.NextOctet() != 10)
1508
				throw new HttpParseException("Linefeed expected");
1509
 
1510
			pos.NextOctet();
1511
		}
1512
 
1513
		private void SkipEmptyLines(ParsePosition pos)
1514
		{
1122 dev 1515
			while(pos.CurrentOctet() == 13)
1092 dev 1516
				ExpectCRLF(pos);
1517
		}
1518
 
1519
		private void ParseRequestLine(ParsePosition pos, HttpMessage http)
1520
		{
1521
			// method
1522
			http.RequestMethod = GetToken(pos, 1024);
1523
			if(http.RequestMethod == null || http.RequestMethod.Length == 0)
1524
				throw new HttpParseException("Request method name expected");
1525
			ExpectSpace(pos);
1526
 
1527
			// URI
1528
			http.RequestUri = GetUntilSpace(pos, 1024);
1529
			if(http.RequestUri == null || http.RequestUri.Length == 0)
1530
				throw new HttpParseException("Request URI expected");
1531
 
1532
			if(pos.IsEnd)
1533
				throw new HttpParseException("Unexpected end of message");
1534
 
1535
			// EoL or version
1536
			byte b = pos.CurrentOctet();
1122 dev 1537
			if(b == 13)
1092 dev 1538
			{
1122 dev 1539
				if(pos.IsEnd || pos.NextOctet() != 10)
1092 dev 1540
				{
1541
					throw new HttpParseException("Linefeed expected");
1542
				}
1122 dev 1543
				else
1092 dev 1544
				{
1545
					if(!pos.IsEnd) ExpectCRLF(pos);
1546
					http.RequestVersion = HttpVersion.V0_9;
1547
					return;
1548
				}
1549
			}
1122 dev 1550
			else if(b != 32)
1092 dev 1551
			{
1552
				throw new HttpParseException("HTTP version expected");
1553
			}
1554
			pos.NextOctet();
1555
 
1556
			// check version
1557
			string versionStr = GetUntilEoL(pos, 20);
1558
			if(pos.IsEnd || versionStr == null || versionStr.Length == 0)
1559
				throw new HttpParseException("HTTP version expected");
1560
 
1122 dev 1561
			if(versionStr == "HTTP/1.0")
1092 dev 1562
			{
1563
				http.RequestVersion = HttpVersion.V1_0;
1564
			}
1122 dev 1565
			else if(versionStr == "HTTP/1.1")
1092 dev 1566
			{
1567
				http.RequestVersion = HttpVersion.V1_1;
1568
			}
1122 dev 1569
			else
1092 dev 1570
			{
1571
				throw new HttpParseException("Unknown HTTP version: " + versionStr);
1572
			}
1573
 
1574
			ExpectCRLF(pos);
1575
		}
1576
 
1577
		private void ParseHeaders(ParsePosition pos, HttpMessage http, bool request)
1578
		{
1579
			if(pos.IsEnd) return;  // end of TCP messages
1580
 
1122 dev 1581
			while(true)
1092 dev 1582
			{
1583
				if(pos.IsEnd)
1584
					throw new HttpParseException("Unexpected end of message");
1585
 
1122 dev 1586
				if(pos.CurrentOctet() == 13)
1092 dev 1587
				{
1122 dev 1588
					if(pos.IsEnd || pos.NextOctet() != 10)
1092 dev 1589
					{
1590
						throw new HttpParseException("Linefeed expected");
1591
					}
1122 dev 1592
					else
1092 dev 1593
					{
1594
						pos.NextOctet(); // end of header, move to body
1595
						return;
1596
					}
1597
				}
1598
				if(pos.IsEnd) return;  // end of TCP messages
1599
 
1600
				string name = GetToken(pos, 0);
1601
				if(name == null || name.Length == 0)
1602
					throw new HttpParseException("Request header name expected");
1603
 
1604
				if(pos.IsEnd || pos.CurrentOctet() != 58)   // :
1605
					throw new HttpParseException("Request header value expected");
1606
 
1607
				pos.NextOctet();
1608
				string s = TrimHeaderValue(GetUntilEoL(pos, 0));
1609
 
1610
				ExpectCRLF(pos);
1611
 
1612
				if(request)
1613
					http.AddRequestHeader(name, s);
1614
				else
1615
					http.AddResponseHeader(name, s);
1616
			}
1617
		}
1618
 
1619
		enum HeaderValueState
1620
		{
1621
			Space,
1622
			Token,
1623
			Quoted
1624
		}
1625
 
1626
		private string TrimHeaderValue(string s)
1627
		{
1628
			if(s == null) return null;
1629
 
1630
			HeaderValueState state = HeaderValueState.Space;
1631
			StringBuilder    buf   = new StringBuilder();
1632
 
1122 dev 1633
			for(int i = 0, l = s.Length; i < l; i++)
1092 dev 1634
			{
1635
				char c = s[i];
1122 dev 1636
				switch(state)
1092 dev 1637
				{
1638
					case HeaderValueState.Space:
1639
						if(c != ' ' && c != '\t')
1640
						{
1122 dev 1641
							if(c == '"')
1092 dev 1642
							{
1643
								if(buf.Length > 0) buf.Append(' ');
1644
								buf.Append(c);
1645
								state =	HeaderValueState.Quoted;
1646
							}
1122 dev 1647
							else
1092 dev 1648
							{
1649
								if(buf.Length > 0) buf.Append(' ');
1650
								buf.Append(c);
1651
								state =	HeaderValueState.Token;
1652
							}
1653
						}
1654
						break;
1655
 
1656
					case HeaderValueState.Token:
1122 dev 1657
						if(c == ' ' || c == '\t')
1092 dev 1658
						{
1659
							state =	HeaderValueState.Space;
1660
						}
1122 dev 1661
						else if(c == '"')
1092 dev 1662
						{
1663
							buf.Append(c);
1664
							state =	HeaderValueState.Quoted;
1665
						}
1122 dev 1666
						else
1092 dev 1667
						{
1668
							buf.Append(c);
1669
						}
1670
						break;
1671
 
1672
					case HeaderValueState.Quoted:
1122 dev 1673
						if(c == '"')
1092 dev 1674
						{
1675
							buf.Append(c);
1676
							i++;
1677
							if(i < l)
1122 dev 1678
							{
1092 dev 1679
								c = s[i];
1122 dev 1680
								if(c == ' ' || c == '\t')
1092 dev 1681
								{
1682
									state =	HeaderValueState.Space;
1683
								}
1122 dev 1684
								else if(c == '"')
1092 dev 1685
								{
1686
									buf.Append(c);
1687
									state =	HeaderValueState.Quoted;
1688
								}
1689
								else
1690
								{
1691
									buf.Append(c);
1692
									state =	HeaderValueState.Token;
1693
								}
1694
							}
1695
						}
1122 dev 1696
						else
1092 dev 1697
						{
1698
							buf.Append(c);
1699
						}
1700
						break;
1701
				}
1702
			}
1703
 
1704
			return buf.ToString();
1705
		}
1706
 
1707
		private HttpEncoding ParseEncoding(string encoding)
1708
		{
1122 dev 1709
			if(encoding == null || encoding == "identity")
1092 dev 1710
				return HttpEncoding.Identify;
1122 dev 1711
			else if(encoding == "gzip")
1092 dev 1712
				return HttpEncoding.Gzip;
1122 dev 1713
			else if(encoding == "compress")
1092 dev 1714
				return HttpEncoding.Compress;
1122 dev 1715
			else if(encoding == "deflate")
1092 dev 1716
				return HttpEncoding.Deflate;
1122 dev 1717
			else
1092 dev 1718
				return HttpEncoding.Unknown;
1719
		}
1720
 
1721
		private void SetRequestProperties(HttpMessage http)
1722
		{
1723
			// length
1724
			string contentLength = (string)http.RequestHeadersHash["Content-Length"];
1122 dev 1725
			if(contentLength != null)
1092 dev 1726
			{
1727
				http.RequestLength = int.Parse(contentLength);
1728
			}
1729
 
1730
			// encoding
1731
			http.RequestEncoding = ParseEncoding((string)http.RequestHeadersHash["Content-Encoding"]);
1732
 
1733
			// type & charset
1734
			string contentType = (string)http.RequestHeadersHash["Content-Type"];
1122 dev 1735
			if(contentType != null)
1092 dev 1736
			{
1737
				Match match = Regex.Match(contentType, @"^\s*(\S+)/(\S+)\s*($|;\s*(charset=""?(\S+)""?)?)");
1122 dev 1738
				if(match.Success)
1092 dev 1739
				{
1740
					http.RequestContentType    = match.Groups[1].Captures[0].Value;
1741
					http.RequestContentSubtype = match.Groups[2].Captures[0].Value;
1742
					if(match.Groups.Count >= 6) http.RequestCharset = match.Groups[5].Captures[0].Value.Trim('"');
1743
				}
1744
			}
1745
 
1746
			// soap action
1747
			string soapAction = (string)http.RequestHeadersHash["soapaction"];
1122 dev 1748
			if(soapAction != null)
1092 dev 1749
			{
1750
				http.SoapAction = soapAction.Trim('"');
1751
			}
1752
		}
1753
 
1754
		private bool ParseBody(ParsePosition pos, HttpMessage http, bool request)
1755
		{
1756
			if(request && http.RequestMethod != "POST") return false;
1757
 
1758
			// FIXME parse and save on-the-fly, dont wait util end of message
1759
 
1760
			byte[]       bin            = new byte[8*1024];
1761
			int          len            = 0;
1762
			int          limit          = (request ? http.RequestLength         : http.ResponseLength);
1763
			HttpEncoding encoding       = (request ? http.RequestEncoding       : http.ResponseEncoding);
1764
			string       contentType    = (request ? http.RequestContentType    : http.ResponseContentType);
1765
			string       contentSubtype = (request ? http.RequestContentSubtype : http.ResponseContentSubtype);
1766
			string       charset        = (request ? http.RequestCharset        : http.ResponseCharset);
1767
 
1768
			for(byte b = pos.CurrentOctet(); !pos.IsEnd; b = pos.NextOctet())
1769
			{
1122 dev 1770
				if(len >= bin.Length)
1092 dev 1771
				{
1772
					byte[] newBin = new byte[bin.Length*2];
1773
					Array.Copy(bin, newBin, len);
1774
					bin = newBin;
1775
				}
1776
 
1777
				bin[len++] = b;
1778
				if(limit > 0 && limit <= len)  // full length
1779
				{
1780
					break;
1781
				}
1782
			}
1783
 
1784
			string text = null;
1122 dev 1785
			if(encoding == HttpEncoding.Identify && contentType == "text")
1092 dev 1786
			{
1122 dev 1787
				try
1092 dev 1788
				{
1789
					Encoding enc = Encoding.GetEncoding(charset == null ? (contentSubtype == "xml" ? "UTF-8" : "ASCII") : charset);
1790
					text = enc.GetString(bin, 0, len);
1791
				}
1122 dev 1792
				catch(NotSupportedException)
1092 dev 1793
				{
1794
					Console.WriteLine("Unsupported encoding: " + charset);
1795
				}
1796
			}
1797
 
1122 dev 1798
			if(request)
1092 dev 1799
			{
1800
				http.RequestLength = len;
1801
				http.RequestBody   = bin;
1802
				http.RequestText   = text;
1803
			}
1122 dev 1804
			else
1092 dev 1805
			{
1806
				http.ResponseLength = len;
1807
				http.ResponseBody   = bin;
1808
				http.ResponseText   = text;
1809
			}
1810
 
1811
			return (limit > 0 && limit <= len);  // full length reached, need to go to next octet
1812
		}
1813
 
1814
		private void ParseResponseLine(ParsePosition pos, HttpMessage http)
1815
		{
1816
			// version
1817
			string versionStr = GetUntilSpace(pos, 20);
1818
			if(pos.IsEnd || versionStr == null || versionStr.Length == 0)
1819
				throw new HttpParseException("HTTP version expected");
1820
 
1122 dev 1821
			if(versionStr == "HTTP/1.0")
1092 dev 1822
			{
1823
				http.ResponseVersion = HttpVersion.V1_0;
1824
			}
1122 dev 1825
			else if(versionStr == "HTTP/1.1")
1092 dev 1826
			{
1827
				http.ResponseVersion = HttpVersion.V1_1;
1828
			}
1122 dev 1829
			else
1092 dev 1830
			{
1831
				throw new HttpParseException("Unknown HTTP version: " + versionStr);
1832
			}
1833
			ExpectSpace(pos);
1834
 
1835
			// status code
1836
			string code = GetToken(pos, 3);
1837
			if(code == null || code.Length != 3)
1838
				throw new HttpParseException("Status code expected");
1839
 
1122 dev 1840
			try
1092 dev 1841
			{
1842
				int c = int.Parse(code);
1843
				if(c < 100 || c >= 1000) throw new HttpParseException("Status code expected");
1844
				http.ResponseStatusCode = c;
1845
			}
1122 dev 1846
			catch(FormatException)
1092 dev 1847
			{
1848
				throw new HttpParseException("Status code expected");
1849
			}
1850
			ExpectSpace(pos);
1851
 
1852
			// status message
1853
			http.ResponseStatusMessage = GetUntilEoL(pos, 0);
1122 dev 1854
 
1092 dev 1855
			if(pos.IsEnd)
1856
				throw new HttpParseException("Unexpected end of message");
1857
 
1858
			ExpectCRLF(pos);
1859
		}
1860
 
1861
		private void SetResponseProperties(HttpMessage http)
1862
		{
1863
			// length
1864
			HttpHeader contentLength = (HttpHeader)http.ResponseHeadersHash["Content-Length"];
1122 dev 1865
			if(contentLength != null)
1092 dev 1866
			{
1867
				http.ResponseLength = int.Parse(contentLength.Values[0]);
1868
			}
1869
 
1870
			// encoding
1871
			HttpHeader contentEncoding = (HttpHeader)http.ResponseHeadersHash["Content-Encoding"];
1872
			http.ResponseEncoding = ParseEncoding((contentEncoding == null) ? null : contentEncoding.Values[0]);
1873
 
1874
			// type & charset
1875
			HttpHeader contentType = (HttpHeader)http.ResponseHeadersHash["Content-Type"];
1122 dev 1876
			if(contentType != null)
1092 dev 1877
			{
1878
				Match match = Regex.Match(contentType.Values[0], @"^\s*(\S+)/(\S+)\s*($|;\s*(charset=""?(\S+)""?)?)");
1122 dev 1879
				if(match.Success)
1092 dev 1880
				{
1881
					http.ResponseContentType    = match.Groups[1].Captures[0].Value;
1882
					http.ResponseContentSubtype = match.Groups[2].Captures[0].Value;
1122 dev 1883
					if(match.Groups.Count >= 6 && match.Groups[5].Captures.Count > 0)
1092 dev 1884
						http.ResponseCharset = match.Groups[5].Captures[0].Value.Trim('"');
1885
				}
1886
			}
1887
		}
1888
	}
1889
 
1890
	public class HttpHeader
1891
	{
1892
		private string   name;
1893
		private string[] headerValues;
1894
 
1895
		public string Name
1896
		{
1897
			get { return name; }
1898
		}
1899
 
1900
		public string[] Values
1901
		{
1902
			get { return headerValues; }
1903
		}
1904
 
1905
		internal HttpHeader()
1906
		{
1907
		}
1908
 
1909
		internal HttpHeader(string name, string headerValue)
1910
		{
1911
			this.name = name;
1912
			AddValue(headerValue);
1913
		}
1914
 
1915
		internal void AddValue(string value)
1916
		{
1917
			if(headerValues == null)
1918
			{
1919
				headerValues = new string[1];
1920
			}
1122 dev 1921
			else
1092 dev 1922
			{
1923
				string[] newValues = new string[headerValues.Length + 1];
1924
				Array.Copy(headerValues, 0, newValues, 0, headerValues.Length);
1925
				headerValues = newValues;
1926
			}
1927
 
1928
			headerValues[headerValues.Length-1] = value;
1929
		}
1930
	}
1931
 
1932
	public class HttpMessage
1933
	{
1125 dev 1934
		private bool         requestComplete       = false;
1092 dev 1935
		private HttpVersion  requestVersion;
1936
		private string       requestMethod;
1937
		private string       requestUri;
1125 dev 1938
		private LinkedList   requestHeaders        = new LinkedList();
1939
		private Hashtable    requestHeadersHash    = new Hashtable();
1940
		private int          requestLength         = -1; // -1 == unknown
1941
		private HttpEncoding requestEncoding       = HttpEncoding.Identify;
1092 dev 1942
		private string       requestContentType;
1943
		private string       requestContentSubtype;
1944
		private string       requestCharset;
1945
		private string       soapAction;
1946
		private byte[]       requestBody;
1947
		private string       requestText;
1948
		private XmlMessage   requestXml;
1125 dev 1949
		private DateTime     requestStartTimestamp  = DateTime.MinValue;
1092 dev 1950
 
1125 dev 1951
		private bool         responseComplete       = false;
1092 dev 1952
		private HttpVersion  responseVersion;
1953
		private int          responseStatusCode;
1954
		private string       responseStatusMessage;
1125 dev 1955
		private LinkedList   responseHeaders        = new LinkedList();
1956
		private Hashtable    responseHeadersHash    = new Hashtable();
1957
		private int          responseLength         = -1; // -1 == unknown
1958
		private HttpEncoding responseEncoding       = HttpEncoding.Identify;
1092 dev 1959
		private string       responseContentType;
1960
		private string       responseContentSubtype;
1961
		private string       responseCharset;
1962
		private byte[]       responseBody;
1963
		private string       responseText;
1964
		private XmlMessage   responseXml;
1125 dev 1965
		private DateTime     responseStartTimestamp = DateTime.MinValue;
1092 dev 1966
 
1967
		public bool RequestComplete
1968
		{
1969
			get { return requestComplete; }
1970
			set { requestComplete = value; }
1971
		}
1972
 
1973
		public HttpVersion RequestVersion
1974
		{
1975
			get { return requestVersion; }
1976
			set { requestVersion = value; }
1977
		}
1978
 
1979
		public string RequestMethod
1980
		{
1981
			get { return requestMethod; }
1982
			set { requestMethod = value; }
1983
		}
1984
 
1985
		public string RequestUri
1986
		{
1987
			get { return requestUri; }
1988
			set { requestUri = value; }
1989
		}
1990
 
1991
		public LinkedList RequestHeaders
1992
		{
1993
			get { return requestHeaders; }
1994
		}
1995
 
1996
		public IDictionary RequestHeadersHash
1997
		{
1998
			get { return requestHeadersHash; }
1999
		}
2000
 
2001
		public int RequestLength
2002
		{
2003
			get { return requestLength; }
2004
			set { requestLength = value; }
2005
		}
2006
 
2007
		public HttpEncoding RequestEncoding
2008
		{
2009
			get { return requestEncoding; }
2010
			set { requestEncoding = value; }
2011
		}
2012
 
2013
		public string RequestContentType
2014
		{
2015
			get { return requestContentType; }
2016
			set { requestContentType = value; }
2017
		}
2018
 
2019
		public string RequestContentSubtype
2020
		{
2021
			get { return requestContentSubtype; }
2022
			set { requestContentSubtype = value; }
2023
		}
2024
 
2025
		public string RequestCharset
2026
		{
2027
			get { return requestCharset; }
2028
			set { requestCharset = value; }
2029
		}
2030
 
2031
		public string SoapAction
2032
		{
2033
			get { return soapAction; }
2034
			set { soapAction = value; }
2035
		}
2036
 
2037
		public byte[] RequestBody
2038
		{
2039
			get { return requestBody; }
2040
			set { requestBody = value; }
2041
		}
2042
 
2043
		public string RequestText
2044
		{
2045
			get { return requestText; }
2046
			set { requestText = value; }
2047
		}
2048
 
2049
		public XmlMessage RequestXml
2050
		{
2051
			get { return requestXml; }
2052
			set { requestXml = value; }
2053
		}
2054
 
1125 dev 2055
        public DateTime RequestStartTimestamp
2056
        {
2057
        	get { return requestStartTimestamp; }
2058
        	set { requestStartTimestamp = value; }
2059
        }
2060
 
1092 dev 2061
		public bool ResponseComplete
2062
		{
2063
			get { return responseComplete; }
2064
			set { responseComplete = value; }
2065
		}
2066
 
2067
		public HttpVersion ResponseVersion
2068
		{
2069
			get { return responseVersion; }
2070
			set { responseVersion = value; }
2071
		}
2072
 
2073
		public int ResponseStatusCode
2074
		{
2075
			get { return responseStatusCode; }
2076
			set { responseStatusCode = value; }
2077
		}
2078
 
2079
		public string ResponseStatusMessage
2080
		{
2081
			get { return responseStatusMessage; }
2082
			set { responseStatusMessage = value; }
2083
		}
2084
 
2085
		public LinkedList ResponseHeaders
2086
		{
2087
			get { return responseHeaders; }
2088
		}
2089
 
2090
		public IDictionary ResponseHeadersHash
2091
		{
2092
			get { return responseHeadersHash; }
2093
		}
2094
 
2095
		public int ResponseLength
2096
		{
2097
			get { return responseLength; }
2098
			set { responseLength = value; }
2099
		}
2100
 
2101
		public HttpEncoding ResponseEncoding
2102
		{
2103
			get { return responseEncoding; }
2104
			set { responseEncoding = value; }
2105
		}
2106
 
2107
		public string ResponseContentType
2108
		{
2109
			get { return responseContentType; }
2110
			set { responseContentType = value; }
2111
		}
2112
 
2113
		public string ResponseContentSubtype
2114
		{
2115
			get { return responseContentSubtype; }
2116
			set { responseContentSubtype = value; }
2117
		}
2118
 
2119
		public string ResponseCharset
2120
		{
2121
			get { return responseCharset; }
2122
			set { responseCharset = value; }
2123
		}
2124
 
2125
		public byte[] ResponseBody
2126
		{
2127
			get { return responseBody; }
2128
			set { responseBody = value; }
2129
		}
2130
 
2131
		public string ResponseText
2132
		{
2133
			get { return responseText; }
2134
			set { responseText = value; }
2135
		}
2136
 
2137
		public XmlMessage ResponseXml
2138
		{
2139
			get { return responseXml; }
2140
			set { responseXml = value; }
2141
		}
2142
 
1125 dev 2143
        public DateTime ResponseStartTimestamp
2144
        {
2145
        	get { return responseStartTimestamp; }
2146
        	set { responseStartTimestamp = value; }
2147
        }
2148
 
1092 dev 2149
		public void AddRequestHeader(string name, string headerValue)
2150
		{
2151
			requestHeaders.Add(new HttpHeader(name, headerValue));
2152
			requestHeadersHash.Add(name, headerValue);
2153
		}
2154
 
2155
		public void AddResponseHeader(string name, string headerValue)
2156
		{
2157
			HttpHeader header = (HttpHeader)responseHeadersHash[name];
2158
			if(header == null)
2159
			{
2160
				header = new HttpHeader(name, headerValue);
2161
				responseHeaders.Add(header);
2162
				responseHeadersHash.Add(name, header);
2163
			}
2164
			else
2165
			{
2166
				header.AddValue(headerValue);
2167
			}
2168
		}
2169
 
2170
		public override string ToString()  // FIXME delete the method
2171
		{
1122 dev 2172
			return (soapAction != null ? soapAction
1092 dev 2173
				: (requestMethod == null ? "" : requestMethod) + " " + (requestUri == null ? "" : requestUri));
2174
		}
2175
 
2176
		public event TcpEventHandler Update;
2177
 
2178
		protected virtual void OnUpdate(TcpEventArgs e)
2179
		{
1122 dev 2180
			if(Update != null)
1092 dev 2181
			{
1122 dev 2182
				Update(this, e);
1092 dev 2183
			}
2184
		}
2185
 
2186
		internal void UpdateHttpMessage()
2187
		{
2188
			OnUpdate(new TcpEventArgs());
2189
		}
2190
	}
2191
 
2192
	internal class HttpParseException : Exception
2193
	{
2194
		public HttpParseException() : base()
2195
		{
2196
		}
2197
 
2198
		public HttpParseException(string message) : base(message)
2199
		{
2200
		}
2201
 
2202
		public HttpParseException(System.Runtime.Serialization.SerializationInfo info,
2203
			System.Runtime.Serialization.StreamingContext context) : base(info, context)
2204
		{
2205
		}
2206
 
2207
		public HttpParseException(string message, Exception innerException) : base(message, innerException)
2208
		{
2209
		}
2210
	}
2211
 
2212
	public class XmlMessage
2213
	{
2214
		private XmlDocument xml;
2215
		private XmlException parseException;
2216
 
2217
		public XmlDocument Xml
2218
		{
2219
			get { return xml; }
2220
		}
2221
 
2222
		public XmlException ParseException
2223
		{
2224
			get { return parseException; }
2225
		}
2226
 
2227
		internal XmlMessage(string text)
2228
		{
1122 dev 2229
			try
1092 dev 2230
			{
2231
				this.xml = new XmlDocument();
2232
				this.xml.LoadXml(text);
2233
			}
2234
			catch(XmlException ex)
2235
			{
2236
				parseException = ex;
2237
			}
2238
		}
2239
	}
2240
 
2241
	public class TcpMessage
2242
	{
2243
		private TcpMessageDirection direction;
2244
		private byte[]              bytes;
2245
		private int                 length = 0;
2246
		private DateTime            timestamp;
2247
 
2248
		public TcpMessageDirection Direction
2249
		{
2250
			get { return direction; }
2251
			set { direction = value; }
2252
		}
2253
 
2254
		public int Length
2255
		{
2256
			get { return length; }
2257
		}
2258
 
2259
		public byte[] Bytes
2260
		{
1122 dev 2261
			get
1092 dev 2262
			{
2263
				return bytes;
2264
			}
2265
			set
2266
			{
2267
				length = 0;
2268
				Append(value);
2269
			}
2270
		}
2271
 
2272
		public DateTime Timestamp
2273
		{
2274
			get { return timestamp; }
2275
		}
2276
 
2277
		internal TcpMessage()
2278
		{
2279
			this.timestamp = DateTime.Now;
2280
			this.bytes  = new byte[1024];
2281
		}
2282
 
2283
		internal TcpMessage(byte[] bytes, int length)
2284
		{
2285
			this.timestamp = DateTime.Now;
2286
			this.bytes     = new byte[length];
2287
			this.length   = length;
2288
			Array.Copy(this.bytes, bytes, length);
2289
		}
2290
 
2291
		internal TcpMessage Append(byte[] newBytes)
2292
		{
2293
			if(newBytes == null) return this;
2294
 
2295
			return Append(newBytes, newBytes.Length);
2296
		}
2297
 
2298
		internal TcpMessage Append(byte[] newBytes, int length)
2299
		{
2300
			if(newBytes == null) return this;
2301
 
1122 dev 2302
			lock(this)
1092 dev 2303
			{
2304
				// grow array
1122 dev 2305
				if(this.length + length > bytes.Length)
1092 dev 2306
				{
2307
					int newLength = bytes.Length;
2308
					while(this.length + length > newLength) newLength *= 2;
2309
					byte[] newArray = new byte[newLength];
2310
 
2311
					Array.Copy(bytes, newArray, this.length);
2312
					bytes = newArray;
2313
				}
2314
 
2315
				// store received bytes
2316
				Array.Copy(newBytes, 0, bytes, this.length, length);
2317
				this.length += length;
2318
 
2319
				return this;
2320
			}
2321
		}
2322
	}
2323
 
2324
}