To be able to edit code and run cells, you need to run the notebook yourself. Where would you like to run the notebook?

This notebook takes about 1 minute to run.

In the cloud (experimental)

Binder is a free, open source service that runs scientific notebooks in the cloud! It will take a while, usually 2-7 minutes to get a session.

On your computer

(Recommended if you want to store your changes.)

  1. Copy the notebook URL:
  2. Run Pluto

    (Also see: How to install Julia and Pluto)

  3. Paste URL in the Open box

Frontmatter

If you are publishing this notebook on the web, you can set the parameters below to provide HTML metadata. This is useful for search engines and social media.

Author 1

Fetch.jl: a simple HTTP request function

inspired by fetch from JS

👀 Reading hidden code
253 μs

You can use Fetch.jl to request something from the web:

👀 Reading hidden code
177 μs
👀 Reading hidden code
fetch("https://api.github.com/users/JuliaLang").json()
364 ms
👀 Reading hidden code
66.2 μs

Response

The fetch function returns a special object, a FetchResponse, containing the response status and headers.

👀 Reading hidden code
230 μs
👀 Reading hidden code
response = fetch("https://api.github.com/users/JuliaLang")
7.1 ms
200
👀 Reading hidden code
response.status
14.4 μs
👀 Reading hidden code
response.headers
14.2 μs
👀 Reading hidden code
65.8 μs

Reading data

You can use .json(), .text() or .arrayBuffer() to read the response data. Let's read the response as JSON:

👀 Reading hidden code
253 μs
response.json()
👀 Reading hidden code
12.4 ms

Each response can only be read once. Reading it a second time will raise an error:

👀 Reading hidden code
175 μs
Error message

AssertionError: Body stream has already been read.

Stack trace

