Bagaimana cara mengukur waktu permintaan dan respons sekaligus menggunakan cURL?


659

Saya memiliki layanan web yang menerima data dalam format JSON, memproses data, dan kemudian mengembalikan hasilnya ke pemohon.

Saya ingin mengukur permintaan, respons, dan total waktu penggunaan cURL.

Contoh permintaan saya terlihat seperti:

curl -X POST -d @file server:port

dan saya saat ini mengukur ini menggunakan timeperintah di Linux:

time curl -X POST -d @file server:port

Perintah waktu hanya mengukur waktu total , - yang bukan apa yang saya cari.

Apakah ada cara untuk mengukur waktu permintaan dan tanggapan menggunakan cURL?

Jawaban:


1677

Dari posting blog yang brilian ini ... https://blog.josephscott.org/2011/10/14/timing-details-with-curl/

cURL mendukung output yang diformat untuk perincian permintaan (lihat manual CURL untuk detail , di bawah -w, –write-out <format>). Untuk tujuan kami, kami akan fokus hanya pada detail waktu yang disediakan. Waktu di bawah ini dalam detik .

  1. Buat file baru, curl-format.txt, dan rekatkan di:

        time_namelookup:  %{time_namelookup}s\n
           time_connect:  %{time_connect}s\n
        time_appconnect:  %{time_appconnect}s\n
       time_pretransfer:  %{time_pretransfer}s\n
          time_redirect:  %{time_redirect}s\n
     time_starttransfer:  %{time_starttransfer}s\n
                        ----------\n
             time_total:  %{time_total}s\n
    
  2. Buat sebuah permintaan:

    curl -w "@curl-format.txt" -o /dev/null -s "http://wordpress.com/"
    

    Atau di Windows, itu ...

    curl -w "@curl-format.txt" -o NUL -s "http://wordpress.com/"
    


Apa yang dilakukan:

-w "@curl-format.txt"memberitahu cURL untuk menggunakan file format kami untuk
-o /dev/nullmengarahkan ulang output permintaan ke / dev / null
-s memberitahu cURL untuk tidak menunjukkan meteran kemajuan
"http://wordpress.com/"adalah URL yang kami minta. Gunakan tanda kutip terutama jika URL Anda memiliki "&" parameter string kueri


Dan inilah yang Anda dapatkan:

   time_namelookup:  0.001s
      time_connect:  0.037s
   time_appconnect:  0.000s
  time_pretransfer:  0.037s
     time_redirect:  0.000s
time_starttransfer:  0.092s
                   ----------
        time_total:  0.164s


Buat pintasan Linux / Mac (alias)

alias curltime="curl -w \"@$HOME/.curl-format.txt\" -o NUL -s "

Maka Anda cukup menelepon ...

curltime wordpress.org

Terima kasih kepada komentator Pete Doyle!


Buat skrip mandiri Linux / Mac

Skrip ini tidak memerlukan file .txt terpisah untuk memuat pemformatan.

Buat file baru, curltime, di suatu tempat di jalur yang dapat dieksekusi, dan tempel di:

#!/bin/bash

curl -w @- -o /dev/null -s "$@" <<'EOF'
    time_namelookup:  %{time_namelookup}\n
       time_connect:  %{time_connect}\n
    time_appconnect:  %{time_appconnect}\n
   time_pretransfer:  %{time_pretransfer}\n
      time_redirect:  %{time_redirect}\n
 time_starttransfer:  %{time_starttransfer}\n
                    ----------\n
         time_total:  %{time_total}\n
EOF

Panggil dengan cara yang sama dengan alias:

curltime wordpress.org


Buat pintasan Windows (alias file BAT)

Letakkan perintah ini di CURLTIME.BAT (dalam folder yang sama dengan curl.exe)

curl -w "@%~dp0curl-format.txt" -o NUL -s %*

Maka Anda cukup menelepon ...

curltime wordpress.org

26
jawaban yang luar biasa. Terima kasih. satu hal yang harus saya lakukan adalah menambahkan \nuntuk mematahkan baris dalam file teks
Jason Kim

