relay: fix close handling
[project/uhttpd.git] / relay.c
1 /*
2 * uhttpd - Tiny single-threaded httpd
3 *
4 * Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
5 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <signal.h>
21 #include "uhttpd.h"
22
23 void uh_relay_free(struct relay *r)
24 {
25 if (!r->cl)
26 return;
27
28 if (r->proc.pending)
29 kill(r->proc.pid, SIGKILL);
30
31 uloop_process_delete(&r->proc);
32 ustream_free(&r->sfd.stream);
33 close(r->sfd.fd.fd);
34
35 r->cl = NULL;
36 }
37
38 void uh_relay_close(struct relay *r, int ret)
39 {
40 struct ustream *us = &r->sfd.stream;
41
42 if (!us->notify_read)
43 return;
44
45 us->notify_read = NULL;
46 us->notify_write = NULL;
47 us->notify_state = NULL;
48
49 if (r->close)
50 r->close(r, ret);
51 }
52
53 static void relay_error(struct relay *r)
54 {
55 struct ustream *s = &r->sfd.stream;
56 int len;
57
58 s->eof = true;
59 ustream_get_read_buf(s, &len);
60 if (len)
61 ustream_consume(s, len);
62 ustream_state_change(s);
63 }
64
65 static void relay_process_headers(struct relay *r)
66 {
67 struct ustream *s = &r->sfd.stream;
68 char *buf, *newline;
69 int len;
70
71 if (!r->header_cb)
72 return;
73
74 while (r->header_cb) {
75 int line_len;
76 char *val;
77
78 buf = ustream_get_read_buf(s, &len);
79 if (!buf || !len)
80 break;
81
82 newline = strchr(buf, '\n');
83 if (!newline)
84 break;
85
86 line_len = newline + 1 - buf;
87 if (newline > buf && newline[-1] == '\r')
88 newline--;
89
90 *newline = 0;
91 if (newline == buf) {
92 r->header_cb = NULL;
93 if (r->header_end)
94 r->header_end(r);
95 ustream_consume(s, line_len);
96 break;
97 }
98
99 val = uh_split_header(buf);
100 if (!val) {
101 relay_error(r);
102 return;
103 }
104
105 r->header_cb(r, buf, val);
106 ustream_consume(s, line_len);
107 }
108 }
109
110 static void relay_read_cb(struct ustream *s, int bytes)
111 {
112 struct relay *r = container_of(s, struct relay, sfd.stream);
113 struct client *cl = r->cl;
114 struct ustream *us = cl->us;
115 char *buf;
116 int len;
117
118 if (r->process_done)
119 uloop_timeout_set(&r->timeout, 1);
120
121 relay_process_headers(r);
122
123 if (r->header_cb) {
124 /*
125 * if eof, ensure that remaining data is discarded, so the
126 * state change cb will tear down the stream
127 */
128 if (s->eof)
129 relay_error(r);
130 return;
131 }
132
133 if (!s->eof && ustream_pending_data(us, true)) {
134 ustream_set_read_blocked(s, true);
135 return;
136 }
137
138 buf = ustream_get_read_buf(s, &len);
139 if (!buf || !len)
140 return;
141
142 uh_chunk_write(cl, buf, len);
143 ustream_consume(s, len);
144 }
145
146 static void relay_close_if_done(struct uloop_timeout *timeout)
147 {
148 struct relay *r = container_of(timeout, struct relay, timeout);
149 struct ustream *s = &r->sfd.stream;
150
151 while (ustream_poll(&r->sfd.stream));
152
153 if (!(r->process_done || s->eof) || ustream_pending_data(s, false))
154 return;
155
156 uh_relay_close(r, r->ret);
157 }
158
159 static void relay_state_cb(struct ustream *s)
160 {
161 struct relay *r = container_of(s, struct relay, sfd.stream);
162
163 if (r->process_done)
164 uloop_timeout_set(&r->timeout, 1);
165 }
166
167 static void relay_proc_cb(struct uloop_process *proc, int ret)
168 {
169 struct relay *r = container_of(proc, struct relay, proc);
170
171 r->process_done = true;
172 r->ret = ret;
173 uloop_timeout_set(&r->timeout, 1);
174 }
175
176 void uh_relay_kill(struct client *cl, struct relay *r)
177 {
178 struct ustream *us = &r->sfd.stream;
179
180 kill(r->proc.pid, SIGKILL);
181 us->eof = true;
182 ustream_state_change(us);
183 }
184
185 void uh_relay_open(struct client *cl, struct relay *r, int fd, int pid)
186 {
187 struct ustream *us = &r->sfd.stream;
188
189 r->cl = cl;
190 us->notify_read = relay_read_cb;
191 us->notify_state = relay_state_cb;
192 us->string_data = true;
193 ustream_fd_init(&r->sfd, fd);
194
195 r->proc.pid = pid;
196 r->proc.cb = relay_proc_cb;
197 uloop_process_add(&r->proc);
198
199 r->timeout.cb = relay_close_if_done;
200 }