Here is what happened, the most recent locations are first:

  1. (::Main.workspace#5.var"#4#10"{Main.workspace#5.var"#5#11"{Main.workspace#5.FetchResponse}, Main.workspace#5.FetchResponse})()
    		function()			ref = getfield(r, :bodyUsed_ref)			@assert !ref[] "Body stream has already been read."			ref[] = true			f()
  2. Show more...
response.json()
👀 Reading hidden code
---
👀 Reading hidden code
65.8 μs

There are three ways to read a response:

👀 Reading hidden code
170 μs
fetch("https://api.github.com/users/JuliaLang").json()
👀 Reading hidden code
6.8 ms
"{\n  \"login\": \"JuliaLang\",\n  \"id\": 743164,\n  \"node_id\": \"MDEyOk9yZ2FuaXphdGlvbjc0MzE2NA==\",\n  \"avatar_url\": \"https://avatars.githubusercontent.com/u/743164?v=4\",\n  \"gravatar_id\": \"\",\n  \"url\": \"https://api.github.com/users/JuliaLang\",\n  \"html_url\": \"https://github.com/JuliaLang\",\n  \"followers_url\": \"" ⋯ 820 bytes ⋯ "ocation\": null,\n  \"email\": null,\n  \"hireable\": null,\n  \"bio\": \"The Julia Programming Language\",\n  \"twitter_username\": \"JuliaLanguage\",\n  \"public_repos\": 59,\n  \"public_gists\": 0,\n  \"followers\": 1668,\n  \"following\": 0,\n  \"created_at\": \"2011-04-21T06:33:51Z\",\n  \"updated_at\": \"2025-06-09T21:01:31Z\"\n}\n"
fetch("https://api.github.com/users/JuliaLang").text()
👀 Reading hidden code
6.8 ms
fetch("https://api.github.com/users/JuliaLang").arrayBuffer()
👀 Reading hidden code
6.9 ms
👀 Reading hidden code
65.2 μs

Request

Besides getting data from a URL, you can also make requests with a body. Here is an example of a POST request:

👀 Reading hidden code
268 μs
fetch(
"https://httpbin.org/post";
method="POST",
body="hello!",
).json()
👀 Reading hidden code
766 ms
👀 Reading hidden code
84.7 μs

Implementation

👀 Reading hidden code
165 μs
using Downloads
👀 Reading hidden code
210 μs
fetch (generic function with 1 method)
function fetch(url;
method=nothing,
headers=Pair{String,String}[],
body::Union{Nothing,AbstractVector{UInt8},AbstractString}=nothing,
# credentials=nothing,
# cache=nothing,
# redirect=nothing,
# referrer,
# referrerPolicy,
# integrity,
# keepalive
# signal
)
has_input = body !== nothing
input = has_input ? IOBuffer(body_data(body); write=false, append=false, read=true) : nothing
output = IOBuffer()
result = request(url;
method,
headers,
input,
output,
)

FetchResponse(;
url,
body=take!(output),
status=result.status,
statusText=result.message,
headers=result.headers,
)
end
👀 Reading hidden code
2.6 ms
begin
Base.@kwdef struct FetchResponse
url::String
body::Vector{UInt8}
bodyUsed_ref::Ref{Bool}=Ref(false)
status::Int
statusText::String=""
headers :: Vector{Pair{String,String}}
ok::Bool=true
redirected::Bool=false
end
function Base.getproperty(r::FetchResponse, name::Symbol)
handle_getproperty(r, name)
end
end
👀 Reading hidden code
37.1 ms

Reading response data

👀 Reading hidden code
183 μs
using JSON
👀 Reading hidden code
97.2 ms
handle_getproperty (generic function with 1 method)
function handle_getproperty(r, name)
function with_use_body(f)
function()
ref = getfield(r, :bodyUsed_ref)
@assert !ref[] "Body stream has already been read."
ref[] = true
f()
end
end
if name === :json
with_use_body() do
JSON.parse(String(r.body))
end
elseif name === :text
with_use_body() do
String(r.body)
end
elseif name === :arrayBuffer
with_use_body() do
r.body
end
elseif name === :formData || name === :formdata
with_use_body() do
read_form_data(r)
end
elseif name === :bodyUsed
getfield(r, :bodyUsed_ref)[]
else
getfield(r, name)
end
end
👀 Reading hidden code
16.8 ms
body_data (generic function with 1 method)
body_data(x::AbstractVector{UInt8}) = x
👀 Reading hidden code
380 μs
body_data (generic function with 2 methods)
body_data(x::AbstractString) = codeunits(x)
👀 Reading hidden code
385 μs

Examples

👀 Reading hidden code
163 μs
"https://httpbin.org/uuid"
const url_uuid = "https://httpbin.org/uuid"
👀 Reading hidden code
109 μs
"https://registry.npmjs.org/react"
const url_npm_react = "https://registry.npmjs.org/react"
👀 Reading hidden code
110 μs
r = request(url_uuid)
👀 Reading hidden code
705 ms

👀 Reading hidden code
65.5 μs

Multipart form data

👀 Reading hidden code
169 μs
import HTTP
👀 Reading hidden code
118 ms
FormFile
Base.@kwdef struct FormFile
data
name
contenttype
filename
end
👀 Reading hidden code
3.0 ms
read_form_data (generic function with 1 method)
function read_form_data(r)
fs = HTTP.parse_multipart_form(
HTTP.Request(
"GET",
"http://asfd.com",
r.headers,
r.body
)
)

map(fs) do f
FormFile(;
data = take!(f.data),
name = f.name,
filename = f.filename,
contenttype = f.contenttype,
)
end
end
👀 Reading hidden code
1.7 ms
rm = HTTP.parse_multipart_form(HTTP.Request("GET", "http://asfd.com", ["Content-Type" => "multipart/form-data; boundary=WebKitFormBoundary"], mpt))
👀 Reading hidden code
353 ms
HTTP.Multipart(filename="invoice.pdf", data=::Base.GenericIOBuffer{SubArray{UInt8, 1, Vector{UInt8}, Tuple{UnitRange{Int64}}, true}}, contenttype="text/plain", contenttransferencoding=""))
rm1 = rm[3]
👀 Reading hidden code
18.2 μs
Error message

syntax: incomplete: premature end of input

