Subversion Repositories general

Rev

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