Deleted Added
full compact
accf_http.c (95552) accf_http.c (95759)
1/*
2 * Copyright (c) 2000 Paycounter, Inc.
3 * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
1/*
2 * Copyright (c) 2000 Paycounter, Inc.
3 * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/netinet/accf_http.c 95552 2002-04-27 08:24:29Z tanimura $
27 * $FreeBSD: head/sys/netinet/accf_http.c 95759 2002-04-30 01:54:54Z tanimura $
28 */
29
30#define ACCEPT_FILTER_MOD
31
32#include <sys/param.h>
33#include <sys/kernel.h>
28 */
29
30#define ACCEPT_FILTER_MOD
31
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/lock.h>
35#include <sys/mbuf.h>
36#include <sys/signalvar.h>
34#include <sys/sysctl.h>
35#include <sys/socketvar.h>
37#include <sys/sysctl.h>
38#include <sys/socketvar.h>
36#include <sys/mbuf.h>
39#include <sys/sx.h>
37
38/* check for GET/HEAD */
39static void sohashttpget(struct socket *so, void *arg, int waitflag);
40/* check for HTTP/1.0 or HTTP/1.1 */
41static void soparsehttpvers(struct socket *so, void *arg, int waitflag);
42/* check for end of HTTP/1.x request */
43static void soishttpconnected(struct socket *so, void *arg, int waitflag);
44/* strcmp on an mbuf chain */
45static int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp);
46/* strncmp on an mbuf chain */
47static int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
48 int max, char *cmp);
49/* socketbuffer is full */
50static int sbfull(struct sockbuf *sb);
51
52static struct accept_filter accf_http_filter = {
53 "httpready",
54 sohashttpget,
55 NULL,
56 NULL
57};
58
59static moduledata_t accf_http_mod = {
60 "accf_http",
61 accept_filt_generic_mod_event,
62 &accf_http_filter
63};
64
65DECLARE_MODULE(accf_http, accf_http_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
66
67static int parse_http_version = 1;
68
69SYSCTL_NODE(_net_inet_accf, OID_AUTO, http, CTLFLAG_RW, 0,
70"HTTP accept filter");
71SYSCTL_INT(_net_inet_accf_http, OID_AUTO, parsehttpversion, CTLFLAG_RW,
72&parse_http_version, 1,
73"Parse http version so that non 1.x requests work");
74
75#ifdef ACCF_HTTP_DEBUG
76#define DPRINT(fmt, args...) \
77 do { \
78 printf("%s:%d: " fmt "\n", __func__, __LINE__ , ##args); \
79 } while (0)
80#else
81#define DPRINT(fmt, args...)
82#endif
83
84static int
85sbfull(struct sockbuf *sb)
86{
87
88 DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, mbcnt(%ld) >= mbmax(%ld): %d",
89 sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
90 sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
91 return(sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
92}
93
94/*
95 * start at mbuf m, (must provide npkt if exists)
96 * starting at offset in m compare characters in mbuf chain for 'cmp'
97 */
98static int
99mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp)
100{
101 struct mbuf *n;
102
103 for (;m != NULL; m = n) {
104 n = npkt;
105 if (npkt)
106 npkt = npkt->m_nextpkt;
107 for (; m; m = m->m_next) {
108 for (; offset < m->m_len; offset++, cmp++) {
109 if (*cmp == '\0') {
110 return (1);
111 } else if (*cmp != *(mtod(m, char *) + offset)) {
112 return (0);
113 }
114 }
115 offset = 0;
116 }
117 }
118 return (0);
119}
120
121/*
122 * start at mbuf m, (must provide npkt if exists)
123 * starting at offset in m compare characters in mbuf chain for 'cmp'
124 * stop at 'max' characters
125 */
126static int
127mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int max, char *cmp)
128{
129 struct mbuf *n;
130
131 for (;m != NULL; m = n) {
132 n = npkt;
133 if (npkt)
134 npkt = npkt->m_nextpkt;
135 for (; m; m = m->m_next) {
136 for (; offset < m->m_len; offset++, cmp++, max--) {
137 if (max == 0 || *cmp == '\0') {
138 return (1);
139 } else if (*cmp != *(mtod(m, char *) + offset)) {
140 return (0);
141 }
142 }
143 offset = 0;
144 }
145 }
146 return (0);
147}
148
149#define STRSETUP(sptr, slen, str) \
150 do { \
151 sptr = str; \
152 slen = sizeof(str) - 1; \
153 } while(0)
154
155static void
156sohashttpget(struct socket *so, void *arg, int waitflag)
157{
158
159 if ((so->so_state & SS_CANTRCVMORE) == 0 && !sbfull(&so->so_rcv)) {
160 struct mbuf *m;
161 char *cmp;
162 int cmplen, cc;
163
164 m = so->so_rcv.sb_mb;
165 cc = so->so_rcv.sb_cc - 1;
166 if (cc < 1)
167 return;
168 switch (*mtod(m, char *)) {
169 case 'G':
170 STRSETUP(cmp, cmplen, "ET ");
171 break;
172 case 'H':
173 STRSETUP(cmp, cmplen, "EAD ");
174 break;
175 default:
176 goto fallout;
177 }
178 if (cc < cmplen) {
179 if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
180 DPRINT("short cc (%d) but mbufstrncmp ok", cc);
181 return;
182 } else {
183 DPRINT("short cc (%d) mbufstrncmp failed", cc);
184 goto fallout;
185 }
186 }
187 if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
188 DPRINT("mbufstrcmp ok");
189 if (parse_http_version == 0)
190 soishttpconnected(so, arg, waitflag);
191 else
192 soparsehttpvers(so, arg, waitflag);
193 return;
194 }
195 DPRINT("mbufstrcmp bad");
196 }
197
198fallout:
199 DPRINT("fallout");
200 SIGIO_SLOCK();
201 so->so_upcall = NULL;
202 so->so_rcv.sb_flags &= ~SB_UPCALL;
203 soisconnected_locked(so);
204 SIGIO_SUNLOCK();
205 return;
206}
207
208static void
209soparsehttpvers(struct socket *so, void *arg, int waitflag)
210{
211 struct mbuf *m, *n;
212 int i, cc, spaces, inspaces;
213
214 if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
215 goto fallout;
216
217 m = so->so_rcv.sb_mb;
218 cc = so->so_rcv.sb_cc;
219 inspaces = spaces = 0;
220 for (m = so->so_rcv.sb_mb; m; m = n) {
221 n = m->m_nextpkt;
222 for (; m; m = m->m_next) {
223 for (i = 0; i < m->m_len; i++, cc--) {
224 switch (*(mtod(m, char *) + i)) {
225 case ' ':
226 if (!inspaces) {
227 spaces++;
228 inspaces = 1;
229 }
230 break;
231 case '\r':
232 case '\n':
233 DPRINT("newline");
234 goto fallout;
235 default:
236 if (spaces == 2) {
237 /* make sure we have enough data left */
238 if (cc < sizeof("HTTP/1.0") - 1) {
239 if (mbufstrncmp(m, n, i, cc, "HTTP/1.") == 1) {
240 DPRINT("mbufstrncmp ok");
241 goto readmore;
242 } else {
243 DPRINT("mbufstrncmp bad");
244 goto fallout;
245 }
246 } else if (mbufstrcmp(m, n, i, "HTTP/1.0") == 1 ||
247 mbufstrcmp(m, n, i, "HTTP/1.1") == 1) {
248 DPRINT("mbufstrcmp ok");
249 soishttpconnected(so, arg, waitflag);
250 return;
251 } else {
252 DPRINT("mbufstrcmp bad");
253 goto fallout;
254 }
255 }
256 inspaces = 0;
257 break;
258 }
259 }
260 }
261 }
262readmore:
263 DPRINT("readmore");
264 /*
265 * if we hit here we haven't hit something
266 * we don't understand or a newline, so try again
267 */
268 so->so_upcall = soparsehttpvers;
269 so->so_rcv.sb_flags |= SB_UPCALL;
270 return;
271
272fallout:
273 DPRINT("fallout");
274 SIGIO_SLOCK();
275 so->so_upcall = NULL;
276 so->so_rcv.sb_flags &= ~SB_UPCALL;
277 soisconnected_locked(so);
278 SIGIO_SUNLOCK();
279 return;
280}
281
282
283#define NCHRS 3
284
285static void
286soishttpconnected(struct socket *so, void *arg, int waitflag)
287{
288 char a, b, c;
289 struct mbuf *m, *n;
290 int ccleft, copied;
291
292 DPRINT("start");
293 if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
294 goto gotit;
295
296 /*
297 * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
298 * copied - how much we've copied so far
299 * ccleft - how many bytes remaining in the socketbuffer
300 * just loop over the mbufs subtracting from 'ccleft' until we only
301 * have NCHRS left
302 */
303 copied = 0;
304 ccleft = so->so_rcv.sb_cc;
305 if (ccleft < NCHRS)
306 goto readmore;
307 a = b = c = '\0';
308 for (m = so->so_rcv.sb_mb; m; m = n) {
309 n = m->m_nextpkt;
310 for (; m; m = m->m_next) {
311 ccleft -= m->m_len;
312 if (ccleft <= NCHRS) {
313 char *src;
314 int tocopy;
315
316 tocopy = (NCHRS - ccleft) - copied;
317 src = mtod(m, char *) + (m->m_len - tocopy);
318
319 while (tocopy--) {
320 switch (copied++) {
321 case 0:
322 a = *src++;
323 break;
324 case 1:
325 b = *src++;
326 break;
327 case 2:
328 c = *src++;
329 break;
330 }
331 }
332 }
333 }
334 }
335 if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
336 /* we have all request headers */
337 goto gotit;
338 }
339
340readmore:
341 so->so_upcall = soishttpconnected;
342 so->so_rcv.sb_flags |= SB_UPCALL;
343 return;
344
345gotit:
346 SIGIO_SLOCK();
347 so->so_upcall = NULL;
348 so->so_rcv.sb_flags &= ~SB_UPCALL;
349 soisconnected_locked(so);
350 SIGIO_SUNLOCK();
351 return;
352}
40
41/* check for GET/HEAD */
42static void sohashttpget(struct socket *so, void *arg, int waitflag);
43/* check for HTTP/1.0 or HTTP/1.1 */
44static void soparsehttpvers(struct socket *so, void *arg, int waitflag);
45/* check for end of HTTP/1.x request */
46static void soishttpconnected(struct socket *so, void *arg, int waitflag);
47/* strcmp on an mbuf chain */
48static int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp);
49/* strncmp on an mbuf chain */
50static int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
51 int max, char *cmp);
52/* socketbuffer is full */
53static int sbfull(struct sockbuf *sb);
54
55static struct accept_filter accf_http_filter = {
56 "httpready",
57 sohashttpget,
58 NULL,
59 NULL
60};
61
62static moduledata_t accf_http_mod = {
63 "accf_http",
64 accept_filt_generic_mod_event,
65 &accf_http_filter
66};
67
68DECLARE_MODULE(accf_http, accf_http_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
69
70static int parse_http_version = 1;
71
72SYSCTL_NODE(_net_inet_accf, OID_AUTO, http, CTLFLAG_RW, 0,
73"HTTP accept filter");
74SYSCTL_INT(_net_inet_accf_http, OID_AUTO, parsehttpversion, CTLFLAG_RW,
75&parse_http_version, 1,
76"Parse http version so that non 1.x requests work");
77
78#ifdef ACCF_HTTP_DEBUG
79#define DPRINT(fmt, args...) \
80 do { \
81 printf("%s:%d: " fmt "\n", __func__, __LINE__ , ##args); \
82 } while (0)
83#else
84#define DPRINT(fmt, args...)
85#endif
86
87static int
88sbfull(struct sockbuf *sb)
89{
90
91 DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, mbcnt(%ld) >= mbmax(%ld): %d",
92 sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
93 sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
94 return(sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
95}
96
97/*
98 * start at mbuf m, (must provide npkt if exists)
99 * starting at offset in m compare characters in mbuf chain for 'cmp'
100 */
101static int
102mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp)
103{
104 struct mbuf *n;
105
106 for (;m != NULL; m = n) {
107 n = npkt;
108 if (npkt)
109 npkt = npkt->m_nextpkt;
110 for (; m; m = m->m_next) {
111 for (; offset < m->m_len; offset++, cmp++) {
112 if (*cmp == '\0') {
113 return (1);
114 } else if (*cmp != *(mtod(m, char *) + offset)) {
115 return (0);
116 }
117 }
118 offset = 0;
119 }
120 }
121 return (0);
122}
123
124/*
125 * start at mbuf m, (must provide npkt if exists)
126 * starting at offset in m compare characters in mbuf chain for 'cmp'
127 * stop at 'max' characters
128 */
129static int
130mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int max, char *cmp)
131{
132 struct mbuf *n;
133
134 for (;m != NULL; m = n) {
135 n = npkt;
136 if (npkt)
137 npkt = npkt->m_nextpkt;
138 for (; m; m = m->m_next) {
139 for (; offset < m->m_len; offset++, cmp++, max--) {
140 if (max == 0 || *cmp == '\0') {
141 return (1);
142 } else if (*cmp != *(mtod(m, char *) + offset)) {
143 return (0);
144 }
145 }
146 offset = 0;
147 }
148 }
149 return (0);
150}
151
152#define STRSETUP(sptr, slen, str) \
153 do { \
154 sptr = str; \
155 slen = sizeof(str) - 1; \
156 } while(0)
157
158static void
159sohashttpget(struct socket *so, void *arg, int waitflag)
160{
161
162 if ((so->so_state & SS_CANTRCVMORE) == 0 && !sbfull(&so->so_rcv)) {
163 struct mbuf *m;
164 char *cmp;
165 int cmplen, cc;
166
167 m = so->so_rcv.sb_mb;
168 cc = so->so_rcv.sb_cc - 1;
169 if (cc < 1)
170 return;
171 switch (*mtod(m, char *)) {
172 case 'G':
173 STRSETUP(cmp, cmplen, "ET ");
174 break;
175 case 'H':
176 STRSETUP(cmp, cmplen, "EAD ");
177 break;
178 default:
179 goto fallout;
180 }
181 if (cc < cmplen) {
182 if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
183 DPRINT("short cc (%d) but mbufstrncmp ok", cc);
184 return;
185 } else {
186 DPRINT("short cc (%d) mbufstrncmp failed", cc);
187 goto fallout;
188 }
189 }
190 if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
191 DPRINT("mbufstrcmp ok");
192 if (parse_http_version == 0)
193 soishttpconnected(so, arg, waitflag);
194 else
195 soparsehttpvers(so, arg, waitflag);
196 return;
197 }
198 DPRINT("mbufstrcmp bad");
199 }
200
201fallout:
202 DPRINT("fallout");
203 SIGIO_SLOCK();
204 so->so_upcall = NULL;
205 so->so_rcv.sb_flags &= ~SB_UPCALL;
206 soisconnected_locked(so);
207 SIGIO_SUNLOCK();
208 return;
209}
210
211static void
212soparsehttpvers(struct socket *so, void *arg, int waitflag)
213{
214 struct mbuf *m, *n;
215 int i, cc, spaces, inspaces;
216
217 if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
218 goto fallout;
219
220 m = so->so_rcv.sb_mb;
221 cc = so->so_rcv.sb_cc;
222 inspaces = spaces = 0;
223 for (m = so->so_rcv.sb_mb; m; m = n) {
224 n = m->m_nextpkt;
225 for (; m; m = m->m_next) {
226 for (i = 0; i < m->m_len; i++, cc--) {
227 switch (*(mtod(m, char *) + i)) {
228 case ' ':
229 if (!inspaces) {
230 spaces++;
231 inspaces = 1;
232 }
233 break;
234 case '\r':
235 case '\n':
236 DPRINT("newline");
237 goto fallout;
238 default:
239 if (spaces == 2) {
240 /* make sure we have enough data left */
241 if (cc < sizeof("HTTP/1.0") - 1) {
242 if (mbufstrncmp(m, n, i, cc, "HTTP/1.") == 1) {
243 DPRINT("mbufstrncmp ok");
244 goto readmore;
245 } else {
246 DPRINT("mbufstrncmp bad");
247 goto fallout;
248 }
249 } else if (mbufstrcmp(m, n, i, "HTTP/1.0") == 1 ||
250 mbufstrcmp(m, n, i, "HTTP/1.1") == 1) {
251 DPRINT("mbufstrcmp ok");
252 soishttpconnected(so, arg, waitflag);
253 return;
254 } else {
255 DPRINT("mbufstrcmp bad");
256 goto fallout;
257 }
258 }
259 inspaces = 0;
260 break;
261 }
262 }
263 }
264 }
265readmore:
266 DPRINT("readmore");
267 /*
268 * if we hit here we haven't hit something
269 * we don't understand or a newline, so try again
270 */
271 so->so_upcall = soparsehttpvers;
272 so->so_rcv.sb_flags |= SB_UPCALL;
273 return;
274
275fallout:
276 DPRINT("fallout");
277 SIGIO_SLOCK();
278 so->so_upcall = NULL;
279 so->so_rcv.sb_flags &= ~SB_UPCALL;
280 soisconnected_locked(so);
281 SIGIO_SUNLOCK();
282 return;
283}
284
285
286#define NCHRS 3
287
288static void
289soishttpconnected(struct socket *so, void *arg, int waitflag)
290{
291 char a, b, c;
292 struct mbuf *m, *n;
293 int ccleft, copied;
294
295 DPRINT("start");
296 if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
297 goto gotit;
298
299 /*
300 * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
301 * copied - how much we've copied so far
302 * ccleft - how many bytes remaining in the socketbuffer
303 * just loop over the mbufs subtracting from 'ccleft' until we only
304 * have NCHRS left
305 */
306 copied = 0;
307 ccleft = so->so_rcv.sb_cc;
308 if (ccleft < NCHRS)
309 goto readmore;
310 a = b = c = '\0';
311 for (m = so->so_rcv.sb_mb; m; m = n) {
312 n = m->m_nextpkt;
313 for (; m; m = m->m_next) {
314 ccleft -= m->m_len;
315 if (ccleft <= NCHRS) {
316 char *src;
317 int tocopy;
318
319 tocopy = (NCHRS - ccleft) - copied;
320 src = mtod(m, char *) + (m->m_len - tocopy);
321
322 while (tocopy--) {
323 switch (copied++) {
324 case 0:
325 a = *src++;
326 break;
327 case 1:
328 b = *src++;
329 break;
330 case 2:
331 c = *src++;
332 break;
333 }
334 }
335 }
336 }
337 }
338 if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
339 /* we have all request headers */
340 goto gotit;
341 }
342
343readmore:
344 so->so_upcall = soishttpconnected;
345 so->so_rcv.sb_flags |= SB_UPCALL;
346 return;
347
348gotit:
349 SIGIO_SLOCK();
350 so->so_upcall = NULL;
351 so->so_rcv.sb_flags &= ~SB_UPCALL;
352 soisconnected_locked(so);
353 SIGIO_SUNLOCK();
354 return;
355}