Security-163.tar.gz
[apple/security.git] / Network / http-protocol.cpp
CommitLineData
bac41a7b
A
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
48namespace Security {
49namespace Network {
50
51
52//
53// Construct the protocol object
54//
55HTTPProtocol::HTTPProtocol(Manager &mgr, const char *scheme) : Protocol(mgr, scheme)
56{
57}
58
59
60//
61// Create a Transfer object for our protocol
62//
63HTTPProtocol::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//
72HTTPProtocol::HTTPConnection::HTTPConnection(Protocol &proto,
73 const HostTarget &hostTarget)
74 : TCPConnection(proto, hostTarget),
29654253 75 subVersion(defaultSubVersion),
bac41a7b
A
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//
89void HTTPProtocol::HTTPConnection::request(const char *operation)
90{
91 mOperation = operation;
92 if (state == idle) // already waiting for request
93 sendRequest();
94}
95
96void HTTPProtocol::HTTPConnection::sendRequest()
97{
98 assert(state == idle);
99
29654253
A
100 // what version of HTTP/1 shall we use?
101 subVersion = getv<int>(kNetworkHttpUseVersion, defaultSubVersion);
102
bac41a7b
A
103 flushOutput(false); // hold output until we're done
104 const Target &target = this->target();
105 if (transfer().useProxyHeaders()) {
29654253 106 printfe("%s %s HTTP/1.%d", mOperation.c_str(), target.urlForm().c_str(), subVersion);
bac41a7b
A
107 authorizationHeader("Proxy-Authorization", hostTarget,
108 kNetworkGenericProxyUsername, kNetworkGenericProxyPassword);
109 } else {
29654253 110 printfe("%s %s HTTP/1.%d", mOperation.c_str(), target.path.c_str(), subVersion);
bac41a7b
A
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 }
29654253 150 printfe("Content-Type: %s", getv<string>(kNetworkHttpPostContentType, "text/plain").c_str());
bac41a7b
A
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
162void 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
171void 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//
206void 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 **");
29654253 281 observe(Observer::downloading, input);
bac41a7b
A
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
ded8f8e2 298 state = readWholeBody;
bac41a7b
A
299 if (const char *lengthArg = headers().find("Content-Length")) {
300 size_t length = strtol(lengthArg, NULL, 10);
301 sink().setSize(length);
ded8f8e2
A
302 if (length > 0)
303 mode(sink(), length);
304 else // null body, already done
305 finish();
306 } else { // read until EOI
bac41a7b
A
307 mode(sink());
308 }
bac41a7b
A
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) {
df0e469f 321 secdebug("http", "reading chunk of %ld bytes", chunkLength);
bac41a7b
A
322 mode(sink(), chunkLength);
323 state = chunkDownload;
324 } else {
df0e469f 325 secdebug("http", "final chunk marker");
bac41a7b
A
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
df0e469f 364 secdebug("http",
ded8f8e2 365 "%p event %d while idle; destroying connection", this, event);
bac41a7b
A
366 abort();
367 state = dead;
368 }
369 break;
370 default:
371 assert(false);
372 }
373}
374
375void 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
382void HTTPProtocol::HTTPConnection::finish()
383{
ded8f8e2 384 flushInput(); // clear excess garbage input (resynchronize)
bac41a7b 385 chooseRetain(); // shall we keep the Connection?
bac41a7b
A
386 mode(lineInput); // ensure valid input mode
387 state = idle; // idle state
ded8f8e2 388 Connection::finish(); // finish this transfer
bac41a7b
A
389}
390
391
392void 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
402bool HTTPProtocol::HTTPConnection::validate()
403{
404 assert(state == idle);
405 tickle(); // may change state
406 return state == idle;
407}
408
409
410void 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//
421HTTPProtocol::HTTPTransfer::HTTPTransfer(Protocol &proto,
422 const Target &tgt, Operation operation, IPPort defaultPort)
423 : Transfer(proto, tgt, operation, defaultPort),
424 mResultClass(unclassifiedFailure)
425{
426}
427
428void 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
440void HTTPProtocol::HTTPTransfer::abort()
441{
c25717e3 442 observe(Observer::aborting);
bac41a7b
A
443 setError("aborted");
444 connectionAs<HTTPConnection>().abort();
445}
446
447void 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//
457void 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//
474bool HTTPProtocol::HTTPTransfer::useProxyHeaders() const
475{
476 return protocol.isProxy();
477}
478
479Transfer::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();
ded8f8e2 489 if (code == 401 || code == 407 || code == 305) // auth or proxy auth required
bac41a7b 490 return authorizationFailure;
ded8f8e2
A
491 else if (code / 100 == 3) // redirect (interpreted as success)
492 return success;
bac41a7b
A
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
505void 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//
515void 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