2
Di windows BAT file hanya mengirim parameter pertama, ubah ke ini untuk meneruskan semua parameter dan untuk menonaktifkan echo perintah itu sendiri: @curl -w "@%~dp0curl-format.txt" -o NUL -s %*Great answer
padilo

Terima kasih @udoh, saya telah memperbarui jawaban untuk memasukkan itu.
Simon East

jawaban yang bagus bagaimana cara saya juga memasukkan tanggal + waktu saat ini ketika ikal memulai permintaan?
Saqib Ali

4
Untuk Linux, saya membuat dotfile dan alias dan tampaknya bekerja dengan baik: alias curltime="curl -w \"@$HOME/.curl-format.txt\" -o NUL -s ". Kemungkinan bekerja pada MacOS juga.
Pete Doyle

161

Inilah jawabannya:

curl -X POST -d @file server:port -w %{time_connect}:%{time_starttransfer}:%{time_total}

Semua variabel yang digunakan dengan -wdapat ditemukan di man curl.


19
Lebih baik untuk pengalaman pengguna untuk menambahkan baris baru:"\n%{time_connect}:%{time_starttransfer}:%{time_total}\n"

1
Bagi saya itu tidak bekerja tanpa tanda kutip. Saya akan menyarankan menambahkan tanda kutip saat menentukan format / h / a / c / haproxy # ❯❯❯ curl -w "% {time_total} \ n" google.com -o / dev / null -s 0,055
Geek

@Geek Biasanya masuk akal untuk menunjukkan kesalahan saat beroperasi dalam mode diam ( -sS).
x-yuri

139

Opsi 1. Untuk mengukur total time:

curl -o /dev/null -s -w 'Total: %{time_total}s\n'  https://www.google.com

Output sampel:

masukkan deskripsi gambar di sini

Opsi 2. Untuk mendapatkan time to establish connection, TTFB: time to first bytedan total time:

curl -o /dev/null -s -w 'Establish Connection: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n'  https://www.google.com

Output sampel:

masukkan deskripsi gambar di sini

Ref: Dapatkan waktu respons dengan curl


53

Pintasan yang dapat Anda tambahkan ke .bashrc dll, berdasarkan jawaban lain di sini:

function perf {
  curl -o /dev/null -s -w "%{time_connect} + %{time_starttransfer} = %{time_total}\n" "$1"
}

Pemakaian:

> perf stackoverflow.com
0.521 + 0.686 = 1.290

5
Saya menggunakan variasi yang menampilkan jumlah byte yang diunduh selama waktu yang diukur:curl -o /dev/null -s -w "time_total: %{time_total} sec\nsize_download: %{size_download} bytes\n" https://www.google.com
jambroseclarke

39

Berikut ini diilhami oleh jawaban Simon. Ini mandiri (tidak memerlukan file format terpisah), yang membuatnya bagus untuk dimasukkan ke dalam .bashrc.

curl_time() {
    curl -so /dev/null -w "\
   namelookup:  %{time_namelookup}s\n\
      connect:  %{time_connect}s\n\
   appconnect:  %{time_appconnect}s\n\
  pretransfer:  %{time_pretransfer}s\n\
     redirect:  %{time_redirect}s\n\
starttransfer:  %{time_starttransfer}s\n\
-------------------------\n\
        total:  %{time_total}s\n" "$@"
}

Lebih lanjut, itu harus bekerja dengan semua argumen yang curlbiasanya diperlukan, karena "$@"hanya melewati mereka. Misalnya, Anda dapat melakukan:

curl_time -X POST -H "Content-Type: application/json" -d '{"key": "val"}' https://postman-echo.com/post

Keluaran:

   namelookup:  0,125000s
      connect:  0,250000s
   appconnect:  0,609000s
  pretransfer:  0,609000s
     redirect:  0,000000s
starttransfer:  0,719000s
-------------------------
        total:  0,719000s

34

Jika Anda ingin menganalisis atau merangkum latensi, Anda dapat mencoba bangku apache:

ab -n [number of samples] [url]

Sebagai contoh:

