@@ -46,6 +46,8 @@ extern Value importprivkey(const Array& params, bool fHelp);
46
46
47
47
const Object emptyobj;
48
48
49
+ void ThreadRPCServer3 (void * parg);
50
+
49
51
Object JSONRPCError (int code, const string& message)
50
52
{
51
53
Object error;
@@ -2021,7 +2023,7 @@ Value getwork(const Array& params, bool fHelp)
2021
2023
throw JSONRPCError (-10 , " Bitcoin is downloading blocks..." );
2022
2024
2023
2025
typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
2024
- static mapNewBlock_t mapNewBlock;
2026
+ static mapNewBlock_t mapNewBlock; // FIXME: thread safety
2025
2027
static vector<CBlock*> vNewBlock;
2026
2028
static CReserveKey reservekey (pwalletMain);
2027
2029
@@ -2355,7 +2357,7 @@ string rfc1123Time()
2355
2357
return string (buffer);
2356
2358
}
2357
2359
2358
- static string HTTPReply (int nStatus, const string& strMsg)
2360
+ static string HTTPReply (int nStatus, const string& strMsg, bool keepalive )
2359
2361
{
2360
2362
if (nStatus == 401 )
2361
2363
return strprintf (" HTTP/1.0 401 Authorization Required\r\n "
@@ -2384,7 +2386,7 @@ static string HTTPReply(int nStatus, const string& strMsg)
2384
2386
return strprintf (
2385
2387
" HTTP/1.1 %d %s\r\n "
2386
2388
" Date: %s\r\n "
2387
- " Connection: close \r\n "
2389
+ " Connection: %s \r\n "
2388
2390
" Content-Length: %d\r\n "
2389
2391
" Content-Type: application/json\r\n "
2390
2392
" Server: bitcoin-json-rpc/%s\r\n "
@@ -2393,19 +2395,24 @@ static string HTTPReply(int nStatus, const string& strMsg)
2393
2395
nStatus,
2394
2396
cStatus,
2395
2397
rfc1123Time ().c_str (),
2398
+ keepalive ? " keep-alive" : " close" ,
2396
2399
strMsg.size (),
2397
2400
FormatFullVersion ().c_str (),
2398
2401
strMsg.c_str ());
2399
2402
}
2400
2403
2401
- int ReadHTTPStatus (std::basic_istream<char >& stream)
2404
+ int ReadHTTPStatus (std::basic_istream<char >& stream, int &proto )
2402
2405
{
2403
2406
string str;
2404
2407
getline (stream, str);
2405
2408
vector<string> vWords;
2406
2409
boost::split (vWords, str, boost::is_any_of (" " ));
2407
2410
if (vWords.size () < 2 )
2408
2411
return 500 ;
2412
+ proto = 0 ;
2413
+ const char *ver = strstr (str.c_str (), " HTTP/1." );
2414
+ if (ver != NULL )
2415
+ proto = atoi (ver+7 );
2409
2416
return atoi (vWords[1 ].c_str ());
2410
2417
}
2411
2418
@@ -2440,7 +2447,8 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe
2440
2447
strMessageRet = " " ;
2441
2448
2442
2449
// Read status
2443
- int nStatus = ReadHTTPStatus (stream);
2450
+ int nProto;
2451
+ int nStatus = ReadHTTPStatus (stream, nProto);
2444
2452
2445
2453
// Read header
2446
2454
int nLen = ReadHTTPHeader (stream, mapHeadersRet);
@@ -2455,6 +2463,16 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe
2455
2463
strMessageRet = string (vch.begin (), vch.end ());
2456
2464
}
2457
2465
2466
+ string sConHdr = mapHeadersRet[" connection" ];
2467
+
2468
+ if ((sConHdr != " close" ) && (sConHdr != " keep-alive" ))
2469
+ {
2470
+ if (nProto >= 1 )
2471
+ mapHeadersRet[" connection" ] = " keep-alive" ;
2472
+ else
2473
+ mapHeadersRet[" connection" ] = " close" ;
2474
+ }
2475
+
2458
2476
return nStatus;
2459
2477
}
2460
2478
@@ -2507,7 +2525,7 @@ void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
2507
2525
if (code == -32600 ) nStatus = 400 ;
2508
2526
else if (code == -32601 ) nStatus = 404 ;
2509
2527
string strReply = JSONRPCReply (Value::null, objError, id);
2510
- stream << HTTPReply (nStatus, strReply) << std::flush;
2528
+ stream << HTTPReply (nStatus, strReply, false ) << std::flush;
2511
2529
}
2512
2530
2513
2531
bool ClientAllowed (const string& strAddress)
@@ -2573,20 +2591,34 @@ class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
2573
2591
SSLStream& stream;
2574
2592
};
2575
2593
2594
+ class AcceptedConnection
2595
+ {
2596
+ public:
2597
+ SSLStream sslStream;
2598
+ SSLIOStreamDevice d;
2599
+ iostreams::stream<SSLIOStreamDevice> stream;
2600
+
2601
+ ip::tcp::endpoint peer;
2602
+
2603
+ AcceptedConnection (asio::io_service &io_service, ssl::context &context,
2604
+ bool fUseSSL ) : sslStream(io_service, context), d(sslStream, fUseSSL ),
2605
+ stream (d) { ; }
2606
+ };
2607
+
2576
2608
void ThreadRPCServer (void * parg)
2577
2609
{
2578
2610
IMPLEMENT_RANDOMIZE_STACK (ThreadRPCServer (parg));
2579
2611
try
2580
2612
{
2581
- vnThreadsRunning[THREAD_RPCSERVER ]++;
2613
+ vnThreadsRunning[THREAD_RPCLISTENER ]++;
2582
2614
ThreadRPCServer2 (parg);
2583
- vnThreadsRunning[THREAD_RPCSERVER ]--;
2615
+ vnThreadsRunning[THREAD_RPCLISTENER ]--;
2584
2616
}
2585
2617
catch (std::exception& e) {
2586
- vnThreadsRunning[THREAD_RPCSERVER ]--;
2618
+ vnThreadsRunning[THREAD_RPCLISTENER ]--;
2587
2619
PrintException (&e, " ThreadRPCServer()" );
2588
2620
} catch (...) {
2589
- vnThreadsRunning[THREAD_RPCSERVER ]--;
2621
+ vnThreadsRunning[THREAD_RPCLISTENER ]--;
2590
2622
PrintException (NULL , " ThreadRPCServer()" );
2591
2623
}
2592
2624
printf (" ThreadRPCServer exiting\n " );
@@ -2664,55 +2696,78 @@ void ThreadRPCServer2(void* parg)
2664
2696
loop
2665
2697
{
2666
2698
// Accept connection
2667
- SSLStream sslStream (io_service, context);
2668
- SSLIOStreamDevice d (sslStream, fUseSSL );
2669
- iostreams::stream<SSLIOStreamDevice> stream (d);
2670
-
2671
- ip::tcp::endpoint peer;
2672
- vnThreadsRunning[THREAD_RPCSERVER]--;
2673
- acceptor.accept (sslStream.lowest_layer (), peer);
2674
- vnThreadsRunning[4 ]++;
2699
+ AcceptedConnection *conn =
2700
+ new AcceptedConnection (io_service, context, fUseSSL );
2701
+
2702
+ vnThreadsRunning[THREAD_RPCLISTENER]--;
2703
+ acceptor.accept (conn->sslStream .lowest_layer (), conn->peer );
2704
+ vnThreadsRunning[THREAD_RPCLISTENER]++;
2705
+
2675
2706
if (fShutdown )
2707
+ {
2708
+ delete conn;
2676
2709
return ;
2710
+ }
2677
2711
2678
- // Restrict callers by IP
2679
- if (!ClientAllowed (peer.address ().to_string ()))
2712
+ // Restrict callers by IP. It is important to
2713
+ // do this before starting client thread, to filter out
2714
+ // certain DoS and misbehaving clients.
2715
+ if (!ClientAllowed (conn->peer .address ().to_string ()))
2680
2716
{
2681
2717
// Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
2682
2718
if (!fUseSSL )
2683
- stream << HTTPReply (403 , " " ) << std::flush;
2684
- continue ;
2719
+ conn->stream << HTTPReply (403 , " " , false ) << std::flush;
2720
+ delete conn;
2721
+ }
2722
+
2723
+ // start HTTP client thread
2724
+ else if (!CreateThread (ThreadRPCServer3, conn)) {
2725
+ printf (" Failed to create RPC server client thread\n " );
2726
+ delete conn;
2685
2727
}
2728
+ }
2729
+ }
2730
+
2731
+ void ThreadRPCServer3 (void * parg)
2732
+ {
2733
+ IMPLEMENT_RANDOMIZE_STACK (ThreadRPCServer3 (parg));
2734
+ vnThreadsRunning[THREAD_RPCHANDLER]++;
2735
+ AcceptedConnection *conn = (AcceptedConnection *) parg;
2686
2736
2737
+ bool fRun = true ;
2738
+ loop {
2739
+ if (fShutdown || !fRun )
2740
+ {
2741
+ conn->stream .close ();
2742
+ delete conn;
2743
+ --vnThreadsRunning[THREAD_RPCHANDLER];
2744
+ return ;
2745
+ }
2687
2746
map<string, string> mapHeaders;
2688
2747
string strRequest;
2689
2748
2690
- boost::thread api_caller (ReadHTTP, boost::ref (stream), boost::ref (mapHeaders), boost::ref (strRequest));
2691
- if (!api_caller.timed_join (boost::posix_time::seconds (GetArg (" -rpctimeout" , 30 ))))
2692
- { // Timed out:
2693
- acceptor.cancel ();
2694
- printf (" ThreadRPCServer ReadHTTP timeout\n " );
2695
- continue ;
2696
- }
2749
+ ReadHTTP (conn->stream , mapHeaders, strRequest);
2697
2750
2698
2751
// Check authorization
2699
2752
if (mapHeaders.count (" authorization" ) == 0 )
2700
2753
{
2701
- stream << HTTPReply (401 , " " ) << std::flush;
2702
- continue ;
2754
+ conn-> stream << HTTPReply (401 , " " , false ) << std::flush;
2755
+ break ;
2703
2756
}
2704
2757
if (!HTTPAuthorized (mapHeaders))
2705
2758
{
2706
- printf (" ThreadRPCServer incorrect password attempt from %s\n " ,peer.address ().to_string ().c_str ());
2759
+ printf (" ThreadRPCServer incorrect password attempt from %s\n " , conn-> peer .address ().to_string ().c_str ());
2707
2760
/* Deter brute-forcing short passwords.
2708
2761
If this results in a DOS the user really
2709
2762
shouldn't have their RPC port exposed.*/
2710
2763
if (mapArgs[" -rpcpassword" ].size () < 20 )
2711
2764
Sleep (250 );
2712
2765
2713
- stream << HTTPReply (401 , " " ) << std::flush;
2714
- continue ;
2766
+ conn-> stream << HTTPReply (401 , " " , false ) << std::flush;
2767
+ break ;
2715
2768
}
2769
+ if (mapHeaders[" connection" ] == " close" )
2770
+ fRun = false ;
2716
2771
2717
2772
Value id = Value::null;
2718
2773
try
@@ -2750,17 +2805,22 @@ void ThreadRPCServer2(void* parg)
2750
2805
2751
2806
// Send reply
2752
2807
string strReply = JSONRPCReply (result, Value::null, id);
2753
- stream << HTTPReply (200 , strReply) << std::flush;
2808
+ conn-> stream << HTTPReply (200 , strReply, fRun ) << std::flush;
2754
2809
}
2755
2810
catch (Object& objError)
2756
2811
{
2757
- ErrorReply (stream, objError, id);
2812
+ ErrorReply (conn->stream , objError, id);
2813
+ break ;
2758
2814
}
2759
2815
catch (std::exception& e)
2760
2816
{
2761
- ErrorReply (stream, JSONRPCError (-32700 , e.what ()), id);
2817
+ ErrorReply (conn->stream , JSONRPCError (-32700 , e.what ()), id);
2818
+ break ;
2762
2819
}
2763
2820
}
2821
+
2822
+ delete conn;
2823
+ vnThreadsRunning[THREAD_RPCHANDLER]--;
2764
2824
}
2765
2825
2766
2826
json_spirit::Value CRPCTable::execute (const std::string &strMethod, const json_spirit::Array ¶ms) const
0 commit comments