summaryrefslogtreecommitdiff
path: root/src/gw_backend.h
blob: 90d3a8c34532c90234996a616672f65b6685c7f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#ifndef INCLUDED_GW_BACKEND_H
#define INCLUDED_GW_BACKEND_H

#include "first.h"

#include <sys/types.h>
#include "sys-socket.h"

#include "array.h"
#include "buffer.h"

typedef struct {
    char **ptr;

    size_t size;
    size_t used;
} char_array;

typedef struct gw_proc {
    size_t id; /* id will be between 1 and max_procs */
    buffer *unixsocket; /* config.socket + "-" + id */
    unsigned port;  /* config.port + pno */
    socklen_t saddrlen;
    struct sockaddr *saddr;

    /* either tcp:<host>:<port> or unix:<socket> for debugging purposes */
    buffer *connection_name;

    pid_t pid;   /* PID of the spawned process (0 if not spawned locally) */


    size_t load; /* number of requests waiting on this process */

    time_t last_used; /* see idle_timeout */
    size_t requests;  /* see max_requests */
    struct gw_proc *prev, *next; /* see first */

    time_t disabled_until; /* proc disabled until given time */

    int is_local;

    enum {
        PROC_STATE_RUNNING,    /* alive */
        PROC_STATE_OVERLOADED, /* listen-queue is full */
        PROC_STATE_DIED_WAIT_FOR_PID, /* */
        PROC_STATE_DIED,       /* marked as dead, should be restarted */
        PROC_STATE_KILLED      /* killed (signal sent to proc) */
    } state;
} gw_proc;

typedef struct {
    /* the key that is used to reference this value */
    buffer *id;

    /* list of processes handling this extension
     * sorted by lowest load
     *
     * whenever a job is done move it up in the list
     * until it is sorted, move it down as soon as the
     * job is started
     */
    gw_proc *first;
    gw_proc *unused_procs;

    /*
     * spawn at least min_procs, at max_procs.
     *
     * as soon as the load of the first entry
     * is max_load_per_proc we spawn a new one
     * and add it to the first entry and give it
     * the load
     *
     */

    unsigned short min_procs;
    unsigned short max_procs;
    size_t num_procs;    /* how many procs are started */
    size_t active_procs; /* how many procs in state PROC_STATE_RUNNING */

    unsigned short max_load_per_proc;

    /*
     * kick the process from the list if it was not
     * used for idle_timeout until min_procs is
     * reached. this helps to get the processlist
     * small again we had a small peak load.
     *
     */

    unsigned short idle_timeout;

    /*
     * time after a disabled remote connection is tried to be re-enabled
     *
     *
     */

    unsigned short disable_time;

    /*
     * some gw processes get a little bit larger
     * than wanted. max_requests_per_proc kills a
     * process after a number of handled requests.
     *
     */
    size_t max_requests_per_proc;


    /* config */

    /*
     * host:port
     *
     * if host is one of the local IP adresses the
     * whole connection is local
     *
     * if port is not 0, and host is not specified,
     * "localhost" (INADDR_LOOPBACK) is assumed.
     *
     */
    buffer *host;
    unsigned short port;
    unsigned short family; /* sa_family_t */

    /*
     * Unix Domain Socket
     *
     * instead of TCP/IP we can use Unix Domain Sockets
     * - more secure (you have fileperms to play with)
     * - more control (on locally)
     * - more speed (no extra overhead)
     */
    buffer *unixsocket;

    /* if socket is local we can start the gw process ourself
     *
     * bin-path is the path to the binary
     *
     * check min_procs and max_procs for the number
     * of process to start up
     */
    buffer *bin_path;

    /* bin-path is set bin-environment is taken to
     * create the environement before starting the
     * FastCGI process
     *
     */
    array *bin_env;

    array *bin_env_copy;

    /*
     * docroot-translation between URL->phys and the
     * remote host
     *
     * reasons:
     * - different dir-layout if remote
     * - chroot if local
     *
     */
    buffer *docroot;

    /*
     * check_local tells you if the phys file is stat()ed
     * or not. FastCGI doesn't care if the service is
     * remote. If the web-server side doesn't contain
     * the FastCGI-files we should not stat() for them
     * and say '404 not found'.
     */
    unsigned short check_local;

    /*
     * append PATH_INFO to SCRIPT_FILENAME
     *
     * php needs this if cgi.fix_pathinfo is provided
     *
     */

    unsigned short break_scriptfilename_for_php;

    /*
     * workaround for program when prefix="/"
     *
     * rule to build PATH_INFO is hardcoded for when check_local is disabled
     * enable this option to use the workaround
     *
     */

    unsigned short fix_root_path_name;

    /*
     * If the backend includes X-Sendfile in the response
     * we use the value as filename and ignore the content.
     *
     */
    unsigned short xsendfile_allow;
    array *xsendfile_docroot;

    ssize_t load;

    size_t max_id; /* corresponds most of the time to num_procs */

    buffer *strip_request_uri;

    unsigned short kill_signal; /* we need a setting for this as libfcgi
                                   applications prefer SIGUSR1 while the
                                   rest of the world would use SIGTERM
                                   *sigh* */

    int listen_backlog;
    int refcount;

    char_array args;
} gw_host;

