1
+ using System ;
2
+ using System . IO ;
3
+ using System . Net ;
4
+ using System . Text ;
5
+ using System . Threading ;
6
+ using System . Threading . Tasks ;
7
+ using Microsoft . Extensions . Hosting ;
8
+ using WalletWasabi . TorControl ;
9
+ using Chaincase . Common . Contracts ;
10
+
11
+ namespace Chaincase . Common . Services
12
+ {
13
+ public class P2EPServer : BackgroundService
14
+ {
15
+ private ITorManager TorManager => Global . TorManager ;
16
+ private HttpListener Listener { get ; }
17
+ public string ServiceId { get ; private set ; }
18
+ public Global Global { get ; }
19
+ public string PaymentEndpoint => $ "http://{ ServiceId } .onion:37129";
20
+
21
+ public P2EPServer ( Global global )
22
+ {
23
+ Global = global ;
24
+ Listener = new HttpListener ( ) ;
25
+ Listener . AuthenticationSchemes = AuthenticationSchemes . Anonymous ;
26
+ Listener . Prefixes . Add ( $ "http://+:37129/") ;
27
+ }
28
+
29
+ public override async Task StartAsync ( CancellationToken cancellationToken )
30
+ {
31
+ // Assummming running
32
+ //while (!Tor)
33
+ //{
34
+ // await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
35
+ //}
36
+
37
+ ServiceId = await TorManager . CreateHiddenServiceAsync ( ) . ConfigureAwait ( false ) ;
38
+
39
+ await TorControlClient . ConnectAsync ( ) . ConfigureAwait ( false ) ;
40
+ await TorControlClient . AuthenticateAsync ( "MyLittlePonny" ) . ConfigureAwait ( false ) ;
41
+ ServiceId = await TorControlClient . CreateHiddenServiceAsync ( ) . ConfigureAwait ( false ) ;
42
+
43
+ Listener . Start ( ) ;
44
+ await base . StartAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
45
+ }
46
+
47
+ public override async Task StopAsync ( CancellationToken cancellationToken )
48
+ {
49
+ await base . StopAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
50
+ Listener . Stop ( ) ;
51
+ var serviceId = ServiceId ;
52
+ if ( ! string . IsNullOrWhiteSpace ( serviceId ) )
53
+ {
54
+ await TorControlClient . DestroyHiddenService ( serviceId ) ;
55
+ }
56
+ }
57
+
58
+ protected override async Task ExecuteAsync ( CancellationToken stoppingToken )
59
+ {
60
+ var handler = new P2EPRequestHandler ( Global . Network , Global . WalletManager , Global . Config . PrivacyLevelSome ) ;
61
+
62
+ while ( ! stoppingToken . IsCancellationRequested )
63
+ {
64
+ try
65
+ {
66
+ var context = await GetHttpContextAsync ( stoppingToken ) . ConfigureAwait ( false ) ;
67
+ var request = context . Request ;
68
+ var response = context . Response ;
69
+
70
+ if ( request . HttpMethod == "POST" )
71
+ {
72
+ using var reader = new StreamReader ( request . InputStream ) ;
73
+ string body = await reader . ReadToEndAsync ( ) . ConfigureAwait ( false ) ;
74
+
75
+ try
76
+ {
77
+ var result = await handler . HandleAsync ( body , stoppingToken ) . ConfigureAwait ( false ) ;
78
+
79
+ var output = response . OutputStream ;
80
+ var buffer = Encoding . UTF8 . GetBytes ( result ) ;
81
+ await output . WriteAsync ( buffer , 0 , buffer . Length , stoppingToken ) . ConfigureAwait ( false ) ;
82
+ await output . FlushAsync ( stoppingToken ) . ConfigureAwait ( false ) ;
83
+ }
84
+ catch ( P2EPException e )
85
+ {
86
+ response . StatusCode = ( int ) HttpStatusCode . BadRequest ;
87
+ response . StatusDescription = e . Message ;
88
+ }
89
+ }
90
+ else
91
+ {
92
+ response . StatusCode = ( int ) HttpStatusCode . MethodNotAllowed ;
93
+ }
94
+ response . ContentType = "text/html; charset=UTF-8" ;
95
+ response . Close ( ) ;
96
+ }
97
+ catch ( OperationCanceledException )
98
+ {
99
+ break ;
100
+ }
101
+ catch ( Exception ex )
102
+ {
103
+ Logger . LogError ( ex ) ;
104
+ }
105
+ }
106
+ }
107
+
108
+ private async Task < HttpListenerContext > GetHttpContextAsync ( CancellationToken cancellationToken )
109
+ {
110
+ var getHttpContextTask = Listener . GetContextAsync ( ) ;
111
+ var tcs = new TaskCompletionSource < bool > ( ) ;
112
+ using ( cancellationToken . Register ( state => ( ( TaskCompletionSource < bool > ) state ) . TrySetResult ( true ) , tcs ) )
113
+ {
114
+ var firstTaskToComplete = await Task . WhenAny ( getHttpContextTask , tcs . Task ) . ConfigureAwait ( false ) ;
115
+ if ( getHttpContextTask != firstTaskToComplete )
116
+ {
117
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
118
+ }
119
+ }
120
+ return await getHttpContextTask . ConfigureAwait ( false ) ;
121
+ }
122
+ }
123
+ }
0 commit comments