http and your angry dog
Post on 08-May-2015
7.082 Views
Preview:
DESCRIPTION
TRANSCRIPT
HTTP
ZendCon 2014
& Your Angry Dog
Ross Tuck
Freerange CodemonkeyKnow-It-All
Hot-Air Balloon
Today's topic:
Dogs
HTTP & Dogs
The Agenda
Basics
Client Server
Request
Response
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
2 Parts
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
The body
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>My application</title>
...
Request
The body
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
The body
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
The headers
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
The good stuf
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
GET, POST, PUT, DELETE
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request Relative URLHTTP version
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
Key/Value pairs
POST /gists HTTP/1.1
Authorization: Basic xxxxxxxx
Host: api.github.com
Content-Length: 146
{
"description": "the description for this gist",
"public": false,
"files": {
...
Request
HTTP/1.1 201 Created
Date: Sun, 09 Sep 2012 11:42:41 GMT
Content-Length: 1848
Location: https://api.github.com/gists/a43a0cf58
{
"description": "the description for this gist",
"comments": 0,
"created_at": "2012-09-09T11:42:40Z",
...
ResponseStatus code
• 2xx
• 3xx
• 4xx
• 5xx
OK!
Over there!
Client screwed up!
Server screwed up!
Content Negotiation
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Request
HTTP/1.1 200 OK
Date: Sun, 26 Aug 2012 18:00:43 GMT
{
"cute": true,
"big": false,
"data_dog": true
}
Response
HTTP/1.1 200 OK
Date: Sun, 26 Aug 2012 18:00:43 GMT
<dog breed="corgi">
<cute>true</cute>
<data capacity="on" />
</dog>
Response
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Request
GET /dogs/corgi.json HTTP/1.1
Host: api.example.com
Request
/dogs/corgi.json !== /dogs/corgi.xml
Imagine the URL as your primary key.
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Request
GET /dogs/corgi?_format=json HTTP/1.1
Host: api.example.com
Request
POST /dogs/corgi?_format=json HTTP/1.1
Host: api.example.com
Request
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Request
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Accept: application/json
Request
More POWAH
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Accept: application/json
Request
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Accept: application/json, application/xml
Request
How do I choose?
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Accept: application/json, application/xml
Request
HTTP/1.1 200 OK
Date: Sun, 26 Aug 2012 18:00:43 GMT
Content-Type: application/json
{
"cute": true,
"big": false,
"data_dog": true
}
Response
Nifty.
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Accept: application/json, application/xml
Request
text/html, text/plain
text/html;key=value, text/plain
text/html;key=value;foo=bar, text/plain
text/html, text/plain
text/html, text/plain;q=0.5
Quality(Default 1.0)
text/html, text/plain;q=0.5, text/*;q=0.1
Wildcards
text/html, text/plain;q=0.5, */*;q=0.1
Anything at all
Accept HeadersLittle weird...
But not so scary.
text/html,application/xhtml+xml,
application/xml;q=0.9,*/*;q=0.8
Cool...
What the heck is it good for?
Accept is a “Pattern”
Accept-LanguageAccept-EncodingAccept-CharsetAccept-Ranges
Content-LanguageContent-Encoding(works differently)
Content-Range
Resource vs Representation
/dog/corgi
JSON, Dutch,Gzipped,/dog/corgi
Resource vs Representation
Best way to version your API.
Arguably.Right now.
/v1/dogs/corgi
Accept: application/vnd.dogipedia-v1+json
Accept: application/vnd.dogipedia-v2+json
Vary
Client Server
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Client Server
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Client ServerProxy
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Client ServerProxy
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Accept: application/json, text/plain
User-Species: cat
Same URL.Different output.
WTF should I return?
Client ServerProxy
GET /dogs/corgi HTTP/1.1
Host: api.example.com
Accept: application/json, text/plain
User-Species: cat
Client ServerProxy
Here's how.
Hint:Involves theVary header!
Client ServerProxy
/dogs/corgiAccept: application/json, text/plainUser-Species: cat
HTTP/1.1 200 OK
Date: Sun, 26 Aug 2012 18:00:43 GMT
Content-Type: application/json
Vary: Accept
{“json”: “omgz”}
Response
Client ServerProxy
URL and Accept?Okay, I got this.
Some time later...
Client ServerProxy
/dogs/corgiAccept: application/json, text/plainUser-Species: aardvark
Client ServerProxy
Valid cache.I has it.
Client ServerProxy
ZZ ZZZ
Z
HTTP/1.1 200 OK
Date: Sun, 26 Aug 2012 18:00:43 GMT
Content-Type: application/json
Vary: Accept
{“json”: “omgz”}
Response
HTTP/1.1 200 OK
Date: Sun, 26 Aug 2012 18:00:43 GMT
Content-Type: application/json
Vary: Accept, User-Species
{“json”: “omgz”}
Response
HTTP/1.1 200 OK
Date: Sun, 26 Aug 2012 18:00:43 GMT
Content-Type: application/json
Vary: Accept, User-Species
{“json”: “dogs rule, cats drool”}
Response
Request headers.Not Response!
Bad Reputation?2 Reasons
1. Accept-Encoding -Language
2. Internet Explorer
Caching
ExpiresPragma
Cache-Control
ExpiresPragma
Cache-Control
Expires
Cache-Control
HTTP 1.0
HTTP 1.1
HTTP/1.1 200 OK
Expires: Wed, 29 Oct 2014 22:00:00 GMT
{“herp”: “derp”}
Response
HTTP/1.1 200 OK
Cache-Control: max-age=120
{“herp”: “derp”}
Response
HTTP/1.1 200 OK
Cache-Control: max-age=120
Response
Expires
Cache-Control
HTTP 1.0
HTTP 1.1
HTTP/1.1 200 OK
Cache-Control: max-age=120
{“herp”: “derp”}
Response
HTTP/1.1 200 OK
Cache-Control: max-age=120, s-maxage=120
{“herp”: “derp”}
Response
Dude, Where's my dash?
publicprivate
no-storeno-cache
no-transform
must-revalidateproxy-revalidate
Mark Nottingham's Caching Tutorialhttp://www.mnot.net/cache_docs/
Much better than me.
Conditional Requests
Conditional Requests
Conditional Requests
DELETE /ross/reputation HTTP/1.1
Host: api.joind.in
If-Talk-Quality: Crap
Request
if ($talkQuality === 'Crap') {
delete($rossReputation);
}
Not real code
Server
What kind of conditions?
If-MatchIf-None-Match
If-Modified-SinceIf-Unmodified-Since
If-Range
ETags
Datetimes
Either
Wait a second, Ross.
Audience
What the heck is an ETag?
A string.Any string.
One rule:
Represent the current state.
“14”“a381bedb5d4478053eb04be35f8798dd”
“winnie-the-pooh”
...for the current representation.
etag(“v14-json-en”) !== etag(“v14-xml-en”)
Don't cross the streams
Server
Last Modified Date sounds easier...
Audience
Wed, 15 Nov 1995 04:58:08 GMT
One second of precision
Caching With Conditionals
Use Case
GET /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
Request
HTTP/1.1 200 OK
Server: nginx/1.0.13
Date: Sun, 26 Aug 2012 18:00:43 GMT
Vary: Accept
ETag: "f4e15911542b92b44bb38186e71cc8f5"
"history": [
{
"version": "529f6311d5518977534b6e1fd313...",
...
Response
...
"user": {
"gravatar_id": "c26bfcbd5f786591e036fa0",
"avatar_url": "https://secure.gravatar...",
"login": "rosstuck",
"url": "https://api.github.com/users/rosstuck",
"id": 146766
},
"change_status": {
"additions": 1,
"deletions": 0,
"total": 1
},
Response
"url": "https://api.github.com/gists/348...",
"committed_at": "2012-08-26T17:40:03Z"
}
],
"git_pull_url": "git://gist.github.com/34819...",
"forks": [
],
"html_url": "https://gist.github.com/3481910",
"git_push_url": "git@gist.github.com:3481910.git",
"comments": 0,
"user": {
Response
HTTP/1.1 200 OK
Server: nginx/1.0.13
Date: Sun, 26 Aug 2012 18:00:43 GMT
Vary: Accept
ETag: "f4e15911542b92b44bb38186e71cc8f5"
{
"history": [
{
"version": "529f6311d5518970903cb5427534b6e1fd313aca",
"user": {
"gravatar_id": "c26bfcbd5f786591e036fa0958a11e8b",
"avatar_url": "https://secure.gravatar.com/avatar/c26bfcbd5f786591e036fa0958a11e8b?d=https://a2...
"login": "rosstuck",
"url": "https://api.github.com/users/rosstuck",
"id": 146766
},
"change_status": {
"additions": 1,
"deletions": 0,
"total": 1
},
"url": "https://api.github.com/gists/3481910/529f6311d5518970903cb5427534b6e1fd313aca",
"committed_at": "2012-08-26T17:40:03Z"
}
],
"git_pull_url": "git://gist.github.com/3481910.git",
Response
"forks": [
],
"html_url": "https://gist.github.com/3481910",
"git_push_url": "git@gist.github.com:3481910.git",
"comments": 0,
"user": {
"gravatar_id": "c26bfcbd5f786591e036fa0958a11e8b",
"avatar_url": "https://secure.gravatar.com/avatar/c26bfcbd5f78659....",}
"login": "rosstuck",
"url": "https://api.github.com/users/rosstuck",
"id": 146766
},
"public": true,
"created_at": "2012-08-26T17:40:03Z",
"files": {
"gistfile1.txt": {
"type": "text/plain",
"filename": "gistfile1.txt",
"raw_url": "https://gist.github.com/raw/3481910/8b6946739e8098408ee3af96...
"content": "Hello PFC!",
"language": null,
"size": 10
}
},
"description": "",
"url": "https://api.github.com/gists/3481910",
"updated_at": "2012-08-26T17:40:03Z",
"id": "3481910"
}
GET /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
If-None-Match: "f4e15911542b92b44bb38186e71cc8f5"
Request
HTTP/1.1 304 Not Modified
Server: nginx/1.0.13
Date: Sun, 26 Aug 2012 18:00:43 GMT
Vary: Accept
ETag: "f4e15911542b92b44bb38186e71cc8f5"
Response
HTTP/1.1 304 Not Modified
Server: nginx/1.0.13
Date: Sun, 26 Aug 2012 18:00:43 GMT
Vary: Accept
ETag: "f4e15911542b92b44bb38186e71cc8f5"
Response
HTTP/1.1 304 Not Modified
Server: nginx/1.0.13
Date: Sun, 26 Aug 2012 18:00:43 GMT
Vary: Accept
ETag: "f4e15911542b92b44bb38186e71cc8f5"
Response
No giant body!
Caching.You has it.
GET /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
If-None-Match: "a381bedb5d4478053eb04be35f8798dd"
Request
GET /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
If-None-Match: "ross-is-a-poo-poo-head"
Request
HTTP/1.1 200 OK
Server: nginx/1.0.13
Date: Sun, 26 Aug 2012 18:00:43 GMT
Vary: Accept
ETag: "f4e15911542b92b44bb38186e71cc8f5"
"history": [
{
"version": "529f6311d5518977534b6e1fd313...",
Response
Recap
No ETag Old ETag Matching ETag
Full BodyFull BodyNo Body
→
→
→
...on supported servers.
Why?
ParsingBandwidth
Response time
Probably
.Maybe
However...
“The fastest request is one you don't make.”
- Jesus
More Fun With ETags
Optimistic Concurrency Control
“Record Versioning”
Request
GET /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
If-None-Match: "f4e15911542b92b44bb38186e71cc8f5"
Request
PATCH /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
If-None-Match: "f4e15911542b92b44bb38186e71cc8f5"
Request
PATCH /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
If-Match: "f4e15911542b92b44bb38186e71cc8f5"
Request
PATCH /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
If-Match: "f4e15911542b92b44bb38186e71cc8f5"
{ "description": "cheese om nom nom" }
Request
Response
Response
HTTP/1.1 200 OK
Server: nginx/1.0.13
Date: Sat, 01 Sep 2012 14:01:38 GMT
ETag: "899b76047a5e68445668374c2e0faa32"
{
"description": "cheese om nom nom",
"user": {
"login": "rosstuck",
...
It works.
So what?
What if I send something...
PATCH /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
If-Match: "899b76047a5e68445668374c2e0faa32"
{ "description": "cheese om nom nom" }
Request
PATCH /gists/3481910 HTTP/1.1
Host: api.github.com
Accept: */*
If-Match: "stay-puft-marshmellow-dog!"
{ "description": "cheese om nom nom" }
Request
HTTP/1.1 412 Precondition Failed
Server: nginx/1.0.13
Date: Sun, 26 Aug 2012 18:00:43 GMT
Response
Response
if (“stay-puft-marshmellow-dog” == “f4e1591..”) {
patchTheRecord();
}
Server
if (“stay-puft-marshmellow-dog” == “f4e1591..”) {
patchTheRecord();
} else {
sendScary412Message();
}
Server
Your ETag is out of date.
“Two guys on the same record” problem
Scary Precondition Error pt. 2
DisclaimerNew Stuff Ahead
DELETE /gists/3481910 HTTP/1.1
Host: api.github.com
Request
HTTP/1.1 428 Precondition Required
Server: nginx/1.0.13
Date: Sun, 26 Aug 2012 18:00:43 GMT
Response
Am I operating on the latest version?
DELETE /gists/3481910 HTTP/1.1
Host: api.github.com
Request
DELETE /gists/3481910 HTTP/1.1
Host: api.github.com
If-Match: "f4e15911542b92b44bb38186e71cc8f5"
Request
HTTP/1.1 204 No Content
Server: nginx/1.0.13
Date: Sun, 26 Aug 2012 18:00:43 GMT
Response
Look before you leap.
Tooling
Epilogue: HTTP & Dogs
Content NegotiationVary
CachingPreconditions
Treat it like your framework.
Questions?
Resources
• RFC 7231–Sections 4 - 7
• http://redbot.org/
• http://mnot.net/cache_docs/
• http://charlesproxy.com/
• http://guzzlephp.org/
Image Credits• http://www.sxc.hu/photo/555539• http://www.flickr.com/photos/thedalogs/2953136078/• http://www.sxc.hu/photo/678952• http://www.flickr.com/photos/designgate/8317884432/• http://www.flickr.com/photos/barretthall/3289317664/• http://www.flickr.com/photos/binaryape/3702275400/lightbox/• http://www.flickr.com/photos/istolethetv/2956799679/in/photostream/• http://theflashbackspodcast.blogspot.nl/2012/01/30-day-film-challenge-day-8.html• http://www.flickr.com/photos/jonomueller/6906420190/• http://www.flickr.com/photos/cookbookman/6175752147• http://www.flickr.com/photos/creativedc/2913123330/• http://www.flickr.com/photos/epc/44053757/• http://www.flickr.com/photos/soggydan/4698849104/• http://www.flickr.com/photos/owldreams/4430175427/• http://www.flickr.com/photos/piddleville/2499539542/• http://www.flickr.com/photos/danja/5665671907/• http://www.flickr.com/photos/cradlehall/3574160744/• http://www.flickr.com/photos/ironypoisoning/7114801437/• http://www.flickr.com/photos/piddleville/2499539542/lightbox/• http://everydayidrawadog.blogspot.nl/2009_02_01_archive.html
joind.in/12071
@rosstuck
Ross Tuckrosstuck.com
top related