]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpRequest.cc
- Many ICAP fixes from Alex Rousskov accumulated on the
[thirdparty/squid.git] / src / HttpRequest.cc
1
2 /*
3 * $Id: HttpRequest.cc,v 1.69 2006/10/31 23:30:56 wessels Exp $
4 *
5 * DEBUG: section 73 HTTP Request
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
35 */
36
37 #include "squid.h"
38 #include "HttpRequest.h"
39 #include "AuthUserRequest.h"
40 #include "HttpHeaderRange.h"
41 #include "MemBuf.h"
42 #include "Store.h"
43
44 HttpRequest::HttpRequest() : HttpMsg(hoRequest)
45 {
46 init();
47 }
48
49 HttpRequest::HttpRequest(method_t aMethod, protocol_t aProtocol, const char *aUrlpath) : HttpMsg(hoRequest)
50 {
51 init();
52 initHTTP(aMethod, aProtocol, aUrlpath);
53 }
54
55 HttpRequest::~HttpRequest()
56 {
57 clean();
58 }
59
60 void
61 HttpRequest::initHTTP(method_t aMethod, protocol_t aProtocol, const char *aUrlpath)
62 {
63 method = aMethod;
64 protocol = aProtocol;
65 urlpath = aUrlpath;
66 }
67
68 void
69 HttpRequest::init()
70 {
71 method = METHOD_NONE;
72 protocol = PROTO_NONE;
73 urlpath = NULL;
74 login[0] = '\0';
75 host[0] = '\0';
76 auth_user_request = NULL;
77 port = 0;
78 canonical = NULL;
79 memset(&flags, '\0', sizeof(flags));
80 range = NULL;
81 ims = -1;
82 imslen = 0;
83 lastmod = -1;
84 max_forwards = -1;
85 client_addr = no_addr;
86 my_addr = no_addr;
87 my_port = 0;
88 client_port = 0;
89 body_reader = NULL;
90 // hier
91 errType = ERR_NONE;
92 peer_login = NULL; // not allocated/deallocated by this class
93 peer_domain = NULL; // not allocated/deallocated by this class
94 vary_headers = NULL;
95 tag = null_string;
96 extacl_user = null_string;
97 extacl_passwd = null_string;
98 extacl_log = null_string;
99 pstate = psReadyToParseStartLine;
100 }
101
102 void
103 HttpRequest::clean()
104 {
105 if (body_reader != NULL)
106 fatal ("request being destroyed with body reader intact\n");
107
108 if (auth_user_request) {
109 auth_user_request->unlock();
110 auth_user_request = NULL;
111 }
112
113 safe_free(canonical);
114
115 safe_free(vary_headers);
116
117 urlpath.clean();
118
119 header.clean();
120
121 if (cache_control) {
122 httpHdrCcDestroy(cache_control);
123 cache_control = NULL;
124 }
125
126 if (range) {
127 delete range;
128 range = NULL;
129 }
130
131 tag.clean();
132
133 extacl_user.clean();
134
135 extacl_passwd.clean();
136
137 extacl_log.clean();
138 }
139
140 void
141 HttpRequest::reset()
142 {
143 clean();
144 init();
145 }
146
147 bool
148 HttpRequest::sanityCheckStartLine(MemBuf *buf, http_status *error)
149 {
150 /*
151 * Just see if the request buffer starts with a known
152 * HTTP request method. NOTE this whole function is somewhat
153 * superfluous and could just go away.
154 */
155
156 if (METHOD_NONE == HttpRequestMethod(buf->content())) {
157 debug(73, 3)("HttpRequest::sanityCheckStartLine: did not find HTTP request method\n");
158 return false;
159 }
160
161 return true;
162 }
163
164 bool
165 HttpRequest::parseFirstLine(const char *start, const char *end)
166 {
167 const char *t = start + strcspn(start, w_space);
168 method = HttpRequestMethod(start, t);
169
170 if (METHOD_NONE == method)
171 return false;
172
173 start = t + strspn(t, w_space);
174
175 const char *ver = findTrailingHTTPVersion(start, end);
176
177 if (ver) {
178 end = ver - 1;
179
180 while (xisspace(*end)) // find prev non-space
181 end--;
182
183 end++; // back to space
184
185 if (2 != sscanf(ver + 5, "%d.%d", &http_ver.major, &http_ver.minor)) {
186 debug(73, 1) ("parseRequestLine: Invalid HTTP identifier.\n");
187 return false;
188 }
189 } else {
190 http_ver.major = 0;
191 http_ver.minor = 9;
192 }
193
194 if (end < start) // missing URI
195 return false;
196
197 char save = *end;
198
199 * (char *) end = '\0'; // temp terminate URI, XXX dangerous?
200
201 HttpRequest *tmp = urlParse(method, (char *) start, this);
202
203 * (char *) end = save;
204
205 if (NULL == tmp)
206 return false;
207
208 return true;
209 }
210
211 int
212 HttpRequest::parseHeader(const char *parse_start, int len)
213 {
214 const char *blk_start, *blk_end;
215
216 if (!httpMsgIsolateHeaders(&parse_start, len, &blk_start, &blk_end))
217 return 0;
218
219 int result = header.parse(blk_start, blk_end);
220
221 if (result)
222 hdrCacheInit();
223
224 return result;
225 }
226
227 /* swaps out request using httpRequestPack */
228 void
229 HttpRequest::swapOut(StoreEntry * e)
230 {
231 Packer p;
232 assert(e);
233 packerToStoreInit(&p, e);
234 pack(&p);
235 packerClean(&p);
236 }
237
238 /* packs request-line and headers, appends <crlf> terminator */
239 void
240 HttpRequest::pack(Packer * p)
241 {
242 assert(p);
243 /* pack request-line */
244 packerPrintf(p, "%s %s HTTP/1.0\r\n",
245 RequestMethodStr[method], urlpath.buf());
246 /* headers */
247 header.packInto(p);
248 /* trailer */
249 packerAppend(p, "\r\n", 2);
250 }
251
252 /*
253 * A wrapper for debugObj()
254 */
255 void
256 httpRequestPack(void *obj, Packer *p)
257 {
258 HttpRequest *request = static_cast<HttpRequest*>(obj);
259 request->pack(p);
260 }
261
262 /* returns the length of request line + headers + crlf */
263 int
264 HttpRequest::prefixLen()
265 {
266 return strlen(RequestMethodStr[method]) + 1 +
267 urlpath.size() + 1 +
268 4 + 1 + 3 + 2 +
269 header.len + 2;
270 }
271
272 /*
273 * Returns true if HTTP allows us to pass this header on. Does not
274 * check anonymizer (aka header_access) configuration.
275 */
276 int
277 httpRequestHdrAllowed(const HttpHeaderEntry * e, String * strConn)
278 {
279 assert(e);
280 /* check connection header */
281
282 if (strConn && strListIsMember(strConn, e->name.buf(), ','))
283 return 0;
284
285 return 1;
286 }
287
288 /* sync this routine when you update HttpRequest struct */
289 void
290 HttpRequest::hdrCacheInit()
291 {
292 HttpMsg::hdrCacheInit();
293
294 range = header.getRange();
295 }
296
297 /* request_flags */
298 bool
299 request_flags::resetTCP() const
300 {
301 return reset_tcp != 0;
302 }
303
304 void
305 request_flags::setResetTCP()
306 {
307 debug (73, 9) ("request_flags::setResetTCP\n");
308 reset_tcp = 1;
309 }
310
311 void
312 request_flags::clearResetTCP()
313 {
314 debug(73, 9) ("request_flags::clearResetTCP\n");
315 reset_tcp = 0;
316 }
317
318 bool
319 HttpRequest::multipartRangeRequest() const
320 {
321 return (range && range->specs.count > 1);
322 }
323
324 void
325 request_flags::destinationIPLookupCompleted()
326 {
327 destinationIPLookedUp_ = true;
328 }
329
330 bool
331 request_flags::destinationIPLookedUp() const
332 {
333 return destinationIPLookedUp_;
334 }
335
336 const char *HttpRequest::packableURI(bool full_uri) const
337 {
338 if (full_uri)
339 return urlCanonical((HttpRequest*)this);
340
341 if (urlpath.size())
342 return urlpath.buf();
343
344 return "/";
345 }
346
347 void HttpRequest::packFirstLineInto(Packer * p, bool full_uri) const
348 {
349 // form HTTP request-line
350 packerPrintf(p, "%s %s HTTP/%d.%d\r\n",
351 RequestMethodStr[method],
352 packableURI(full_uri),
353 http_ver.major, http_ver.minor);
354 }
355
356 /*
357 * Indicate whether or not we would usually expect an entity-body
358 * along with this request
359 */
360 bool
361 HttpRequest::expectingBody(method_t unused, ssize_t& theSize) const
362 {
363 bool expectBody = false;
364
365 /*
366 * GET and HEAD don't usually have bodies, but we should be prepared
367 * to accept one if the request_entities directive is set
368 */
369
370 if (method == METHOD_GET || method == METHOD_HEAD)
371 expectBody = Config.onoff.request_entities ? true : false;
372 else if (method == METHOD_PUT || method == METHOD_POST)
373 expectBody = true;
374 else if (header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ','))
375 expectBody = true;
376 else if (content_length >= 0)
377 expectBody = true;
378 else
379 expectBody = false;
380
381 if (expectBody) {
382 if (header.hasListMember(HDR_TRANSFER_ENCODING, "chunked", ','))
383 theSize = -1;
384 else if (content_length >= 0)
385 theSize = content_length;
386 else
387 theSize = -1;
388 }
389
390 return expectBody;
391 }
392
393 /*
394 * Create a Request from a URL and METHOD.
395 *
396 * If the METHOD is CONNECT, then a host:port pair is looked for instead of a URL.
397 * If the request cannot be created cleanly, NULL is returned
398 */
399 HttpRequest *
400 HttpRequest::CreateFromUrlAndMethod(char * url, method_t method)
401 {
402 return urlParse(method, url, NULL);
403 }
404
405 /*
406 * Create a Request from a URL.
407 *
408 * If the request cannot be created cleanly, NULL is returned
409 */
410 HttpRequest *
411 HttpRequest::CreateFromUrl(char * url)
412 {
413 return urlParse(METHOD_GET, url, NULL);
414 }
415
416 /*
417 * Are responses to this request possible cacheable ?
418 * If false then no matter what the response must not be cached.
419 */
420 bool
421 HttpRequest::cacheable() const
422 {
423 if (protocol == PROTO_HTTP)
424 return httpCachable(method);
425
426 /* FTP is always cachable */
427
428 /* WAIS is never cachable */
429 if (protocol == PROTO_WAIS)
430 return 0;
431
432 /*
433 * The below looks questionable: what non HTTP protocols use connect,
434 * trace, put and post? RC
435 */
436 if (method == METHOD_CONNECT)
437 return 0;
438
439 if (method == METHOD_TRACE)
440 return 0;
441
442 if (method == METHOD_PUT)
443 return 0;
444
445 if (method == METHOD_POST)
446 return 0;
447
448 /*
449 * XXX POST may be cached sometimes.. ignored
450 * for now
451 */
452 if (protocol == PROTO_GOPHER)
453 return gopherCachable(this);
454
455 if (protocol == PROTO_CACHEOBJ)
456 return 0;
457
458 return 1;
459 }