source: trunk/libtransmission/rpc-server.c @ 5843

Last change on this file since 5843 was 5843, checked in by charles, 14 years ago

RPC/IPC redesign

File size: 5.7 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id:$
11 */
12
13#include <assert.h>
14#include <string.h>
15
16#include <third-party/libevent/event.h>
17#include <third-party/shttpd/shttpd.h>
18
19#include "transmission.h"
20#include "rpc.h"
21#include "rpc-server.h"
22#include "utils.h"
23
24#define BUSY_INTERVAL_MSEC 30
25#define IDLE_INTERVAL_MSEC 1000
26
27struct tr_rpc_server
28{
29    int port;
30    struct shttpd_ctx * ctx;
31    tr_handle * session;
32    struct evbuffer * in;
33    struct evbuffer * out;
34    struct event timer;
35    char * acl;
36};
37
38static void
39handle_rpc( struct shttpd_arg * arg )
40{
41    struct tr_rpc_server * s = arg->user_data;
42
43    if( !EVBUFFER_LENGTH( s->out ) )
44    {
45        int len = 0;
46        char * response = NULL;
47        const char * request_method = shttpd_get_env( arg, "REQUEST_METHOD" );
48        const char * query_string = shttpd_get_env( arg, "QUERY_STRING" );
49
50        if( query_string && *query_string )
51            response = tr_rpc_request_exec_uri( s->session,
52                                                query_string,
53                                                strlen( query_string ),
54                                                &len );
55        else if( !strcmp( request_method, "POST" ) )
56        {
57            evbuffer_add( s->in, arg->in.buf, arg->in.len );
58            arg->in.num_bytes = arg->in.len;
59           
60            if( ! ( arg->flags & SHTTPD_MORE_POST_DATA ) )
61                return;
62            response = tr_rpc_request_exec_uri( s->session,
63                                                EVBUFFER_DATA( s->in ),
64                                                EVBUFFER_LENGTH( s->in ),
65                                                &len );
66            evbuffer_drain( s->in, EVBUFFER_LENGTH( s->in ) );
67        }
68
69        evbuffer_add_printf( s->out, "HTTP/1.1 200 OK\r\n"
70                                     "Content-Type: text/x-json\r\n"
71                                     "Content-Length: %d\r\n"
72                                     "\r\n"
73                                     "%*.*s\r\n", len, len, len, response );
74        tr_free( response );
75    }
76
77    if( EVBUFFER_LENGTH( s->out ) )
78    {
79        const int n = MIN( ( int )EVBUFFER_LENGTH( s->out ), arg->out.len );
80        memcpy( arg->out.buf, EVBUFFER_DATA( s->out ), n );
81        evbuffer_drain( s->out, n );
82        arg->out.num_bytes = n;
83    }
84
85    if( !EVBUFFER_LENGTH( s->out ) )
86        arg->flags |= SHTTPD_END_OF_OUTPUT;
87}
88
89static void
90rpcPulse( int socket UNUSED, short action UNUSED, void * vserver )
91{
92    int interval;
93    struct timeval tv;
94    tr_rpc_server * server = vserver;
95
96    assert( server );
97
98    shttpd_poll( server->ctx, 1 );
99
100    /* set a timer for the next pulse */
101    if( EVBUFFER_LENGTH( server->in ) || EVBUFFER_LENGTH( server->out ) )
102        interval = BUSY_INTERVAL_MSEC;
103    else
104        interval = IDLE_INTERVAL_MSEC;
105    tv = tr_timevalMsec( interval );
106    evtimer_add( &server->timer, &tv );
107}
108
109static void
110startServer( tr_rpc_server * server )
111{
112    if( !server->ctx )
113    {
114        char ports[128];
115        struct timeval tv = tr_timevalMsec( IDLE_INTERVAL_MSEC );
116
117        server->ctx = shttpd_init( );
118        snprintf( ports, sizeof( ports ), "%d", server->port );
119        shttpd_register_uri( server->ctx, "/transmission", handle_rpc, server );
120        shttpd_set_option( server->ctx, "ports", ports );
121        shttpd_set_option( server->ctx, "dir_list", "0" );
122        shttpd_set_option( server->ctx, "root", "/dev/null" );
123        if( server->acl )
124            shttpd_set_option( server->ctx, "acl", server->acl );
125
126        evtimer_set( &server->timer, rpcPulse, server );
127        evtimer_add( &server->timer, &tv );
128    }
129}
130
131static void
132stopServer( tr_rpc_server * server )
133{
134    if( server->ctx )
135    {
136        evtimer_del( &server->timer );
137        shttpd_fini( server->ctx );
138        server->ctx = NULL;
139    }
140}
141
142void
143tr_rpcSetEnabled( tr_rpc_server * server, int isEnabled )
144{
145    if( !isEnabled && server->ctx )
146        stopServer( server );
147
148    if( isEnabled && !server->ctx )
149        startServer( server );
150}
151
152int
153tr_rpcIsEnabled( const tr_rpc_server * server )
154{
155    return server->ctx != NULL;
156}
157
158void
159tr_rpcSetPort( tr_rpc_server * server, int port )
160{
161    if( server->port != port )
162    {
163        server->port = port;
164
165        if( server->ctx )
166        {
167            stopServer( server );
168            startServer( server );
169        }
170    }
171}
172
173int
174tr_rpcGetPort( const tr_rpc_server * server )
175{
176    return server->port;
177}
178
179void
180tr_rpcSetACL( tr_rpc_server * server, const char * acl )
181{
182    const int isRunning = server->ctx != NULL;
183
184    if( isRunning )
185        stopServer( server );
186
187    tr_free( server->acl );
188    server->acl = tr_strdup( acl );
189
190    if( isRunning )
191        startServer( server );
192}
193
194const char*
195tr_rpcGetACL( const tr_rpc_server * server )
196{
197    return server->acl ? server->acl : "";
198}
199
200void
201tr_rpcClose( tr_rpc_server ** ps )
202{
203    tr_rpc_server * s = *ps;
204    *ps = NULL;
205
206    stopServer( s );
207    evbuffer_free( s->in );
208    evbuffer_free( s->out );
209    tr_free( s );
210}
211
212tr_rpc_server *
213tr_rpcInit( tr_handle   * session,
214            int           isEnabled,
215            int           port,
216            const char  * acl )
217{
218    tr_rpc_server * s = tr_new0( tr_rpc_server, 1 );
219    s->session = session;
220    s->port = port;
221    s->in = evbuffer_new( );
222    s->out = evbuffer_new( );
223    s->acl = tr_strdup( acl );
224   
225    if( isEnabled )
226        startServer( s );
227    return s;   
228}
Note: See TracBrowser for help on using the repository browser.