relay: do not process headers after the first error
[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         r->error = true;
59         s->eof = true;
60         ustream_get_read_buf(s, &len);
61         if (len)
62                 ustream_consume(s, len);
63         ustream_state_change(s);
64 }
65
66 static void relay_process_headers(struct relay *r)
67 {
68         struct ustream *s = &r->sfd.stream;
69         char *buf, *newline;
70         int len;
71
72         if (!r->header_cb)
73                 return;
74
75         while (r->header_cb) {
76                 int line_len;
77                 char *val;
78
79                 buf = ustream_get_read_buf(s, &len);
80                 if (!buf || !len)
81                         break;
82
83                 newline = strchr(buf, '\n');
84                 if (!newline)
85                         break;
86
87                 line_len = newline + 1 - buf;
88                 if (newline > buf && newline[-1] == '\r')
89                         newline--;
90
91                 *newline = 0;
92                 if (newline == buf) {
93                         r->header_cb = NULL;
94                         if (r->header_end)
95                                 r->header_end(r);
96                         ustream_consume(s, line_len);
97                         break;
98                 }
99
100                 val = uh_split_header(buf);
101                 if (!val) {
102                         relay_error(r);
103                         return;
104                 }
105
106                 r->header_cb(r, buf, val);
107                 ustream_consume(s, line_len);
108         }
109 }
110
111 static void relay_read_cb(struct ustream *s, int bytes)
112 {
113         struct relay *r = container_of(s, struct relay, sfd.stream);
114         struct client *cl = r->cl;
115         struct ustream *us = cl->us;
116         char *buf;
117         int len;
118
119         if (r->process_done)
120                 uloop_timeout_set(&r->timeout, 1);
121
122         if (!r->error)
123                 relay_process_headers(r);
124
125         if (r->header_cb) {
126                 /*
127                  * if eof, ensure that remaining data is discarded, so the
128                  * state change cb will tear down the stream
129                  */
130                 if (s->eof)
131                         relay_error(r);
132                 return;
133         }
134
135         if (!s->eof && ustream_pending_data(us, true)) {
136                 ustream_set_read_blocked(s, true);
137                 return;
138         }
139
140         buf = ustream_get_read_buf(s, &len);
141         if (!buf || !len)
142                 return;
143
144         uh_chunk_write(cl, buf, len);
145         ustream_consume(s, len);
146 }
147
148 static void relay_close_if_done(struct uloop_timeout *timeout)
149 {
150         struct relay *r = container_of(timeout, struct relay, timeout);
151         struct ustream *s = &r->sfd.stream;
152
153         while (ustream_poll(&r->sfd.stream));
154
155         if (!(r->process_done || s->eof) || ustream_pending_data(s, false))
156                 return;
157
158         uh_relay_close(r, r->ret);
159 }
160
161 static void relay_state_cb(struct ustream *s)
162 {
163         struct relay *r = container_of(s, struct relay, sfd.stream);
164
165         if (r->process_done)
166                 uloop_timeout_set(&r->timeout, 1);
167 }
168
169 static void relay_proc_cb(struct uloop_process *proc, int ret)
170 {
171         struct relay *r = container_of(proc, struct relay, proc);
172
173         r->process_done = true;
174         r->ret = ret;
175         uloop_timeout_set(&r->timeout, 1);
176 }
177
178 void uh_relay_kill(struct client *cl, struct relay *r)
179 {
180         struct ustream *us = &r->sfd.stream;
181
182         kill(r->proc.pid, SIGKILL);
183         us->eof = true;
184         ustream_state_change(us);
185 }
186
187 void uh_relay_open(struct client *cl, struct relay *r, int fd, int pid)
188 {
189         struct ustream *us = &r->sfd.stream;
190
191         r->cl = cl;
192         us->notify_read = relay_read_cb;
193         us->notify_state = relay_state_cb;
194         us->string_data = true;
195         ustream_fd_init(&r->sfd, fd);
196
197         r->proc.pid = pid;
198         r->proc.cb = relay_proc_cb;
199         uloop_process_add(&r->proc);
200
201         r->timeout.cb = relay_close_if_done;
202 }