]> git.ipfire.org Git - thirdparty/squid.git/blob - src/servers/HttpServer.cc
Boilerplate: update copyright blurbs on src/
[thirdparty/squid.git] / src / servers / HttpServer.cc
1 /*
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 33 Client-side Routines */
10
11 #include "squid.h"
12 #include "client_side.h"
13 #include "client_side_request.h"
14 #include "comm/Write.h"
15 #include "HttpHeaderTools.h"
16 #include "profiler/Profiler.h"
17 #include "servers/forward.h"
18 #include "SquidConfig.h"
19
20 namespace Http
21 {
22
23 /// Manages a connection from an HTTP client.
24 class Server: public ConnStateData
25 {
26 public:
27 Server(const MasterXaction::Pointer &xact, const bool beHttpsServer);
28 virtual ~Server() {}
29
30 void readSomeHttpData();
31
32 protected:
33 /* ConnStateData API */
34 virtual ClientSocketContext *parseOneRequest(Http::ProtocolVersion &ver);
35 virtual void processParsedRequest(ClientSocketContext *context, const Http::ProtocolVersion &ver);
36 virtual void handleReply(HttpReply *rep, StoreIOBuffer receivedData);
37 virtual void writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call);
38 virtual time_t idleTimeout() const;
39
40 /* BodyPipe API */
41 virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer);
42 virtual void noteBodyConsumerAborted(BodyPipe::Pointer);
43
44 /* AsyncJob API */
45 virtual void start();
46
47 private:
48 void processHttpRequest(ClientSocketContext *const context);
49 void handleHttpRequestData();
50
51 HttpParser parser_;
52 HttpRequestMethod method_; ///< parsed HTTP method
53
54 /// temporary hack to avoid creating a true HttpsServer class
55 const bool isHttpsServer;
56
57 CBDATA_CLASS2(Server);
58 };
59
60 } // namespace Http
61
62 CBDATA_NAMESPACED_CLASS_INIT(Http, Server);
63
64 Http::Server::Server(const MasterXaction::Pointer &xact, bool beHttpsServer):
65 AsyncJob("Http::Server"),
66 ConnStateData(xact),
67 isHttpsServer(beHttpsServer)
68 {
69 }
70
71 time_t
72 Http::Server::idleTimeout() const
73 {
74 return Config.Timeout.clientIdlePconn;
75 }
76
77 void
78 Http::Server::start()
79 {
80 ConnStateData::start();
81
82 #if USE_OPENSSL
83 // XXX: Until we create an HttpsServer class, use this hack to allow old
84 // client_side.cc code to manipulate ConnStateData object directly
85 if (isHttpsServer) {
86 postHttpsAccept();
87 return;
88 }
89 #endif
90
91 typedef CommCbMemFunT<Server, CommTimeoutCbParams> TimeoutDialer;
92 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
93 TimeoutDialer, this, Http::Server::requestTimeout);
94 commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
95 readSomeData();
96 }
97
98 void
99 Http::Server::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
100 {
101 if (!handleRequestBodyData())
102 return;
103
104 // too late to read more body
105 if (!isOpen() || stoppedReceiving())
106 return;
107
108 readSomeData();
109 }
110
111 ClientSocketContext *
112 Http::Server::parseOneRequest(Http::ProtocolVersion &ver)
113 {
114 ClientSocketContext *context = NULL;
115 PROF_start(HttpServer_parseOneRequest);
116 HttpParserInit(&parser_, in.buf.c_str(), in.buf.length());
117 context = parseHttpRequest(this, &parser_, &method_, &ver);
118 PROF_stop(HttpServer_parseOneRequest);
119 return context;
120 }
121
122 void
123 Http::Server::processParsedRequest(ClientSocketContext *context, const Http::ProtocolVersion &ver)
124 {
125 clientProcessRequest(this, &parser_, context, method_, ver);
126 }
127
128 void
129 Http::Server::noteBodyConsumerAborted(BodyPipe::Pointer ptr)
130 {
131 ConnStateData::noteBodyConsumerAborted(ptr);
132 stopReceiving("virgin request body consumer aborted"); // closes ASAP
133 }
134
135 void
136 Http::Server::handleReply(HttpReply *rep, StoreIOBuffer receivedData)
137 {
138 // the caller guarantees that we are dealing with the current context only
139 ClientSocketContext::Pointer context = getCurrentContext();
140 Must(context != NULL);
141 const ClientHttpRequest *http = context->http;
142 Must(http != NULL);
143
144 // After sending Transfer-Encoding: chunked (at least), always send
145 // the last-chunk if there was no error, ignoring responseFinishedOrFailed.
146 const bool mustSendLastChunk = http->request->flags.chunkedReply &&
147 !http->request->flags.streamError &&
148 !context->startOfOutput();
149 const bool responseFinishedOrFailed = !rep &&
150 !receivedData.data &&
151 !receivedData.length;
152 if (responseFinishedOrFailed && !mustSendLastChunk) {
153 context->writeComplete(context->clientConnection, NULL, 0, Comm::OK);
154 return;
155 }
156
157 if (!context->startOfOutput()) {
158 context->sendBody(rep, receivedData);
159 return;
160 }
161
162 assert(rep);
163 http->al->reply = rep;
164 HTTPMSGLOCK(http->al->reply);
165 context->sendStartOfMessage(rep, receivedData);
166 }
167
168 void
169 Http::Server::writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call)
170 {
171 // apply selected clientReplyContext::buildReplyHeader() mods
172 // it is not clear what headers are required for control messages
173 rep->header.removeHopByHopEntries();
174 rep->header.putStr(HDR_CONNECTION, "keep-alive");
175 httpHdrMangleList(&rep->header, getCurrentContext()->http->request, ROR_REPLY);
176
177 MemBuf *mb = rep->pack();
178
179 debugs(11, 2, "HTTP Client " << clientConnection);
180 debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb->buf << "\n----------");
181
182 Comm::Write(context->clientConnection, mb, call);
183
184 delete mb;
185 }
186
187 ConnStateData *
188 Http::NewServer(MasterXactionPointer &xact)
189 {
190 return new Server(xact, false);
191 }
192
193 ConnStateData *
194 Https::NewServer(MasterXactionPointer &xact)
195 {
196 return new Http::Server(xact, true);
197 }