]> git.saurik.com Git - apple/security.git/blob - Network/http-protocol.cpp
Security-28.tar.gz
[apple/security.git] / Network / http-protocol.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 //
20 // http-protocol - HTTP protocol objects
21 //
22 // HTTP Transfers succeed (state() == finished) if the HTTP protocol was successfully
23 // observed. This means that even 300/400/500 type results are "successful" as far
24 // as state() is concerned. ResultClass() will attempt to classify both successful and
25 // unsuccessful outcomes, and errorDescription() is the primary HTTP response line
26 // (HTTP/1.n ccc some-string). HTTP Transfers fail (state() == failed) only if they can't
27 // talk to the server, or a protocol violation (or unimplemented feature) is detected.
28 // Deal with it.
29 //
30 // Note that the protected flag deferSendRequest allows the state sequencer to be
31 // interrupted at the idle stage (before an HTTP request is sent over the virtual wire).
32 // This is used by the https protocol driver to "wedge in" the SSL negotiation. Not very
33 // elegant, but it works.
34 //
35 // This implementation of the http protocol includes http proxy operation. As a result,
36 // it is very important to distinguish the various HostTargets and Targets involved:
37 // Connection::hostTarget is the host we're talking to - it could be a proxy.
38 // Transfer::target.host is the host we're trying to reach.
39 // From the HTTPConnection's point of view:
40 // hostTarget may be a proxy or the destination
41 // target().host is always the host we're trying to reach
42 // If we're not in proxy mode, these two are usually the same (caveat tester).
43 //
44 #include "http-protocol.h"
45 #include "netparameters.h"
46
47
48 namespace Security {
49 namespace Network {
50
51
52 //
53 // Construct the protocol object
54 //
55 HTTPProtocol::HTTPProtocol(Manager &mgr, const char *scheme) : Protocol(mgr, scheme)
56 {
57 }
58
59
60 //
61 // Create a Transfer object for our protocol
62 //
63 HTTPProtocol::HTTPTransfer *HTTPProtocol::makeTransfer(const Target &target, Operation operation)
64 {
65 return new HTTPTransfer(*this, target, operation, defaultHttpPort);
66 }
67
68
69 //
70 // Construct an HTTPConnection object
71 //
72 HTTPProtocol::HTTPConnection::HTTPConnection(Protocol &proto,
73 const HostTarget &hostTarget)
74 : TCPConnection(proto, hostTarget),
75 state(errorState), deferSendRequest(false)
76 {
77 const HostTarget &host = proxyHostTarget();
78 connect(host.host(), host.port());
79 state = connecting;
80 }
81
82
83 //
84 // Start a request/response transaction on this Connection. This puts out all the
85 // HTTP request headers in one fell swoop (but not any request body).
86 // The Connection must be in idle state.
87 //
88 void HTTPProtocol::HTTPConnection::request(const char *operation)
89 {
90 mOperation = operation;
91 if (state == idle) // already waiting for request
92 sendRequest();
93 }
94
95 void HTTPProtocol::HTTPConnection::sendRequest()
96 {
97 assert(state == idle);
98
99 flushOutput(false); // hold output until we're done
100 const Target &target = this->target();
101 if (transfer().useProxyHeaders()) {
102 printfe("%s %s HTTP/1.1",
103 mOperation.c_str(), target.urlForm().c_str());
104 authorizationHeader("Proxy-Authorization", hostTarget,
105 kNetworkGenericProxyUsername, kNetworkGenericProxyPassword);
106 } else {
107 printfe("%s %s HTTP/1.1", mOperation.c_str(), target.path.c_str());
108 }
109 hostHeader();
110 authorizationHeader("Authorization", target,
111 kNetworkGenericUsername, kNetworkGenericPassword);
112 printfe("User-Agent: %s",
113 getv<string>(kNetworkHttpUserAgent, "MacNetwork/1.0 (Macintosh)").c_str());
114
115 // if restarting, add a Range header
116 if (int restartOffset = getv<int>(kNetworkRestartPosition, 0)) {
117 printfe("Range: bytes=%d-", restartOffset);
118 }
119
120 // add other headers set by caller, if any
121 {
122 string otherHeaders;
123 if (get(kNetworkHttpMoreHeaders, otherHeaders)) {
124 // launder and rinse - don't let the caller screw up the HTTP header structure
125 static const char lineEndings[] = "\r\n";
126 const char *p = otherHeaders.c_str();
127 while (const char *q = strpbrk(p, lineEndings)) {
128 if (q > p)
129 printfe("%.*s", q - p, p);
130 p = q + strspn(q, lineEndings);
131 }
132 // now send any last (unterminated) line
133 if (*p)
134 printfe("%s", p);
135 }
136 }
137
138 // add fields used for upstream transfer, if any, and initiate
139 if (transfer().hasSource()) {
140 Source &source = transfer().source();
141 size_t size = source.getSize();
142 if (size == Source::unknownSize) {
143 //@@@ try to use Transfer-encoding: chunked -- for now, just use EOF delimiting
144 } else {
145 printfe("Content-length: %ld", size);
146 }
147 printfe(""); // end of headers
148 mode(source); // initiate autoWrite mode
149 } else {
150 printfe(""); // end of headers, no data
151 }
152
153 flushOutput(); // release pent-up output
154 mode(lineInput); // line input mode
155 state = primaryResponse; // prime the state machine
156 }
157
158 void HTTPProtocol::HTTPConnection::hostHeader()
159 {
160 const HostTarget &host = target().host;
161 if (host.port())
162 printfe("Host: %s:%d", host.host().name().c_str(), host.port());
163 else
164 printfe("Host: %s", host.host().name().c_str());
165 }
166
167 void HTTPProtocol::HTTPConnection::authorizationHeader(const char *headerName,
168 const HostTarget &host,
169 ParameterSource::Key userKey, ParameterSource::Key passKey)
170 {
171 string username = host.haveUserPass() ? host.username() : getv<string>(userKey);
172 string password = host.haveUserPass() ? host.password() : getv<string>(passKey);
173 //@@@ only "Basic" authentication supported for now
174 if (!username.empty()) {
175 //@@@ ad-hoc Base64 encoding. Replace with suitable stream encoder when available
176 static const char alphabet[] =
177 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
178 string token = username + ":" + password;
179 char *buffer = new char[4 * token.length() / 3 + 2]; // just enough
180 const char *src = token.c_str(), *end = src + token.length();
181 char *outp = buffer;
182 while (src < end) {
183 uint32 binary = src[0] << 16;
184 if (src+1 < end)
185 binary |= src[1] << 8 | src[2];
186 *outp++ = alphabet[(binary >> 18) & 0x3F];
187 *outp++ = alphabet[(binary >> 12) & 0x3F];
188 *outp++ = (src+1 < end) ? alphabet[(binary >> 6) & 0x3F] : '=';
189 *outp++ = (src+2 < end) ? alphabet[binary & 0x3F] : '=';
190 src += 3;
191 }
192 *outp = '\0';
193 printfe("%s: Basic %s", headerName, buffer);
194 delete[] buffer;
195 }
196 }
197
198
199 //
200 // This is the master state transit machine for HTTP.
201 //
202 void HTTPProtocol::HTTPConnection::transit(Event event, char *input, size_t length)
203 {
204 switch (event) {
205 case autoWriteDone: // ingore: it's asynchronous to our state machine
206 return;
207 case endOfInput: // most of the time, this is a protocol error, so filter it out now
208 switch (state) {
209 case idle:
210 case readWholeBody: // expected
211 break;
212 case primaryResponse: // Connection failed; restart it
213 return restart();
214 default: // unexpected; fail
215 UnixError::throwMe(ECONNRESET); // @@@ diagnostic?
216 }
217 break;
218 case connectionDone: // TCP connection complete or failed
219 {
220 assert(state == connecting);
221 int error = length;
222 observe(Observer::connectEvent, &error);
223 if (error) { // retry
224 connect();
225 } else { // connection good
226 state = idle;
227 if (!deferSendRequest) { // (subclass wants to wedge in)
228 mode(lineInput);
229 sendRequest();
230 }
231 }
232 }
233 return;
234 default:
235 break;
236 }
237
238 switch (state) {
239 case primaryResponse:
240 {
241 assert(mode() == lineInput);
242 observe(Observer::protocolReceive, input);
243 transfer().httpResponse() = input; // remember response for caller
244 // --> HTTP/major.minor status reason-phrase
245 int reasonPos;
246 if (sscanf(input, "HTTP/%d.%d %u %n",
247 &httpVersionMajor, &httpVersionMinor,
248 &transfer().httpResponseCode(), &reasonPos) != 3) {
249 // malformed response header
250 fail(Transfer::remoteFailure);
251 }
252
253 if (httpVersionMajor != 1) // wrong major protocol Version
254 fail(Transfer::remoteFailure);
255 if (httpVersionMinor < 0 || httpVersionMinor > 1)
256 fail(Transfer::remoteFailure);
257
258 // notify the URLAccess emulation that we have the result code
259 observe (Observer::resultCodeReady);
260
261 // okay, we grok the version. We'll proceed for now reading headers etc.
262 state = readHeaders;
263
264 // we got input from the server, so this Connection is now confirmed good
265 restarting(false);
266 break;
267 }
268 case readHeaders:
269 {
270 assert(mode() == lineInput);
271 if (length) { // another header
272 headers().add(input);
273 observe(Observer::protocolReceive, input);
274 } else { // end of headers
275 // we are now handling the transition from response headers to response body
276 observe(Observer::protocolReceive, "** END OF HEADER **");
277
278 // Transfer-Encoding overrides Content-Length as per RFC2616 p.34
279 if (const char *encoding = headers().find("Transfer-Encoding")) {
280 if (!strcasecmp(encoding, "chunked")) {
281 // eat input in chunks
282 state = chunkHeader;
283 // mode remains lineInput
284 break;
285 } else if (!strcasecmp(encoding, "identity")) {
286 // allowed and ignored
287 } else {
288 // unrecognized transfer-encoding
289 fail(Transfer::remoteFailure);
290 }
291 }
292 // no transfer-encoding (or transfer-encoding: identity): big gulp mode
293 if (const char *lengthArg = headers().find("Content-Length")) {
294 size_t length = strtol(lengthArg, NULL, 10);
295 sink().setSize(length);
296 mode(sink(), length);
297 } else {
298 mode(sink());
299 }
300 state = readWholeBody;
301 }
302 break;
303 }
304 case chunkHeader:
305 {
306 assert(mode() == lineInput);
307 // line should be (just) a hex number, sans "0x" prefix or spaces. Be strict
308 char *endOfMatch;
309 size_t chunkLength = strtol(input, &endOfMatch, 0x10);
310 if (length == 0 || endOfMatch == input) // no valid number
311 fail(Transfer::remoteFailure);
312 if (chunkLength) {
313 debug("http", "reading chunk of %ld bytes", chunkLength);
314 mode(sink(), chunkLength);
315 state = chunkDownload;
316 } else {
317 debug("http", "final chunk marker");
318 state = chunkTrailer;
319 observe(Observer::protocolReceive, "** END OF DATA **");
320 }
321 break;
322 }
323 case chunkGap:
324 {
325 assert(mode() == lineInput);
326 state = chunkHeader;
327 break;
328 }
329 case chunkTrailer:
330 {
331 assert(mode() == lineInput);
332 if (input[0] == '\0') { // end of trailer
333 finish();
334 } else {
335 headers().add(input);
336 observe(Observer::protocolReceive, input);
337 }
338 break;
339 }
340 case chunkDownload:
341 {
342 assert(event == autoReadDone);
343 state = chunkGap;
344 mode(lineInput);
345 break;
346 }
347 case readWholeBody:
348 {
349 assert(event == autoReadDone || event == endOfInput);
350 finish();
351 break;
352 }
353 case idle:
354 {
355 // the only asynchronous event in idle mode is a connection drop
356 debug("http",
357 "event %d while idle; destroying connection", event);
358 abort();
359 state = dead;
360 }
361 break;
362 default:
363 assert(false);
364 }
365 }
366
367 void HTTPProtocol::HTTPConnection::transitError(const CssmCommonError &error)
368 {
369 // note that fail(const char * [, OSStatus]) has already called setError
370 fail(true); // fail transfer and throw out connection
371 }
372
373
374 void HTTPProtocol::HTTPConnection::finish()
375 {
376 chooseRetain(); // shall we keep the Connection?
377 Connection::finish(); // finish this transfer
378 mode(lineInput); // ensure valid input mode
379 state = idle; // idle state
380 }
381
382
383 void HTTPProtocol::HTTPConnection::fail(bool forceDrop)
384 {
385 if (forceDrop)
386 retain(false); // drop the Connection
387 else
388 chooseRetain(); // perhaps keep it
389 Connection::fail(); // fail this transfer
390 }
391
392
393 bool HTTPProtocol::HTTPConnection::validate()
394 {
395 assert(state == idle);
396 tickle(); // may change state
397 return state == idle;
398 }
399
400
401 void HTTPProtocol::HTTPConnection::chooseRetain()
402 {
403 // figure out whether to stay alive
404 retain(strcasecmp(headers().find("Connection", "Keep"), "Close"));
405 //@@@ need to handle the HTTP/1.0 case
406 }
407
408
409 //
410 // Transfer objects
411 //
412 HTTPProtocol::HTTPTransfer::HTTPTransfer(Protocol &proto,
413 const Target &tgt, Operation operation, IPPort defaultPort)
414 : Transfer(proto, tgt, operation, defaultPort),
415 mResultClass(unclassifiedFailure)
416 {
417 }
418
419 void HTTPProtocol::HTTPTransfer::start()
420 {
421 // HTTP servers can serve both proxy requests and direct requests,
422 // and can be pooled based on that fact. Use proxy==target here.
423 const HostTarget &host = proxyHostTarget();
424 HTTPConnection *connection = protocol.manager.findConnection<HTTPConnection>(host);
425 if (connection == NULL)
426 connection = new HTTPConnection(protocol, host);
427 connection->dock(this);
428 startRequest();
429 }
430
431 void HTTPProtocol::HTTPTransfer::abort()
432 {
433 setError("aborted");
434 connectionAs<HTTPConnection>().abort();
435 }
436
437 void HTTPProtocol::HTTPConnection::abort()
438 {
439 close();
440 fail(true);
441 }
442
443
444 //
445 // This lower-level request startup function can be called directly by children.
446 //
447 void HTTPProtocol::HTTPTransfer::startRequest()
448 {
449 const char *defaultForm;
450 switch (operation()) {
451 case Protocol::upload: defaultForm = "PUT"; break;
452 case Protocol::transaction: defaultForm = "POST"; break;
453 default: defaultForm = "GET"; break;
454 }
455 connectionAs<HTTPConnection>().request(getv<string>(kNetworkHttpCommand, defaultForm).c_str());
456 }
457
458
459 //
460 // Determine whether we should use the proxy form of HTTP headers.
461 // By default, this is true iff we are used by a proxy Protocol.
462 // However, children may override this determination.
463 //
464 bool HTTPProtocol::HTTPTransfer::useProxyHeaders() const
465 {
466 return protocol.isProxy();
467 }
468
469 Transfer::ResultClass HTTPProtocol::HTTPTransfer::resultClass() const
470 {
471 switch (state()) {
472 case failed:
473 return mResultClass;
474 case finished:
475 {
476 if (mResultClass != unclassifiedFailure)
477 return mResultClass; // preclassified
478 unsigned int code = httpResponseCode();
479 if (code == 401 || code == 407) // auth or proxy auth required
480 return authorizationFailure;
481 else if (code / 100 == 2) // success codes
482 return success;
483 else // when in doubt, blame the remote end :-)
484 return remoteFailure;
485 }
486 default:
487 assert(false);
488 return localFailure;
489 }
490 }
491
492
493 void HTTPProtocol::HTTPTransfer::fail(ResultClass why, OSStatus how)
494 {
495 mResultClass = why;
496 Error::throwMe(how);
497 }
498
499
500 //
501 // Manage the HTTP version of a HeaderMap
502 //
503 void HTTPProtocol::HTTPHeaderMap::merge(string key, string &old, string newValue)
504 {
505 // duplicates must be CSV type; concatenate (RFC 2616; section 4.2)
506 old = old + ", " + newValue;
507 }
508
509
510 } // end namespace Network
511 } // end namespace Security