computer bad, you GREAT!
rm1.
👀 Reading hidden code
---
take!(rm1.data)
👀 Reading hidden code
19.9 μs
"\r\n--WebKitFormBoundary\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\ninvoice_text\r\n--WebKitFormBoundary\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\ninvoice_title\r\n--WebKitFormBoundary\r\nContent-Disposition: form-data; name=\"invoice\"; filename=\"invoice.pdf\"\r\nContent-Type: application/pdf\r\n\r\n\x90\xd7?\x1e\x13\xb9&\x1c\xef\xd3\xefb\xb2>\r\xa8\xb5\x90\x92Hc\xa4{\xcazA\u05cee\xa2\x87\x94SH\xaa8\x90\xb6\x19\xc4\xc18\xc0\xab0)|\xab\xed\xf5\r\n--WebKitFormBoundary--\r\n"
const mpt = replace("""

--WebKitFormBoundary
Content-Disposition: form-data; name="text"

invoice_text
--WebKitFormBoundary
Content-Disposition: form-data; name="title"

invoice_title
--WebKitFormBoundary
Content-Disposition: form-data; name="invoice"; filename="invoice.pdf"
Content-Type: application/pdf

$(String(rand(UInt8, 50)))
--WebKitFormBoundary--
""", "\n" => "\r\n")
👀 Reading hidden code
108 ms
# 1 method for generic function parse_multipart_form:
methods(HTTP.parse_multipart_form)
👀 Reading hidden code
31.8 μs

Experiments

👀 Reading hidden code
187 μs
let
io = IOBuffer(; maxsize=100)

xs = zeros(Int, 10000)

r = request(url_npm_react;
output=io
)
for i in eachindex(xs)
xs[i] = io.ptr
end
xs
end
👀 Reading hidden code
469 ms
f1 (generic function with 1 method)
function f1(url)
pipe = Pipe()
Base.link_pipe!(pipe; reader_supports_async = true, writer_supports_async = true)
json_task = @async JSON.parse(read(pipe, String))
request(url;
output=pipe.in,
)
fetch(json_task)
end
👀 Reading hidden code
1.8 ms
f2 (generic function with 1 method)
function f2(url)
s = sprint() do io
request(url;
output=io,
)
end

JSON.parse(s)
end
👀 Reading hidden code
1.0 ms
@time f2(url_uuid)
👀 Reading hidden code
❔
  0.291953 seconds (346 allocations: 19.594 KiB, 1.33% compilation time)
335 ms
Error message

MethodError: no method matching request(::Task; method=nothing, headers=Pair{String, String}[], input=nothing, output=IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1))

Closest candidates are:

request(::AbstractString; input, output, method, headers, timeout, progress, verbose, debug, throw, downloader) at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Downloads/src/Downloads.jl:293

Stack trace

Here is what happened, the most recent locations are first:

  1. fetch(url::Task; method::Nothing, headers::Vector{Pair{String, String}}, body::Nothing)
    	output = IOBuffer()		result = request(url;		method,		headers,
  2. Show more...
computer bad, you GREAT!
@time f1(url_uuid)
👀 Reading hidden code
---
@time f2(url_npm_react)
👀 Reading hidden code
❔
  0.323751 seconds (816.37 k allocations: 63.980 MiB, 9.85% gc time, 17.04% compilation time)
330 ms
Error message

MethodError: no method matching request(::Task; method=nothing, headers=Pair{String, String}[], input=nothing, output=IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1))

Closest candidates are:

request(::AbstractString; input, output, method, headers, timeout, progress, verbose, debug, throw, downloader) at /opt/hostedtoolcache/julia/1.7.3/x64/share/julia/stdlib/v1.7/Downloads/src/Downloads.jl:293

Stack trace

