Subversion Repositories general

Rev

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