annotate osx/include/kj/compat/http.h @ 151:fe80428a60a5

Add AppImage files
author Chris Cannam <cannam@all-day-breakfast.com>
date Tue, 26 Jun 2018 18:00:44 +0100
parents 45360b968bf4
children
rev   line source
cannam@147 1 // Copyright (c) 2017 Sandstorm Development Group, Inc. and contributors
cannam@147 2 // Licensed under the MIT License:
cannam@147 3 //
cannam@147 4 // Permission is hereby granted, free of charge, to any person obtaining a copy
cannam@147 5 // of this software and associated documentation files (the "Software"), to deal
cannam@147 6 // in the Software without restriction, including without limitation the rights
cannam@147 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
cannam@147 8 // copies of the Software, and to permit persons to whom the Software is
cannam@147 9 // furnished to do so, subject to the following conditions:
cannam@147 10 //
cannam@147 11 // The above copyright notice and this permission notice shall be included in
cannam@147 12 // all copies or substantial portions of the Software.
cannam@147 13 //
cannam@147 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
cannam@147 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
cannam@147 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
cannam@147 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
cannam@147 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
cannam@147 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
cannam@147 20 // THE SOFTWARE.
cannam@147 21
cannam@147 22 #ifndef KJ_COMPAT_HTTP_H_
cannam@147 23 #define KJ_COMPAT_HTTP_H_
cannam@147 24 // The KJ HTTP client/server library.
cannam@147 25 //
cannam@147 26 // This is a simple library which can be used to implement an HTTP client or server. Properties
cannam@147 27 // of this library include:
cannam@147 28 // - Uses KJ async framework.
cannam@147 29 // - Agnostic to transport layer -- you can provide your own.
cannam@147 30 // - Header parsing is zero-copy -- it results in strings that point directly into the buffer
cannam@147 31 // received off the wire.
cannam@147 32 // - Application code which reads and writes headers refers to headers by symbolic names, not by
cannam@147 33 // string literals, with lookups being array-index-based, not map-based. To make this possible,
cannam@147 34 // the application announces what headers it cares about in advance, in order to assign numeric
cannam@147 35 // values to them.
cannam@147 36 // - Methods are identified by an enum.
cannam@147 37
cannam@147 38 #include <kj/string.h>
cannam@147 39 #include <kj/vector.h>
cannam@147 40 #include <kj/memory.h>
cannam@147 41 #include <kj/one-of.h>
cannam@147 42 #include <kj/async-io.h>
cannam@147 43
cannam@147 44 namespace kj {
cannam@147 45
cannam@147 46 #define KJ_HTTP_FOR_EACH_METHOD(MACRO) \
cannam@147 47 MACRO(GET) \
cannam@147 48 MACRO(HEAD) \
cannam@147 49 MACRO(POST) \
cannam@147 50 MACRO(PUT) \
cannam@147 51 MACRO(DELETE) \
cannam@147 52 MACRO(PATCH) \
cannam@147 53 MACRO(PURGE) \
cannam@147 54 MACRO(OPTIONS) \
cannam@147 55 MACRO(TRACE) \
cannam@147 56 /* standard methods */ \
cannam@147 57 /* */ \
cannam@147 58 /* (CONNECT is intentionally omitted since it is handled specially in HttpHandler) */ \
cannam@147 59 \
cannam@147 60 MACRO(COPY) \
cannam@147 61 MACRO(LOCK) \
cannam@147 62 MACRO(MKCOL) \
cannam@147 63 MACRO(MOVE) \
cannam@147 64 MACRO(PROPFIND) \
cannam@147 65 MACRO(PROPPATCH) \
cannam@147 66 MACRO(SEARCH) \
cannam@147 67 MACRO(UNLOCK) \
cannam@147 68 /* WebDAV */ \
cannam@147 69 \
cannam@147 70 MACRO(REPORT) \
cannam@147 71 MACRO(MKACTIVITY) \
cannam@147 72 MACRO(CHECKOUT) \
cannam@147 73 MACRO(MERGE) \
cannam@147 74 /* Subversion */ \
cannam@147 75 \
cannam@147 76 MACRO(MSEARCH) \
cannam@147 77 MACRO(NOTIFY) \
cannam@147 78 MACRO(SUBSCRIBE) \
cannam@147 79 MACRO(UNSUBSCRIBE)
cannam@147 80 /* UPnP */
cannam@147 81
cannam@147 82 #define KJ_HTTP_FOR_EACH_CONNECTION_HEADER(MACRO) \
cannam@147 83 MACRO(connection, "Connection") \
cannam@147 84 MACRO(contentLength, "Content-Length") \
cannam@147 85 MACRO(keepAlive, "Keep-Alive") \
cannam@147 86 MACRO(te, "TE") \
cannam@147 87 MACRO(trailer, "Trailer") \
cannam@147 88 MACRO(transferEncoding, "Transfer-Encoding") \
cannam@147 89 MACRO(upgrade, "Upgrade")
cannam@147 90
cannam@147 91 enum class HttpMethod {
cannam@147 92 // Enum of known HTTP methods.
cannam@147 93 //
cannam@147 94 // We use an enum rather than a string to allow for faster parsing and switching and to reduce
cannam@147 95 // ambiguity.
cannam@147 96
cannam@147 97 #define DECLARE_METHOD(id) id,
cannam@147 98 KJ_HTTP_FOR_EACH_METHOD(DECLARE_METHOD)
cannam@147 99 #undef DECALRE_METHOD
cannam@147 100 };
cannam@147 101
cannam@147 102 kj::StringPtr KJ_STRINGIFY(HttpMethod method);
cannam@147 103 kj::Maybe<HttpMethod> tryParseHttpMethod(kj::StringPtr name);
cannam@147 104
cannam@147 105 class HttpHeaderTable;
cannam@147 106
cannam@147 107 class HttpHeaderId {
cannam@147 108 // Identifies an HTTP header by numeric ID that indexes into an HttpHeaderTable.
cannam@147 109 //
cannam@147 110 // The KJ HTTP API prefers that headers be identified by these IDs for a few reasons:
cannam@147 111 // - Integer lookups are much more efficient than string lookups.
cannam@147 112 // - Case-insensitivity is awkward to deal with when const strings are being passed to the lookup
cannam@147 113 // method.
cannam@147 114 // - Writing out strings less often means fewer typos.
cannam@147 115 //
cannam@147 116 // See HttpHeaderTable for usage hints.
cannam@147 117
cannam@147 118 public:
cannam@147 119 HttpHeaderId() = default;
cannam@147 120
cannam@147 121 inline bool operator==(const HttpHeaderId& other) const { return id == other.id; }
cannam@147 122 inline bool operator!=(const HttpHeaderId& other) const { return id != other.id; }
cannam@147 123 inline bool operator< (const HttpHeaderId& other) const { return id < other.id; }
cannam@147 124 inline bool operator> (const HttpHeaderId& other) const { return id > other.id; }
cannam@147 125 inline bool operator<=(const HttpHeaderId& other) const { return id <= other.id; }
cannam@147 126 inline bool operator>=(const HttpHeaderId& other) const { return id >= other.id; }
cannam@147 127
cannam@147 128 inline size_t hashCode() const { return id; }
cannam@147 129
cannam@147 130 kj::StringPtr toString() const;
cannam@147 131
cannam@147 132 void requireFrom(HttpHeaderTable& table) const;
cannam@147 133 // In debug mode, throws an exception if the HttpHeaderId is not from the given table.
cannam@147 134 //
cannam@147 135 // In opt mode, no-op.
cannam@147 136
cannam@147 137 #define KJ_HTTP_FOR_EACH_BUILTIN_HEADER(MACRO) \
cannam@147 138 MACRO(HOST, "Host") \
cannam@147 139 MACRO(DATE, "Date") \
cannam@147 140 MACRO(LOCATION, "Location") \
cannam@147 141 MACRO(CONTENT_TYPE, "Content-Type")
cannam@147 142 // For convenience, these very-common headers are valid for all HttpHeaderTables. You can refer
cannam@147 143 // to them like:
cannam@147 144 //
cannam@147 145 // HttpHeaderId::HOST
cannam@147 146 //
cannam@147 147 // TODO(0.7): Fill this out with more common headers.
cannam@147 148
cannam@147 149 #define DECLARE_HEADER(id, name) \
cannam@147 150 static const HttpHeaderId id;
cannam@147 151 // Declare a constant for each builtin header, e.g.: HttpHeaderId::CONNECTION
cannam@147 152
cannam@147 153 KJ_HTTP_FOR_EACH_BUILTIN_HEADER(DECLARE_HEADER);
cannam@147 154 #undef DECLARE_HEADER
cannam@147 155
cannam@147 156 private:
cannam@147 157 HttpHeaderTable* table;
cannam@147 158 uint id;
cannam@147 159
cannam@147 160 inline explicit constexpr HttpHeaderId(HttpHeaderTable* table, uint id): table(table), id(id) {}
cannam@147 161 friend class HttpHeaderTable;
cannam@147 162 friend class HttpHeaders;
cannam@147 163 };
cannam@147 164
cannam@147 165 class HttpHeaderTable {
cannam@147 166 // Construct an HttpHeaderTable to declare which headers you'll be interested in later on, and
cannam@147 167 // to manufacture IDs for them.
cannam@147 168 //
cannam@147 169 // Example:
cannam@147 170 //
cannam@147 171 // // Build a header table with the headers we are interested in.
cannam@147 172 // kj::HttpHeaderTable::Builder builder;
cannam@147 173 // const HttpHeaderId accept = builder.add("Accept");
cannam@147 174 // const HttpHeaderId contentType = builder.add("Content-Type");
cannam@147 175 // kj::HttpHeaderTable table(kj::mv(builder));
cannam@147 176 //
cannam@147 177 // // Create an HTTP client.
cannam@147 178 // auto client = kj::newHttpClient(table, network);
cannam@147 179 //
cannam@147 180 // // Get http://example.com.
cannam@147 181 // HttpHeaders headers(table);
cannam@147 182 // headers.set(accept, "text/html");
cannam@147 183 // auto response = client->send(kj::HttpMethod::GET, "http://example.com", headers)
cannam@147 184 // .wait(waitScope);
cannam@147 185 // auto msg = kj::str("Response content type: ", response.headers.get(contentType));
cannam@147 186
cannam@147 187 struct IdsByNameMap;
cannam@147 188
cannam@147 189 public:
cannam@147 190 HttpHeaderTable();
cannam@147 191 // Constructs a table that only contains the builtin headers.
cannam@147 192
cannam@147 193 class Builder {
cannam@147 194 public:
cannam@147 195 Builder();
cannam@147 196 HttpHeaderId add(kj::StringPtr name);
cannam@147 197 Own<HttpHeaderTable> build();
cannam@147 198
cannam@147 199 HttpHeaderTable& getFutureTable();
cannam@147 200 // Get the still-unbuilt header table. You cannot actually use it until build() has been
cannam@147 201 // called.
cannam@147 202 //
cannam@147 203 // This method exists to help when building a shared header table -- the Builder may be passed
cannam@147 204 // to several components, each of which will register the headers they need and get a reference
cannam@147 205 // to the future table.
cannam@147 206
cannam@147 207 private:
cannam@147 208 kj::Own<HttpHeaderTable> table;
cannam@147 209 };
cannam@147 210
cannam@147 211 KJ_DISALLOW_COPY(HttpHeaderTable); // Can't copy because HttpHeaderId points to the table.
cannam@147 212 ~HttpHeaderTable() noexcept(false);
cannam@147 213
cannam@147 214 uint idCount();
cannam@147 215 // Return the number of IDs in the table.
cannam@147 216
cannam@147 217 kj::Maybe<HttpHeaderId> stringToId(kj::StringPtr name);
cannam@147 218 // Try to find an ID for the given name. The matching is case-insensitive, per the HTTP spec.
cannam@147 219 //
cannam@147 220 // Note: if `name` contains characters that aren't allowed in HTTP header names, this may return
cannam@147 221 // a bogus value rather than null, due to optimizations used in case-insensitive matching.
cannam@147 222
cannam@147 223 kj::StringPtr idToString(HttpHeaderId id);
cannam@147 224 // Get the canonical string name for the given ID.
cannam@147 225
cannam@147 226 private:
cannam@147 227 kj::Vector<kj::StringPtr> namesById;
cannam@147 228 kj::Own<IdsByNameMap> idsByName;
cannam@147 229 };
cannam@147 230
cannam@147 231 class HttpHeaders {
cannam@147 232 // Represents a set of HTTP headers.
cannam@147 233 //
cannam@147 234 // This class guards against basic HTTP header injection attacks: Trying to set a header name or
cannam@147 235 // value containing a newline, carriage return, or other invalid character will throw an
cannam@147 236 // exception.
cannam@147 237
cannam@147 238 public:
cannam@147 239 explicit HttpHeaders(HttpHeaderTable& table);
cannam@147 240
cannam@147 241 KJ_DISALLOW_COPY(HttpHeaders);
cannam@147 242 HttpHeaders(HttpHeaders&&) = default;
cannam@147 243 HttpHeaders& operator=(HttpHeaders&&) = default;
cannam@147 244
cannam@147 245 void clear();
cannam@147 246 // Clears all contents, as if the object was freshly-allocated. However, calling this rather
cannam@147 247 // than actually re-allocating the object may avoid re-allocation of internal objects.
cannam@147 248
cannam@147 249 HttpHeaders clone() const;
cannam@147 250 // Creates a deep clone of the HttpHeaders. The returned object owns all strings it references.
cannam@147 251
cannam@147 252 HttpHeaders cloneShallow() const;
cannam@147 253 // Creates a shallow clone of the HttpHeaders. The returned object references the same strings
cannam@147 254 // as the original, owning none of them.
cannam@147 255
cannam@147 256 kj::Maybe<kj::StringPtr> get(HttpHeaderId id) const;
cannam@147 257 // Read a header.
cannam@147 258
cannam@147 259 template <typename Func>
cannam@147 260 void forEach(Func&& func) const;
cannam@147 261 // Calls `func(name, value)` for each header in the set -- including headers that aren't mapped
cannam@147 262 // to IDs in the header table. Both inputs are of type kj::StringPtr.
cannam@147 263
cannam@147 264 void set(HttpHeaderId id, kj::StringPtr value);
cannam@147 265 void set(HttpHeaderId id, kj::String&& value);
cannam@147 266 // Sets a header value, overwriting the existing value.
cannam@147 267 //
cannam@147 268 // The String&& version is equivalent to calling the other version followed by takeOwnership().
cannam@147 269 //
cannam@147 270 // WARNING: It is the caller's responsibility to ensure that `value` remains valid until the
cannam@147 271 // HttpHeaders object is destroyed. This allows string literals to be passed without making a
cannam@147 272 // copy, but complicates the use of dynamic values. Hint: Consider using `takeOwnership()`.
cannam@147 273
cannam@147 274 void add(kj::StringPtr name, kj::StringPtr value);
cannam@147 275 void add(kj::StringPtr name, kj::String&& value);
cannam@147 276 void add(kj::String&& name, kj::String&& value);
cannam@147 277 // Append a header. `name` will be looked up in the header table, but if it's not mapped, the
cannam@147 278 // header will be added to the list of unmapped headers.
cannam@147 279 //
cannam@147 280 // The String&& versions are equivalent to calling the other version followed by takeOwnership().
cannam@147 281 //
cannam@147 282 // WARNING: It is the caller's responsibility to ensure that `name` and `value` remain valid
cannam@147 283 // until the HttpHeaders object is destroyed. This allows string literals to be passed without
cannam@147 284 // making a copy, but complicates the use of dynamic values. Hint: Consider using
cannam@147 285 // `takeOwnership()`.
cannam@147 286
cannam@147 287 void unset(HttpHeaderId id);
cannam@147 288 // Removes a header.
cannam@147 289 //
cannam@147 290 // It's not possible to remove a header by string name because non-indexed headers would take
cannam@147 291 // O(n) time to remove. Instead, construct a new HttpHeaders object and copy contents.
cannam@147 292
cannam@147 293 void takeOwnership(kj::String&& string);
cannam@147 294 void takeOwnership(kj::Array<char>&& chars);
cannam@147 295 void takeOwnership(HttpHeaders&& otherHeaders);
cannam@147 296 // Takes overship of a string so that it lives until the HttpHeaders object is destroyed. Useful
cannam@147 297 // when you've passed a dynamic value to set() or add() or parse*().
cannam@147 298
cannam@147 299 struct ConnectionHeaders {
cannam@147 300 // These headers govern details of the specific HTTP connection or framing of the content.
cannam@147 301 // Hence, they are managed internally within the HTTP library, and never appear in an
cannam@147 302 // HttpHeaders structure.
cannam@147 303
cannam@147 304 #define DECLARE_HEADER(id, name) \
cannam@147 305 kj::StringPtr id;
cannam@147 306 KJ_HTTP_FOR_EACH_CONNECTION_HEADER(DECLARE_HEADER)
cannam@147 307 #undef DECLARE_HEADER
cannam@147 308 };
cannam@147 309
cannam@147 310 struct Request {
cannam@147 311 HttpMethod method;
cannam@147 312 kj::StringPtr url;
cannam@147 313 ConnectionHeaders connectionHeaders;
cannam@147 314 };
cannam@147 315 struct Response {
cannam@147 316 uint statusCode;
cannam@147 317 kj::StringPtr statusText;
cannam@147 318 ConnectionHeaders connectionHeaders;
cannam@147 319 };
cannam@147 320
cannam@147 321 kj::Maybe<Request> tryParseRequest(kj::ArrayPtr<char> content);
cannam@147 322 kj::Maybe<Response> tryParseResponse(kj::ArrayPtr<char> content);
cannam@147 323 // Parse an HTTP header blob and add all the headers to this object.
cannam@147 324 //
cannam@147 325 // `content` should be all text from the start of the request to the first occurrance of two
cannam@147 326 // newlines in a row -- including the first of these two newlines, but excluding the second.
cannam@147 327 //
cannam@147 328 // The parse is performed with zero copies: The callee clobbers `content` with '\0' characters
cannam@147 329 // to split it into a bunch of shorter strings. The caller must keep `content` valid until the
cannam@147 330 // `HttpHeaders` is destroyed, or pass it to `takeOwnership()`.
cannam@147 331
cannam@147 332 kj::String serializeRequest(HttpMethod method, kj::StringPtr url,
cannam@147 333 const ConnectionHeaders& connectionHeaders) const;
cannam@147 334 kj::String serializeResponse(uint statusCode, kj::StringPtr statusText,
cannam@147 335 const ConnectionHeaders& connectionHeaders) const;
cannam@147 336 // Serialize the headers as a complete request or response blob. The blob uses '\r\n' newlines
cannam@147 337 // and includes the double-newline to indicate the end of the headers.
cannam@147 338
cannam@147 339 kj::String toString() const;
cannam@147 340
cannam@147 341 private:
cannam@147 342 HttpHeaderTable* table;
cannam@147 343
cannam@147 344 kj::Array<kj::StringPtr> indexedHeaders;
cannam@147 345 // Size is always table->idCount().
cannam@147 346
cannam@147 347 struct Header {
cannam@147 348 kj::StringPtr name;
cannam@147 349 kj::StringPtr value;
cannam@147 350 };
cannam@147 351 kj::Vector<Header> unindexedHeaders;
cannam@147 352
cannam@147 353 kj::Vector<kj::Array<char>> ownedStrings;
cannam@147 354
cannam@147 355 kj::Maybe<uint> addNoCheck(kj::StringPtr name, kj::StringPtr value);
cannam@147 356
cannam@147 357 kj::StringPtr cloneToOwn(kj::StringPtr str);
cannam@147 358
cannam@147 359 kj::String serialize(kj::ArrayPtr<const char> word1,
cannam@147 360 kj::ArrayPtr<const char> word2,
cannam@147 361 kj::ArrayPtr<const char> word3,
cannam@147 362 const ConnectionHeaders& connectionHeaders) const;
cannam@147 363
cannam@147 364 bool parseHeaders(char* ptr, char* end, ConnectionHeaders& connectionHeaders);
cannam@147 365
cannam@147 366 // TODO(perf): Arguably we should store a map, but header sets are never very long
cannam@147 367 // TODO(perf): We could optimize for common headers by storing them directly as fields. We could
cannam@147 368 // also add direct accessors for those headers.
cannam@147 369 };
cannam@147 370
cannam@147 371 class WebSocket {
cannam@147 372 public:
cannam@147 373 WebSocket(kj::Own<kj::AsyncIoStream> stream);
cannam@147 374 // Create a WebSocket wrapping the given I/O stream.
cannam@147 375
cannam@147 376 kj::Promise<void> send(kj::ArrayPtr<const byte> message);
cannam@147 377 kj::Promise<void> send(kj::ArrayPtr<const char> message);
cannam@147 378 };
cannam@147 379
cannam@147 380 class HttpClient {
cannam@147 381 // Interface to the client end of an HTTP connection.
cannam@147 382 //
cannam@147 383 // There are two kinds of clients:
cannam@147 384 // * Host clients are used when talking to a specific host. The `url` specified in a request
cannam@147 385 // is actually just a path. (A `Host` header is still required in all requests.)
cannam@147 386 // * Proxy clients are used when the target could be any arbitrary host on the internet.
cannam@147 387 // The `url` specified in a request is a full URL including protocol and hostname.
cannam@147 388
cannam@147 389 public:
cannam@147 390 struct Response {
cannam@147 391 uint statusCode;
cannam@147 392 kj::StringPtr statusText;
cannam@147 393 const HttpHeaders* headers;
cannam@147 394 kj::Own<kj::AsyncInputStream> body;
cannam@147 395 // `statusText` and `headers` remain valid until `body` is dropped.
cannam@147 396 };
cannam@147 397
cannam@147 398 struct Request {
cannam@147 399 kj::Own<kj::AsyncOutputStream> body;
cannam@147 400 // Write the request entity body to this stream, then drop it when done.
cannam@147 401 //
cannam@147 402 // May be null for GET and HEAD requests (which have no body) and requests that have
cannam@147 403 // Content-Length: 0.
cannam@147 404
cannam@147 405 kj::Promise<Response> response;
cannam@147 406 // Promise for the eventual respnose.
cannam@147 407 };
cannam@147 408
cannam@147 409 virtual Request request(HttpMethod method, kj::StringPtr url, const HttpHeaders& headers,
cannam@147 410 kj::Maybe<uint64_t> expectedBodySize = nullptr) = 0;
cannam@147 411 // Perform an HTTP request.
cannam@147 412 //
cannam@147 413 // `url` may be a full URL (with protocol and host) or it may be only the path part of the URL,
cannam@147 414 // depending on whether the client is a proxy client or a host client.
cannam@147 415 //
cannam@147 416 // `url` and `headers` need only remain valid until `request()` returns (they can be
cannam@147 417 // stack-allocated).
cannam@147 418 //
cannam@147 419 // `expectedBodySize`, if provided, must be exactly the number of bytes that will be written to
cannam@147 420 // the body. This will trigger use of the `Content-Length` connection header. Otherwise,
cannam@147 421 // `Transfer-Encoding: chunked` will be used.
cannam@147 422
cannam@147 423 struct WebSocketResponse {
cannam@147 424 uint statusCode;
cannam@147 425 kj::StringPtr statusText;
cannam@147 426 const HttpHeaders* headers;
cannam@147 427 kj::OneOf<kj::Own<kj::AsyncInputStream>, kj::Own<WebSocket>> upstreamOrBody;
cannam@147 428 // `statusText` and `headers` remain valid until `upstreamOrBody` is dropped.
cannam@147 429 };
cannam@147 430 virtual kj::Promise<WebSocketResponse> openWebSocket(
cannam@147 431 kj::StringPtr url, const HttpHeaders& headers, kj::Own<WebSocket> downstream);
cannam@147 432 // Tries to open a WebSocket. Default implementation calls send() and never returns a WebSocket.
cannam@147 433 //
cannam@147 434 // `url` and `headers` are invalidated when the returned promise resolves.
cannam@147 435
cannam@147 436 virtual kj::Promise<kj::Own<kj::AsyncIoStream>> connect(kj::String host);
cannam@147 437 // Handles CONNECT requests. Only relevant for proxy clients. Default implementation throws
cannam@147 438 // UNIMPLEMENTED.
cannam@147 439 };
cannam@147 440
cannam@147 441 class HttpService {
cannam@147 442 // Interface which HTTP services should implement.
cannam@147 443 //
cannam@147 444 // This interface is functionally equivalent to HttpClient, but is intended for applications to
cannam@147 445 // implement rather than call. The ergonomics and performance of the method signatures are
cannam@147 446 // optimized for the serving end.
cannam@147 447 //
cannam@147 448 // As with clients, there are two kinds of services:
cannam@147 449 // * Host services are used when talking to a specific host. The `url` specified in a request
cannam@147 450 // is actually just a path. (A `Host` header is still required in all requests, and the service
cannam@147 451 // may in fact serve multiple origins via this header.)
cannam@147 452 // * Proxy services are used when the target could be any arbitrary host on the internet, i.e. to
cannam@147 453 // implement an HTTP proxy. The `url` specified in a request is a full URL including protocol
cannam@147 454 // and hostname.
cannam@147 455
cannam@147 456 public:
cannam@147 457 class Response {
cannam@147 458 public:
cannam@147 459 virtual kj::Own<kj::AsyncOutputStream> send(
cannam@147 460 uint statusCode, kj::StringPtr statusText, const HttpHeaders& headers,
cannam@147 461 kj::Maybe<uint64_t> expectedBodySize = nullptr) = 0;
cannam@147 462 // Begin the response.
cannam@147 463 //
cannam@147 464 // `statusText` and `headers` need only remain valid until send() returns (they can be
cannam@147 465 // stack-allocated).
cannam@147 466 };
cannam@147 467
cannam@147 468 virtual kj::Promise<void> request(
cannam@147 469 HttpMethod method, kj::StringPtr url, const HttpHeaders& headers,
cannam@147 470 kj::AsyncInputStream& requestBody, Response& response) = 0;
cannam@147 471 // Perform an HTTP request.
cannam@147 472 //
cannam@147 473 // `url` may be a full URL (with protocol and host) or it may be only the path part of the URL,
cannam@147 474 // depending on whether the service is a proxy service or a host service.
cannam@147 475 //
cannam@147 476 // `url` and `headers` are invalidated on the first read from `requestBody` or when the returned
cannam@147 477 // promise resolves, whichever comes first.
cannam@147 478
cannam@147 479 class WebSocketResponse: public Response {
cannam@147 480 public:
cannam@147 481 kj::Own<WebSocket> startWebSocket(
cannam@147 482 uint statusCode, kj::StringPtr statusText, const HttpHeaders& headers,
cannam@147 483 WebSocket& upstream);
cannam@147 484 // Begin the response.
cannam@147 485 //
cannam@147 486 // `statusText` and `headers` need only remain valid until startWebSocket() returns (they can
cannam@147 487 // be stack-allocated).
cannam@147 488 };
cannam@147 489
cannam@147 490 virtual kj::Promise<void> openWebSocket(
cannam@147 491 kj::StringPtr url, const HttpHeaders& headers, WebSocketResponse& response);
cannam@147 492 // Tries to open a WebSocket. Default implementation calls request() and never returns a
cannam@147 493 // WebSocket.
cannam@147 494 //
cannam@147 495 // `url` and `headers` are invalidated when the returned promise resolves.
cannam@147 496
cannam@147 497 virtual kj::Promise<kj::Own<kj::AsyncIoStream>> connect(kj::String host);
cannam@147 498 // Handles CONNECT requests. Only relevant for proxy services. Default implementation throws
cannam@147 499 // UNIMPLEMENTED.
cannam@147 500 };
cannam@147 501
cannam@147 502 kj::Own<HttpClient> newHttpClient(HttpHeaderTable& responseHeaderTable, kj::Network& network,
cannam@147 503 kj::Maybe<kj::Network&> tlsNetwork = nullptr);
cannam@147 504 // Creates a proxy HttpClient that connects to hosts over the given network.
cannam@147 505 //
cannam@147 506 // `responseHeaderTable` is used when parsing HTTP responses. Requests can use any header table.
cannam@147 507 //
cannam@147 508 // `tlsNetwork` is required to support HTTPS destination URLs. Otherwise, only HTTP URLs can be
cannam@147 509 // fetched.
cannam@147 510
cannam@147 511 kj::Own<HttpClient> newHttpClient(HttpHeaderTable& responseHeaderTable, kj::AsyncIoStream& stream);
cannam@147 512 // Creates an HttpClient that speaks over the given pre-established connection. The client may
cannam@147 513 // be used as a proxy client or a host client depending on whether the peer is operating as
cannam@147 514 // a proxy.
cannam@147 515 //
cannam@147 516 // Note that since this client has only one stream to work with, it will try to pipeline all
cannam@147 517 // requests on this stream. If one request or response has an I/O failure, all subsequent requests
cannam@147 518 // fail as well. If the destination server chooses to close the connection after a response,
cannam@147 519 // subsequent requests will fail. If a response takes a long time, it blocks subsequent responses.
cannam@147 520 // If a WebSocket is opened successfully, all subsequent requests fail.
cannam@147 521
cannam@147 522 kj::Own<HttpClient> newHttpClient(HttpService& service);
cannam@147 523 kj::Own<HttpService> newHttpService(HttpClient& client);
cannam@147 524 // Adapts an HttpClient to an HttpService and vice versa.
cannam@147 525
cannam@147 526 struct HttpServerSettings {
cannam@147 527 kj::Duration headerTimeout = 15 * kj::SECONDS;
cannam@147 528 // After initial connection open, or after receiving the first byte of a pipelined request,
cannam@147 529 // the client must send the complete request within this time.
cannam@147 530
cannam@147 531 kj::Duration pipelineTimeout = 5 * kj::SECONDS;
cannam@147 532 // After one request/response completes, we'll wait up to this long for a pipelined request to
cannam@147 533 // arrive.
cannam@147 534 };
cannam@147 535
cannam@147 536 class HttpServer: private kj::TaskSet::ErrorHandler {
cannam@147 537 // Class which listens for requests on ports or connections and sends them to an HttpService.
cannam@147 538
cannam@147 539 public:
cannam@147 540 typedef HttpServerSettings Settings;
cannam@147 541
cannam@147 542 HttpServer(kj::Timer& timer, HttpHeaderTable& requestHeaderTable, HttpService& service,
cannam@147 543 Settings settings = Settings());
cannam@147 544 // Set up an HttpServer that directs incoming connections to the given service. The service
cannam@147 545 // may be a host service or a proxy service depending on whether you are intending to implement
cannam@147 546 // an HTTP server or an HTTP proxy.
cannam@147 547
cannam@147 548 kj::Promise<void> drain();
cannam@147 549 // Stop accepting new connections or new requests on existing connections. Finish any requests
cannam@147 550 // that are already executing, then close the connections. Returns once no more requests are
cannam@147 551 // in-flight.
cannam@147 552
cannam@147 553 kj::Promise<void> listenHttp(kj::ConnectionReceiver& port);
cannam@147 554 // Accepts HTTP connections on the given port and directs them to the handler.
cannam@147 555 //
cannam@147 556 // The returned promise never completes normally. It may throw if port.accept() throws. Dropping
cannam@147 557 // the returned promise will cause the server to stop listening on the port, but already-open
cannam@147 558 // connections will continue to be served. Destroy the whole HttpServer to cancel all I/O.
cannam@147 559
cannam@147 560 kj::Promise<void> listenHttp(kj::Own<kj::AsyncIoStream> connection);
cannam@147 561 // Reads HTTP requests from the given connection and directs them to the handler. A successful
cannam@147 562 // completion of the promise indicates that all requests received on the connection resulted in
cannam@147 563 // a complete response, and the client closed the connection gracefully or drain() was called.
cannam@147 564 // The promise throws if an unparseable request is received or if some I/O error occurs. Dropping
cannam@147 565 // the returned promise will cancel all I/O on the connection and cancel any in-flight requests.
cannam@147 566
cannam@147 567 private:
cannam@147 568 class Connection;
cannam@147 569
cannam@147 570 kj::Timer& timer;
cannam@147 571 HttpHeaderTable& requestHeaderTable;
cannam@147 572 HttpService& service;
cannam@147 573 Settings settings;
cannam@147 574
cannam@147 575 bool draining = false;
cannam@147 576 kj::ForkedPromise<void> onDrain;
cannam@147 577 kj::Own<kj::PromiseFulfiller<void>> drainFulfiller;
cannam@147 578
cannam@147 579 uint connectionCount = 0;
cannam@147 580 kj::Maybe<kj::Own<kj::PromiseFulfiller<void>>> zeroConnectionsFulfiller;
cannam@147 581
cannam@147 582 kj::TaskSet tasks;
cannam@147 583
cannam@147 584 HttpServer(kj::Timer& timer, HttpHeaderTable& requestHeaderTable, HttpService& service,
cannam@147 585 Settings settings, kj::PromiseFulfillerPair<void> paf);
cannam@147 586
cannam@147 587 kj::Promise<void> listenLoop(kj::ConnectionReceiver& port);
cannam@147 588
cannam@147 589 void taskFailed(kj::Exception&& exception) override;
cannam@147 590 };
cannam@147 591
cannam@147 592 // =======================================================================================
cannam@147 593 // inline implementation
cannam@147 594
cannam@147 595 inline void HttpHeaderId::requireFrom(HttpHeaderTable& table) const {
cannam@147 596 KJ_IREQUIRE(this->table == nullptr || this->table == &table,
cannam@147 597 "the provided HttpHeaderId is from the wrong HttpHeaderTable");
cannam@147 598 }
cannam@147 599
cannam@147 600 inline kj::Own<HttpHeaderTable> HttpHeaderTable::Builder::build() { return kj::mv(table); }
cannam@147 601 inline HttpHeaderTable& HttpHeaderTable::Builder::getFutureTable() { return *table; }
cannam@147 602
cannam@147 603 inline uint HttpHeaderTable::idCount() { return namesById.size(); }
cannam@147 604
cannam@147 605 inline kj::StringPtr HttpHeaderTable::idToString(HttpHeaderId id) {
cannam@147 606 id.requireFrom(*this);
cannam@147 607 return namesById[id.id];
cannam@147 608 }
cannam@147 609
cannam@147 610 inline kj::Maybe<kj::StringPtr> HttpHeaders::get(HttpHeaderId id) const {
cannam@147 611 id.requireFrom(*table);
cannam@147 612 auto result = indexedHeaders[id.id];
cannam@147 613 return result == nullptr ? kj::Maybe<kj::StringPtr>(nullptr) : result;
cannam@147 614 }
cannam@147 615
cannam@147 616 inline void HttpHeaders::unset(HttpHeaderId id) {
cannam@147 617 id.requireFrom(*table);
cannam@147 618 indexedHeaders[id.id] = nullptr;
cannam@147 619 }
cannam@147 620
cannam@147 621 template <typename Func>
cannam@147 622 inline void HttpHeaders::forEach(Func&& func) const {
cannam@147 623 for (auto i: kj::indices(indexedHeaders)) {
cannam@147 624 if (indexedHeaders[i] != nullptr) {
cannam@147 625 func(table->idToString(HttpHeaderId(table, i)), indexedHeaders[i]);
cannam@147 626 }
cannam@147 627 }
cannam@147 628
cannam@147 629 for (auto& header: unindexedHeaders) {
cannam@147 630 func(header.name, header.value);
cannam@147 631 }
cannam@147 632 }
cannam@147 633
cannam@147 634 } // namespace kj
cannam@147 635
cannam@147 636 #endif // KJ_COMPAT_HTTP_H_