move timegm declaration to utils.h
[project/uhttpd.git] / relay.c
1 /*
2 * uhttpd - Tiny single-threaded httpd
3 *
4 * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
5 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
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 newline = strchr(buf, '\n');
80 if (!newline)
81 break;
82
83 line_len = newline + 1 - buf;
84 if (newline > buf && newline[-1] == '\r')
85 newline--;
86
87 *newline = 0;
88 if (newline == buf) {
89 r->header_cb = NULL;
90 if (r->header_end)
91 r->header_end(r);
92 ustream_consume(s, line_len);
93 break;
94 }
95
96 val = uh_split_header(buf);
97 if (!val) {
98 relay_error(r);
99 return;
100 }
101
102 r->header_cb(r, buf, val);
103 ustream_consume(s, line_len);
104 }
105 }
106
107 static void relay_read_cb(struct ustream *s, int bytes)
108 {
109 struct relay *r = container_of(s, struct relay, sfd.stream);
110 struct client *cl = r->cl;
111 struct ustream *us = cl->us;
112 char *buf;
113 int len;
114
115 relay_process_headers(r);
116
117 if (r->header_cb) {
118 /*
119 * if eof, ensure that remaining data is discarded, so the
120 * state change cb will tear down the stream
121 */
122 if (s->eof)
123 relay_error(r);
124 return;
125 }
126
127 if (!s->eof && ustream_pending_data(us, true)) {
128 ustream_set_read_blocked(s, true);
129 return;
130 }
131
132 buf = ustream_get_read_buf(s, &len);
133 uh_chunk_write(cl, buf, len);
134 ustream_consume(s, len);
135 }
136
137 static void relay_close_if_done(struct relay *r)
138 {
139 struct ustream *s = &r->sfd.stream;
140
141 if (!s->eof || ustream_pending_data(s, false))
142 return;
143
144 uh_relay_close(r, r->ret);
145 }
146
147 static void relay_state_cb(struct ustream *s)
148 {
149 struct relay *r = container_of(s, struct relay, sfd.stream);
150
151 if (r->process_done)
152 relay_close_if_done(r);
153 }
154
155 static void relay_proc_cb(struct uloop_process *proc, int ret)
156 {
157 struct relay *r = container_of(proc, struct relay, proc);
158
159 r->process_done = true;
160 r->ret = ret;
161 relay_close_if_done(r);
162 }
163
164 void uh_relay_open(struct client *cl, struct relay *r, int fd, int pid)
165 {
166 struct ustream *us = &r->sfd.stream;
167
168 r->cl = cl;
169 ustream_fd_init(&r->sfd, fd);
170 us->notify_read = relay_read_cb;
171 us->notify_state = relay_state_cb;
172 us->string_data = true;
173
174 r->proc.pid = pid;
175 r->proc.cb = relay_proc_cb;
176 uloop_process_add(&r->proc);
177 }