HNHacker News
TopNewBestAskShowJobs

Lukasa

333 karma · joined April 24, 2013

My name is Cory. I build networking software. I work on open source Python projects. I write about networking and about Python. See https://lukasa.co.uk for, you know, me.
submissionscomments
Lukasa··on The Way of the Gopher: Making the Switch from Node.js to Golang
I agree with this entirely. As I said many times before, I like Go and I like its approach to concurrency.

All I'm trying to do is to make sure that people who make bold claims about abstracting away blocking code aren't misleading others: when calling other code you should always be aware of how it interacts with the flow control of your program.

Lukasa··on The Way of the Gopher: Making the Switch from Node.js to Golang
Honestly, I think the distinction between M:N threading and straight OS threading is pretty minor. It grants some advantages to the language runtime: it can control the stack size, for example. But in terms of how it affects the development style and what kinds of bugs it encourages/discourages I don't think it dramatically differs from the OS threading model.
Lukasa··on The Way of the Gopher: Making the Switch from Node.js to Golang
> The caller can make function "non-blocking" by wrapping the call in a goroutine themselves.

Sure, but if they want the return value then either they need to construct the Future-y wrapper I just described or they need to assemble it together in a collection of other function calls wrapped inside a function that itself is either Future-y or uses a long-lived channel to communicate results.

It is not novel to build up a non-blocking system from purely blocking method invocations. We've been doing that for years: it's called threading. Doing things this way has many advantages when written with appropriate diligence, and I'm not pretending otherwise. However, if you actually care about communicating between these arbitrary threads of execution than you either need Futures or queues (both of which are essentially just channels in Go), and at this point you've got the exact same problems as you get in NodeJS or any other asynchronous programming environment.

> The rest of your post built on this example seems shaky to me, since it seems built on an example API that doesn't need to exist.

