Commit Graph
27 Commits
Author SHA1 Message Date
bf41ad1595 Improve HTTPHeaders description performance (#3063)
### Motivation:

As outlined in this
[issue](https://github.com/apple/swift-nio/issues/2930) by @weissi, the
current performance of calling `description` on `HTTPHeaders` is
undermined by the dynamism of Array.

### Modifications:

The proposed solution replaces the implementation of `description` to
iterate over the items and print them out manually. This provides a
faster solution since we bypass the cost of calling into `description`
of an Array.


### Result:

A more performant implementation of `HTTPHeaders` description.

---------

Co-authored-by: Johannes Weiss <johannesweiss@apple.com>
Co-authored-by: Cory Benfield <lukasa@apple.com>
2025-01-20 16:39:21 +00:00
Franz Busch c9756e1083 Adopt swift-format (#2794)
* Apply formatting

* Apply no block comments rule

* Apply OmitExplicitReturns

* Apple OnlyOneTrailingClosureArgument

* Apply NoAssignmentInExpressions

* Fix up DontRepeatTypeInStaticProperties lint errors

* Apply `OrderedImports`

* Apply `ReplaceForEachWithForLoop`

* format file

* Enable the formatting pipeline

* Adopt `AmbiguousTrailingClosureOverload`

* Fix license header

* Fix format check

* Fix `EndOfLineComment`

* Fix CI

* Adapt CI script to check if changes when running formatting

* Separate lint and format into to steps

* Fix format

* Adopt `UseEarlyExits`

* Revert "Adopt `UseEarlyExits`"

This reverts commit d1ac5bbe12.
2024-07-19 11:48:17 +02:00
George Barnett f475d152fb Don't count CR or LF as whitespace when trimming canonical header values (#1954)
Motivation:

Follow up to https://github.com/apple/swift-nio/pull/1952#discussion_r707351015

The OWS rule from the HTTP semantics draft only considers 'SP' and
'HTAB' to be whitespace. We also (unnecessarily) consider CR and LF to
be whitespace.

Modifications:

- Remove CR and LF from the characters we consider to be whitespace
- Update tests

Result:

We no longer consider CR or LF to be whitespace when trimming
whitespace when producing the canonical form of header values.
2021-09-14 08:26:05 +01:00
Cory BenfieldandGeorge Barnett 64285cbff2 Clean up dependencies and imports. (#1935)
Motivation:

As we've largely completed our move to split out our core abstractions,
we now have an opportunity to clean up our dependencies and imports. We
should arrange for everything to only import NIO if it actually needs
it, and to correctly express dependencies on NIOCore and NIOEmbedded
where they exist.

We aren't yet splitting out tests that only test functionality in
NIOCore, that will follow in a separate patch.

Modifications:

- Fixed up imports
- Made sure our protocols only require NIOCore.

Result:

Better expression of dependencies.

Co-authored-by: George Barnett <gbarnett@apple.com>
2021-08-12 13:49:46 +01:00
Cory Benfield 8ea768b0b8 Add static vars for common HTTP versions (#1723)
Motivation:

I'm sick of typing `.init(major: 1, minor: 1)`.

Modifications:

- Added static vars for common HTTP versions.

Result:

Maybe I'll never type `.init(major: 1, minor: 1)` ever again.
2021-01-19 17:27:02 +00:00
Ludovic DewaillyandLudovic Dewailly fefd0c854f Provides a means to control the underlying header storage capacity. (#1451)
Motivation:

It's not always possible to initialize `HTTPHeaders` with all the headers one needs to set. In such case, the underlying array will be expanded as and when needed upon headers being appended. `Array` provides the means to reserve capacity to optimize such use case. This PR exposes this functionality in `HTTPHeaders`.

Modifications:

Expose method `reserveCapacity(_ minimumCapacity: Int)` and computed variable `capacity: Int` in `HTTPHeaders` and added some tests.

Result:

Developers will be able to control the capacity of the underlying headers array as such:

```
var headers = HTTPHeaders()
headers.reserveCapacity(5)
```

Co-authored-by: Ludovic Dewailly <ldewailly@apple.com>
2020-03-16 17:47:34 +00:00
George Barnett 6761c87d1e Add first(name:) to HTTPHeaders (#1282)
Motivation:

In some cases you only expect (or care about) a single value for a
header. However, getting this value without allocating an array or
traversing the whole array requres using first(where:) provided by
Sequence. This is not very ergonomic as it requires users to do the
case-insensitive comparison and then exctact the value from the
HTTPHeaders. Additionally, users cannot use NIO's case insensitive
ASCII comparison.

Modifications:

- Provide a first(name:) method on HTTPHeaders which returns the value
  from the first header whose name is a case insentive match of the
  subscript parameter.

Result:

It's easier to get the first value from HTTPHeaders matching a given
name.
2019-12-06 16:31:38 +00:00
George Barnett 6023c65341 Allow adding a sequence of headers to HTTPHeaders (#1004)
* Allow adding a sequence of headers to HTTPHeaders

Motivation:

When combining two collections of headers, adding each pair one-by-one
using add(name:value:) can cause multiple unnecessary reallocations of
the underlying array.

Modifications:

Provide two additional add methods to HTTPHeaders: one to add a sequence
of name/value pairs and another to add the headers from another
HTTPHeaders, both of which use `append(contentsOf:)` on the underlying
array to avoid additional reallocations.

Result:

Fewer reallocations when adding multiple headers.

* Fix incorrect documentation, make add<S: Sequence>(contentsOf:) @inlineable

* Reserve capacity and add sequentially instead of using append(contentsOf:)

* Add tests and missing @usableFromInline
2019-05-16 17:21:21 +01:00
Johannes Weiss a8fbcf080b EmbeddedChannel: remove mystery bools (#917)
Motivation:

EmbeddedChannel.finish/writeInbound/writeOutbound returned some mystery
bools. I always had to check the code to remember what they actually
meaned.

Whilst this is technically a breaking change, I couldn't find any users
of the return values on Github that are using the convergence releases.

Modifications:

Replace them by enums giving you all the information.

Result:

- fixes #916
- clearer APIs
2019-03-22 12:34:16 +00:00
Johannes Weiss 22e6cd3c67 HTTP/1 headers simplification & cleanup (#857)
Motivation:

The HTTP/1 headers were quite complicated, CoW-boxed and exposed their
guts (being a ByteBuffer). In Swift 5 this is no longer necessary
because of native UTF-8 Strings.

Modifications:

- make headers simply `[(String, String)]`
- remove `HTTPHeaderIndex`, `HTTPListHeaderIterator`, etc

Result:

- simpler and more performant code
2019-03-06 18:11:40 +00:00
JovanMilenkovic 0aee5bb91b Drop HTTPHeaders Sequence conformances in favor of RandomAccessCollection (#784)
Motivation:

Use better protocol instead of Sequence since it doesn't do much. Inspired by #676

Modifications:

Conform HTTPHeaders to RandomAccessCollection

Result:

HTTPHeaders will have RandomAccessCollection functionalities.
2019-03-05 09:41:49 +00:00
John Holdsworth f84b5b8087 Accept dictionary literals for HTTPHeaders (#824)
### Motivation:
A convenience/shorthand for developers.

### Modifications:
Add conformance to ExpressibleByDictionaryLiteral along with implementation.

### Result:
The following is would then be legal in Vapor:

```
        return HTTPResponse(status: .ok, headers: ["Content-Type": "text/html"],
                            body: "<table border=1><tr><td><i>Hello</i>, plugin!")
```
2019-02-14 09:03:31 +00:00
Johannes Weiss 1198931823 ByteBuffer: rename set(<type>:, ...) to set<Type>(...) (#812)
Motivation:

ByteBuffer methods like `set(string:)` never felt very Swift-like and
also didn't look the same as their counterparts like `getString(...)`.

Modifications:

- rename all `ByteBuffer.set/write(<type>:,...)` methods to
  `ByteBuffer.set/write<Type>(...)`
- polyfill the old spellings in `_NIO1APIShims`

Result:

code more Swift-like
2019-02-12 11:11:45 +00:00
Johannes Weiss 9b78557aff kick off NIO2 (#678)
Motivation:

NIO2 development starts now.

Modifications:

Made NIO Swift 5-only for everything else see docs/public-api-changes-NIO1-to-NIO2.md

Result:

NIO2 development can start.
2018-12-07 21:15:34 +00:00
Jim Dovey 5279d73d25 Changes to HTTPHeaders API to integrate with HTTP/2 HPACKHeaders API (#525)
Motivation:

HTTP/2 adds an additional item to a header key/value pair:
its indexability. By default, headers can be inserted into
the HPACK header table index; sometimes this will be denied
(common case is 'content-length' header, which is rarely going
to be the same, and will just cause the table to purge more
often than is really necessary). Additionally, a header may be
marked as not only non-indexable but also non-rewritable by
proxies. HTTPHeaders provides nowhere to store this, so the
HPACKHeaders implementation referenced in
apple/swift-nio-http2#10 was created to add in that capability.

Now, given that we really want the HTTP version to be an
implementation detail, we want to keep the HPACK details hidden,
and would thus be using HTTPHeaders in client APIs. NIOHTTP2
already has a HTTP1-to-HTTP2 channel handler that translates
between the two. Thus it behooves us to have the means to copy
the actual key/value pairs between the two types without making
a round-trip from UTF-8 bytes to UTF-16 Strings. These changes
allow NIOHPACK or NIOHTTP2 to implement that round-trip internally.

Modifications:

- HTTPHeader and HTTPHeaderIndex types are now public.
- HTTPHeaders.buffer and HTTPHeaders.headers properties are now
  public.
- A new static method, HTTPHeaders.createHeaderBlock(buffer:headers:)
  was created to serve as a public means to create a HTTPHeaders from
  a ByteBuffer from outside NIOHTTP1. @Lukasa suggested this should
  be a static method rather than an initializer.

Result:

Nothing in NIOHTTP1 changes in operation. All public types have
documentation comments noting that they are only public for very
specific reasons, and are not intended for general use. Once this
is committed, NIOHPACK & NIOHTTP2 will be able to implement fast
round-trips between HTTPHeaders and HPACKHeaders.
2018-08-09 14:03:31 +01:00
fadi-botros 78729bd4ad Solving issue #410 (#414)
match all values in HTTPHeaders.isKeepAlive(...)

HTTPHeaders.isKeepAlive(...) does only match the first value.

### Motivation:

Keep-alive and Close may be something on comma separated arrays

### Modifications:

 - Made an extension function to `ByteBuffer` that could separate the strings at low level, without Swift string API, so that it could split the values
 - Another extension functions that compares the comma separated array with a given array to tell the caller which exists and which no
 - Added unit tests for them
 - Used them in the HTTPHeader

### Result:

- Now, if a request is sent using Keep-alive or Close where it was in an array, it will be handled
- fixes #410
2018-08-07 18:49:30 +01:00
Norman Maurer a7614d9180 Reuse detected keepalive of http_parser as long as possible (#299)
Motivation:

To detect if keepalive is used we need to search the headers (if HTTP/1.1 is used). This may be expensive depending how many headers are present. http_parser itself detects already if keep alive should be used and so we can re-use this as long as the user did not modify the headers.

Modifications:

Reuse keepalive parsed by http_parser as long as the headers were not modified.

Result:

Less overhead as long as the headers are not modified.
2018-05-14 18:04:52 +02:00
Norman Maurer 575e7686cf Optimize for HTTP/1.1 and HTTP/1.0 responses (#300)
Motivation:

Its very likely that the response will be HTTP/1.1 or HTTP/1.0 so we can optimize for it by minimizing the buffer.write(...) calls.
Beside this we should also change the pattern of *.write(into: inout ByteBuffer) to extension on ByteBuffer itself.

Modificiations:

- Optimize for HTTP/1.0 and 1.1
- Use extensions on ByteBuffer

Result:

Faster and more clean code.
2018-04-12 16:22:32 +02:00
Norman Maurer 48cef4a59b Implement lazy headers parsing (#291)
Motivation:

We are currently parsing each header eagly which means we need to convert from bytes to String frequently. The reality is that most of the times the user is not really interested in all the headers and so it is kind of wasteful to do so.

Modification:

Rewrite our internal storage of HTTPHeaders to use a ByteBuffer as internal storage and so only parse headers on demand.

Result:

Less overhead for parsing headers.
2018-04-10 14:57:58 +02:00
Johannes Weiß 7e70bf5a87 rename HTTPHeaders.getCanonicalForm to subject and improve efficiency (#293)
Motivation:

HTTPHeaders had an unusual API for Swift: `getCanonicalForm(String)` which is
better suited as a subscript `[canonicalForm: String]`.
Also the implementation was needlessly inefficient.

Modifications:

- renamed HTTPHeaders.getCanonicalForm to HTTPHeaders[canonicalForm]
- improved efficiency by replacing anti-pattern
  Array.map { ...  }.reduce(+, []) by flatMap

Result:

more Swift in both meanings
2018-04-09 08:18:48 +01:00
Johannes Weiss ca6b0ff28d replace Foundation whitespace trimming
Motivation:

For HTTP parsing, we used Foundation to trim whitespace which is not
needed.

Modifications:

Implemented whitespace trimming in Swift.

Result:

less Foundation
2018-02-19 12:55:29 +00:00
Johannes Weiss eab7d314f3 make Channel non optional in ChannelHandlerContext 2018-01-30 12:03:51 +00:00
Johannes Weiss 64ef7728f9 finish renaming ByteBuffer getters to getType 2018-01-09 09:32:39 +00:00
Cory Benfield 0803012b7a Add support for retrieving headers in canonical form 2017-10-13 14:13:45 -07:00
Johannes Weiss 1adb581f59 remove more Foundation imports 2017-10-10 16:25:43 +01:00
Cory Benfield 47ac46bbe3 Don't join Set-Cookie headers together 2017-10-09 10:37:13 -07:00
Kevin Clark 78ba7a819e Make HTTPHeaders iterate with originalcase'd names
* Make HTTPHeaders iterate with originalcase'd names

Interface change as a consequence of this: now iterating headers
produces (name, value) tuples rather than (name, [values]).

* Add linux test wrapper

* Add case insensitive lookup test

* Update generate test headers

* Make HTTPHeadersIterator private
2017-07-24 12:55:12 -07:00