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