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 state(errorState
), deferSendRequest(false)
77 const HostTarget
&host
= proxyHostTarget();
78 connect(host
.host(), host
.port());
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.
88 void HTTPProtocol::HTTPConnection::request(const char *operation
)
90 mOperation
= operation
;
91 if (state
== idle
) // already waiting for request
95 void HTTPProtocol::HTTPConnection::sendRequest()
97 assert(state
== idle
);
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
);
107 printfe("%s %s HTTP/1.1", mOperation
.c_str(), target
.path
.c_str());
110 authorizationHeader("Authorization", target
,
111 kNetworkGenericUsername
, kNetworkGenericPassword
);
112 printfe("User-Agent: %s",
113 getv
<string
>(kNetworkHttpUserAgent
, "MacNetwork/1.0 (Macintosh)").c_str());
115 // if restarting, add a Range header
116 if (int restartOffset
= getv
<int>(kNetworkRestartPosition
, 0)) {
117 printfe("Range: bytes=%d-", restartOffset
);
120 // add other headers set by caller, if any
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
)) {
129 printfe("%.*s", q
- p
, p
);
130 p
= q
+ strspn(q
, lineEndings
);
132 // now send any last (unterminated) line
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
145 printfe("Content-length: %ld", size
);
147 printfe(""); // end of headers
148 mode(source
); // initiate autoWrite mode
150 printfe(""); // end of headers, no data
153 flushOutput(); // release pent-up output
154 mode(lineInput
); // line input mode
155 state
= primaryResponse
; // prime the state machine
158 void HTTPProtocol::HTTPConnection::hostHeader()
160 const HostTarget
&host
= target().host
;
162 printfe("Host: %s:%d", host
.host().name().c_str(), host
.port());
164 printfe("Host: %s", host
.host().name().c_str());
167 void HTTPProtocol::HTTPConnection::authorizationHeader(const char *headerName
,
168 const HostTarget
&host
,
169 ParameterSource::Key userKey
, ParameterSource::Key passKey
)
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();
183 uint32 binary
= src
[0] << 16;
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] : '=';
193 printfe("%s: Basic %s", headerName
, buffer
);
200 // This is the master state transit machine for HTTP.
202 void HTTPProtocol::HTTPConnection::transit(Event event
, char *input
, size_t length
)
205 case autoWriteDone
: // ingore: it's asynchronous to our state machine
207 case endOfInput
: // most of the time, this is a protocol error, so filter it out now
210 case readWholeBody
: // expected
212 case primaryResponse
: // Connection failed; restart it
214 default: // unexpected; fail
215 UnixError::throwMe(ECONNRESET
); // @@@ diagnostic?
218 case connectionDone
: // TCP connection complete or failed
220 assert(state
== connecting
);
222 observe(Observer::connectEvent
, &error
);
223 if (error
) { // retry
225 } else { // connection good
227 if (!deferSendRequest
) { // (subclass wants to wedge in)
239 case primaryResponse
:
241 assert(mode() == lineInput
);
242 observe(Observer::protocolReceive
, input
);
243 transfer().httpResponse() = input
; // remember response for caller
244 // --> HTTP/major.minor status reason-phrase
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
);
253 if (httpVersionMajor
!= 1) // wrong major protocol Version
254 fail(Transfer::remoteFailure
);
255 if (httpVersionMinor
< 0 || httpVersionMinor
> 1)
256 fail(Transfer::remoteFailure
);
258 // notify the URLAccess emulation that we have the result code
259 observe (Observer::resultCodeReady
);
261 // okay, we grok the version. We'll proceed for now reading headers etc.
264 // we got input from the server, so this Connection is now confirmed good
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 **");
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
283 // mode remains lineInput
285 } else if (!strcasecmp(encoding
, "identity")) {
286 // allowed and ignored
288 // unrecognized transfer-encoding
289 fail(Transfer::remoteFailure
);
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
);
300 state
= readWholeBody
;
306 assert(mode() == lineInput
);
307 // line should be (just) a hex number, sans "0x" prefix or spaces. Be strict
309 size_t chunkLength
= strtol(input
, &endOfMatch
, 0x10);
310 if (length
== 0 || endOfMatch
== input
) // no valid number
311 fail(Transfer::remoteFailure
);
313 debug("http", "reading chunk of %ld bytes", chunkLength
);
314 mode(sink(), chunkLength
);
315 state
= chunkDownload
;
317 debug("http", "final chunk marker");
318 state
= chunkTrailer
;
319 observe(Observer::protocolReceive
, "** END OF DATA **");
325 assert(mode() == lineInput
);
331 assert(mode() == lineInput
);
332 if (input
[0] == '\0') { // end of trailer
335 headers().add(input
);
336 observe(Observer::protocolReceive
, input
);
342 assert(event
== autoReadDone
);
349 assert(event
== autoReadDone
|| event
== endOfInput
);
355 // the only asynchronous event in idle mode is a connection drop
357 "event %d while idle; destroying connection", event
);
367 void HTTPProtocol::HTTPConnection::transitError(const CssmCommonError
&error
)
369 // note that fail(const char * [, OSStatus]) has already called setError
370 fail(true); // fail transfer and throw out connection
374 void HTTPProtocol::HTTPConnection::finish()
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
383 void HTTPProtocol::HTTPConnection::fail(bool forceDrop
)
386 retain(false); // drop the Connection
388 chooseRetain(); // perhaps keep it
389 Connection::fail(); // fail this transfer
393 bool HTTPProtocol::HTTPConnection::validate()
395 assert(state
== idle
);
396 tickle(); // may change state
397 return state
== idle
;
401 void HTTPProtocol::HTTPConnection::chooseRetain()
403 // figure out whether to stay alive
404 retain(strcasecmp(headers().find("Connection", "Keep"), "Close"));
405 //@@@ need to handle the HTTP/1.0 case
412 HTTPProtocol::HTTPTransfer::HTTPTransfer(Protocol
&proto
,
413 const Target
&tgt
, Operation operation
, IPPort defaultPort
)
414 : Transfer(proto
, tgt
, operation
, defaultPort
),
415 mResultClass(unclassifiedFailure
)
419 void HTTPProtocol::HTTPTransfer::start()
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);
431 void HTTPProtocol::HTTPTransfer::abort()
434 connectionAs
<HTTPConnection
>().abort();
437 void HTTPProtocol::HTTPConnection::abort()
445 // This lower-level request startup function can be called directly by children.
447 void HTTPProtocol::HTTPTransfer::startRequest()
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;
455 connectionAs
<HTTPConnection
>().request(getv
<string
>(kNetworkHttpCommand
, defaultForm
).c_str());
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.
464 bool HTTPProtocol::HTTPTransfer::useProxyHeaders() const
466 return protocol
.isProxy();
469 Transfer::ResultClass
HTTPProtocol::HTTPTransfer::resultClass() const
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
483 else // when in doubt, blame the remote end :-)
484 return remoteFailure
;
493 void HTTPProtocol::HTTPTransfer::fail(ResultClass why
, OSStatus how
)
501 // Manage the HTTP version of a HeaderMap
503 void HTTPProtocol::HTTPHeaderMap::merge(string key
, string
&old
, string newValue
)
505 // duplicates must be CSV type; concatenate (RFC 2616; section 4.2)
506 old
= old
+ ", " + newValue
;
510 } // end namespace Network
511 } // end namespace Security