]> git.saurik.com Git - apple/security.git/blob - Network/http-protocol.cpp
34450cf0f06721bb5e445fe7109d3085a3fcecd5
[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 subVersion(defaultSubVersion),
76 state(errorState), deferSendRequest(false)
77 {
78 const HostTarget &host = proxyHostTarget();
79 connect(host.host(), host.port());
80 state = connecting;
81 }
82
83
84 //
85 // Start a request/response transaction on this Connection. This puts out all the
86 // HTTP request headers in one fell swoop (but not any request body).
87 // The Connection must be in idle state.
88 //
89 void HTTPProtocol::HTTPConnection::request(const char *operation)
90 {
91 mOperation = operation;
92 if (state == idle) // already waiting for request
93 sendRequest();
94 }
95
96 void HTTPProtocol::HTTPConnection::sendRequest()
97 {
98 assert(state == idle);
99
100 // what version of HTTP/1 shall we use?
101 subVersion = getv<int>(kNetworkHttpUseVersion, defaultSubVersion);
102
103 flushOutput(false); // hold output until we're done
104 const Target &target = this->target();
105 if (transfer().useProxyHeaders()) {
106 printfe("%s %s HTTP/1.%d", mOperation.c_str(), target.urlForm().c_str(), subVersion);
107 authorizationHeader("Proxy-Authorization", hostTarget,
108 kNetworkGenericProxyUsername, kNetworkGenericProxyPassword);
109 } else {
110 printfe("%s %s HTTP/1.%d", mOperation.c_str(), target.path.c_str(), subVersion);
111 }
112 hostHeader();
113 authorizationHeader("Authorization", target,
114 kNetworkGenericUsername, kNetworkGenericPassword);
115 printfe("User-Agent: %s",
116 getv<string>(kNetworkHttpUserAgent, "MacNetwork/1.0 (Macintosh)").c_str());
117
118 // if restarting, add a Range header
119 if (int restartOffset = getv<int>(kNetworkRestartPosition, 0)) {
120 printfe("Range: bytes=%d-", restartOffset);
121 }
122
123 // add other headers set by caller, if any
124 {
125 string otherHeaders;
126 if (get(kNetworkHttpMoreHeaders, otherHeaders)) {
127 // launder and rinse - don't let the caller screw up the HTTP header structure
128 static const char lineEndings[] = "\r\n";
129 const char *p = otherHeaders.c_str();
130 while (const char *q = strpbrk(p, lineEndings)) {
131 if (q > p)
132 printfe("%.*s", q - p, p);
133 p = q + strspn(q, lineEndings);
134 }
135 // now send any last (unterminated) line
136 if (*p)
137 printfe("%s", p);
138 }
139 }
140
141 // add fields used for upstream transfer, if any, and initiate
142 if (transfer().hasSource()) {
143 Source &source = transfer().source();
144 size_t size = source.getSize();
145 if (size == Source::unknownSize) {
146 //@@@ try to use Transfer-encoding: chunked -- for now, just use EOF delimiting
147 } else {
148 printfe("Content-length: %ld", size);
149 }
150 printfe("Content-Type: %s", getv<string>(kNetworkHttpPostContentType, "text/plain").c_str());
151 printfe(""); // end of headers
152 mode(source); // initiate autoWrite mode
153 } else {
154 printfe(""); // end of headers, no data
155 }
156
157 flushOutput(); // release pent-up output
158 mode(lineInput); // line input mode
159 state = primaryResponse; // prime the state machine
160 }
161
162 void HTTPProtocol::HTTPConnection::hostHeader()
163 {
164 const HostTarget &host = target().host;
165 if (host.port())
166 printfe("Host: %s:%d", host.host().name().c_str(), host.port());
167 else
168 printfe("Host: %s", host.host().name().c_str());
169 }
170
171 void HTTPProtocol::HTTPConnection::authorizationHeader(const char *headerName,
172 const HostTarget &host,
173 ParameterSource::Key userKey, ParameterSource::Key passKey)
174 {
175 string username = host.haveUserPass() ? host.username() : getv<string>(userKey);
176 string password = host.haveUserPass() ? host.password() : getv<string>(passKey);
177 //@@@ only "Basic" authentication supported for now
178 if (!username.empty()) {
179 //@@@ ad-hoc Base64 encoding. Replace with suitable stream encoder when available
180 static const char alphabet[] =
181 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
182 string token = username + ":" + password;
183 char *buffer = new char[4 * token.length() / 3 + 2]; // just enough
184 const char *src = token.c_str(), *end = src + token.length();
185 char *outp = buffer;
186 while (src < end) {
187 uint32 binary = src[0] << 16;
188 if (src+1 < end)
189 binary |= src[1] << 8 | src[2];
190 *outp++ = alphabet[(binary >> 18) & 0x3F];
191 *outp++ = alphabet[(binary >> 12) & 0x3F];
192 *outp++ = (src+1 < end) ? alphabet[(binary >> 6) & 0x3F] : '=';
193 *outp++ = (src+2 < end) ? alphabet[binary & 0x3F] : '=';
194 src += 3;
195 }
196 *outp = '\0';
197 printfe("%s: Basic %s", headerName, buffer);
198 delete[] buffer;
199 }
200 }
201
202
203 //
204 // This is the master state transit machine for HTTP.
205 //
206 void HTTPProtocol::HTTPConnection::transit(Event event, char *input, size_t length)
207 {
208 switch (event) {
209 case autoWriteDone: // ingore: it's asynchronous to our state machine
210 return;
211 case endOfInput: // most of the time, this is a protocol error, so filter it out now
212 switch (state) {
213 case idle:
214 case readWholeBody: // expected
215 break;
216 case primaryResponse: // Connection failed; restart it
217 return restart();
218 default: // unexpected; fail
219 UnixError::throwMe(ECONNRESET); // @@@ diagnostic?
220 }
221 break;
222 case connectionDone: // TCP connection complete or failed
223 {
224 assert(state == connecting);
225 int error = length;
226 observe(Observer::connectEvent, &error);
227 if (error) { // retry
228 connect();
229 } else { // connection good
230 state = idle;
231 if (!deferSendRequest) { // (subclass wants to wedge in)
232 mode(lineInput);
233 sendRequest();
234 }
235 }
236 }
237 return;
238 default:
239 break;
240 }
241
242 switch (state) {
243 case primaryResponse:
244 {
245 assert(mode() == lineInput);
246 observe(Observer::protocolReceive, input);
247 transfer().httpResponse() = input; // remember response for caller
248 // --> HTTP/major.minor status reason-phrase
249 int reasonPos;
250 if (sscanf(input, "HTTP/%d.%d %u %n",
251 &httpVersionMajor, &httpVersionMinor,
252 &transfer().httpResponseCode(), &reasonPos) != 3) {
253 // malformed response header
254 fail(Transfer::remoteFailure);
255 }
256
257 if (httpVersionMajor != 1) // wrong major protocol Version
258 fail(Transfer::remoteFailure);
259 if (httpVersionMinor < 0 || httpVersionMinor > 1)
260 fail(Transfer::remoteFailure);
261
262 // notify the URLAccess emulation that we have the result code
263 observe (Observer::resultCodeReady);
264
265 // okay, we grok the version. We'll proceed for now reading headers etc.
266 state = readHeaders;
267
268 // we got input from the server, so this Connection is now confirmed good
269 restarting(false);
270 break;
271 }
272 case readHeaders:
273 {
274 assert(mode() == lineInput);
275 if (length) { // another header
276 headers().add(input);
277 observe(Observer::protocolReceive, input);
278 } else { // end of headers
279 // we are now handling the transition from response headers to response body
280 observe(Observer::protocolReceive, "** END OF HEADER **");
281 observe(Observer::downloading, input);
282
283 // Transfer-Encoding overrides Content-Length as per RFC2616 p.34
284 if (const char *encoding = headers().find("Transfer-Encoding")) {
285 if (!strcasecmp(encoding, "chunked")) {
286 // eat input in chunks
287 state = chunkHeader;
288 // mode remains lineInput
289 break;
290 } else if (!strcasecmp(encoding, "identity")) {
291 // allowed and ignored
292 } else {
293 // unrecognized transfer-encoding
294 fail(Transfer::remoteFailure);
295 }
296 }
297 // no transfer-encoding (or transfer-encoding: identity): big gulp mode
298 state = readWholeBody;
299 if (const char *lengthArg = headers().find("Content-Length")) {
300 size_t length = strtol(lengthArg, NULL, 10);
301 sink().setSize(length);
302 if (length > 0)
303 mode(sink(), length);
304 else // null body, already done
305 finish();
306 } else { // read until EOI
307 mode(sink());
308 }
309 }
310 break;
311 }
312 case chunkHeader:
313 {
314 assert(mode() == lineInput);
315 // line should be (just) a hex number, sans "0x" prefix or spaces. Be strict
316 char *endOfMatch;
317 size_t chunkLength = strtol(input, &endOfMatch, 0x10);
318 if (length == 0 || endOfMatch == input) // no valid number
319 fail(Transfer::remoteFailure);
320 if (chunkLength) {
321 secdebug("http", "reading chunk of %ld bytes", chunkLength);
322 mode(sink(), chunkLength);
323 state = chunkDownload;
324 } else {
325 secdebug("http", "final chunk marker");
326 state = chunkTrailer;
327 observe(Observer::protocolReceive, "** END OF DATA **");
328 }
329 break;
330 }
331 case chunkGap:
332 {
333 assert(mode() == lineInput);
334 state = chunkHeader;
335 break;
336 }
337 case chunkTrailer:
338 {
339 assert(mode() == lineInput);
340 if (input[0] == '\0') { // end of trailer
341 finish();
342 } else {
343 headers().add(input);
344 observe(Observer::protocolReceive, input);
345 }
346 break;
347 }
348 case chunkDownload:
349 {
350 assert(event == autoReadDone);
351 state = chunkGap;
352 mode(lineInput);
353 break;
354 }
355 case readWholeBody:
356 {
357 assert(event == autoReadDone || event == endOfInput);
358 finish();
359 break;
360 }
361 case idle:
362 {
363 // the only asynchronous event in idle mode is a connection drop
364 secdebug("http",
365 "%p event %d while idle; destroying connection", this, event);
366 abort();
367 state = dead;
368 }
369 break;
370 default:
371 assert(false);
372 }
373 }
374
375 void HTTPProtocol::HTTPConnection::transitError(const CssmCommonError &error)
376 {
377 // note that fail(const char * [, OSStatus]) has already called setError
378 fail(true); // fail transfer and throw out connection
379 }
380
381
382 void HTTPProtocol::HTTPConnection::finish()
383 {
384 flushInput(); // clear excess garbage input (resynchronize)
385 chooseRetain(); // shall we keep the Connection?
386 mode(lineInput); // ensure valid input mode
387 state = idle; // idle state
388 Connection::finish(); // finish this transfer
389 }
390
391
392 void HTTPProtocol::HTTPConnection::fail(bool forceDrop)
393 {
394 if (forceDrop)
395 retain(false); // drop the Connection
396 else
397 chooseRetain(); // perhaps keep it
398 Connection::fail(); // fail this transfer
399 }
400
401
402 bool HTTPProtocol::HTTPConnection::validate()
403 {
404 assert(state == idle);
405 tickle(); // may change state
406 return state == idle;
407 }
408
409
410 void HTTPProtocol::HTTPConnection::chooseRetain()
411 {
412 // figure out whether to stay alive
413 retain(strcasecmp(headers().find("Connection", "Keep"), "Close"));
414 //@@@ need to handle the HTTP/1.0 case
415 }
416
417
418 //
419 // Transfer objects
420 //
421 HTTPProtocol::HTTPTransfer::HTTPTransfer(Protocol &proto,
422 const Target &tgt, Operation operation, IPPort defaultPort)
423 : Transfer(proto, tgt, operation, defaultPort),
424 mResultClass(unclassifiedFailure)
425 {
426 }
427
428 void HTTPProtocol::HTTPTransfer::start()
429 {
430 // HTTP servers can serve both proxy requests and direct requests,
431 // and can be pooled based on that fact. Use proxy==target here.
432 const HostTarget &host = proxyHostTarget();
433 HTTPConnection *connection = protocol.manager.findConnection<HTTPConnection>(host);
434 if (connection == NULL)
435 connection = new HTTPConnection(protocol, host);
436 connection->dock(this);
437 startRequest();
438 }
439
440 void HTTPProtocol::HTTPTransfer::abort()
441 {
442 observe(Observer::aborting);
443 setError("aborted");
444 connectionAs<HTTPConnection>().abort();
445 }
446
447 void HTTPProtocol::HTTPConnection::abort()
448 {
449 close();
450 fail(true);
451 }
452
453
454 //
455 // This lower-level request startup function can be called directly by children.
456 //
457 void HTTPProtocol::HTTPTransfer::startRequest()
458 {
459 const char *defaultForm;
460 switch (operation()) {
461 case Protocol::upload: defaultForm = "PUT"; break;
462 case Protocol::transaction: defaultForm = "POST"; break;
463 default: defaultForm = "GET"; break;
464 }
465 connectionAs<HTTPConnection>().request(getv<string>(kNetworkHttpCommand, defaultForm).c_str());
466 }
467
468
469 //
470 // Determine whether we should use the proxy form of HTTP headers.
471 // By default, this is true iff we are used by a proxy Protocol.
472 // However, children may override this determination.
473 //
474 bool HTTPProtocol::HTTPTransfer::useProxyHeaders() const
475 {
476 return protocol.isProxy();
477 }
478
479 Transfer::ResultClass HTTPProtocol::HTTPTransfer::resultClass() const
480 {
481 switch (state()) {
482 case failed:
483 return mResultClass;
484 case finished:
485 {
486 if (mResultClass != unclassifiedFailure)
487 return mResultClass; // preclassified
488 unsigned int code = httpResponseCode();
489 if (code == 401 || code == 407 || code == 305) // auth or proxy auth required
490 return authorizationFailure;
491 else if (code / 100 == 3) // redirect (interpreted as success)
492 return success;
493 else if (code / 100 == 2) // success codes
494 return success;
495 else // when in doubt, blame the remote end :-)
496 return remoteFailure;
497 }
498 default:
499 assert(false);
500 return localFailure;
501 }
502 }
503
504
505 void HTTPProtocol::HTTPTransfer::fail(ResultClass why, OSStatus how)
506 {
507 mResultClass = why;
508 Error::throwMe(how);
509 }
510
511
512 //
513 // Manage the HTTP version of a HeaderMap
514 //
515 void HTTPProtocol::HTTPHeaderMap::merge(string key, string &old, string newValue)
516 {
517 // duplicates must be CSV type; concatenate (RFC 2616; section 4.2)
518 old = old + ", " + newValue;
519 }
520
521
522 } // end namespace Network
523 } // end namespace Security