Here is what happened, the most recent locations are first:

  1. fetch(url::Task; method::Nothing, headers::Vector{Pair{String, String}}, body::Nothing)
    	output = IOBuffer()		result = request(url;		method,		headers,
  2. Show more...
@time f1(url_npm_react)
👀 Reading hidden code
---
"<html>\r\n<head><title>502 Bad Gateway</title></head>\r\n<body>\r\n<center><h1>502 Bad Gateway</h1></center>\r\n</body>\r\n</html>\r\n"
sprint() do io
request(url_uuid;
output=io
)
end
👀 Reading hidden code
2.1 s

JSON.parse(io::IO) benchmark

Some benchmarks with https://github.com/JuliaIO/JSON.jl/issues/339 as conclusion.

👀 Reading hidden code
1.8 ms
j1 (generic function with 1 method)
j1(io) = JSON.parse(read(io, String))
👀 Reading hidden code
430 μs
j2 (generic function with 1 method)
j2(io) = JSON.parse(io)
👀 Reading hidden code
391 μs
"{\"_id\":\"react\",\"_rev\":\"4556-38606e4086d9df83b101248af9d3d7b0\",\"name\":\"react\",\"dist-tags\":{\"beta\":\"19.0.0-beta-26f2496093-20240514\",\"rc\":\"19.0.0-rc.1\",\"latest\":\"19.1.0\",\"experimental\":\"0.0.0-experimental-12bc60f5-20250613\",\"next\":\"19.2.0-canary-12bc60f5-20250613\",\"canary\":\"19.2.0-canary-12bc60f5-2" ⋯ 5779370 bytes ⋯ "ue,\"javascriptismagic\":true,\"sebastian-schmidt\":true,\"arulsakthiprakasam\":true,\"ashtonsoftwarelabs\":true,\"avanthikameenakshi\":true,\"emesfun_blueraster\":true,\"vision_tecnologica\":true,\"azulejosmetrosubway\":true,\"robertocarvalho7458\":true,\"dylanthomasfernandez\":true,\"nguyenvanhoang26041994\":true}}"
nrs = sprint() do io
request(url_npm_react;
output=io,
)
end
👀 Reading hidden code
211 ms
using BenchmarkTools
👀 Reading hidden code
768 ms
BenchmarkTools.Trial: 87 samples with 1 evaluation.
 Range (min … max):  41.291 ms … 106.524 ms  ┊ GC (min … max):  0.00% … 48.97%
 Time  (median):     53.670 ms               ┊ GC (median):    20.11%
 Time  (mean ± σ):   57.682 ms ±  13.545 ms  ┊ GC (mean ± σ):  19.33% ± 14.43%

   █   ▂  █     ▄▆▂       ▂                                     
  ██▆▁▆██▄██▄▁▁▄███▄▄▁▁▄▄▆█▆▄▆▄▁▁▁▁█▄▆▄▁▄▁▁▁▁▆▄█▁▄▁▄▄▄▁▄▄▁▁▁▁▄ ▁
  41.3 ms         Histogram: frequency by time         88.7 ms <

 Memory estimate: 57.59 MiB, allocs estimate: 762476.
@benchmark let
io = IOBuffer()
write(io, $nrs)
seekstart(io)
r = j1(io)
close(io)
r
end
👀 Reading hidden code
12.4 s
BenchmarkTools.Trial: 46 samples with 1 evaluation.
 Range (min … max):   82.137 ms … 249.694 ms  ┊ GC (min … max):  0.00% … 66.38%
 Time  (median):     102.759 ms               ┊ GC (median):    17.69%
 Time  (mean ± σ):   111.442 ms ±  27.698 ms  ┊ GC (mean ± σ):  23.09% ± 13.85%

  ▂   ▂  ▄        █                                              
  █▁▃▇█▃▁█▁▁▁▁▅▁▃▁█▆▁▁▃▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃ ▁
  82.1 ms          Histogram: frequency by time          250 ms <

 Memory estimate: 103.57 MiB, allocs estimate: 1282427.
@benchmark let
io = IOBuffer()
write(io, $nrs)
seekstart(io)
r = j2(io)
close(io)
r
end
👀 Reading hidden code
12.5 s
parse(io::IO; dicttype, inttype, allownan, null) in JSON.Parser at /home/runner/.julia/packages/JSON/NeJ9k/src/Parser.jl:474
@which JSON.parse(stdin)
👀 Reading hidden code
105 ms
parse(str::AbstractString; dicttype, inttype, allownan, null) in JSON.Parser at /home/runner/.julia/packages/JSON/NeJ9k/src/Parser.jl:443
@which JSON.parse("asdf")
👀 Reading hidden code
83.7 μs