This article is part of the series on the rust-http redesign, Teepee.
Of the first article, most details are superseded here; the most important part that is still definitely in place is the status code class part.
The criticism and responses to it
The main point of criticism levelled at the designs I posted is that it was still treating the
Reason-Phrase too importantly.
Some argue in favour of dropping the reason phrase altogether. That I am not willing to do, citing the 451 example for justification. No, the reason phrase must remain around, somewhere.
A common suggestion is splitting the status code and reason phrase apart. This is looking like a healthy option. It is one that had not occurred to me; I had become too tied up in their correlation, that being what I had been working with in the past in rust-http.
Library designers, beware—your own perspective will become warped over time so that you often see only slight variations from what you are doing. Getting fresh eyes on a solution is marvellous; I am very glad that I wrote about this matter. Thank you, everyone, for the feedback: Teepee will be the stronger for it.
The new design
I now present a new design, influenced strongly by the feedback received.
At the lowest level, rather than giving a single
Status object, the code will work with the pair
Responses received by the client will have both of these:
Responses a server is writing will have both fields, but with the reason phrase optional:
Normally people will just set the status code. If the reason phrase is
None, the appropriate value will be inferred, or for unknown statuses, something like “<no reason phrase given>”.
I am satisfied with this design. Now the only question remaining is how
StatusCode itself is represented.
StatusCode part: a few designs
There are a few possibilities for the Status-Code part; I present here a few, each with its benefits and drawbacks. Their ordering is not significant.
#1: an enum with an extension code variant
This is similar to what is done in rust-http. It takes four bytes, while the others take two bytes.
This maps extremely well onto the RFC 2616 definition of
However, this has the backwards‐compatibility problem (one I identified earlier but forgot to write down in my previous article) that when a new status
103 Newly Registered Value is registered, things will change from using
ExtensionCode(103) to using
NewlyRegisteredValue. It’s a very rare case, but very significant.
#2: a 500‐variant C‐style enum
This scheme has the slight weakness that when a status becomes registered, its variant will change; this is not, however, a severe backwards‐compatibility problem, for we would leave static aliases behind (e.g.
pub static Code103: StatusCode = NewlyRegisteredValue). That could also be used to take care of cases like 451.
#3: a simple wrapping struct with associated statics
I handle this as a separate struct rather than just as
u16, because there are only 500 valid values (100–599). Just as
~str applies checks to maintain the invariant that it is valid UTF-8, I will ensure that an illegal
StatusCode can never exist. (That applies to the
ExtensionCode variant of the enum solution too, by the way.)
The biggest thing this has going against it is that pattern matching won’t work.
#4: just a number
A variant of #3, one can cause pattern matching to work by using a number type directly:
I am not in favour of this solution because of semantic mismatch. A status is not just a number—it has 500 valid values, as mentioned above, and I wish to maintain the invariant. Also things like
+ simply don’t make sense for a status. Sure, it’s shown as a number, but it is opaque data.
I am now satisfied with the overall design of the
Status-Line handling. As to the representation of
StatusCode, my current order of preferences, from favourite to least favourite, is #2, #3, #1, #4.
I now invite you to join in the discussion at /r/rust.
Update: the HTTP/2.0 situation
Something I neglected to do all along was refer to the HTTP/2.0 specification. (I looked at that part a few months ago, but had forgotten about it. I’ve been focusing more on the multiplexing aspects of HTTP/2.0, as that’s the main change and the main area of implementation difficulty for Teepee.) It has a couple of notable changes:
The reason phrase is gone altogether (Section 18.104.22.168, Response Header Fields). This confirms that separating the code and the reason is the correct thing to do.
1xx Informational class statuses are removed (Section 8.1.1, Informational Responses). This suggests that restricting the values structurally in the type system might not be quite so valuable, as only a subset of those values are actually valid in certain situations.