/*
 * one extension can have multiple hosts assigned
 * one host can spawn additional processes on the same
 *   socket (if we control it)
 *
 * ext -> host -> procs
 *    1:n     1:n
 *
 * if the gw process is remote that whole goes down
 * to
 *
 * ext -> host -> procs
 *    1:n     1:1
 *
 * in case of PHP and FCGI_CHILDREN we have again a procs
 * but we don't control it directly.
 *
 */

typedef struct {
    buffer *key; /* like .php */

    int note_is_sent;
    int last_used_ndx;

    gw_host **hosts;

    size_t used;
    size_t size;
} gw_extension;

typedef struct {
    gw_extension **exts;

    size_t used;
    size_t size;
} gw_exts;




#include "base.h"
#include "plugin.h"
#include "response.h"

typedef struct gw_plugin_config {
    gw_exts *exts;
    gw_exts *exts_auth;
    gw_exts *exts_resp;

    array *ext_mapping;

    int balance;
    int proto;
    int debug;
} gw_plugin_config;

/* generic plugin data, shared between all connections */
typedef struct gw_plugin_data {
    PLUGIN_DATA;
    gw_plugin_config **config_storage;

    gw_plugin_config conf; /* used only as long as no gw_handler_ctx is setup */
    pid_t srv_pid;
} gw_plugin_data;

/* connection specific data */
typedef enum {
    GW_STATE_INIT,
    GW_STATE_CONNECT_DELAYED,
    GW_STATE_PREPARE_WRITE,
    GW_STATE_WRITE,
    GW_STATE_READ
} gw_connection_state_t;

#define GW_RESPONDER  1
#define GW_AUTHORIZER 2
#define GW_FILTER     3  /*(not implemented)*/

typedef struct gw_handler_ctx {
    gw_proc *proc;
    gw_host *host;
    gw_extension *ext;
    gw_extension *ext_auth; /* (future: might allow multiple authorizers)*/
    unsigned short gw_mode; /* mode: GW_AUTHORIZER or GW_RESPONDER */

    gw_connection_state_t state;
    time_t   state_timestamp;

    chunkqueue *rb; /* read queue */
    chunkqueue *wb; /* write queue */
    off_t     wb_reqlen;

    buffer   *response;

    int       fd;        /* fd to the gw process */
    int       fde_ndx;   /* index into the fd-event buffer */

    pid_t     pid;
    int       reconnects; /* number of reconnect attempts */

    int       request_id;
    int       send_content_body;

    http_response_opts opts;
    gw_plugin_config conf;

    connection *remote_conn;     /* dumb pointer */
    gw_plugin_data *plugin_data; /* dumb pointer */
    handler_t(*stdin_append)(server *srv, struct gw_handler_ctx *hctx);
    handler_t(*create_env)(server *srv, struct gw_handler_ctx *hctx);
    void(*backend_error)(struct gw_handler_ctx *hctx);
    void(*handler_ctx_free)(void *hctx);
} gw_handler_ctx;


void * gw_init(void);
void gw_plugin_config_free(gw_plugin_config *s);
handler_t gw_free(server *srv, void *p_d);
int gw_set_defaults_backend(server *srv, gw_plugin_data *p, data_unset *du, size_t i, int sh_exec);
int gw_set_defaults_balance(server *srv, gw_plugin_config *s, data_unset *du);
handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, int uri_path_handler, size_t hctx_sz);
handler_t gw_connection_reset(server *srv, connection *con, void *p_d);
handler_t gw_handle_subrequest(server *srv, connection *con, void *p_d);
handler_t gw_handle_trigger(server *srv, void *p_d);
handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status);

void gw_set_transparent(server *srv, gw_handler_ctx *hctx);

#endif