Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
I am doing HTTP wrong
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Armin Ronacher
May 13, 2012
Programming
5.2k
23
Share
I am doing HTTP wrong
A fresh look at HTTP for agile languages (more importantly: Python)
Armin Ronacher
May 13, 2012
More Decks by Armin Ronacher
See All by Armin Ronacher
Agentic Coding: The Future of Software Development with Agents
mitsuhiko
0
780
Do Dumb Things
mitsuhiko
0
950
No Assumptions
mitsuhiko
0
410
The Complexity Genie
mitsuhiko
0
330
The Catch in Rye: Seeding Change and Lessons Learned
mitsuhiko
0
430
Runtime Objects in Rust
mitsuhiko
0
420
Rust at Sentry
mitsuhiko
0
590
Overcoming Variable Payloads to Optimize for Performance
mitsuhiko
0
290
Rust API Design Learnings
mitsuhiko
0
670
Other Decks in Programming
See All in Programming
RTSPクライアントを自作してみた話
simotin13
0
480
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.5k
運用エージェントは "作る" から "育てる" へ - 記憶と自己進化の3層設計パターン / self-evolving-agents-three-layer-agent-design
gawa
12
3.5k
Stage 3 Decorators でできること / できないこと / TSKaigi 2026
susisu
1
1.5k
Modding RubyKaigi for Myself
yui_knk
0
890
DynamoDBには集計系のクエリがないけどなんとかしたい
musan
1
130
柔軟なPDFレイアウトエディタを支える型システム設計 — Discriminated UnionとConditional Typeの実践
minako__ph
4
1.4k
Javaの型とAI時代に型が大事な理由 / java types and type in AI era
kishida
2
100
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
350
プロパティの順序で型推論が壊れる!? TypeScript6.0の修正からContext-Sensitivityの仕組みを追う
bicstone
2
1.3k
Composerを使ったサプライチェーン攻撃の様子を眺めてみる #phpstudy
o0h
PRO
2
220
Why Laravel apps break—Mastering the fundamentals to keep them maintainable
kentaroutakeda
1
340
Featured
See All Featured
Building Flexible Design Systems
yeseniaperezcruz
330
40k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.4k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
122
22k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2k
Done Done
chrislema
186
16k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
[SF Ruby Conf 2025] Rails X
palkan
2
1.1k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
340
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
320
Navigating Weather and Climate Data
rabernat
0
210
Evolving SEO for Evolving Search Engines
ryanjones
0
210
Transcript
I am doing HTTP wrong — a presentation by Armin
Ronacher @mitsuhiko
The Web developer's Evolution
echo
request.send_header(…) request.end_headers() request.write(…)
return Response(…)
Why Stop there?
What do we love about HTTP?
Text Based
REST
Cacheable
Content Negotiation
Well Supported
Works where TCP doesn't
Somewhat Simple
Upgrades to custom protocols
Why does my application look like HTTP?
everybody does it
Natural Conclusion
we can do better!
we're a level too low
Streaming: one piece at the time, constant memory usage, no
seeking.
Buffering: have some data in memory, variable memory usage, seeking.
TYPICAL Request / Response Cycle User Agent Proxy Server Application
Stream “Buffered” Dispatcher View
In Python Terms def application(environ, start_response): # Step 1: acquire
data data = environ['wsgi.input'].read(...) # Step 2: process data response = process_data(data) # Step 3: respond start_response('200 OK', [('Content-Type', 'text/plain')]) return [response]
One Level Up s = socket.accept() f = s.makefile('rb') requestline
= f.readline() headers = [] while 1: headerline = f.readline() if headerline == '\r\n': break headers.append(headerline)
Weird Mixture on the app request.headers <- buffered request.form <-
buffered request.files <- buffered to disk request.body <- streamed
HTTP's Limited signalling Strict Request / Response The only communication
during request from the server to the client is closing the connection once you started accepting the body.
Bailing out early def application(request): # At this point, headers
are parsed, everything else # is not parsed yet. if request.content_length > TWO_MEGABYTES: return error_response() ...
Bailing out a little bit later def application(request): # Read
a little bit of data request.input.read(4096) # You just committed to accepting data, now you have to # read everything or the browser will be very unhappy and # Just time out. No more responding with 413 ...
Rejecting Form fields -> memory File uploads -> disk What's
your limit? 16MB in total? All could go to memory. Reject file sizes individually? Needs overall check as well!
The Consequences How much data do you accept? Limit the
overall request size? Not helpful because all of it could be in-memory
It's not just limiting Consider a layered system How many
of you write code that streams? What happens if you pass streamed data through your layers?
A new approach
Dynamic typing made us lazy
we're trying to solve both use cases in one we're
not supporting either well
How we do it Hide HTTP from the apps HTTP
is an implementation detail
Pseudocode user_pagination = make_pagination_schema(User) @export( specs=[('page', types.Int32()), ('per_page', types.Int32())], returns=user_pagination,
semantics='select', http_path='/users/' ) def list_users(page, per_page): users = User.query.paginate(page, per_page) return users.to_dict()
Types are specific user_type = types.Object([ ('username', types.String(30)), ('email', types.Optional(types.String(250))),
('password_hash', types.String(250)), ('is_active', types.Boolean()), ('registration_date', types.DateTime()) ])
Why? Support for different input/output formats keyless transport support for
non-HTTP no hash collision attacks :-) Predictable memory usage
Comes for free Easier to test Helps documenting the public
APIs Catches common errors early Handle errors without invoking code Predictable dictionary ordering
Strict vs Lenient
Rule of Thumb Be strict in what you send, but
generous in what you receive — variant of Postel's Law
Being Generous In order to be generous you need to
know what to receive. Just accepting any input is a security disaster waiting to happen.
Support unsupported types { "foo": [1, 2, 3], "bar": {"key":
"value"}, "now": "Thu, 10 May 2012 14:16:09 GMT" } foo.0=1& foo.1=2& foo.2=3& bar.key=value& now=Thu%2C%2010%20May%202012%2014:16:09%20GMT
Solves the GET issue GET has no body parameters have
to be URL encoded inconsistency with JSON post requests
Where is the streaming?
There is none
there are always two sides to an API
If the server has streaming endpoints — the client will
have to support them as well
For things that need actual streaming we have separate endpoints.
streaming is different
but we can stream until we need buffering
Discard useless stuff { "foo": [list, of, thousands, of, items,
we don't, need], "an_important_key": "we're actually interested in" }
What if I don't make an API?
modern web apps are APIs
Dumb client? Move the client to the server
Q&A
Oh hai. We're hiring http://fireteam.net/careers