ab -n 100 http://www.google.com/

Ini akan menunjukkan:

This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.google.com (be patient).....done


Server Software:        gws
Server Hostname:        www.google.com
Server Port:            80

Document Path:          /
Document Length:        12419 bytes

Concurrency Level:      1
Time taken for tests:   10.700 seconds
Complete requests:      100
Failed requests:        97
   (Connect: 0, Receive: 0, Length: 97, Exceptions: 0)
Total transferred:      1331107 bytes
HTML transferred:       1268293 bytes
Requests per second:    9.35 [#/sec] (mean)
Time per request:       107.004 [ms] (mean)
Time per request:       107.004 [ms] (mean, across all concurrent requests)
Transfer rate:          121.48 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       20   22   0.8     22      26
Processing:    59   85 108.7     68     911
Waiting:       59   85 108.7     67     910
Total:         80  107 108.8     90     932

Percentage of the requests served within a certain time (ms)
  50%     90
  66%     91
  75%     93
  80%     95
  90%    105
  95%    111
  98%    773
  99%    932
 100%    932 (longest request)

1
cara yang lebih sederhana dari jawaban lainnya. Benar-benar lupa tentang perintah ini!
FacePalm

Ini jawaban yang fantastis. Dan abdengan mudah menerima banyak flag yang sama seperti curlmisalnya -Huntuk header. Saya menggunakan perintah ini untuk membandingkan waktu respons API pihak ketiga (memasok token pembawa di header Otorisasi). Bekerja dengan cemerlang.
tsamb

21

Cara lain adalah mengkonfigurasi ~/.curlrcseperti ini

-w "\n\n==== cURL measurements stats ====\ntotal: %{time_total} seconds \nsize: %{size_download} bytes \ndnslookup: %{time_namelookup} seconds \nconnect: %{time_connect} seconds \nappconnect: %{time_appconnect} seconds \nredirect: %{time_redirect} seconds \npretransfer: %{time_pretransfer} seconds \nstarttransfer: %{time_starttransfer} seconds \ndownloadspeed: %{speed_download} byte/sec \nuploadspeed: %{speed_upload} byte/sec \n\n"

Jadi output dari curlis

❯❯ curl -I https://google.com
HTTP/2 301
location: https://www.google.com/
content-type: text/html; charset=UTF-8
date: Mon, 04 Mar 2019 08:02:43 GMT
expires: Wed, 03 Apr 2019 08:02:43 GMT
cache-control: public, max-age=2592000
server: gws
content-length: 220
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
alt-svc: quic=":443"; ma=2592000; v="44,43,39"



==== cURL measurements stats ====
total: 0.211117 seconds
size: 0 bytes
dnslookup: 0.067179 seconds
connect: 0.098817 seconds
appconnect: 0.176232 seconds
redirect: 0.000000 seconds
pretransfer: 0.176438 seconds
starttransfer: 0.209634 seconds
downloadspeed: 0.000 byte/sec
uploadspeed: 0.000 byte/sec

Apakah Anda menunjukkan kepada saya referensi ke dokumen yang lebih detail tentang itu?
TrĐứn Đức Tâm

@ TrầnĐứcTâm detail dalam curl buku resmi ec.haxx.se/usingcurl-writeout.html
Hieu Huynh

10

Hai lebih baik daripada Apache Bench, memiliki lebih sedikit masalah dengan SSL

./hey https://google.com -more
Summary:
  Total:    3.0960 secs
  Slowest:  1.6052 secs
  Fastest:  0.4063 secs
  Average:  0.6773 secs
  Requests/sec: 64.5992

Response time histogram:
  0.406 [1] |
  0.526 [142]   |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  0.646 [1] |
  0.766 [6] |∎∎
  0.886 [0] |
  1.006 [0] |
  1.126 [0] |
  1.246 [12]    |∎∎∎
  1.365 [32]    |∎∎∎∎∎∎∎∎∎
  1.485 [5] |∎
  1.605 [1] |

Latency distribution:
  10% in 0.4265 secs
  25% in 0.4505 secs
  50% in 0.4838 secs
  75% in 1.2181 secs
  90% in 1.2869 secs
  95% in 1.3384 secs
  99% in 1.4085 secs

Details (average, fastest, slowest):
  DNS+dialup:    0.1150 secs, 0.0000 secs, 0.4849 secs
  DNS-lookup:    0.0032 secs, 0.0000 secs, 0.0319 secs
  req write:     0.0001 secs, 0.0000 secs, 0.0007 secs
  resp wait:     0.2068 secs, 0.1690 secs, 0.4906 secs
  resp read:     0.0117 secs, 0.0011 secs, 0.2375 secs

Status code distribution:
  [200] 200 responses

Referensi


9

Opsi lain yang mungkin paling sederhana dalam hal baris perintah adalah menambahkan --trace-timeopsi bawaan:

curl -X POST -d @file server:port --trace-time

Meskipun secara teknis tidak menampilkan pengaturan waktu dari berbagai langkah seperti yang diminta oleh OP, ia menampilkan cap waktu untuk semua langkah permintaan seperti yang ditunjukkan di bawah ini. Dengan ini, Anda dapat (cukup mudah) menghitung berapa lama setiap langkah telah diambil.

$ curl https://www.google.com --trace-time -v -o /dev/null
13:29:11.148734 * Rebuilt URL to: https://www.google.com/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     013:29:11.149958 *   Trying 172.217.20.36...
13:29:11.149993 * TCP_NODELAY set
13:29:11.163177 * Connected to www.google.com (172.217.20.36) port 443 (#0)
13:29:11.164768 * ALPN, offering h2
13:29:11.164804 * ALPN, offering http/1.1
13:29:11.164833 * successfully set certificate verify locations:
13:29:11.164863 *   CAfile: none
  CApath: /etc/ssl/certs
13:29:11.165046 } [5 bytes data]
13:29:11.165099 * (304) (OUT), TLS handshake, Client hello (1):
13:29:11.165128 } [512 bytes data]
13:29:11.189518 * (304) (IN), TLS handshake, Server hello (2):
13:29:11.189537 { [100 bytes data]
13:29:11.189628 * TLSv1.2 (IN), TLS handshake, Certificate (11):
13:29:11.189658 { [2104 bytes data]
13:29:11.190243 * TLSv1.2 (IN), TLS handshake, Server key exchange (12):
13:29:11.190277 { [115 bytes data]
13:29:11.190507 * TLSv1.2 (IN), TLS handshake, Server finished (14):
13:29:11.190539 { [4 bytes data]
13:29:11.190770 * TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
13:29:11.190797 } [37 bytes data]
13:29:11.190890 * TLSv1.2 (OUT), TLS change cipher, Client hello (1):
13:29:11.190915 } [1 bytes data]
13:29:11.191023 * TLSv1.2 (OUT), TLS handshake, Finished (20):
13:29:11.191053 } [16 bytes data]
13:29:11.204324 * TLSv1.2 (IN), TLS handshake, Finished (20):
13:29:11.204358 { [16 bytes data]
13:29:11.204417 * SSL connection using TLSv1.2 / ECDHE-ECDSA-CHACHA20-POLY1305
13:29:11.204451 * ALPN, server accepted to use h2
13:29:11.204483 * Server certificate:
13:29:11.204520 *  subject: C=US; ST=California; L=Mountain View; O=Google LLC; CN=www.google.com
13:29:11.204555 *  start date: Oct  2 07:29:00 2018 GMT
13:29:11.204585 *  expire date: Dec 25 07:29:00 2018 GMT
13:29:11.204623 *  subjectAltName: host "www.google.com" matched cert's "www.google.com"
13:29:11.204663 *  issuer: C=US; O=Google Trust Services; CN=Google Internet Authority G3
13:29:11.204701 *  SSL certificate verify ok.
13:29:11.204754 * Using HTTP2, server supports multi-use
13:29:11.204795 * Connection state changed (HTTP/2 confirmed)
13:29:11.204840 * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
13:29:11.204881 } [5 bytes data]
13:29:11.204983 * Using Stream ID: 1 (easy handle 0x55846ef24520)
13:29:11.205034 } [5 bytes data]
13:29:11.205104 > GET / HTTP/2
13:29:11.205104 > Host: www.google.com
13:29:11.205104 > User-Agent: curl/7.61.0
13:29:11.205104 > Accept: */*
13:29:11.205104 > 
13:29:11.218116 { [5 bytes data]
13:29:11.218173 * Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
13:29:11.218211 } [5 bytes data]
13:29:11.251936 < HTTP/2 200 
13:29:11.251962 < date: Fri, 19 Oct 2018 10:29:11 GMT
13:29:11.251998 < expires: -1
13:29:11.252046 < cache-control: private, max-age=0
13:29:11.252085 < content-type: text/html; charset=ISO-8859-1
13:29:11.252119 < p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
13:29:11.252160 < server: gws
13:29:11.252198 < x-xss-protection: 1; mode=block
13:29:11.252228 < x-frame-options: SAMEORIGIN
13:29:11.252262 < set-cookie: 1P_JAR=2018-10-19-10; expires=Sun, 18-Nov-2018 10:29:11 GMT; path=/; domain=.google.com
13:29:11.252297 < set-cookie: NID=141=pzXxp1jrJmLwFVl9bLMPFdGCtG8ySQKxB2rlDWgerrKJeXxfdmB1HhJ1UXzX-OaFQcnR1A9LKYxi__PWMigjMBQHmI3xkU53LI_TsYRbkMNJNdxs-caQQ7fEcDGE694S; expires=Sat, 20-Apr-2019 10:29:11 GMT; path=/; domain=.google.com; HttpOnly
13:29:11.252336 < alt-svc: quic=":443"; ma=2592000; v="44,43,39,35"
13:29:11.252368 < accept-ranges: none
13:29:11.252408 < vary: Accept-Encoding
13:29:11.252438 < 
13:29:11.252473 { [5 bytes data]
100 12215    0 12215    0     0   112k      0 --:--:-- --:--:-- --:--:--  112k
13:29:11.255674 * Connection #0 to host www.google.com left intact

Ini sebenarnya jawaban yang bagus yang mungkin cocok untuk sebagian besar kasus penggunaan yang dicari orang di sini. Jawaban lainnya bagus untuk solusi menyeluruh dan mendalam, tetapi ini bagus untuk memeriksa waktu pulang-pergi dengan cepat.
Chris Vandevelde

Terima kasih @ChrisVandevelde. Ya, saya sadar bahwa ada "sesuatu" seperti ini (pernah menggunakan parameter ini sebelumnya), lalu saya mencari Google di pos SO ini dan menemukan bentuk yang lebih canggih, tapi ... Saya merasa ada cara lain juga . :) Seperti yang Anda katakan, itu agak rapi dalam kesederhanaannya dan kadang-kadang cukup baik untuk kasus penggunaan yang lebih sederhana.
Per Lundberg



4

di sini adalah string yang dapat Anda gunakan -w, berisi semua opsi yang curl -wmendukung.

{"contentType":"%{content_type}","filenameEffective":"%{filename_effective}","ftpEntryPath":"%{ftp_entry_path}","httpCode":"%{http_code}","httpConnect":"%{http_connect}","httpVersion":"%{http_version}","localIp":"%{local_ip}","localPort":"%{local_port}","numConnects":"%{num_connects}","numRedirects":"%{num_redirects}","proxySslVerifyResult":"%{proxy_ssl_verify_result}","redirectUrl":"%{redirect_url}","remoteIp":"%{remote_ip}","remotePort":"%{remote_port}","scheme":"%{scheme}","size":{"download":"%{size_download}","header":"%{size_header}","request":"%{size_request}","upload":"%{size_upload}"},"speed":{"download":"%{speed_download}","upload":"%{speed_upload}"},"sslVerifyResult":"%{ssl_verify_result}","time":{"appconnect":"%{time_appconnect}","connect":"%{time_connect}","namelookup":"%{time_namelookup}","pretransfer":"%{time_pretransfer}","redirect":"%{time_redirect}","starttransfer":"%{time_starttransfer}","total":"%{time_total}"},"urlEffective":"%{url_effective}"}

output JSON.


Prepending \nmembantu memisahkan waktu ketika tubuh tidak berakhir dengan baris baru:curl -w '\n{"contentType":"..."}...
Beni Cherniavsky-Paskin

2

Berikut adalah Bash one-liner untuk memukul server yang sama berulang kali:

for i in {1..1000}; do curl -s -o /dev/null -w "%{time_total}\n" http://server/get_things; done

0

Ini adalah versi modifikasi dari jawaban Simons yang membuat keluaran multi-baris menjadi satu baris. Ini juga memperkenalkan timestamp saat ini sehingga lebih mudah untuk mengikuti setiap baris output.

Contoh format fle
$ cat time-format.txt
time_namelookup:%{time_namelookup} time_connect:%{time_connect} time_appconnect:%{time_appconnect} time_pretransfer:%{time_pretransfer} time_redirect:%{time_redirect} time_starttransfer:%{time_starttransfer} time_total:%{time_total}\n
contoh cmd
$ while [ 1 ];do echo -n "$(date) - " ; curl -w @curl-format.txt -o /dev/null -s https://myapp.mydom.com/v1/endpt-http; sleep 1; done | grep -v time_total:0
hasil
Mon Dec 16 17:51:47 UTC 2019 - time_namelookup:0.004 time_connect:0.015 time_appconnect:0.172 time_pretransfer:0.172 time_redirect:0.000 time_starttransfer:1.666 time_total:1.666
Mon Dec 16 17:51:50 UTC 2019 - time_namelookup:0.004 time_connect:0.015 time_appconnect:0.175 time_pretransfer:0.175 time_redirect:0.000 time_starttransfer:3.794 time_total:3.795
Mon Dec 16 17:51:55 UTC 2019 - time_namelookup:0.004 time_connect:0.017 time_appconnect:0.175 time_pretransfer:0.175 time_redirect:0.000 time_starttransfer:1.971 time_total:1.971
Mon Dec 16 17:51:58 UTC 2019 - time_namelookup:0.004 time_connect:0.014 time_appconnect:0.173 time_pretransfer:0.173 time_redirect:0.000 time_starttransfer:1.161 time_total:1.161
Mon Dec 16 17:52:00 UTC 2019 - time_namelookup:0.004 time_connect:0.015 time_appconnect:0.166 time_pretransfer:0.167 time_redirect:0.000 time_starttransfer:1.434 time_total:1.434
Mon Dec 16 17:52:02 UTC 2019 - time_namelookup:0.004 time_connect:0.015 time_appconnect:0.177 time_pretransfer:0.177 time_redirect:0.000 time_starttransfer:5.119 time_total:5.119
Mon Dec 16 17:52:08 UTC 2019 - time_namelookup:0.004 time_connect:0.014 time_appconnect:0.172 time_pretransfer:0.172 time_redirect:0.000 time_starttransfer:30.185 time_total:30.185
Mon Dec 16 17:52:39 UTC 2019 - time_namelookup:0.004 time_connect:0.014 time_appconnect:0.164 time_pretransfer:0.164 time_redirect:0.000 time_starttransfer:30.175 time_total:30.176
Mon Dec 16 17:54:28 UTC 2019 - time_namelookup:0.004 time_connect:0.015 time_appconnect:3.191 time_pretransfer:3.191 time_redirect:0.000 time_starttransfer:3.212 time_total:3.212
Mon Dec 16 17:56:08 UTC 2019 - time_namelookup:0.004 time_connect:0.015 time_appconnect:1.184 time_pretransfer:1.184 time_redirect:0.000 time_starttransfer:1.215 time_total:1.215
Mon Dec 16 18:00:24 UTC 2019 - time_namelookup:0.004 time_connect:0.015 time_appconnect:0.181 time_pretransfer:0.181 time_redirect:0.000 time_starttransfer:1.267 time_total:1.267

Saya menggunakan hal di atas untuk menangkap respons lambat pada titik akhir di atas.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.