2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 // http-protocol - HTTP protocol objects
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.
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.
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).
44 #include "http-protocol.h"
45 #include "netparameters.h"
53 // Construct the protocol object
55 HTTPProtocol::HTTPProtocol(Manager
&mgr
, const char *scheme
) : Protocol(mgr
, scheme
)
61 // Create a Transfer object for our protocol
63 HTTPProtocol::HTTPTransfer
*HTTPProtocol::makeTransfer(const Target
&target
, Operation operation
)
65 return new HTTPTransfer(*this, target
, operation
, defaultHttpPort
);
70 // Construct an HTTPConnection object
72 HTTPProtocol::HTTPConnection::HTTPConnection(Protocol
&proto
,
73 const HostTarget
&hostTarget
)
74 : TCPConnection(proto
, hostTarget
),
75 subVersion(defaultSubVersion
),
76 state(errorState
), deferSendRequest(false)
78 const HostTarget
&host
= proxyHostTarget();
79 connect(host
.host(), host
.port());
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.
89 void HTTPProtocol::HTTPConnection::request(const char *operation
)
91 mOperation
= operation
;
92 if (state
== idle
) // already waiting for request
96 void HTTPProtocol::HTTPConnection::sendRequest()
98 assert(state
== idle
);
100 // what version of HTTP/1 shall we use?
101 subVersion
= getv
<int>(kNetworkHttpUseVersion
, defaultSubVersion
);
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
);
110 printfe("%s %s HTTP/1.%d", mOperation
.c_str(), target
.path
.c_str(), subVersion
);
113 authorizationHeader("Authorization", target
,
114 kNetworkGenericUsername
, kNetworkGenericPassword
);
115 printfe("User-Agent: %s",
116 getv
<string
>(kNetworkHttpUserAgent
, "MacNetwork/1.0 (Macintosh)").c_str());
118 // if restarting, add a Range header
119 if (int restartOffset
= getv
<int>(kNetworkRestartPosition
, 0)) {
120 printfe("Range: bytes=%d-", restartOffset
);
123 // add other headers set by caller, if any
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
)) {
132 printfe("%.*s", q
- p
, p
);
133 p
= q
+ strspn(q
, lineEndings
);
135 // now send any last (unterminated) line
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
148 printfe("Content-length: %ld", size
);
150 printfe("Content-Type: %s", getv
<string
>(kNetworkHttpPostContentType
, "text/plain").c_str());
151 printfe(""); // end of headers
152 mode(source
); // initiate autoWrite mode
154 printfe(""); // end of headers, no data
157 flushOutput(); // release pent-up output
158 mode(lineInput
); // line input mode
159 state
= primaryResponse
; // prime the state machine
162 void HTTPProtocol::HTTPConnection::hostHeader()
164 const HostTarget
&host
= target().host
;
166 printfe("Host: %s:%d", host
.host().name().c_str(), host
.port());
168 printfe("Host: %s", host
.host().name().c_str());
171 void HTTPProtocol::HTTPConnection::authorizationHeader(const char *headerName
,
172 const HostTarget
&host
,
173 ParameterSource::Key userKey
, ParameterSource::Key passKey
)
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();
187 uint32 binary
= src
[0] << 16;
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] : '=';
197 printfe("%s: Basic %s", headerName
, buffer
);
204 // This is the master state transit machine for HTTP.
206 void HTTPProtocol::HTTPConnection::transit(Event event
, char *input
, size_t length
)
209 case autoWriteDone
: // ingore: it's asynchronous to our state machine
211 case endOfInput
: // most of the time, this is a protocol error, so filter it out now
214 case readWholeBody
: // expected
216 case primaryResponse
: // Connection failed; restart it
218 default: // unexpected; fail
219 UnixError::throwMe(ECONNRESET
); // @@@ diagnostic?
222 case connectionDone
: // TCP connection complete or failed
224 assert(state
== connecting
);
226 observe(Observer::connectEvent
, &error
);
227 if (error
) { // retry
229 } else { // connection good
231 if (!deferSendRequest
) { // (subclass wants to wedge in)
243 case primaryResponse
:
245 assert(mode() == lineInput
);
246 observe(Observer::protocolReceive
, input
);
247 transfer().httpResponse() = input
; // remember response for caller
248 // --> HTTP/major.minor status reason-phrase
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
);
257 if (httpVersionMajor
!= 1) // wrong major protocol Version
258 fail(Transfer::remoteFailure
);
259 if (httpVersionMinor
< 0 || httpVersionMinor
> 1)
260 fail(Transfer::remoteFailure
);
262 // notify the URLAccess emulation that we have the result code
263 observe (Observer::resultCodeReady
);
265 // okay, we grok the version. We'll proceed for now reading headers etc.
268 // we got input from the server, so this Connection is now confirmed good
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
);
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
288 // mode remains lineInput
290 } else if (!strcasecmp(encoding
, "identity")) {
291 // allowed and ignored
293 // unrecognized transfer-encoding
294 fail(Transfer::remoteFailure
);
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
);
303 mode(sink(), length
);
304 else // null body, already done
306 } else { // read until EOI
314 assert(mode() == lineInput
);
315 // line should be (just) a hex number, sans "0x" prefix or spaces. Be strict
317 size_t chunkLength
= strtol(input
, &endOfMatch
, 0x10);
318 if (length
== 0 || endOfMatch
== input
) // no valid number
319 fail(Transfer::remoteFailure
);
321 secdebug("http", "reading chunk of %ld bytes", chunkLength
);
322 mode(sink(), chunkLength
);
323 state
= chunkDownload
;
325 secdebug("http", "final chunk marker");
326 state
= chunkTrailer
;
327 observe(Observer::protocolReceive
, "** END OF DATA **");
333 assert(mode() == lineInput
);
339 assert(mode() == lineInput
);
340 if (input
[0] == '\0') { // end of trailer
343 headers().add(input
);
344 observe(Observer::protocolReceive
, input
);
350 assert(event
== autoReadDone
);
357 assert(event
== autoReadDone
|| event
== endOfInput
);
363 // the only asynchronous event in idle mode is a connection drop
365 "%p event %d while idle; destroying connection", this, event
);
375 void HTTPProtocol::HTTPConnection::transitError(const CssmCommonError
&error
)
377 // note that fail(const char * [, OSStatus]) has already called setError
378 fail(true); // fail transfer and throw out connection
382 void HTTPProtocol::HTTPConnection::finish()
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
392 void HTTPProtocol::HTTPConnection::fail(bool forceDrop
)
395 retain(false); // drop the Connection
397 chooseRetain(); // perhaps keep it
398 Connection::fail(); // fail this transfer
402 bool HTTPProtocol::HTTPConnection::validate()
404 assert(state
== idle
);
405 tickle(); // may change state
406 return state
== idle
;
410 void HTTPProtocol::HTTPConnection::chooseRetain()
412 // figure out whether to stay alive
413 retain(strcasecmp(headers().find("Connection", "Keep"), "Close"));
414 //@@@ need to handle the HTTP/1.0 case
421 HTTPProtocol::HTTPTransfer::HTTPTransfer(Protocol
&proto
,
422 const Target
&tgt
, Operation operation
, IPPort defaultPort
)
423 : Transfer(proto
, tgt
, operation
, defaultPort
),
424 mResultClass(unclassifiedFailure
)
428 void HTTPProtocol::HTTPTransfer::start()
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);
440 void HTTPProtocol::HTTPTransfer::abort()
442 observe(Observer::aborting
);
444 connectionAs
<HTTPConnection
>().abort();
447 void HTTPProtocol::HTTPConnection::abort()
455 // This lower-level request startup function can be called directly by children.
457 void HTTPProtocol::HTTPTransfer::startRequest()
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;
465 connectionAs
<HTTPConnection
>().request(getv
<string
>(kNetworkHttpCommand
, defaultForm
).c_str());
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.
474 bool HTTPProtocol::HTTPTransfer::useProxyHeaders() const
476 return protocol
.isProxy();
479 Transfer::ResultClass
HTTPProtocol::HTTPTransfer::resultClass() const
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)
493 else if (code
/ 100 == 2) // success codes
495 else // when in doubt, blame the remote end :-)
496 return remoteFailure
;
505 void HTTPProtocol::HTTPTransfer::fail(ResultClass why
, OSStatus how
)
513 // Manage the HTTP version of a HeaderMap
515 void HTTPProtocol::HTTPHeaderMap::merge(string key
, string
&old
, string newValue
)
517 // duplicates must be CSV type; concatenate (RFC 2616; section 4.2)
518 old
= old
+ ", " + newValue
;
522 } // end namespace Network
523 } // end namespace Security