]> git.saurik.com Git - apple/security.git/blob - Network/http-protocol.cpp
203c39dcdc94733f41861afb57c382628cfe7afb
[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 state = readWholeBody;
294 if (const char *lengthArg = headers().find("Content-Length")) {
295 size_t length = strtol(lengthArg, NULL, 10);
296 sink().setSize(length);
297 if (length > 0)
298 mode(sink(), length);
299 else // null body, already done
300 finish();
301 } else { // read until EOI
302 mode(sink());
303 }
304 }
305 break;
306 }
307 case chunkHeader:
308 {
309 assert(mode() == lineInput);
310 // line should be (just) a hex number, sans "0x" prefix or spaces. Be strict
311 char *endOfMatch;
312 size_t chunkLength = strtol(input, &endOfMatch, 0x10);
313 if (length == 0 || endOfMatch == input) // no valid number
314 fail(Transfer::remoteFailure);
315 if (chunkLength) {
316 debug("http", "reading chunk of %ld bytes", chunkLength);
317 mode(sink(), chunkLength);
318 state = chunkDownload;
319 } else {
320 debug("http", "final chunk marker");
321 state = chunkTrailer;
322 observe(Observer::protocolReceive, "** END OF DATA **");
323 }
324 break;
325 }
326 case chunkGap:
327 {
328 assert(mode() == lineInput);
329 state = chunkHeader;
330 break;
331 }
332 case chunkTrailer:
333 {
334 assert(mode() == lineInput);
335 if (input[0] == '\0') { // end of trailer
336 finish();
337 } else {
338 headers().add(input);
339 observe(Observer::protocolReceive, input);
340 }
341 break;
342 }
343 case chunkDownload:
344 {
345 assert(event == autoReadDone);
346 state = chunkGap;
347 mode(lineInput);
348 break;
349 }
350 case readWholeBody:
351 {
352 assert(event == autoReadDone || event == endOfInput);
353 finish();
354 break;
355 }
356 case idle:
357 {
358 // the only asynchronous event in idle mode is a connection drop
359 debug("http",
360 "%p event %d while idle; destroying connection", this, event);
361 abort();
362 state = dead;
363 }
364 break;
365 default:
366 assert(false);
367 }
368 }
369
370 void HTTPProtocol::HTTPConnection::transitError(const CssmCommonError &error)
371 {
372 // note that fail(const char * [, OSStatus]) has already called setError
373 fail(true); // fail transfer and throw out connection
374 }
375
376
377 void HTTPProtocol::HTTPConnection::finish()
378 {
379 flushInput(); // clear excess garbage input (resynchronize)
380 chooseRetain(); // shall we keep the Connection?
381 mode(lineInput); // ensure valid input mode
382 state = idle; // idle state
383 Connection::finish(); // finish this transfer
384 }
385
386
387 void HTTPProtocol::HTTPConnection::fail(bool forceDrop)
388 {
389 if (forceDrop)
390 retain(false); // drop the Connection
391 else
392 chooseRetain(); // perhaps keep it
393 Connection::fail(); // fail this transfer
394 }
395
396
397 bool HTTPProtocol::HTTPConnection::validate()
398 {
399 assert(state == idle);
400 tickle(); // may change state
401 return state == idle;
402 }
403
404
405 void HTTPProtocol::HTTPConnection::chooseRetain()
406 {
407 // figure out whether to stay alive
408 retain(strcasecmp(headers().find("Connection", "Keep"), "Close"));
409 //@@@ need to handle the HTTP/1.0 case
410 }
411
412
413 //
414 // Transfer objects
415 //
416 HTTPProtocol::HTTPTransfer::HTTPTransfer(Protocol &proto,
417 const Target &tgt, Operation operation, IPPort defaultPort)
418 : Transfer(proto, tgt, operation, defaultPort),
419 mResultClass(unclassifiedFailure)
420 {
421 }
422
423 void HTTPProtocol::HTTPTransfer::start()
424 {
425 // HTTP servers can serve both proxy requests and direct requests,
426 // and can be pooled based on that fact. Use proxy==target here.
427 const HostTarget &host = proxyHostTarget();
428 HTTPConnection *connection = protocol.manager.findConnection<HTTPConnection>(host);
429 if (connection == NULL)
430 connection = new HTTPConnection(protocol, host);
431 connection->dock(this);
432 startRequest();
433 }
434
435 void HTTPProtocol::HTTPTransfer::abort()
436 {
437 observe(Observer::aborting);
438 setError("aborted");
439 connectionAs<HTTPConnection>().abort();
440 }
441
442 void HTTPProtocol::HTTPConnection::abort()
443 {
444 close();
445 fail(true);
446 }
447
448
449 //
450 // This lower-level request startup function can be called directly by children.
451 //
452 void HTTPProtocol::HTTPTransfer::startRequest()
453 {
454 const char *defaultForm;
455 switch (operation()) {
456 case Protocol::upload: defaultForm = "PUT"; break;
457 case Protocol::transaction: defaultForm = "POST"; break;
458 default: defaultForm = "GET"; break;
459 }
460 connectionAs<HTTPConnection>().request(getv<string>(kNetworkHttpCommand, defaultForm).c_str());
461 }
462
463
464 //
465 // Determine whether we should use the proxy form of HTTP headers.
466 // By default, this is true iff we are used by a proxy Protocol.
467 // However, children may override this determination.
468 //
469 bool HTTPProtocol::HTTPTransfer::useProxyHeaders() const
470 {
471 return protocol.isProxy();
472 }
473
474 Transfer::ResultClass HTTPProtocol::HTTPTransfer::resultClass() const
475 {
476 switch (state()) {
477 case failed:
478 return mResultClass;
479 case finished:
480 {
481 if (mResultClass != unclassifiedFailure)
482 return mResultClass; // preclassified
483 unsigned int code = httpResponseCode();
484 if (code == 401 || code == 407 || code == 305) // auth or proxy auth required
485 return authorizationFailure;
486 else if (code / 100 == 3) // redirect (interpreted as success)
487 return success;
488 else if (code / 100 == 2) // success codes
489 return success;
490 else // when in doubt, blame the remote end :-)
491 return remoteFailure;
492 }
493 default:
494 assert(false);
495 return localFailure;
496 }
497 }
498
499
500 void HTTPProtocol::HTTPTransfer::fail(ResultClass why, OSStatus how)
501 {
502 mResultClass = why;
503 Error::throwMe(how);
504 }
505
506
507 //
508 // Manage the HTTP version of a HeaderMap
509 //
510 void HTTPProtocol::HTTPHeaderMap::merge(string key, string &old, string newValue)
511 {
512 // duplicates must be CSV type; concatenate (RFC 2616; section 4.2)
513 old = old + ", " + newValue;
514 }
515
516
517 } // end namespace Network
518 } // end namespace Security