Create gh-pages branch via GitHub
This commit is contained in:
commit
7100994fc9
|
@ -0,0 +1,647 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>ESPAsyncWebServer by me-no-dev</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/normalize.css" media="screen">
|
||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/stylesheet.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/github-light.css" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<section class="page-header">
|
||||
<h1 class="project-name">ESPAsyncWebServer</h1>
|
||||
<h2 class="project-tagline">Async HTTP and WebSocket Server for ESP8266 and ESP32 Arduino</h2>
|
||||
<a href="https://github.com/me-no-dev/ESPAsyncWebServer" class="btn">View on GitHub</a>
|
||||
<a href="https://github.com/me-no-dev/ESPAsyncWebServer/zipball/master" class="btn">Download .zip</a>
|
||||
<a href="https://github.com/me-no-dev/ESPAsyncWebServer/tarball/master" class="btn">Download .tar.gz</a>
|
||||
</section>
|
||||
|
||||
<section class="main-content">
|
||||
<h1>
|
||||
<a id="espasyncwebserver" class="anchor" href="#espasyncwebserver" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>ESPAsyncWebServer</h1>
|
||||
|
||||
<p>Async HTTP and WebSocket Server for ESP8266 and ESP32/ESP31B Arduino</p>
|
||||
|
||||
<p>Requires <a href="https://github.com/me-no-dev/ESPAsyncTCP">ESPAsyncTCP</a> to work</p>
|
||||
|
||||
<h2>
|
||||
<a id="why-should-you-care" class="anchor" href="#why-should-you-care" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Why should you care</h2>
|
||||
|
||||
<ul>
|
||||
<li>Using asynchronous network means that you can handle more than one connection at the same time</li>
|
||||
<li>You are called once the request is ready and parsed</li>
|
||||
<li>When you send the response, you are immediately ready to handle other connections
|
||||
while the server is taking care of sending the response in the background</li>
|
||||
<li>Speed is OMG</li>
|
||||
<li>Easy to use API, HTTP Basic Authentication, ChunkedResponse</li>
|
||||
<li>Easily extendible to handle any type of content</li>
|
||||
<li>Supports Continue 100</li>
|
||||
<li>Async WebSocket plugin offering different locations without extra servers or ports</li>
|
||||
</ul>
|
||||
|
||||
<h2>
|
||||
<a id="important-things-to-remember" class="anchor" href="#important-things-to-remember" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Important things to remember</h2>
|
||||
|
||||
<ul>
|
||||
<li>This is fully asynchronous server and as such does not run on the loop thread.</li>
|
||||
<li>You can not use yield or delay or any function that uses them inside the callbacks</li>
|
||||
<li>The server is smart enough to know when to close the connection and free resources</li>
|
||||
<li>You can not send more than one response to a single request</li>
|
||||
</ul>
|
||||
|
||||
<h2>
|
||||
<a id="principles-of-operation" class="anchor" href="#principles-of-operation" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Principles of operation</h2>
|
||||
|
||||
<h3>
|
||||
<a id="the-async-web-server" class="anchor" href="#the-async-web-server" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>The Async Web server</h3>
|
||||
|
||||
<ul>
|
||||
<li>Listens for connections</li>
|
||||
<li>Wraps the new clients into <code>Request</code>
|
||||
</li>
|
||||
<li>Keeps track of clients and cleans memory</li>
|
||||
<li>Manages <code>Handlers</code> and attaches them to Requests</li>
|
||||
</ul>
|
||||
|
||||
<h3>
|
||||
<a id="request-life-cycle" class="anchor" href="#request-life-cycle" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Request Life Cycle</h3>
|
||||
|
||||
<ul>
|
||||
<li>TCP connection is received by the server</li>
|
||||
<li>The connection is wrapped inside <code>Request</code> object</li>
|
||||
<li>When the request head is received (type, url, get params, http version and host),
|
||||
the server goes through all attached <code>Handlers</code>(in the order they are attached) trying to find one
|
||||
that <code>canHandle</code> the given request. If none are found, the default(catch-all) handler is attached.</li>
|
||||
<li>The rest of the request is received, calling the <code>handleUpload</code> or <code>handleBody</code> methods of the <code>Handler</code> if they are needed (POST+File/Body)</li>
|
||||
<li>When the whole request is parsed, the result is given to the <code>handleRequest</code> method of the <code>Handler</code> and is ready to be responded to</li>
|
||||
<li>In the <code>handleRequest</code> method, to the <code>Request</code> is attached a <code>Response</code> object (see below) that will serve the response data back to the client</li>
|
||||
<li>When the <code>Response</code> is sent, the client is closed and freed from the memory</li>
|
||||
</ul>
|
||||
|
||||
<h3>
|
||||
<a id="handlers-and-how-do-they-work" class="anchor" href="#handlers-and-how-do-they-work" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Handlers and how do they work</h3>
|
||||
|
||||
<ul>
|
||||
<li>The <code>Handlers</code> are used for executing specific actions to particular requests</li>
|
||||
<li>One <code>Handler</code> instance can be attached to any request and lives together with the server</li>
|
||||
<li>The <code>canHandle</code> method is used for filtering the requests that can be handled
|
||||
and declaring any interesting headers that the <code>Request</code> should collect </li>
|
||||
<li>Decision can be based on request method, request url, http version, request host/port/target host and GET parameters</li>
|
||||
<li>Once a <code>Handler</code> is attached to given <code>Request</code> (<code>canHandle</code> returned true)
|
||||
that <code>Handler</code> takes care to receive any file/data upload and attach a <code>Response</code>
|
||||
once the <code>Request</code> has been fully parsed</li>
|
||||
<li>
|
||||
<code>Handler's</code> <code>canHandle</code> is called in the order they are attached to the server.
|
||||
If a <code>Handler</code> attached earlier returns <code>true</code> on <code>canHandle</code>, then this <code>Hander's</code> <code>canHandle</code> will never be called</li>
|
||||
</ul>
|
||||
|
||||
<h3>
|
||||
<a id="responses-and-how-do-they-work" class="anchor" href="#responses-and-how-do-they-work" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Responses and how do they work</h3>
|
||||
|
||||
<ul>
|
||||
<li>The <code>Response</code> objects are used to send the response data back to the client</li>
|
||||
<li>The <code>Response</code> object lives with the <code>Request</code> and is freed on end or disconnect</li>
|
||||
<li>Different techniques are used depending on the response type to send the data in packets
|
||||
returning back almost immediately and sending the next packet when this one is received.
|
||||
Any time in between is spent to run the user loop and handle other network packets</li>
|
||||
<li>Responding asynchronously is probably the most difficult thing for most to understand</li>
|
||||
<li>Many different options exist for the user to make responding a background task</li>
|
||||
</ul>
|
||||
|
||||
<h2>
|
||||
<a id="request-variables" class="anchor" href="#request-variables" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Request Variables</h2>
|
||||
|
||||
<h3>
|
||||
<a id="common-variables" class="anchor" href="#common-variables" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Common Variables</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>request-><span class="pl-en">version</span>(); <span class="pl-c">// uint8_t: 0 = HTTP/1.0, 1 = HTTP/1.1</span>
|
||||
request-><span class="pl-en">method</span>(); <span class="pl-c">// enum: HTTP_GET, HTTP_POST, HTTP_DELETE, HTTP_PUT, HTTP_PATCH, HTTP_HEAD, HTTP_OPTIONS</span>
|
||||
request-><span class="pl-en">url</span>(); <span class="pl-c">// String: URL of the request (not including host, port or GET parameters) </span>
|
||||
request-><span class="pl-en">host</span>(); <span class="pl-c">// String: The requested host (can be used for virtual hosting)</span>
|
||||
request-><span class="pl-en">contentType</span>(); <span class="pl-c">// String: ContentType of the request (not avaiable in Handler::canHandle)</span>
|
||||
request-><span class="pl-en">contentLength</span>(); <span class="pl-c">// size_t: ContentLength of the request (not avaiable in Handler::canHandle)</span>
|
||||
request-><span class="pl-en">multipart</span>(); <span class="pl-c">// bool: True if the request has content type "multipart"</span></pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="headers" class="anchor" href="#headers" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Headers</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//List all collected headers</span>
|
||||
<span class="pl-k">int</span> headers = request-><span class="pl-en">headers</span>();
|
||||
<span class="pl-k">int</span> i;
|
||||
<span class="pl-k">for</span>(i=<span class="pl-c1">0</span>;i<headers;i++){
|
||||
AsyncWebHeader* h = request-><span class="pl-c1">getHeader</span>(i);
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>HEADER[<span class="pl-c1">%s</span>]: <span class="pl-c1">%s</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, h-><span class="pl-c1">name</span>().<span class="pl-c1">c_str</span>(), h-><span class="pl-c1">value</span>().<span class="pl-c1">c_str</span>());
|
||||
}
|
||||
|
||||
<span class="pl-c">//get specific header by name</span>
|
||||
<span class="pl-k">if</span>(request-><span class="pl-en">hasHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>MyHeader<span class="pl-pds">"</span></span>)){
|
||||
AsyncWebHeader* h = request-><span class="pl-c1">getHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>MyHeader<span class="pl-pds">"</span></span>);
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>MyHeader: <span class="pl-c1">%s</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, h-><span class="pl-c1">value</span>().<span class="pl-c1">c_str</span>());
|
||||
}</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="get-post-and-file-parameters" class="anchor" href="#get-post-and-file-parameters" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>GET, POST and FILE parameters</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//List all parameters</span>
|
||||
<span class="pl-k">int</span> params = request-><span class="pl-en">params</span>();
|
||||
<span class="pl-k">for</span>(<span class="pl-k">int</span> i=<span class="pl-c1">0</span>;i<params;i++){
|
||||
AsyncWebParameter* p = request-><span class="pl-c1">getParam</span>(i);
|
||||
<span class="pl-k">if</span>(p-><span class="pl-c1">isFile</span>()){ <span class="pl-c">//p->isPost() is also true</span>
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>FILE[<span class="pl-c1">%s</span>]: <span class="pl-c1">%s</span>, size: <span class="pl-c1">%u</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, p-><span class="pl-c1">name</span>().<span class="pl-c1">c_str</span>(), p-><span class="pl-c1">value</span>().<span class="pl-c1">c_str</span>(), p-><span class="pl-c1">size</span>());
|
||||
} <span class="pl-k">else</span> <span class="pl-k">if</span>(p-><span class="pl-c1">isPost</span>()){
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>POST[<span class="pl-c1">%s</span>]: <span class="pl-c1">%s</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, p-><span class="pl-c1">name</span>().<span class="pl-c1">c_str</span>(), p-><span class="pl-c1">value</span>().<span class="pl-c1">c_str</span>());
|
||||
} <span class="pl-k">else</span> {
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>GET[<span class="pl-c1">%s</span>]: <span class="pl-c1">%s</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, p-><span class="pl-c1">name</span>().<span class="pl-c1">c_str</span>(), p-><span class="pl-c1">value</span>().<span class="pl-c1">c_str</span>());
|
||||
}
|
||||
}
|
||||
|
||||
<span class="pl-c">//Check if GET parameter exists</span>
|
||||
<span class="pl-k">bool</span> exists = request-><span class="pl-en">hasParam</span>(<span class="pl-s"><span class="pl-pds">"</span>download<span class="pl-pds">"</span></span>);
|
||||
AsyncWebParameter* p = request-><span class="pl-en">getParam</span>(<span class="pl-s"><span class="pl-pds">"</span>download<span class="pl-pds">"</span></span>);
|
||||
|
||||
<span class="pl-c">//Check if POST (but not File) parameter exists</span>
|
||||
<span class="pl-k">bool</span> exists = request-><span class="pl-en">hasParam</span>(<span class="pl-s"><span class="pl-pds">"</span>download<span class="pl-pds">"</span></span>, <span class="pl-c1">true</span>);
|
||||
AsyncWebParameter* p = request-><span class="pl-en">getParam</span>(<span class="pl-s"><span class="pl-pds">"</span>download<span class="pl-pds">"</span></span>, <span class="pl-c1">true</span>);
|
||||
|
||||
<span class="pl-c">//Check if FILE was uploaded</span>
|
||||
<span class="pl-k">bool</span> exists = request-><span class="pl-en">hasParam</span>(<span class="pl-s"><span class="pl-pds">"</span>download<span class="pl-pds">"</span></span>, <span class="pl-c1">true</span>, <span class="pl-c1">true</span>);
|
||||
AsyncWebParameter* p = request-><span class="pl-en">getParam</span>(<span class="pl-s"><span class="pl-pds">"</span>download<span class="pl-pds">"</span></span>, <span class="pl-c1">true</span>, <span class="pl-c1">true</span>);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="file-upload-handling" class="anchor" href="#file-upload-handling" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>FILE Upload handling</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-k">void</span> <span class="pl-en">handleUpload</span>(AsyncWebServerRequest *request, String filename, <span class="pl-c1">size_t</span> index, <span class="pl-c1">uint8_t</span> *data, <span class="pl-c1">size_t</span> len, <span class="pl-k">bool</span> final){
|
||||
<span class="pl-k">if</span>(!<span class="pl-c1">index</span>){
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>UploadStart: <span class="pl-c1">%s</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, filename.<span class="pl-c1">c_str</span>());
|
||||
}
|
||||
<span class="pl-k">for</span>(<span class="pl-c1">size_t</span> i=<span class="pl-c1">0</span>; i<len; i++){
|
||||
Serial.<span class="pl-c1">write</span>(data[i]);
|
||||
}
|
||||
<span class="pl-k">if</span>(final){
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>UploadEnd: <span class="pl-c1">%s</span>, <span class="pl-c1">%u</span> B<span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, filename.<span class="pl-c1">c_str</span>(), <span class="pl-c1">index</span>+len);
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="body-data-handling" class="anchor" href="#body-data-handling" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Body data handling</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-k">void</span> <span class="pl-en">handleBody</span>(AsyncWebServerRequest *request, <span class="pl-c1">uint8_t</span> *data, <span class="pl-c1">size_t</span> len, <span class="pl-c1">size_t</span> index, <span class="pl-c1">size_t</span> total){
|
||||
<span class="pl-k">if</span>(!<span class="pl-c1">index</span>){
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>BodyStart: <span class="pl-c1">%u</span> B<span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, total);
|
||||
}
|
||||
<span class="pl-k">for</span>(<span class="pl-c1">size_t</span> i=<span class="pl-c1">0</span>; i<len; i++){
|
||||
Serial.<span class="pl-c1">write</span>(data[i]);
|
||||
}
|
||||
<span class="pl-k">if</span>(<span class="pl-c1">index</span> + len == total){
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>BodyEnd: <span class="pl-c1">%u</span> B<span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, total);
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<h2>
|
||||
<a id="responses" class="anchor" href="#responses" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Responses</h2>
|
||||
|
||||
<h3>
|
||||
<a id="basic-response-with-http-code" class="anchor" href="#basic-response-with-http-code" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Basic response with HTTP Code</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>request-><span class="pl-en">send</span>(<span class="pl-c1">404</span>); <span class="pl-c">//Sends 404 File Not Found</span></pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="basic-response-with-http-code-and-extra-headers" class="anchor" href="#basic-response-with-http-code-and-extra-headers" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Basic response with HTTP Code and extra headers</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>AsyncWebServerResponse *response = request-><span class="pl-en">beginResponse</span>(<span class="pl-c1">404</span>); <span class="pl-c">//Sends 404 File Not Found</span>
|
||||
response-><span class="pl-en">addHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>Server<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>ESP Async Web Server<span class="pl-pds">"</span></span>);
|
||||
request-><span class="pl-en">send</span>(response);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="basic-response-with-string-content" class="anchor" href="#basic-response-with-string-content" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Basic response with string content</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>request-><span class="pl-en">send</span>(<span class="pl-c1">200</span>, <span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>Hello World!<span class="pl-pds">"</span></span>);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="basic-response-with-string-content-and-extra-headers" class="anchor" href="#basic-response-with-string-content-and-extra-headers" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Basic response with string content and extra headers</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>AsyncWebServerResponse *response = request-><span class="pl-en">beginResponse</span>(<span class="pl-c1">200</span>, <span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>Hello World!<span class="pl-pds">"</span></span>);
|
||||
response-><span class="pl-en">addHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>Server<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>ESP Async Web Server<span class="pl-pds">"</span></span>);
|
||||
request-><span class="pl-en">send</span>(response);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="respond-with-content-coming-from-a-stream" class="anchor" href="#respond-with-content-coming-from-a-stream" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Respond with content coming from a Stream</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//read 12 bytes from Serial and send them as Content Type text/plain</span>
|
||||
request-><span class="pl-en">send</span>(Serial, <span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, <span class="pl-c1">12</span>);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="respond-with-content-coming-from-a-stream-and-extra-headers" class="anchor" href="#respond-with-content-coming-from-a-stream-and-extra-headers" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Respond with content coming from a Stream and extra headers</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//read 12 bytes from Serial and send them as Content Type text/plain</span>
|
||||
AsyncWebServerResponse *response = request-><span class="pl-en">beginResponse</span>(Serial, <span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, <span class="pl-c1">12</span>);
|
||||
response-><span class="pl-en">addHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>Server<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>ESP Async Web Server<span class="pl-pds">"</span></span>);
|
||||
request-><span class="pl-en">send</span>(response);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="respond-with-content-coming-from-a-file" class="anchor" href="#respond-with-content-coming-from-a-file" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Respond with content coming from a File</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//Send index.htm with default content type</span>
|
||||
request-><span class="pl-en">send</span>(SPIFFS, <span class="pl-s"><span class="pl-pds">"</span>/index.htm<span class="pl-pds">"</span></span>);
|
||||
|
||||
<span class="pl-c">//Send index.htm as text</span>
|
||||
request-><span class="pl-en">send</span>(SPIFFS, <span class="pl-s"><span class="pl-pds">"</span>/index.htm<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>);
|
||||
|
||||
<span class="pl-c">//Download index.htm</span>
|
||||
request-><span class="pl-en">send</span>(SPIFFS, <span class="pl-s"><span class="pl-pds">"</span>/index.htm<span class="pl-pds">"</span></span>, String(), true);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="respond-with-content-coming-from-a-file-and-extra-headers" class="anchor" href="#respond-with-content-coming-from-a-file-and-extra-headers" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Respond with content coming from a File and extra headers</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//Send index.htm with default content type</span>
|
||||
AsyncWebServerResponse *response = request-><span class="pl-en">beginResponse</span>(SPIFFS, <span class="pl-s"><span class="pl-pds">"</span>/index.htm<span class="pl-pds">"</span></span>);
|
||||
|
||||
<span class="pl-c">//Send index.htm as text</span>
|
||||
AsyncWebServerResponse *response = request-><span class="pl-en">beginResponse</span>(SPIFFS, <span class="pl-s"><span class="pl-pds">"</span>/index.htm<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>);
|
||||
|
||||
<span class="pl-c">//Download index.htm</span>
|
||||
AsyncWebServerResponse *response = request-><span class="pl-en">beginResponse</span>(SPIFFS, <span class="pl-s"><span class="pl-pds">"</span>/index.htm<span class="pl-pds">"</span></span>, String(), true);
|
||||
|
||||
response-><span class="pl-en">addHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>Server<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>ESP Async Web Server<span class="pl-pds">"</span></span>);
|
||||
request-><span class="pl-en">send</span>(response);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="respond-with-content-using-a-callback" class="anchor" href="#respond-with-content-using-a-callback" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Respond with content using a callback</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//send 128 bytes as plain text</span>
|
||||
request-><span class="pl-en">send</span>(<span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, <span class="pl-c1">128</span>, [](<span class="pl-c1">uint8_t</span> *buffer, <span class="pl-c1">size_t</span> maxLen, <span class="pl-c1">size_t</span> index) -> size_t {
|
||||
<span class="pl-c">//Write up to "maxLen" bytes into "buffer" and return the amount written.</span>
|
||||
<span class="pl-c">//index equals the amount of bytes that have been already sent</span>
|
||||
<span class="pl-c">//You will not be asked for more bytes once the content length has been reached.</span>
|
||||
<span class="pl-c">//Keep in mind that you can not delay or yield waiting for more data!</span>
|
||||
<span class="pl-c">//Send what you currently have and you will be asked for more again</span>
|
||||
<span class="pl-k">return</span> mySource.<span class="pl-c1">read</span>(buffer, maxLen);
|
||||
});</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="respond-with-content-using-a-callback-and-extra-headers" class="anchor" href="#respond-with-content-using-a-callback-and-extra-headers" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Respond with content using a callback and extra headers</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//send 128 bytes as plain text</span>
|
||||
AsyncWebServerResponse *response = request-><span class="pl-en">beginResponse</span>(<span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, <span class="pl-c1">128</span>, [](<span class="pl-c1">uint8_t</span> *buffer, <span class="pl-c1">size_t</span> maxLen, <span class="pl-c1">size_t</span> index) -> size_t {
|
||||
<span class="pl-c">//Write up to "maxLen" bytes into "buffer" and return the amount written.</span>
|
||||
<span class="pl-c">//index equals the amount of bytes that have been already sent</span>
|
||||
<span class="pl-c">//You will not be asked for more bytes once the content length has been reached.</span>
|
||||
<span class="pl-c">//Keep in mind that you can not delay or yield waiting for more data!</span>
|
||||
<span class="pl-c">//Send what you currently have and you will be asked for more again</span>
|
||||
<span class="pl-k">return</span> mySource.<span class="pl-c1">read</span>(buffer, maxLen);
|
||||
});
|
||||
response-><span class="pl-en">addHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>Server<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>ESP Async Web Server<span class="pl-pds">"</span></span>);
|
||||
request-><span class="pl-en">send</span>(response);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="chunked-response" class="anchor" href="#chunked-response" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Chunked Response</h3>
|
||||
|
||||
<p>Used when content length is unknown. Works best if the client supports HTTP/1.1</p>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>AsyncWebServerResponse *response = request-><span class="pl-en">beginChunkedResponse</span>(<span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, [](<span class="pl-c1">uint8_t</span> *buffer, <span class="pl-c1">size_t</span> maxLen, <span class="pl-c1">size_t</span> index) -> size_t {
|
||||
<span class="pl-c">//Write up to "maxLen" bytes into "buffer" and return the amount written.</span>
|
||||
<span class="pl-c">//index equals the amount of bytes that have been already sent</span>
|
||||
<span class="pl-c">//You will be asked for more data until 0 is returned</span>
|
||||
<span class="pl-c">//Keep in mind that you can not delay or yield waiting for more data!</span>
|
||||
<span class="pl-k">return</span> mySource.<span class="pl-c1">read</span>(buffer, maxLen);
|
||||
});
|
||||
response-><span class="pl-en">addHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>Server<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>ESP Async Web Server<span class="pl-pds">"</span></span>);
|
||||
request-><span class="pl-en">send</span>(response);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="print-to-response" class="anchor" href="#print-to-response" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Print to response</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>AsyncResponseStream *response = request-><span class="pl-en">beginResponseStream</span>(<span class="pl-s"><span class="pl-pds">"</span>text/html<span class="pl-pds">"</span></span>);
|
||||
response-><span class="pl-en">addHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>Server<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>ESP Async Web Server<span class="pl-pds">"</span></span>);
|
||||
response-><span class="pl-en">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><!DOCTYPE html><html><head><title>Webpage at <span class="pl-c1">%s</span></title></head><body><span class="pl-pds">"</span></span>, request-><span class="pl-en">url</span>().c_str());
|
||||
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span><h2>Hello <span class="pl-pds">"</span></span>);
|
||||
response-><span class="pl-en">print</span>(request-><span class="pl-en">client</span>()->remoteIP());
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span></h2><span class="pl-pds">"</span></span>);
|
||||
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span><h3>General</h3><span class="pl-pds">"</span></span>);
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span><ul><span class="pl-pds">"</span></span>);
|
||||
response-><span class="pl-en">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>Version: HTTP/1.<span class="pl-c1">%u</span></li><span class="pl-pds">"</span></span>, request-><span class="pl-en">version</span>());
|
||||
response-><span class="pl-en">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>Method: <span class="pl-c1">%s</span></li><span class="pl-pds">"</span></span>, request-><span class="pl-en">methodToString</span>());
|
||||
response-><span class="pl-en">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>URL: <span class="pl-c1">%s</span></li><span class="pl-pds">"</span></span>, request-><span class="pl-en">url</span>().c_str());
|
||||
response-><span class="pl-en">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>Host: <span class="pl-c1">%s</span></li><span class="pl-pds">"</span></span>, request-><span class="pl-en">host</span>().c_str());
|
||||
response-><span class="pl-en">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>ContentType: <span class="pl-c1">%s</span></li><span class="pl-pds">"</span></span>, request-><span class="pl-en">contentType</span>().c_str());
|
||||
response-><span class="pl-en">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>ContentLength: <span class="pl-c1">%u</span></li><span class="pl-pds">"</span></span>, request-><span class="pl-en">contentLength</span>());
|
||||
response-><span class="pl-en">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>Multipart: <span class="pl-c1">%s</span></li><span class="pl-pds">"</span></span>, request-><span class="pl-en">multipart</span>()?"true":"false");
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span></ul><span class="pl-pds">"</span></span>);
|
||||
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span><h3>Headers</h3><span class="pl-pds">"</span></span>);
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span><ul><span class="pl-pds">"</span></span>);
|
||||
<span class="pl-k">int</span> headers = request-><span class="pl-en">headers</span>();
|
||||
<span class="pl-k">for</span>(<span class="pl-k">int</span> i=<span class="pl-c1">0</span>;i<headers;i++){
|
||||
AsyncWebHeader* h = request-><span class="pl-c1">getHeader</span>(i);
|
||||
response-><span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li><span class="pl-c1">%s</span>: <span class="pl-c1">%s</span></li><span class="pl-pds">"</span></span>, h-><span class="pl-c1">name</span>().<span class="pl-c1">c_str</span>(), h-><span class="pl-c1">value</span>().<span class="pl-c1">c_str</span>());
|
||||
}
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span></ul><span class="pl-pds">"</span></span>);
|
||||
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span><h3>Parameters</h3><span class="pl-pds">"</span></span>);
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span><ul><span class="pl-pds">"</span></span>);
|
||||
<span class="pl-k">int</span> params = request-><span class="pl-en">params</span>();
|
||||
<span class="pl-k">for</span>(<span class="pl-k">int</span> i=<span class="pl-c1">0</span>;i<params;i++){
|
||||
AsyncWebParameter* p = request-><span class="pl-c1">getParam</span>(i);
|
||||
<span class="pl-k">if</span>(p-><span class="pl-c1">isFile</span>()){
|
||||
response-><span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>FILE[<span class="pl-c1">%s</span>]: <span class="pl-c1">%s</span>, size: <span class="pl-c1">%u</span></li><span class="pl-pds">"</span></span>, p-><span class="pl-c1">name</span>().<span class="pl-c1">c_str</span>(), p-><span class="pl-c1">value</span>().<span class="pl-c1">c_str</span>(), p-><span class="pl-c1">size</span>());
|
||||
} <span class="pl-k">else</span> <span class="pl-k">if</span>(p-><span class="pl-c1">isPost</span>()){
|
||||
response-><span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>POST[<span class="pl-c1">%s</span>]: <span class="pl-c1">%s</span></li><span class="pl-pds">"</span></span>, p-><span class="pl-c1">name</span>().<span class="pl-c1">c_str</span>(), p-><span class="pl-c1">value</span>().<span class="pl-c1">c_str</span>());
|
||||
} <span class="pl-k">else</span> {
|
||||
response-><span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span><li>GET[<span class="pl-c1">%s</span>]: <span class="pl-c1">%s</span></li><span class="pl-pds">"</span></span>, p-><span class="pl-c1">name</span>().<span class="pl-c1">c_str</span>(), p-><span class="pl-c1">value</span>().<span class="pl-c1">c_str</span>());
|
||||
}
|
||||
}
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span></ul><span class="pl-pds">"</span></span>);
|
||||
|
||||
response-><span class="pl-en">print</span>(<span class="pl-s"><span class="pl-pds">"</span></body></html><span class="pl-pds">"</span></span>);
|
||||
<span class="pl-c">//send the response last</span>
|
||||
request-><span class="pl-en">send</span>(response);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="send-a-large-webpage-from-progmem-using-callback-response" class="anchor" href="#send-a-large-webpage-from-progmem-using-callback-response" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Send a large webpage from PROGMEM using callback response</h3>
|
||||
|
||||
<p>Example provided by <a href="https://github.com/nouser2013">@nouser2013</a></p>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-k">const</span> <span class="pl-k">char</span> indexhtml[] PROGMEM = <span class="pl-s"><span class="pl-pds">"</span>...<span class="pl-pds">"</span></span>; <span class="pl-c">// large char array, tested with 5k</span>
|
||||
AsyncWebServerResponse *response = request-><span class="pl-en">beginResponse</span>(
|
||||
<span class="pl-en">String</span>(<span class="pl-s"><span class="pl-pds">"</span>text/html<span class="pl-pds">"</span></span>),
|
||||
strlen_P(indexhtml),
|
||||
[](<span class="pl-c1">uint8_t</span> *buffer, <span class="pl-c1">size_t</span> maxLen, <span class="pl-c1">size_t</span> alreadySent) -> size_t {
|
||||
<span class="pl-k">if</span> (<span class="pl-c1">strlen_P</span>(indexhtml+alreadySent)>maxLen) {
|
||||
<span class="pl-c">// We have more to read than fits in maxLen Buffer</span>
|
||||
<span class="pl-c1">memcpy_P</span>((<span class="pl-k">char</span>*)buffer, indexhtml+alreadySent, maxLen);
|
||||
<span class="pl-k">return</span> maxLen;
|
||||
}
|
||||
<span class="pl-c">// Ok, last chunk</span>
|
||||
<span class="pl-c1">memcpy_P</span>((<span class="pl-k">char</span>*)buffer, indexhtml+alreadySent, <span class="pl-c1">strlen_P</span>(indexhtml+alreadySent));
|
||||
<span class="pl-k">return</span> <span class="pl-c1">strlen_P</span>(indexhtml+alreadySent); <span class="pl-c">// Return from here to end of indexhtml</span>
|
||||
}
|
||||
);
|
||||
response-><span class="pl-en">addHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>Server<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>MyServerString<span class="pl-pds">"</span></span>);
|
||||
request-><span class="pl-en">send</span>(response); </pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="arduinojson-basic-response" class="anchor" href="#arduinojson-basic-response" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>ArduinoJson Basic Response</h3>
|
||||
|
||||
<p>This way of sending Json is great for when the result is below 4KB </p>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>#<span class="pl-k">include</span> <span class="pl-s"><span class="pl-pds">"</span>AsyncJson.h<span class="pl-pds">"</span></span>
|
||||
#<span class="pl-k">include</span> <span class="pl-s"><span class="pl-pds">"</span>ArduinoJson.h<span class="pl-pds">"</span></span>
|
||||
|
||||
|
||||
AsyncResponseStream *response = request-><span class="pl-en">beginResponseStream</span>(<span class="pl-s"><span class="pl-pds">"</span>text/json<span class="pl-pds">"</span></span>);
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject &root = jsonBuffer.createObject();
|
||||
root[<span class="pl-s"><span class="pl-pds">"</span>heap<span class="pl-pds">"</span></span>] = ESP.getFreeHeap();
|
||||
root[<span class="pl-s"><span class="pl-pds">"</span>ssid<span class="pl-pds">"</span></span>] = WiFi.SSID();
|
||||
root.printTo(*response);
|
||||
request-><span class="pl-en">send</span>(response);</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="arduinojson-advanced-response" class="anchor" href="#arduinojson-advanced-response" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>ArduinoJson Advanced Response</h3>
|
||||
|
||||
<p>This response can handle really large Json objects (tested to 40KB)
|
||||
There isn't any noticeable speed decrease for small results with the method above
|
||||
Since ArduinoJson does not allow reading parts of the string, the whole Json has to
|
||||
be passed every time a chunks needs to be sent, which shows speed decrease proportional
|
||||
to the resulting json packets</p>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>#<span class="pl-k">include</span> <span class="pl-s"><span class="pl-pds">"</span>AsyncJson.h<span class="pl-pds">"</span></span>
|
||||
#<span class="pl-k">include</span> <span class="pl-s"><span class="pl-pds">"</span>ArduinoJson.h<span class="pl-pds">"</span></span>
|
||||
|
||||
|
||||
AsyncJsonResponse * response = <span class="pl-k">new</span> AsyncJsonResponse();
|
||||
response-><span class="pl-en">addHeader</span>(<span class="pl-s"><span class="pl-pds">"</span>Server<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>ESP Async Web Server<span class="pl-pds">"</span></span>);
|
||||
JsonObject& root = response-><span class="pl-en">getRoot</span>();
|
||||
root[<span class="pl-s"><span class="pl-pds">"</span>heap<span class="pl-pds">"</span></span>] = ESP.getFreeHeap();
|
||||
root[<span class="pl-s"><span class="pl-pds">"</span>ssid<span class="pl-pds">"</span></span>] = WiFi.SSID();
|
||||
response-><span class="pl-en">setLength</span>();
|
||||
request-><span class="pl-en">send</span>(response);</pre></div>
|
||||
|
||||
<h2>
|
||||
<a id="bad-responses" class="anchor" href="#bad-responses" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Bad Responses</h2>
|
||||
|
||||
<p>Some responses are implemented, but you should not use them, because they do not conform to HTTP.
|
||||
The following example will lead to unclean close of the connection and more time wasted
|
||||
than providing the length of the content</p>
|
||||
|
||||
<h3>
|
||||
<a id="respond-with-content-using-a-callback-without-content-length-to-http10-clients" class="anchor" href="#respond-with-content-using-a-callback-without-content-length-to-http10-clients" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Respond with content using a callback without content length to HTTP/1.0 clients</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//This is used as fallback for chunked responses to HTTP/1.0 Clients</span>
|
||||
request-><span class="pl-en">send</span>(<span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, <span class="pl-c1">0</span>, [](<span class="pl-c1">uint8_t</span> *buffer, <span class="pl-c1">size_t</span> maxLen, <span class="pl-c1">size_t</span> index) -> size_t {
|
||||
<span class="pl-c">//Write up to "maxLen" bytes into "buffer" and return the amount written.</span>
|
||||
<span class="pl-c">//You will be asked for more data until 0 is returned</span>
|
||||
<span class="pl-c">//Keep in mind that you can not delay or yield waiting for more data!</span>
|
||||
<span class="pl-k">return</span> mySource.<span class="pl-c1">read</span>(buffer, maxLen);
|
||||
});</pre></div>
|
||||
|
||||
<h2>
|
||||
<a id="async-websocket-plugin" class="anchor" href="#async-websocket-plugin" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Async WebSocket Plugin</h2>
|
||||
|
||||
<p>The server includes a web socket plugin which lets you define different WebSocket locations to connect to
|
||||
without starting another listening service or using different port</p>
|
||||
|
||||
<h3>
|
||||
<a id="async-websocket-event" class="anchor" href="#async-websocket-event" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Async WebSocket Event</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>
|
||||
<span class="pl-k">void</span> <span class="pl-en">onEvent</span>(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, <span class="pl-k">void</span> * arg, <span class="pl-c1">uint8_t</span> *data, <span class="pl-c1">size_t</span> len){
|
||||
<span class="pl-k">if</span>(type == WS_EVT_CONNECT){
|
||||
<span class="pl-c">//client connected</span>
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] connect<span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>());
|
||||
client-><span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>Hello Client <span class="pl-c1">%u</span> :)<span class="pl-pds">"</span></span>, client-><span class="pl-c1">id</span>());
|
||||
client-><span class="pl-c1">ping</span>();
|
||||
} <span class="pl-k">else</span> <span class="pl-k">if</span>(type == WS_EVT_DISCONNECT){
|
||||
<span class="pl-c">//client disconnected</span>
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] disconnect: <span class="pl-c1">%u</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>());
|
||||
} <span class="pl-k">else</span> <span class="pl-k">if</span>(type == WS_EVT_ERROR){
|
||||
<span class="pl-c">//error was received from the other end</span>
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] error(<span class="pl-c1">%u</span>): <span class="pl-c1">%s</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>(), *((<span class="pl-c1">uint16_t</span>*)arg), (<span class="pl-k">char</span>*)data);
|
||||
} <span class="pl-k">else</span> <span class="pl-k">if</span>(type == WS_EVT_PONG){
|
||||
<span class="pl-c">//pong message was received (in response to a ping request maybe)</span>
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] pong[<span class="pl-c1">%u</span>]: <span class="pl-c1">%s</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>(), len, (len)?(<span class="pl-k">char</span>*)data:<span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>);
|
||||
} <span class="pl-k">else</span> <span class="pl-k">if</span>(type == WS_EVT_DATA){
|
||||
<span class="pl-c">//data packet</span>
|
||||
AwsFrameInfo * info = (AwsFrameInfo*)arg;
|
||||
<span class="pl-k">if</span>(info->num == <span class="pl-c1">0</span> && info->final && info-><span class="pl-c1">index</span> == <span class="pl-c1">0</span> && info->len == len){
|
||||
<span class="pl-c">//the whole message is in a single frame and we got all of it's data</span>
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] <span class="pl-c1">%s</span>-message[<span class="pl-c1">%llu</span>]: <span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>(), (info->opcode == WS_TEXT)?<span class="pl-s"><span class="pl-pds">"</span>text<span class="pl-pds">"</span></span>:<span class="pl-s"><span class="pl-pds">"</span>binary<span class="pl-pds">"</span></span>, info->len);
|
||||
<span class="pl-k">if</span>(info->opcode == WS_TEXT){
|
||||
data[len] = <span class="pl-c1">0</span>;
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-c1">%s</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, (<span class="pl-k">char</span>*)data);
|
||||
} <span class="pl-k">else</span> {
|
||||
<span class="pl-k">for</span>(<span class="pl-c1">size_t</span> i=<span class="pl-c1">0</span>; i < info->len; i++){
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-c1">%02x</span> <span class="pl-pds">"</span></span>, data[i]);
|
||||
}
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>);
|
||||
}
|
||||
<span class="pl-k">if</span>(info->opcode == WS_TEXT)
|
||||
client-><span class="pl-c1">text</span>(<span class="pl-s"><span class="pl-pds">"</span>I got your text message<span class="pl-pds">"</span></span>);
|
||||
<span class="pl-k">else</span>
|
||||
client-><span class="pl-c1">binary</span>(<span class="pl-s"><span class="pl-pds">"</span>I got your binary message<span class="pl-pds">"</span></span>);
|
||||
} <span class="pl-k">else</span> {
|
||||
<span class="pl-c">//message is comprised of multiple frames or the frame is split into multiple packets</span>
|
||||
<span class="pl-k">if</span>(info-><span class="pl-c1">index</span> == <span class="pl-c1">0</span>){
|
||||
<span class="pl-k">if</span>(info->num == <span class="pl-c1">0</span>)
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] <span class="pl-c1">%s</span>-message start<span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>(), (info->message_opcode == WS_TEXT)?<span class="pl-s"><span class="pl-pds">"</span>text<span class="pl-pds">"</span></span>:<span class="pl-s"><span class="pl-pds">"</span>binary<span class="pl-pds">"</span></span>);
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] frame[<span class="pl-c1">%u</span>] start[<span class="pl-c1">%llu</span>]<span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>(), info->num, info->len);
|
||||
}
|
||||
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] frame[<span class="pl-c1">%u</span>] <span class="pl-c1">%s</span>[<span class="pl-c1">%llu</span> - <span class="pl-c1">%llu</span>]: <span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>(), info->num, (info->message_opcode == WS_TEXT)?<span class="pl-s"><span class="pl-pds">"</span>text<span class="pl-pds">"</span></span>:<span class="pl-s"><span class="pl-pds">"</span>binary<span class="pl-pds">"</span></span>, info-><span class="pl-c1">index</span>, info-><span class="pl-c1">index</span> + len);
|
||||
<span class="pl-k">if</span>(info->message_opcode == WS_TEXT){
|
||||
data[len] = <span class="pl-c1">0</span>;
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-c1">%s</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, (<span class="pl-k">char</span>*)data);
|
||||
} <span class="pl-k">else</span> {
|
||||
<span class="pl-k">for</span>(<span class="pl-c1">size_t</span> i=<span class="pl-c1">0</span>; i < len; i++){
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-c1">%02x</span> <span class="pl-pds">"</span></span>, data[i]);
|
||||
}
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-cce">\n</span><span class="pl-pds">"</span></span>);
|
||||
}
|
||||
|
||||
<span class="pl-k">if</span>((info-><span class="pl-c1">index</span> + len) == info->len){
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] frame[<span class="pl-c1">%u</span>] end[<span class="pl-c1">%llu</span>]<span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>(), info->num, info->len);
|
||||
<span class="pl-k">if</span>(info->final){
|
||||
<span class="pl-c1">os_printf</span>(<span class="pl-s"><span class="pl-pds">"</span>ws[<span class="pl-c1">%s</span>][<span class="pl-c1">%u</span>] <span class="pl-c1">%s</span>-message end<span class="pl-cce">\n</span><span class="pl-pds">"</span></span>, server-><span class="pl-c1">url</span>(), client-><span class="pl-c1">id</span>(), (info->message_opcode == WS_TEXT)?<span class="pl-s"><span class="pl-pds">"</span>text<span class="pl-pds">"</span></span>:<span class="pl-s"><span class="pl-pds">"</span>binary<span class="pl-pds">"</span></span>);
|
||||
<span class="pl-k">if</span>(info->message_opcode == WS_TEXT)
|
||||
client-><span class="pl-c1">text</span>(<span class="pl-s"><span class="pl-pds">"</span>I got your text message<span class="pl-pds">"</span></span>);
|
||||
<span class="pl-k">else</span>
|
||||
client-><span class="pl-c1">binary</span>(<span class="pl-s"><span class="pl-pds">"</span>I got your binary message<span class="pl-pds">"</span></span>);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}</pre></div>
|
||||
|
||||
<h3>
|
||||
<a id="methods-for-sending-data-to-a-socket-client" class="anchor" href="#methods-for-sending-data-to-a-socket-client" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Methods for sending data to a socket client</h3>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre><span class="pl-c">//Server methods</span>
|
||||
AsyncWebSocket <span class="pl-en">ws</span>(<span class="pl-s"><span class="pl-pds">"</span>/ws<span class="pl-pds">"</span></span>);
|
||||
<span class="pl-c">//printf to a client</span>
|
||||
ws.printf([client id], [arguments...])
|
||||
<span class="pl-c">//printf to all clients</span>
|
||||
ws.printfAll([arguments...])
|
||||
<span class="pl-c">//send text to a client</span>
|
||||
ws.text([client id], [(<span class="pl-k">char</span>*)text])
|
||||
ws.text([client id], [text], [len])
|
||||
<span class="pl-c">//send text to all clients</span>
|
||||
ws.textAll([(<span class="pl-k">char</span>*text])
|
||||
ws.textAll([text], [len])
|
||||
<span class="pl-c">//send binary to a client</span>
|
||||
ws.binary([client id], [(<span class="pl-k">char</span>*)binary])
|
||||
ws.binary([client id], [binary], [len])
|
||||
<span class="pl-c">//send binary to all clients</span>
|
||||
ws.binaryAll([(<span class="pl-k">char</span>*binary])
|
||||
ws.binaryAll([binary], [len])
|
||||
|
||||
<span class="pl-c">//client methods</span>
|
||||
AsyncWebSocketClient * client;
|
||||
<span class="pl-c">//printf to a client</span>
|
||||
client-><span class="pl-en">printf</span>([arguments...])
|
||||
<span class="pl-c">//send text to a client</span>
|
||||
client->text([(<span class="pl-k">char</span>*)text])
|
||||
client->text([text], [len])
|
||||
<span class="pl-c">//send binary to a client</span>
|
||||
client->binary([(<span class="pl-k">char</span>*)binary])
|
||||
client->binary([binary], [len])
|
||||
</pre></div>
|
||||
|
||||
<h2>
|
||||
<a id="setting-up-the-server" class="anchor" href="#setting-up-the-server" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Setting up the server</h2>
|
||||
|
||||
<div class="highlight highlight-source-c++"><pre>#<span class="pl-k">include</span> <span class="pl-s"><span class="pl-pds">"</span>ESPAsyncTCP.h<span class="pl-pds">"</span></span>
|
||||
#<span class="pl-k">include</span> <span class="pl-s"><span class="pl-pds">"</span>ESPAsyncWebServer.h<span class="pl-pds">"</span></span>
|
||||
|
||||
AsyncWebServer <span class="pl-en">server</span>(<span class="pl-c1">80</span>);
|
||||
AsyncWebSocket <span class="pl-en">control_ws</span>(<span class="pl-s"><span class="pl-pds">"</span>/control<span class="pl-pds">"</span></span>); <span class="pl-c">// access at ws://[esp ip]/control</span>
|
||||
AsyncWebSocket <span class="pl-en">data_ws</span>(<span class="pl-s"><span class="pl-pds">"</span>/data<span class="pl-pds">"</span></span>); <span class="pl-c">// access at ws://[esp ip]/data</span>
|
||||
|
||||
<span class="pl-k">const</span> <span class="pl-k">char</span>* ssid = <span class="pl-s"><span class="pl-pds">"</span>your-ssid<span class="pl-pds">"</span></span>;
|
||||
<span class="pl-k">const</span> <span class="pl-k">char</span>* password = <span class="pl-s"><span class="pl-pds">"</span>your-pass<span class="pl-pds">"</span></span>;
|
||||
<span class="pl-k">const</span> <span class="pl-k">char</span>* http_username = <span class="pl-s"><span class="pl-pds">"</span>admin<span class="pl-pds">"</span></span>;
|
||||
<span class="pl-k">const</span> <span class="pl-k">char</span>* http_password = <span class="pl-s"><span class="pl-pds">"</span>admin<span class="pl-pds">"</span></span>;
|
||||
|
||||
<span class="pl-k">void</span> <span class="pl-en">onRequest</span>(AsyncWebServerRequest *request){
|
||||
<span class="pl-c">//Handle Unknown Request</span>
|
||||
request-><span class="pl-c1">send</span>(<span class="pl-c1">404</span>);
|
||||
}
|
||||
|
||||
<span class="pl-k">void</span> <span class="pl-en">onBody</span>(AsyncWebServerRequest *request, <span class="pl-c1">uint8_t</span> *data, <span class="pl-c1">size_t</span> len, <span class="pl-c1">size_t</span> index, <span class="pl-c1">size_t</span> total){
|
||||
<span class="pl-c">//Handle body</span>
|
||||
}
|
||||
|
||||
<span class="pl-k">void</span> <span class="pl-en">onUpload</span>(AsyncWebServerRequest *request, String filename, <span class="pl-c1">size_t</span> index, <span class="pl-c1">uint8_t</span> *data, <span class="pl-c1">size_t</span> len, <span class="pl-k">bool</span> final){
|
||||
<span class="pl-c">//Handle upload</span>
|
||||
}
|
||||
|
||||
<span class="pl-k">void</span> <span class="pl-en">onEvent</span>(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, <span class="pl-k">void</span> * arg, <span class="pl-c1">uint8_t</span> *data, <span class="pl-c1">size_t</span> len){
|
||||
<span class="pl-c">//Handle WebSocket event</span>
|
||||
}
|
||||
|
||||
<span class="pl-k">void</span> <span class="pl-en">setup</span>(){
|
||||
Serial.<span class="pl-c1">begin</span>(<span class="pl-c1">115200</span>);
|
||||
WiFi.<span class="pl-c1">mode</span>(WIFI_STA);
|
||||
WiFi.<span class="pl-c1">begin</span>(ssid, password);
|
||||
<span class="pl-k">if</span> (WiFi.<span class="pl-c1">waitForConnectResult</span>() != WL_CONNECTED) {
|
||||
Serial.<span class="pl-c1">printf</span>(<span class="pl-s"><span class="pl-pds">"</span>WiFi Failed!<span class="pl-cce">\n</span><span class="pl-pds">"</span></span>);
|
||||
<span class="pl-k">return</span>;
|
||||
}
|
||||
|
||||
<span class="pl-c">// attach Async WebSockets</span>
|
||||
control_ws.<span class="pl-c1">onEvent</span>(onEvent);
|
||||
server.<span class="pl-c1">addHandler</span>(&control_ws);
|
||||
data_ws.<span class="pl-c1">onEvent</span>(onEvent);
|
||||
server.<span class="pl-c1">addHandler</span>(&data_ws);
|
||||
|
||||
<span class="pl-c">// respond to GET requests on URL /heap</span>
|
||||
server.<span class="pl-c1">on</span>(<span class="pl-s"><span class="pl-pds">"</span>/heap<span class="pl-pds">"</span></span>, HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request-><span class="pl-c1">send</span>(<span class="pl-c1">200</span>, <span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, <span class="pl-c1">String</span>(ESP.<span class="pl-c1">getFreeHeap</span>()));
|
||||
});
|
||||
|
||||
<span class="pl-c">// upload a file to /upload</span>
|
||||
server.<span class="pl-c1">on</span>(<span class="pl-s"><span class="pl-pds">"</span>/upload<span class="pl-pds">"</span></span>, HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
request-><span class="pl-c1">send</span>(<span class="pl-c1">200</span>);
|
||||
}, handleUpload);
|
||||
|
||||
<span class="pl-c">// send a file when /index is requested</span>
|
||||
server.<span class="pl-c1">on</span>(<span class="pl-s"><span class="pl-pds">"</span>/index<span class="pl-pds">"</span></span>, HTTP_ANY, [](AsyncWebServerRequest *request){
|
||||
request-><span class="pl-c1">send</span>(SPIFFS, <span class="pl-s"><span class="pl-pds">"</span>/index.htm<span class="pl-pds">"</span></span>);
|
||||
});
|
||||
|
||||
<span class="pl-c">// HTTP basic authentication</span>
|
||||
server.<span class="pl-c1">on</span>(<span class="pl-s"><span class="pl-pds">"</span>/login<span class="pl-pds">"</span></span>, HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
<span class="pl-k">if</span>(!request-><span class="pl-c1">authenticate</span>(http_username, http_password))
|
||||
<span class="pl-k">return</span> request-><span class="pl-c1">requestAuthentication</span>();
|
||||
request-><span class="pl-c1">send</span>(<span class="pl-c1">200</span>, <span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>Login Success!<span class="pl-pds">"</span></span>);
|
||||
});
|
||||
|
||||
<span class="pl-c">// attach filesystem root at URL /fs </span>
|
||||
server.<span class="pl-c1">serveStatic</span>(<span class="pl-s"><span class="pl-pds">"</span>/fs<span class="pl-pds">"</span></span>, SPIFFS, <span class="pl-s"><span class="pl-pds">"</span>/<span class="pl-pds">"</span></span>);
|
||||
|
||||
<span class="pl-c">// Catch-All Handlers</span>
|
||||
<span class="pl-c">// Any request that can not find a Handler that canHandle it</span>
|
||||
<span class="pl-c">// ends in the callbacks below.</span>
|
||||
server.<span class="pl-c1">onNotFound</span>(onRequest);
|
||||
server.<span class="pl-c1">onFileUpload</span>(onUpload);
|
||||
server.<span class="pl-c1">onRequestBody</span>(onBody);
|
||||
|
||||
server.<span class="pl-c1">begin</span>();
|
||||
}
|
||||
|
||||
<span class="pl-k">void</span> <span class="pl-en">loop</span>(){}</pre></div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<span class="site-footer-owner"><a href="https://github.com/me-no-dev/ESPAsyncWebServer">ESPAsyncWebServer</a> is maintained by <a href="https://github.com/me-no-dev">me-no-dev</a>.</span>
|
||||
|
||||
<span class="site-footer-credits">This page was generated by <a href="https://pages.github.com">GitHub Pages</a> using the <a href="https://github.com/jasonlong/cayman-theme">Cayman theme</a> by <a href="https://twitter.com/jasonlong">Jason Long</a>.</span>
|
||||
</footer>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 GitHub, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
.pl-c /* comment */ {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.pl-c1 /* constant, markup.raw, meta.diff.header, meta.module-reference, meta.property-name, support, support.constant, support.variable, variable.other.constant */,
|
||||
.pl-s .pl-v /* string variable */ {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.pl-e /* entity */,
|
||||
.pl-en /* entity.name */ {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.pl-s .pl-s1 /* string source */,
|
||||
.pl-smi /* storage.modifier.import, storage.modifier.package, storage.type.java, variable.other, variable.parameter.function */ {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.pl-ent /* entity.name.tag */ {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.pl-k /* keyword, storage, storage.type */ {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.pl-pds /* punctuation.definition.string, string.regexp.character-class */,
|
||||
.pl-s /* string */,
|
||||
.pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */,
|
||||
.pl-sr /* string.regexp */,
|
||||
.pl-sr .pl-cce /* string.regexp constant.character.escape */,
|
||||
.pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */,
|
||||
.pl-sr .pl-sre /* string.regexp source.ruby.embedded */ {
|
||||
color: #183691;
|
||||
}
|
||||
|
||||
.pl-v /* variable */ {
|
||||
color: #ed6a43;
|
||||
}
|
||||
|
||||
.pl-id /* invalid.deprecated */ {
|
||||
color: #b52a1d;
|
||||
}
|
||||
|
||||
.pl-ii /* invalid.illegal */ {
|
||||
background-color: #b52a1d;
|
||||
color: #f8f8f8;
|
||||
}
|
||||
|
||||
.pl-sr .pl-cce /* string.regexp constant.character.escape */ {
|
||||
color: #63a35c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pl-ml /* markup.list */ {
|
||||
color: #693a17;
|
||||
}
|
||||
|
||||
.pl-mh /* markup.heading */,
|
||||
.pl-mh .pl-en /* markup.heading entity.name */,
|
||||
.pl-ms /* meta.separator */ {
|
||||
color: #1d3e81;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pl-mq /* markup.quote */ {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.pl-mi /* markup.italic */ {
|
||||
color: #333;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pl-mb /* markup.bold */ {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pl-md /* markup.deleted, meta.diff.header.from-file */ {
|
||||
background-color: #ffecec;
|
||||
color: #bd2c00;
|
||||
}
|
||||
|
||||
.pl-mi1 /* markup.inserted, meta.diff.header.to-file */ {
|
||||
background-color: #eaffea;
|
||||
color: #55a532;
|
||||
}
|
||||
|
||||
.pl-mdr /* meta.diff.range */ {
|
||||
color: #795da3;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pl-mo /* meta.output */ {
|
||||
color: #1d3e81;
|
||||
}
|
||||
|
|
@ -0,0 +1,424 @@
|
|||
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
|
||||
|
||||
/**
|
||||
* 1. Set default font family to sans-serif.
|
||||
* 2. Prevent iOS text size adjust after orientation change, without disabling
|
||||
* user zoom.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove default margin.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* HTML5 display definitions
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Correct `block` display not defined for any HTML5 element in IE 8/9.
|
||||
* Correct `block` display not defined for `details` or `summary` in IE 10/11
|
||||
* and Firefox.
|
||||
* Correct `block` display not defined for `main` in IE 11.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
menu,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct `inline-block` display not defined in IE 8/9.
|
||||
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
progress,
|
||||
video {
|
||||
display: inline-block; /* 1 */
|
||||
vertical-align: baseline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent modern browsers from displaying `audio` without controls.
|
||||
* Remove excess height in iOS 5 devices.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address `[hidden]` styling not present in IE 8/9/10.
|
||||
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
|
||||
*/
|
||||
|
||||
[hidden],
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Links
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background color from active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve readability when focused and also mouse hovered in all browsers.
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in Safari and Chrome.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address variable `h1` font-size and margin within `section` and `article`
|
||||
* contexts in Firefox 4+, Safari, and Chrome.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 8/9.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent and variable font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove border when inside `a` element in IE 8/9/10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct overflow not hidden in IE 9/10/11.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address margin not present in IE 8/9 and Safari.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address differences between Firefox and other browsers.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contain overflow in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address odd `em`-unit font size rendering in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Known limitation: by default, Chrome and Safari on OS X allow very limited
|
||||
* styling of `select`, unless a `border` property is set.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 1. Correct color not being inherited.
|
||||
* Known issue: affects color of disabled elements.
|
||||
* 2. Correct font properties not being inherited.
|
||||
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
color: inherit; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
margin: 0; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Address `overflow` set to `hidden` in IE 8/9/10/11.
|
||||
*/
|
||||
|
||||
button {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent `text-transform` inheritance for `button` and `select`.
|
||||
* All other form control elements do not inherit `text-transform` values.
|
||||
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
|
||||
* Correct `select` style inheritance in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||
* and `video` controls.
|
||||
* 2. Correct inability to style clickable `input` types in iOS.
|
||||
* 3. Improve usability and consistency of cursor style between image-type
|
||||
* `input` and others.
|
||||
*/
|
||||
|
||||
button,
|
||||
html input[type="button"], /* 1 */
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
cursor: pointer; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-set default cursor for disabled elements.
|
||||
*/
|
||||
|
||||
button[disabled],
|
||||
html input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and border in Firefox 4+.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
|
||||
* the UA stylesheet.
|
||||
*/
|
||||
|
||||
input {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* It's recommended that you don't attempt to style these elements.
|
||||
* Firefox's implementation doesn't respect box-sizing, padding, or width.
|
||||
*
|
||||
* 1. Address box sizing set to `content-box` in IE 8/9/10.
|
||||
* 2. Remove excess padding in IE 8/9/10.
|
||||
*/
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
|
||||
* `font-size` values of the `input`, it causes the cursor style of the
|
||||
* decrement button to change from `default` to `text`.
|
||||
*/
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
|
||||
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
|
||||
* (include `-moz` to future-proof).
|
||||
*/
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */ /* 2 */
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
|
||||
* Safari (but not Chrome) clips the cancel button when the search input has
|
||||
* padding (and `textfield` appearance).
|
||||
*/
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct `color` not being inherited in IE 8/9/10/11.
|
||||
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
||||
*/
|
||||
|
||||
legend {
|
||||
border: 0; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove default vertical scrollbar in IE 8/9/10/11.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't inherit the `font-weight` (applied by a rule above).
|
||||
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
|
||||
*/
|
||||
|
||||
optgroup {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tables
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove most spacing between table cells.
|
||||
*/
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
padding: 0;
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
* {
|
||||
box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: #606c71; }
|
||||
|
||||
a {
|
||||
color: #1e6bb8;
|
||||
text-decoration: none; }
|
||||
a:hover {
|
||||
text-decoration: underline; }
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
margin-bottom: 1rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: 0.3rem;
|
||||
transition: color 0.2s, background-color 0.2s, border-color 0.2s; }
|
||||
.btn + .btn {
|
||||
margin-left: 1rem; }
|
||||
|
||||
.btn:hover {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
text-decoration: none;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.3); }
|
||||
|
||||
@media screen and (min-width: 64em) {
|
||||
.btn {
|
||||
padding: 0.75rem 1rem; } }
|
||||
|
||||
@media screen and (min-width: 42em) and (max-width: 64em) {
|
||||
.btn {
|
||||
padding: 0.6rem 0.9rem;
|
||||
font-size: 0.9rem; } }
|
||||
|
||||
@media screen and (max-width: 42em) {
|
||||
.btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
font-size: 0.9rem; }
|
||||
.btn + .btn {
|
||||
margin-top: 1rem;
|
||||
margin-left: 0; } }
|
||||
|
||||
.page-header {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
background-color: #159957;
|
||||
background-image: linear-gradient(120deg, #155799, #159957); }
|
||||
|
||||
@media screen and (min-width: 64em) {
|
||||
.page-header {
|
||||
padding: 5rem 6rem; } }
|
||||
|
||||
@media screen and (min-width: 42em) and (max-width: 64em) {
|
||||
.page-header {
|
||||
padding: 3rem 4rem; } }
|
||||
|
||||
@media screen and (max-width: 42em) {
|
||||
.page-header {
|
||||
padding: 2rem 1rem; } }
|
||||
|
||||
.project-name {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.1rem; }
|
||||
|
||||
@media screen and (min-width: 64em) {
|
||||
.project-name {
|
||||
font-size: 3.25rem; } }
|
||||
|
||||
@media screen and (min-width: 42em) and (max-width: 64em) {
|
||||
.project-name {
|
||||
font-size: 2.25rem; } }
|
||||
|
||||
@media screen and (max-width: 42em) {
|
||||
.project-name {
|
||||
font-size: 1.75rem; } }
|
||||
|
||||
.project-tagline {
|
||||
margin-bottom: 2rem;
|
||||
font-weight: normal;
|
||||
opacity: 0.7; }
|
||||
|
||||
@media screen and (min-width: 64em) {
|
||||
.project-tagline {
|
||||
font-size: 1.25rem; } }
|
||||
|
||||
@media screen and (min-width: 42em) and (max-width: 64em) {
|
||||
.project-tagline {
|
||||
font-size: 1.15rem; } }
|
||||
|
||||
@media screen and (max-width: 42em) {
|
||||
.project-tagline {
|
||||
font-size: 1rem; } }
|
||||
|
||||
.main-content :first-child {
|
||||
margin-top: 0; }
|
||||
.main-content img {
|
||||
max-width: 100%; }
|
||||
.main-content h1, .main-content h2, .main-content h3, .main-content h4, .main-content h5, .main-content h6 {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: normal;
|
||||
color: #159957; }
|
||||
.main-content p {
|
||||
margin-bottom: 1em; }
|
||||
.main-content code {
|
||||
padding: 2px 4px;
|
||||
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
font-size: 0.9rem;
|
||||
color: #383e41;
|
||||
background-color: #f3f6fa;
|
||||
border-radius: 0.3rem; }
|
||||
.main-content pre {
|
||||
padding: 0.8rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
color: #567482;
|
||||
word-wrap: normal;
|
||||
background-color: #f3f6fa;
|
||||
border: solid 1px #dce6f0;
|
||||
border-radius: 0.3rem; }
|
||||
.main-content pre > code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
color: #567482;
|
||||
word-break: normal;
|
||||
white-space: pre;
|
||||
background: transparent;
|
||||
border: 0; }
|
||||
.main-content .highlight {
|
||||
margin-bottom: 1rem; }
|
||||
.main-content .highlight pre {
|
||||
margin-bottom: 0;
|
||||
word-break: normal; }
|
||||
.main-content .highlight pre, .main-content pre {
|
||||
padding: 0.8rem;
|
||||
overflow: auto;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.45;
|
||||
border-radius: 0.3rem; }
|
||||
.main-content pre code, .main-content pre tt {
|
||||
display: inline;
|
||||
max-width: initial;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: initial;
|
||||
line-height: inherit;
|
||||
word-wrap: normal;
|
||||
background-color: transparent;
|
||||
border: 0; }
|
||||
.main-content pre code:before, .main-content pre code:after, .main-content pre tt:before, .main-content pre tt:after {
|
||||
content: normal; }
|
||||
.main-content ul, .main-content ol {
|
||||
margin-top: 0; }
|
||||
.main-content blockquote {
|
||||
padding: 0 1rem;
|
||||
margin-left: 0;
|
||||
color: #819198;
|
||||
border-left: 0.3rem solid #dce6f0; }
|
||||
.main-content blockquote > :first-child {
|
||||
margin-top: 0; }
|
||||
.main-content blockquote > :last-child {
|
||||
margin-bottom: 0; }
|
||||
.main-content table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
word-break: normal;
|
||||
word-break: keep-all; }
|
||||
.main-content table th {
|
||||
font-weight: bold; }
|
||||
.main-content table th, .main-content table td {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid #e9ebec; }
|
||||
.main-content dl {
|
||||
padding: 0; }
|
||||
.main-content dl dt {
|
||||
padding: 0;
|
||||
margin-top: 1rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold; }
|
||||
.main-content dl dd {
|
||||
padding: 0;
|
||||
margin-bottom: 1rem; }
|
||||
.main-content hr {
|
||||
height: 2px;
|
||||
padding: 0;
|
||||
margin: 1rem 0;
|
||||
background-color: #eff0f1;
|
||||
border: 0; }
|
||||
|
||||
@media screen and (min-width: 64em) {
|
||||
.main-content {
|
||||
max-width: 64rem;
|
||||
padding: 2rem 6rem;
|
||||
margin: 0 auto;
|
||||
font-size: 1.1rem; } }
|
||||
|
||||
@media screen and (min-width: 42em) and (max-width: 64em) {
|
||||
.main-content {
|
||||
padding: 2rem 4rem;
|
||||
font-size: 1.1rem; } }
|
||||
|
||||
@media screen and (max-width: 42em) {
|
||||
.main-content {
|
||||
padding: 2rem 1rem;
|
||||
font-size: 1rem; } }
|
||||
|
||||
.site-footer {
|
||||
padding-top: 2rem;
|
||||
margin-top: 2rem;
|
||||
border-top: solid 1px #eff0f1; }
|
||||
|
||||
.site-footer-owner {
|
||||
display: block;
|
||||
font-weight: bold; }
|
||||
|
||||
.site-footer-credits {
|
||||
color: #819198; }
|
||||
|
||||
@media screen and (min-width: 64em) {
|
||||
.site-footer {
|
||||
font-size: 1rem; } }
|
||||
|
||||
@media screen and (min-width: 42em) and (max-width: 64em) {
|
||||
.site-footer {
|
||||
font-size: 1rem; } }
|
||||
|
||||
@media screen and (max-width: 42em) {
|
||||
.site-footer {
|
||||
font-size: 0.9rem; } }
|
Loading…
Reference in New Issue