From 87a8fb1aeeec201e70dfbafb020ec7e283aaddba Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 2 Mar 2010 22:52:22 +0000 Subject: [PATCH] - client: generalize the GUI RPC mechanism to access via HTTP. The handler checks for POST headers, and if present adds a reply header. Also: remove the restriction that request messages must be read in their entirety on the first recv(). I'm testing this using javascript's XMLHttpRequest. It's not completely working; the browser sends an OPTIONS request, then sends a POST. The BOINC client parses and replies to these, but for some reason the browser doesn't seem to be parsing the POST reply. svn path=/trunk/boinc/; revision=20774 --- checkin_notes | 18 ++++++++ client/gui_rpc_server.cpp | 1 + client/gui_rpc_server.h | 5 ++- client/gui_rpc_server_ops.cpp | 77 +++++++++++++++++++++++++++++++---- 4 files changed, 91 insertions(+), 10 deletions(-) diff --git a/checkin_notes b/checkin_notes index ac7f580d97..e1e7b92411 100644 --- a/checkin_notes +++ b/checkin_notes @@ -1535,3 +1535,21 @@ David 2 Mar 2010 - add remote job submission system (from Toni Giorgino) rboinc/* + +David 2 Mar 2010 + - client: generalize the GUI RPC mechanism to access via HTTP. + The handler checks for POST headers, + and if present adds a reply header. + Also: remove the restriction that request messages + must be read in their entirety on the first recv(). + + I'm testing this using javascript's XMLHttpRequest. + It's not completely working; + the browser sends an OPTIONS request, then sends a POST. + The BOINC client parses and replies to these, + but for some reason the browser doesn't seem to be + parsing the POST reply. + + client/ + gui_rpc_server.cpp,h + gui_rpc_server_ops.cpp diff --git a/client/gui_rpc_server.cpp b/client/gui_rpc_server.cpp index 5352f5afc1..ed89867b1c 100644 --- a/client/gui_rpc_server.cpp +++ b/client/gui_rpc_server.cpp @@ -80,6 +80,7 @@ GUI_RPC_CONN::GUI_RPC_CONN(int s): got_auth2 = false; sent_unauthorized = false; notice_refresh = false; + request_nbytes = 0; } GUI_RPC_CONN::~GUI_RPC_CONN() { diff --git a/client/gui_rpc_server.h b/client/gui_rpc_server.h index 779133efaa..45abace0bd 100644 --- a/client/gui_rpc_server.h +++ b/client/gui_rpc_server.h @@ -33,10 +33,13 @@ #define AU_MGR_GOT 1 #define AU_MGR_QUIT_REQ 2 #define AU_MGR_QUIT_SENT 3 - + +#define GUI_RPC_REQ_MSG_SIZE 4096 class GUI_RPC_CONN { public: int sock; + char request_msg[GUI_RPC_REQ_MSG_SIZE+1]; + int request_nbytes; char nonce[256]; /// if true, don't allow operations other than authentication bool auth_needed; diff --git a/client/gui_rpc_server_ops.cpp b/client/gui_rpc_server_ops.cpp index 596d2976bc..4021d0ac50 100644 --- a/client/gui_rpc_server_ops.cpp +++ b/client/gui_rpc_server_ops.cpp @@ -1067,30 +1067,84 @@ static void handle_get_notices_public(char* buf, MIOFILE& fout, bool notice_refr notices.write(seqno, fout, true, notice_refresh); } +static bool complete_post_request(char* buf) { + if (strncmp(buf, "POST", 4)) return false; + char* p = strstr(buf, "Content-Length: "); + if (!p) return false; + p += strlen("Content-Length: "); + int n = atoi(p); + p = strstr(p, "\r\n\r\n"); + if (!p) return false; + p += 4; + if ((int)strlen(p) < n) return false; + return true; +} + // Some of the RPCs have empty-element request messages. // We accept both and // #define match_req(buf, tag) (match_tag(buf, "<" tag ">") || match_tag(buf, "<" tag "/>")) int GUI_RPC_CONN::handle_rpc() { - char request_msg[4096]; int n, retval=0; MIOFILE mf; MFILE m; char* p; mf.init_mfile(&m); - // read the request message in one read() - // so that the core client won't hang because - // of malformed request msgs - // + int left = GUI_RPC_REQ_MSG_SIZE - request_nbytes; #ifdef _WIN32 - n = recv(sock, request_msg, 4095, 0); + n = recv(sock, request_msg+request_nbytes, left, 0); #else - n = read(sock, request_msg, 4095); + n = read(sock, request_msg+request_nbytes, left); #endif - if (n <= 0) return ERR_READ; - request_msg[n-1] = 0; // replace 003 with NULL + if (n <= 0) { + request_nbytes = 0; + return ERR_READ; + } + request_nbytes += n; + + // buffer full? + if (request_nbytes >= GUI_RPC_REQ_MSG_SIZE) { + request_nbytes = 0; + return ERR_READ; + } + request_msg[request_nbytes] = 0; + if (!strncmp(request_msg, "OPTIONS", 7)) { + char buf[1024]; + sprintf(buf, "HTTP/1.1 200 OK\n" + "Server: BOINC client\n" + "Access-Control-Allow-Origin: *\n" + "Access-Control-Allow-Methods: POST, GET, OPTIONS\n" + "Content-Length: 0\n" + "Keep-Alive: timeout=2, max=100\n" + "Connection: Keep-Alive\n" + "Content-Type: text/plain\n\n" + ); + send(sock, buf, strlen(buf), 0); + request_nbytes = 0; + if (log_flags.guirpc_debug) { + msg_printf(0, MSG_INFO, + "[guirpc_debug] processed OPTIONS" + ); + } + return 0; + } + bool http_request; + if (complete_post_request(request_msg)) { + http_request = true; + } else if (p = strchr(request_msg, 3)) { + *p = 0; + http_request = false; + } else { + if (log_flags.guirpc_debug) { + msg_printf(0, MSG_INFO, + "[guirpc_debug] partial GUI RPC Command = '%s'\n", request_msg + ); + } + return 0; + } + request_nbytes = 0; if (log_flags.guirpc_debug) { msg_printf(0, MSG_INFO, @@ -1299,6 +1353,11 @@ int GUI_RPC_CONN::handle_rpc() { mf.printf("\n\003"); m.get_buf(p, n); + if (http_request) { + char buf[1024]; + sprintf(buf, "HTTP/1.1 200 OK\nDate: Fri, 31 Dec 1999 23:59:59 GMT\nContent-Type: text/xml\nContent-Length: %d\n\n", n); + send(sock, buf, strlen(buf), 0); + } if (p) { send(sock, p, n, 0); p[n-1]=0; // replace 003 with NULL