Advertisement
Advertisement

More Related Content

Advertisement
Advertisement

HTTP and Your Angry Dog

  1. HTTP & Your Angry Dog ZendCon 2014 Ross Tuck
  2. Freerange Codemonkey Know-It-All Hot-Air Balloon
  3. @rosstuck rosstuck.com
  4. Today's topic:
  5. Dogs
  6. HTTP & Dogs
  7. The Agenda
  8. Basics
  9. Request Response Client Server
  10. 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": { ...
  11. 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": { ... 2 Parts
  12. 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": { ... The body
  13. Request 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> ... The body
  14. 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": { ... The body
  15. 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": { ...
  16. 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": { ... The headers
  17. 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": { ... The good stuf
  18. 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": { ...
  19. 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": { ... GET, POST, PUT, DELETE
  20. Request Relative URL POST /gists HTTP/1.1 Authorization: Basic xxxxxxxx Host: api.github.com Content-Length: 146 { "description": "the description for this gist", "public": false, "files": { ... HTTP version
  21. 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": { ... Key/Value pairs
  22. 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": { ...
  23. Response 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", ... Status code
  24. • 2xx • 3xx • 4xx • 5xx OK! Over there! Client screwed up! Server screwed up!
  25. Content Negotiation
  26. Request GET /dogs/corgi HTTP/1.1 Host: api.example.com
  27. Response HTTP/1.1 200 OK Date: Sun, 26 Aug 2012 18:00:43 GMT { "cute": true, "big": false, "data_dog": true }
  28. 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>
  29. Request GET /dogs/corgi HTTP/1.1 Host: api.example.com
  30. Request GET /dogs/corgi.json HTTP/1.1 Host: api.example.com
  31. /dogs/corgi.json !== /dogs/corgi.xml
  32. Imagine the URL as your primary key.
  33. Request GET /dogs/corgi HTTP/1.1 Host: api.example.com
  34. Request GET /dogs/corgi?_format=json HTTP/1.1 Host: api.example.com
  35. Request POST /dogs/corgi?_format=json HTTP/1.1 Host: api.example.com
  36. Request GET /dogs/corgi HTTP/1.1 Host: api.example.com
  37. Request GET /dogs/corgi HTTP/1.1 Host: api.example.com Accept: application/json
  38. More POWAH
  39. Request GET /dogs/corgi HTTP/1.1 Host: api.example.com Accept: application/json
  40. Request GET /dogs/corgi HTTP/1.1 Host: api.example.com Accept: application/json, application/xml How do I choose?
  41. Request GET /dogs/corgi HTTP/1.1 Host: api.example.com Accept: application/json, application/xml
  42. Response 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 }
  43. Nifty.
  44. Request GET /dogs/corgi HTTP/1.1 Host: api.example.com Accept: application/json, application/xml
  45. text/html, text/plain
  46. text/html;key=value, text/plain
  47. text/html;key=value;foo=bar, text/plain
  48. text/html, text/plain
  49. Quality (Default 1.0) text/html, text/plain;q=0.5
  50. text/html, text/plain;q=0.5, text/*;q=0.1 Wildcards
  51. Anything at all text/html, text/plain;q=0.5, */*;q=0.1
  52. Accept Headers Little weird...
  53. But not so scary.
  54. text/html,application/xhtml+xml, application/xml;q=0.9,*/*;q=0.8
  55. Cool...
  56. What the heck is it good for?
  57. Accept is a “Pattern”
  58. Accept-Language Accept-Encoding Accept-Charset Accept-Ranges
  59. Content-Language Content-Encoding (works differently) Content-Range
  60. /dog/corgi Resource vs Representation JSON, Dutch, Gzipped, /dog/corgi
  61. Resource vs Representation
  62. Best way to version your API. Arguably. Right now.
  63. /v1/dogs/corgi
  64. Accept: application/vnd.dogipedia-v1+json
  65. Accept: application/vnd.dogipedia-v2+json
  66. Vary
  67. GET /dogs/corgi HTTP/1.1 Host: api.example.com Client Server
  68. GET /dogs/corgi HTTP/1.1 Host: api.example.com Client Server
  69. GET /dogs/corgi HTTP/1.1 Host: api.example.com Client Proxy Server
  70. GET /dogs/corgi HTTP/1.1 Host: api.example.com Accept: application/json, text/plain User-Species: cat Client Proxy Server
  71. Same URL. Different output. WTF should I return?
  72. GET /dogs/corgi HTTP/1.1 Host: api.example.com Accept: application/json, text/plain User-Species: cat Client Proxy Server
  73. Client Proxy Server
  74. Here's how. Hint: Involves the Vary header!
  75. /dogs/corgi Accept: application/json, text/plain User-Species: cat Client Proxy Server
  76. Response HTTP/1.1 200 OK Date: Sun, 26 Aug 2012 18:00:43 GMT Content-Type: application/json Vary: Accept {“json”: “omgz”}
  77. URL and Accept? Okay, I got this. Client Proxy Server
  78. Some time later...
  79. /dogs/corgi Accept: application/json, text/plain User-Species: aardvark Client Proxy Server
  80. Valid cache. I has it. Client Proxy Server
  81. ZZZ Z Z Z Client Proxy Server
  82. Response HTTP/1.1 200 OK Date: Sun, 26 Aug 2012 18:00:43 GMT Content-Type: application/json Vary: Accept {“json”: “omgz”}
  83. 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”}
  84. 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”}
  85. Request headers. Not Response!
  86. Bad Reputation? 2 Reasons
  87. 1. Accept-Encoding -Language
  88. 2. Internet Explorer
  89. Caching
  90. Expires Pragma Cache-Control
  91. Expires Pragma Cache-Control
  92. Expires Cache-Control HTTP 1.0 HTTP 1.1
  93. Response HTTP/1.1 200 OK Expires: Wed, 29 Oct 2014 22:00:00 GMT {“herp”: “derp”}
  94. Response HTTP/1.1 200 OK Cache-Control: max-age=120 {“herp”: “derp”}
  95. Response HTTP/1.1 200 OK Cache-Control: max-age=120
  96. Expires Cache-Control HTTP 1.0 HTTP 1.1
  97. Response HTTP/1.1 200 OK Cache-Control: max-age=120 {“herp”: “derp”}
  98. Response HTTP/1.1 200 OK Cache-Control: max-age=120, s-maxage=120 {“herp”: “derp”} Dude, Where's my dash?
  99. public private no-store no-cache no-transform must-revalidate proxy-revalidate
  100. Much better than me. Mark Nottingham's Caching Tutorial http://www.mnot.net/cache_docs/
  101. Conditional Requests
  102. Conditional Requests
  103. The Part About ETags
  104. Conditional Requests
  105. Request DELETE /ross/reputation HTTP/1.1 Host: api.joind.in If-Talk-Quality: Crap
  106. if ($talkQuality === 'Crap') { real code delete($rossReputation); Not } Server
  107. What kind of conditions?
  108. If-Match If-None-Match If-Modified-Since If-Unmodified-Since If-Range ETags Datetimes Either
  109. Wait a second, Ross. Audience
  110. What the heck is an ETag?
  111. A string. Any string.
  112. One rule:
  113. Represent the current state.
  114. “14” “a381bedb5d4478053eb04be35f8798dd” “winnie-the-pooh”
  115. ...for the current representation.
  116. etag(“v14-json-en”) !== etag(“v14-xml-en”) Don't cross the streams Server
  117. Last Modified Date sounds easier... Audience
  118. One second of precision Wed, 15 Nov 1995 04:58:08 GMT
  119. Caching With Conditionals
  120. Use Case
  121. Request GET /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */*
  122. 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": "529f6311d5518977534b6e1fd313...", ...
  123. 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 },
  124. 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": {
  125. 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", "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" }
  126. Request GET /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */* If-None-Match: "f4e15911542b92b44bb38186e71cc8f5"
  127. 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"
  128. 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"
  129. 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" No giant body!
  130. Caching. You has it.
  131. Request GET /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */* If-None-Match: "a381bedb5d4478053eb04be35f8798dd"
  132. Request GET /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */* If-None-Match: "ross-is-a-poo-poo-head"
  133. 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": "529f6311d5518977534b6e1fd313...",
  134. Recap
  135. No ETag Old ETag Matching ETag Full Body Full Body No Body → → →
  136. ...on supported servers.
  137. Why?
  138. Parsing Bandwidth Response time Probably .Maybe
  139. However...
  140. “The fastest request is one you don't make.” - Jesus
  141. More Fun With ETags
  142. Optimistic Concurrency Control “Record Versioning”
  143. Request
  144. Request GET /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */* If-None-Match: "f4e15911542b92b44bb38186e71cc8f5"
  145. Request PATCH /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */* If-None-Match: "f4e15911542b92b44bb38186e71cc8f5"
  146. Request PATCH /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */* If-Match: "f4e15911542b92b44bb38186e71cc8f5"
  147. Request PATCH /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */* If-Match: "f4e15911542b92b44bb38186e71cc8f5" { "description": "cheese om nom nom" }
  148. Response
  149. 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", ...
  150. It works.
  151. So what?
  152. What if I send something...
  153. Request PATCH /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */* If-Match: "899b76047a5e68445668374c2e0faa32" { "description": "cheese om nom nom" }
  154. Request PATCH /gists/3481910 HTTP/1.1 Host: api.github.com Accept: */* If-Match: "stay-puft-marshmellow-dog!" { "description": "cheese om nom nom" }
  155. Response HTTP/1.1 412 Precondition Failed Server: nginx/1.0.13 Date: Sun, 26 Aug 2012 18:00:43 GMT
  156. Response
  157. Server if (“stay-puft-marshmellow-dog” == “f4e1591..”) { patchTheRecord(); }
  158. Server if (“stay-puft-marshmellow-dog” == “f4e1591..”) { patchTheRecord(); } else { sendScary412Message(); }
  159. Your ETag is out of date.
  160. “Two guys on the same record” problem
  161. Scary Precondition Error pt. 2
  162. Disclaimer New Stuff Ahead
  163. Request DELETE /gists/3481910 HTTP/1.1 Host: api.github.com
  164. Response HTTP/1.1 428 Precondition Required Server: nginx/1.0.13 Date: Sun, 26 Aug 2012 18:00:43 GMT
  165. Am I operating on the latest version?
  166. Request DELETE /gists/3481910 HTTP/1.1 Host: api.github.com
  167. Request DELETE /gists/3481910 HTTP/1.1 Host: api.github.com If-Match: "f4e15911542b92b44bb38186e71cc8f5"
  168. Response HTTP/1.1 204 No Content Server: nginx/1.0.13 Date: Sun, 26 Aug 2012 18:00:43 GMT
  169. Look before you leap.
  170. Tooling
  171. Epilogue: HTTP & Dogs
  172. Content Negotiation Vary Caching Preconditions
  173. Treat it like your framework.
  174. Questions?
  175. Resources • RFC 7231 –Sections 4 - 7 • http://redbot.org/ • http://mnot.net/cache_docs/ • http://charlesproxy.com/ • http://guzzlephp.org/
  176. 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
  177. joind.in/12071 Ross Tuck rosstuck.com @rosstuck
Advertisement