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

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

svn propset

  • Property svn:keywords set to Date Rev Author Id
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: rpc-server.c 5959 2008-05-28 17:20:11Z charles $
11 */
12
13#include <assert.h>
14#include <string.h>
15
16#include <libevent/event.h>
17#include <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            if( arg->flags & SHTTPD_MORE_POST_DATA )
60                return;
61            response = tr_rpc_request_exec_json( s->session,
62                                                 EVBUFFER_DATA( s->in ),
63                                                 EVBUFFER_LENGTH( s->in ),
64                                                 &len );
65            evbuffer_drain( s->in, EVBUFFER_LENGTH( s->in ) );
66        }
67
68        evbuffer_add_printf( s->out, "HTTP/1.1 200 OK\r\n"
69                                     "Content-Type: application/json\r\n"
70                                     "Content-Length: %d\r\n"
71                                     "\r\n"
72                                     "%*.*s\r\n", len, len, len, response );
73        tr_free( response );
74    }
75
76    if( EVBUFFER_LENGTH( s->out ) )
77    {
78        const int n = MIN( ( int )EVBUFFER_LENGTH( s->out ), arg->out.len );
79        memcpy( arg->out.buf, EVBUFFER_DATA( s->out ), n );
80        evbuffer_drain( s->out, n );
81        arg->out.num_bytes = n;
82    }
83
84    if( !EVBUFFER_LENGTH( s->out ) )
85        arg->flags |= SHTTPD_END_OF_OUTPUT;
86}
87
88static void
89rpcPulse( int socket UNUSED, short action UNUSED, void * vserver )
90{
91    int interval;
92    struct timeval tv;
93    tr_rpc_server * server = vserver;
94
95    assert( server );
96
97    shttpd_poll( server->ctx, 1 );
98
99    /* set a timer for the next pulse */
100    if( EVBUFFER_LENGTH( server->in ) || EVBUFFER_LENGTH( server->out ) )
101        interval = BUSY_INTERVAL_MSEC;
102    else
103        interval = IDLE_INTERVAL_MSEC;
104    tv = tr_timevalMsec( interval );
105    evtimer_add( &server->timer, &tv );
106}
107
108static void
109startServer( tr_rpc_server * server )
110{
111    if( !server->ctx )
112    {
113        char ports[128];
114        struct timeval tv = tr_timevalMsec( IDLE_INTERVAL_MSEC );
115
116        server->ctx = shttpd_init( );
117        snprintf( ports, sizeof( ports ), "%d", server->port );
118        shttpd_register_uri( server->ctx, "/transmission", handle_rpc, server );
119        shttpd_set_option( server->ctx, "ports", ports );
120        shttpd_set_option( server->ctx, "dir_list", "0" );
121        shttpd_set_option( server->ctx, "root", "/dev/null" );
122        if( server->acl )
123            shttpd_set_option( server->ctx, "acl", server->acl );
124
125        evtimer_set( &server->timer, rpcPulse, server );
126        evtimer_add( &server->timer, &tv );
127    }
128}
129
130static void
131stopServer( tr_rpc_server * server )
132{
133    if( server->ctx )
134    {
135        evtimer_del( &server->timer );
136        shttpd_fini( server->ctx );
137        server->ctx = NULL;
138    }
139}
140
141void
142tr_rpcSetEnabled( tr_rpc_server * server, int isEnabled )
143{
144    if( !isEnabled && server->ctx )
145        stopServer( server );
146
147    if( isEnabled && !server->ctx )
148        startServer( server );
149}
150
151int
152tr_rpcIsEnabled( const tr_rpc_server * server )
153{
154    return server->ctx != NULL;
155}
156
157void
158tr_rpcSetPort( tr_rpc_server * server, int port )
159{
160    if( server->port != port )
161    {
162        server->port = port;
163
164        if( server->ctx )
165        {
166            stopServer( server );
167            startServer( server );
168        }
169    }
170}
171
172int
173tr_rpcGetPort( const tr_rpc_server * server )
174{
175    return server->port;
176}
177
178void
179tr_rpcSetACL( tr_rpc_server * server, const char * acl )
180{
181    const int isRunning = server->ctx != NULL;
182
183    if( isRunning )
184        stopServer( server );
185
186    tr_free( server->acl );
187    server->acl = tr_strdup( acl );
188
189    if( isRunning )
190        startServer( server );
191}
192
193const char*
194tr_rpcGetACL( const tr_rpc_server * server )
195{
196    return server->acl ? server->acl : "";
197}
198
199void
200tr_rpcClose( tr_rpc_server ** ps )
201{
202    tr_rpc_server * s = *ps;
203    *ps = NULL;
204
205    stopServer( s );
206    evbuffer_free( s->in );
207    evbuffer_free( s->out );
208    tr_free( s->acl );
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.