Subversion Repositories general

Rev

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