Subversion Repositories general

Rev

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

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