I don't think that's fair: as I mentioned above, the fact that you as library author would not write the Future-y extension doesn't mean that the Future-y extension isn't built: you just force your caller to build it. That's fine, it's a perfectly good architectural decision (probably you should't be making those decisions for your user), but it doesn't remove the problem.

> I'd say that "you don't have to care whether your code is async or not" is a overstating the case. I would append the qualifier "unless you're introducing concurrency".

Sure. The thing that matters here is that Node is always introducing concurrency, because Node is concurrent. This is why all Node programs have to care about concurrency: they are all concurrent because their system is concurrent.

This is desperately inconvenient for many one-off programs, which is why I personally don't use Node for anything like that: I'd much rather use Python or Rust or Go. But that was never my argument. My argument was about OP's assertion that "with Go it doesn't matter if an operation is blocking or non blocking, that fact can totally be abstracted from the client code. Writing callbacks is tedious, promises are tedious, co routines with yield need plumbing and async isn't in the spec."

The first sentence is dangerously misleading (while technically true, any system that does that is usable only in that one context), and the second one misses the point, which is that those things get effectively built anyway in any moderate-scale concurrent system in Go.

But my biggest point is this: Go isn't magic in regard to concurrency, and there is a weird amount of magical thinking around Go. Go is a very good language with a lot to like, and I like it quite a lot. But when boiled down to it, Go's concurrency model is threads with a couple of really useful primitives. And that's great, and it works really well. But it's not new or novel.

The sentence "with Go it doesn't matter if an operation is blocking or non blocking, that fact can totally be abstracted from the client code" is equally true if you replace "Go" with "C", or "Python", or "Java", or any language with a threaded concurrency model. There's no magic here. It's the same building blocks everyone else is using.

Lukasa··on The Way of the Gopher: Making the Switch from Node.js to Golang
I agree with almost all of your post except this:

> With Go it doesn't matter if an operation is blocking or non blocking, that fact can totally be abstracted from the client code.

No, it can't, and pretending that it can is misleading in a way that allows large teams of developers to cause themselves real problems.

The only sense in which this is true is that you can write a Go function with a simple signature like this:

    func DoSomeStuff (error) {}
And the caller has no idea whether this involves concurrency under the hood (e.g. the function can spawn its own goroutines and channels as it sees fit).

But, make no bones about it: this function blocks until it completes. This means that while this function may do concurrent work it is absolutely a blocking function. This is fine: sometimes blocking functions are good. But you cannot write a non-blocking Go function in the same way.

To make a function non-blocking you can return a channel out of it for the return value to appear on, like this:

    func DoSomeStuff (chan error) {}
In this model the caller really doesn't have to care whether the function is synchronous or not (though the fact that it was written this way strongly suggests that it is going to return asynchronously, or at least that the developer believes it will have to in the future).

Except...that return value just there? That's a Future. It's a terrible, half-implemented version of a Future, but that's exactly what it is. It's a promise to return some kind of result at some point when the underlying process has returned.

And if you don't want to block your current goroutine, you cannot block on that channel receive either. That means that you need a callback. There are two patterns for doing that: you could have some kind of central loop that selects over all channels like this and calls the callback functions (boy that looks a lot like Node's event loop, doesn't it!), or you can manually spawn your own callback functions in their own goroutines. Either way, you have callbacks and futures here: you're just building them yourself and calling them something different.

There are lots of good reasons to switch to Go: it's a language that makes lots of developers remarkably productive, it has an ingrained philosophy of building concurrent programs, it's pretty damn fast, and it runs on all kinds of awesome platforms. But claiming that Go has learned something magic and new about how to write concurrent software in such a way that you don't have to care whether your code is async or not is just not true: you always have to care.

Lukasa··on Asyncssh – Python Asyncio Client/Server Implementation of SSHv2 Protocol
There's already lots of this. That's basically what Twisted is, for example.
Lukasa··on Abusing Your Browser: Infinitely Large Favicons
Why?
Lukasa··on Abusing Your Browser: Infinitely Large Favicons
Only if the file is served as one large chunk. If sent using Transfer-Encoding: chunked, there is no requirement to provide a Content-Length header, and a HEAD request may not show one.

The way Go's built in HTTP libraries work is that, if you don't attach a Content-Length header yourself, you will automatically send a chunk-encoded response. That's exactly what the OP's server does, and as a result there is no Content-Length to look for.

User-Agents need to defend against this by setting an appropriate upper-limit on the size of the object they want to receive.

Note that there's another obvious way to try to consume bandwidth: host a "file download" that actually just sends a chunk-encoded random byte sequence until the connection closes.

Lukasa··on Starters and Maintainers
Yeah, so this is an interesting feature because in my experience it doesn't work.

[We added one](https://github.com/kennethreitz/requests/blob/master/CONTRIB...) for the Requests module a little while ago because we kept getting bug reports that contained incomplete tracebacks or vague problem statements, or questions that were better suited to Stack Overflow.

However, we still get plenty of issues opened that suggest that the user hasn't read the CONTRIBUTING.md. We still get questions on the tracker, we still get people not telling us what version they're using or what platform they're running on.

What I want to know is: is this our fault? Is our text bad? Or does GitHub's UI not accentuate the need to read this document firmly enough?

Lukasa··on SHA-1 Certificates: A History of Hard Choices
Oh if only that were possible. However, many people are using older OpenSSLs with no route to upgrading. For example, Ubuntu 14.04 is using OpenSSL 1.0.1 currently, and will not upgrade to any later release of OpenSSL. It's unlikely to get backported to earlier releases because it fundamentally changes the logic and API of that OpenSSL release (by requiring a whole slew of extra functions that affect cert chain building). This would mean anyone not using a bleeding-edge system is caught in this situation where they can't upgrade.

It's definitely an interesting idea to allow constraints to be set on what certificates can be used during chain construction, but the scope of the problem is very large: there are lots of fields in an X509 certificate, many with complex values that complex filtering may want to be done on. In principle this can be done using callbacks from OpenSSL, but because of the potential up-and-down nature of building these trees there will be a substantial amount of complexity in place.

And the reality is that backward compatibility is already right. OpenSSL, today, is backward compatible with SHA1 certs. The problem here is forward compatibility: making older SSL stacks meet the security requirements of the modern day, many years after they were written. That's hard, and may actually be impossible.

Lukasa··on SHA-1 Certificates: A History of Hard Choices
You have to read the follow-up post to get a good description of why this won't work, but let me try.

The problem is that most clients currently are not able to build up multiple trust chains. They hand OpenSSL the certificates the remote server gave them, a pile of root certs, and say "tell me if you can build a trust chain". To remove SHA1 support, they'd have to inspect the returned trust chain and check whether any certs have SHA1 signatures. If they do, you'd fail outright, even though there may have been a perfectly good SHA2-only path available that OpenSSL didn't take, because there's no way to tell OpenSSL "try again but don't use this cert".

This means that we're telling the 97% of users with "modern" clients to force all TLS stacks to add support for new path building logic with arbitrary certificate avoidance in order to avoid the pain of the 3% of users who didn't get upgrades.

Note that I said "OpenSSL" here, but it actually applies to a wide range of TLS stacks: OpenSSL is just one of the more widely deployed ones.

Lukasa··on Adventures in debugging: etcd, HTTP pipelining, and file descriptor leaks
Good spot Rob!

In the past we (urllib3) have been pretty optimistic about assuming connections are still useful when we hit exceptions, but increasingly that optimism seems to be hurting us.

For that reason, I've taken your fix, added tests for it, then proposed it to the main urllib3 repository[0]. Right now the commits are in my name, but I'd be totally happy for you to make them yourself and open a pull request containing my commits. Either way, I've also added you to our contributors list.

Thanks for the write up!

[0]: https://github.com/shazow/urllib3/pull/734

Lukasa··on Entweet: Securing Twitter
Yes.

Remember, signing/encryption do not ensure that you can communicate, only that if you communicate you can do so with integrity, authenticity, and privacy. The same limitation applies here, with the added bonus of this particular method being totally stupid.

Lukasa··on Entweet: Securing Twitter
Twitter re-compresses the image, sadly, so that doesn't work.

As I said elsewhere, there are other options, like QR codes or high capacity colour barcodes, but those aren't funny.

Lukasa··on Entweet: Securing Twitter
So you can't do a direct 1-to-1 mapping of pixels to bytes, because Twitter re-compresses the image. You cannot rely on the binary representation of the image to make that work.

However, there are plenty of other, less funny, options, like QR codes or High Capacity Colour Barcodes.

Lukasa··on Entweet: Securing Twitter
Entirely accurate!

The interesting thing about the poor man's OCR is that it works way better than out of the box OCR tools. They're all built around handling arbitrary, often quite noisy input, so their handling of 1s and ls in this data is often not great.

Doing the cheap OCR works really well because we can overfit our data: we know the font, we know the grid, and we know the orientation, so our problem is way simpler than generic OCR.

Lukasa··on Yesterday's best-practices are today's HTTP/2 anti-patterns
> Do these HTTP/2 server implementations downgrade to HTTP0.x/1.x if the client support only an older version?

Many do, yes.

> Will there be v2-only servers in near future?

Yes.

> Debugging and implementing older protocols seem to be easier, as they were text based.

Implementing text protocols seems to be easier, but writing an implementation that can handle the wide variety of both compliant and slightly non-compliant traffic is an exercise in frustration.

This is not helped by the fact that people see the text protocol and think that it's easy to implement, so they go and write their own HTTP/1.1 server and leave it on the internet. Their server is probably not quite spec-compliant, so everyone else is left trying to interop with it.

Binary protocols are hard to debug by eye, but they aren't hard to write parsers for.

Lukasa··on Yesterday's best-practices are today's HTTP/2 anti-patterns
Sure it does. HTTP/2 streams are bidirectional. The websocket flow can be perfectly emulated in HTTP/2: make a websockety request, get a 200 HTTP response, then both sides keep the stream open and send data through it.
Lukasa··on Show HN: Caddy, a cross-platform HTTP/2 web server
Nope, nghttp2 and h2o have been floating around for a long time now.
Lukasa··on Http2 explained
Ah, sorry, I misread your question! I don't actually know the lengthy reasoning for that edge case. =(
Lukasa··on Http2 explained
Sure. The only way to stop a message before the content-length is transmitted is to kill your TCP connection. This is inefficient: you need to recreate it, bearing all the TCP set-up cost all over again, and then deal with the small initial congestion window on your new connection.

HTTP/2 allows you to avoid that by saying "I'm done with this stream now, sorry!"

Lukasa··on Http2 explained
Because running multiple TCP connections in parallel plays havoc with TCP congestion control and also plays poorly with the TCP slow-start logic. Every TCP connection begins its receive window again and so it starts small, so fetching many moderately-sized or large resources (think images) will cost you many round trips you didn't need to spend.
Lukasa··on How speedy is SPDY? [pdf]
Leaving aside the technical statements about SPDY, the reality of HTTP pipelining is that no-one uses it. According to Wikipedia, Opera is the only major browser that ships with pipelining enabled. Most intermediaries don't support pipelining either.

Pipelining was a well-intentioned feature which didn't solve the core problem: namely, that a big or slow request can block you from doing anything else for a really long time unless you open another TCP connection.

Lukasa··on HTTP2 [pdf]
Working well with quic is not a design goal of HTTP/2. However, my understanding is that quic was originally designed to work well with SPDY, and so ought to work well with HTTP/2 as well.

There'll definitely be some Googlers paying attention to the WG to ensure that the two protocols play well together.

Lukasa··on HTTP/2 all the things
> Also being an old guy(tm) I worry about loosing the ability for humans to talk to these services or see the conversation in a textual way.

This is definitely a loss. It's real, it's unfortunate. But the binary-vs-text change is actually pretty minor: a well-crafted binary protocol permits simple tools that can be used to debug it.

The actual problem here is that HTTP/2 is highly stateful. Connection state, stream state, and header compression state are all required to actually reliably debug a HTTP/2 flow. In practice, that means either your HTTP/2 implementation needs to be able to dump its state, or you need to capture the entire session to understand it.

For my part, I will not mourn the ability to telnet to a port and type HTTP. In practice, that approach lacks value in most modern cases. However, I will mourn the loss of tcpdump as a single HTTP debugging tool. Your implementations now need to make debugging possible in their own right.

Lukasa··on HTTP/2 all the things
I got the impression that Microsoft is backing this because of pressure from large enterprises, which do not want to deploy TLS in their intranets. I don't believe IE will prefer plaintext HTTP/2 to TLS HTTP/2, and it will certainly deploy both.

Put another way: I don't believe Microsoft is worse than Google in this regard.

Lukasa··on HTTP/2 all the things
There are a few key points here.

First, Ilya's slides are out of date. The reference set got removed (it was a complexity nightmare), so there's a bit less efficiency in deltas but a substantially simpler algorithm for header sets.

As for your badly behaved router, there are two options. The first is that it's a router, not a proxy. In that case, the strict ordering of TCP ensures that there's no problem here. A router cannot cause the server to miss a request without knowing about it.

Assuming by 'router' you meant 'proxy', the key here is that HPACK compression state is hop-by-hop, not end-to-end. That is, HPACK compression state is only synchronised at each end of a TCP connection. The strict ordering of TCP ensures that those two ends cannot be confused about the order of requests. Across TCP connections, there's new HPACK state.

Lukasa··on HTTP/2 all the things
No problem!

> Is there a C implementation of it?

Currently there's no good standalone version, because we're still at interop stage. Take a look at nghttp2[0], however. They've got a HPACK layer that I believe can be separated out. I've got a plan to actually do that work for them, but haven't had time to sit down and do it.

Nevertheless, theirs is the best implementation around at the moment in terms of compression efficiency. My own HTTP/2 library can optionally use it.

> Will all clients respect this, or will they start trying to send you HPACK requests anyway?

So we need to be clear on what this means. The actual binary format of headers via HPACK cannot be turned off. However, all the compression can be. This isn't optional: the other party must respect your preferences.

> So is this going to be yet another "your PC boots in 16-bit real mode in 2014", where servers have to keep switching from HTTP/1 to HTTP/2 on each new startup forevermore?

Hopefully no. Hopefully.

You're allowed to start straight away with HTTP/2 if you have some form of 'prior knowledge' that HTTP/2 will work. What 'prior knowledge' actually consists of is not entirely clear-cut, but in principle you can go straight to HTTP/2.

Note also that Upgrade: is only a problem for plaintext HTTP/2. If you use TLS, going to HTTP/2 is exactly as fast as going to HTTP/1.1 (because the negotiation is done at the same time as the TLS negotiation).

However, servers are clearly going to have to support HTTP/1.1 for a long time to come. This is unfortunate, but unavoidable.

[0]: http://nghttp2.org/

Lukasa··on HTTP/2 all the things
In practice it very nearly does. Only IE plan to support plaintext HTTP/2, all other browsers will only support it over TLS.

Opportunistic Encryption is still on the table as well.

Lukasa··on HTTP/2 all the things
HTTP/2 is redesigning the application layer protocol for the modern internet. QUIC is an attempt to redesign the transport layer. The two should work hand-in-hand. It'll be interesting to see how it works out!
Lukasa··on HTTP/2 all the things
These are all really good points, let me just address one or two.

> We are talking about ~300 bytes of data per header ... are the bandwidth gains really so superior to outweigh the CPU costs in having to compress the data?

The commonly-cited reason for pursuing header compression is making the very first request of a session, when your initial congestion window is small. The goal is to make the request small enough that it can fit in one round-trip so that you don't have to wait for all the ACKs.

Reducing duplication is great and will help for a lot of cases as well, but my impression from the WG has been that it's less important than that first round trip.

Note also that HPACK is really quite efficient in terms of CPU cycles. It takes relatively few to compress the headers very heavily.

Finally, if you really don't like it, you can turn HPACK off. That gives an out for constrained devices that genuinely can't spare the cycles.

> Does anyone know how clients are going to go about requesting HTTP/2 capabilities? Is there a special HTTP/1.1 header? Because Apache will respond to "GET / HTTP/2.0" with an HTTP/1.1 OK response at present.

Yes, because there's a draft spec. I invite you to read it[0]. The summary is that you have three options:

1. For plaintext HTTP/2, you use a HTTP Upgrade: header, just as everyone expects you to. 2. For HTTP/2 over TLS you use ALPN, a feature of TLS that lets you request a protocol in the TLS negotiation (to avoid an extra round trip). 3. Alternatively, you might be able to use HTTP Alternative Services (a WIP draft).

The behaviour Apache is using is clearly a bug.

[0]: https://tools.ietf.org/html/draft-ietf-httpbis-http2

← PreviousPage 2 of 3Next →