Subversion Repositories general

Rev

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