diff --git a/config.toml b/config.toml index 696cd472..f7303e4f 100644 --- a/config.toml +++ b/config.toml @@ -744,6 +744,27 @@ theme = "doc-theme" parent = "firmwareapi@pycom@network" weight = 20 +[[menu.main]] + name = "HTTPS" + url = "/firmwareapi/pycom/network/https/" + identifier = "firmwareapi@pycom@network@https" + parent = "firmwareapi@pycom@network" + weight = 40 + +[[menu.main]] + name = "Server" + url = "/firmwareapi/pycom/network/https/server/" + identifier = "firmwareapi@pycom@network@https@server" + parent = "firmwareapi@pycom@network@https" + weight = 10 + +[[menu.main]] + name = "Client" + url = "/firmwareapi/pycom/network/https/client/" + identifier = "firmwareapi@pycom@network@https@client" + parent = "firmwareapi@pycom@network@https" + weight = 10 + [[menu.main]] name = "Bluetooth" url = "/firmwareapi/pycom/network/bluetooth/" diff --git a/content/firmwareapi/pycom/network/https/_index.md b/content/firmwareapi/pycom/network/https/_index.md new file mode 100644 index 00000000..2312e8b8 --- /dev/null +++ b/content/firmwareapi/pycom/network/https/_index.md @@ -0,0 +1,70 @@ +--- +title: "HTTP/S" +aliases: + - chapter/firmwareapi/pycom/network/https +--- + +This module implements an HTTP and HTTPS Server and Client, operating individually or as both at the same time. + +## Quick Usage Example +Below is an example demonstrating the usage of `HTTP_Server` and `HTTP_Client` at the same time: + +```python +from network import WLAN +from network import HTTP_Server +from network import HTTP_Client + +# The callback that handles the responses generated from the requests sent to a HTTP/S Server +def server_callback(uri, method, headers, body, new_uri, status): + print("Request URI: {}".format(uri)) + print("Request Method: {}".format(method)) + for key, value in headers.items(): + print("Request headers:", (key, value)) + print("Request Body: {}".format(body)) + print("Request New URI: {}".format(new_uri)) + print("Request Status: {}".format(status)) + +# The callback that handles the responses generated from the requests sent to a HTTP/S Server +def client_callback(status, headers, body): + print("Response Status: {}".format(status)) + for key, value in headers.items(): + print("Response headers:", (key, value)) + print("Response Body: {}".format(body)) + +# Connect to the network +wlan = WLAN(mode=WLAN.STA) +wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) +while not wlan.isconnected(): + pass +print(wlan.ifconfig()) + +# Initilise an HTTP Server +HTTP_Server.init() + +# Add new resource to Server +res = HTTP_Server.add_resource('/resource', value = "Hello Client!") +# Register resource request handler +res.register_request_handler(HTTP_Server.GET, callback=server_callback) + +# Initialize an HTTP Client +HTTP_Client.init('http://' + str(wlan.ifconfig()[0] + '/resource'), callback=client_callback) +# Send request with body +HTTP_Client.send_request(body='Hello Server!') +``` +To implement HTTPS Server and Client, only the two init methods need to be changed. HTTP Server and Client init: + +```python +# HTTP Server init +HTTP_Server.init() + +# HTTP Client init +HTTP_Client.init('http://' + str(wlan.ifconfig()[0] + '/resource'), callback=client_callback) +``` +HTTPS Server and Client init: +```python +# HTTPS Server init +HTTP_Server.init(port=443, keyfile='/flash/cert/prvtkey.pem', certfile='/flash/cert/cacert.pem') + +# HTTPS Client init +HTTP_Client.init('https://' + str(wlan.ifconfig()[0] + '/resource'), callback=client_callback) +``` diff --git a/content/firmwareapi/pycom/network/https/client.md b/content/firmwareapi/pycom/network/https/client.md new file mode 100644 index 00000000..7e04019c --- /dev/null +++ b/content/firmwareapi/pycom/network/https/client.md @@ -0,0 +1,141 @@ +--- +title: "HTTP/S Client" +aliases: + - firmwareapi/pycom/network/https/client.html + - firmwareapi/pycom/network/https/client.md + - chapter/firmwareapi/pycom/network/https/client +--- +This module implements HTTP/S Client. + +## Quick Usage Example + +```python +from network import WLAN +from network import HTTP_Client + +# The callback that handles the responses generated from the requests sent to a HTTP/S Server +def client_callback(status, headers, body): + print("Status Code: {}".format(status)) + for key, value in headers.items(): + print(key, ":", value) + print("Body: {}".format(body)) + +# Connect to the network +wlan = WLAN(mode=WLAN.STA) +wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) +while not wlan.isconnected(): + pass +print(wlan.ifconfig()) + +# Initialize HTTP Client +HTTP_Client.init("http://httpbin.org/get", callback=client_callback) +# Send request with GET method +HTTP_Client.send_request(method=HTTP_Client.GET) + +# Set HTTPS url +HTTP_Client.url("https://httpbin.org/post") +# Send request with POST method and custom body +HTTP_Client.send_request(method=HTTP_Client.POST, body='post data') + +# Optionally deinit Client +HTTP_Client.deinit() +``` + +## Initialization + +#### HTTP_Client.init(url, *, auth=None, callback=None) + +Initialize the HTTP_Client module. + +The arguments are: + +* `url` is the address where the HTTP_Client module handles communication. Based on HTTP or HTTPS Client mode, `url` string must be started with 'http://' or 'https://' respectively. +* `auth` is a tuple with (username, password). `Basic` authentication is supported in case of configured username and password. +* `callback` registers a callback function which will be called when a response received on the Client's request. `callback` must have the following arguments: + * `status` is the status code issued by a Server in response to a Client's request (e.g. `200` OK or `404` Not Found). + * `headers` is a dictionary contains all the response headers as key-value pairs. Size of `headers` depends on the number of headers received. + * `body` is the message body or payload transmitted in response. + +## Methods: + +#### HTTP_Client.deinit() + +Disables and deinitiates HTTP_Client module. + +#### HTTP_Client.url(url) + +Get or set the `url` for HTTP_Client. +* Calling method without argument gets the currently set `url` as string. +* `url` is the address where the HTTP_Client module handles communication. Based on HTTP or HTTPS Client mode, `url` string must be started with 'http://' or 'https://. +This method (re)initiates HTTP_Client. + +```python +# Get the url of Client +HTTP_Client.url() + +# Set the url for Client +HTTP_Client.url("http://httpbin.org/get") +``` + +#### HTTP_Client.auth(auth) + +Get or set the `auth` for HTTP_Client. +* Calling method without argument gets the currently set `auth` as tuple. +* `auth` is a tuple with (username, password). `Basic` authentication is supported in case of configured username and password. Setting empty strings ("") for username and password disables `Basic` authentication. + +```python +# Get the auth of Client +HTTP_Client.auth() + +# Set the auth for Client +HTTP_Client.auth("user", "pass") + +# Disable the auth for Client +HTTP_Client.auth("", "") +``` + +#### HTTP_Client.callback(callback, *, action=True) + +Register or unregister method for HTTP_Client's response handler. +* `callback` registers/unregisters (based on `action` flag) a callback function which will be called when a response received on the Client's request. `callback` must have the following arguments: + * `status` is the status code issued by a Server in response to a Client's request (e.g. `200` OK or `404` Not Found). + * `headers` is a dictionary contains all the response headers as key-value pairs. Size of `headers` depends on the number of headers received. + * `body` is the message body or payload transmitted in response. +* `action` is a flag which decides if the given `callback` is registered (`action`=True) or unregistered(`action`=False). + +```python +# The callback that handles the responses generated from the requests sent to a HTTP/S Server +def client_callback(status, headers, body): + # Define callback method + pass + +# Register callback for Client +HTTP_Client.callback(client_callback) + +# Unregister callback for Client +HTTP_Client.callback(client_callback, action=False) +``` + +#### HTTP\_Client.send\_request(method=HTTP_Client.GET, body=None, content_type=HTTP_Client.TEXT, accept=None, user_agent='ESP32 HTTP Client/1.0') + +* `method` is the method to be sent to the server, can be: `HTTP_Client.GET`, `HTTP_Client.POST`, `HTTP_Client.PUT` or `HTTP_Client.DELETE`. +* `body` is the message body (or content) of the request in string format. +* `content_type` is the Content-Type header of the request, can be: `HTTP_Client.TEXT`, `HTTP_Client.XML`, `HTTP_Client.PLAIN`, `HTTP_Client.JSON`, `HTTP_Client.OCTET` or `HTTP_Client.APP_XML`. +* `accept` is the Accept header of the request in string format. +* `user_agent` is the User-Agent header of the request in string format, which lets server identify the client. The default value is: \'ESP32 HTTP Client/1.0\'. + +## Constants +* HTTP_Client methods: `HTTP_Client.GET`, `HTTP_Client.POST`, `HTTP_Client.PUT`, `HTTP_Client.DELETE` +* HTTP_Client media types: + + `HTTP_Client.TEXT`: HyperText Markup Language (HTML): "text/html" + + `HTTP_Client.XML`: XML format: "text/xml" + + `HTTP_Client.PLAIN`: Text format: "text/plain" + + `HTTP_Client.JSON`: Json format: "application/json" + + `HTTP_Client.OCTET`: Binary data: "application/octet-stream" + + `HTTP_Client.APP_XML`: Json format: "application/xml diff --git a/content/firmwareapi/pycom/network/https/server.md b/content/firmwareapi/pycom/network/https/server.md new file mode 100644 index 00000000..8d1897c6 --- /dev/null +++ b/content/firmwareapi/pycom/network/https/server.md @@ -0,0 +1,172 @@ +--- +title: "HTTP/S Server" +aliases: + - firmwareapi/pycom/network/https/server.html + - firmwareapi/pycom/network/https/server.md + - chapter/firmwareapi/pycom/network/https/server +--- + +This module implements HTTP/S Server. + +## Quick Usage Example + +```python +from network import WLAN +from network import HTTP_Server + +# The callback that handles the responses generated from the requests sent to a HTTP/S Server +def server_callback(uri, method, headers, body, new_uri, status): + print("Request URI: {}".format(uri)) + print("Request Method: {}".format(method)) + for key, value in headers.items(): + print("Request headers:", (key, value)) + print("Request Body: {}".format(body)) + print("Request New URI: {}".format(new_uri)) + print("Request Status: {}".format(status)) + +# Connect to the network +wlan = WLAN(mode=WLAN.STA) +wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) +while not wlan.isconnected(): + pass +print(wlan.ifconfig()) + +# Initilise an HTTP Server +HTTP_Server.init() + +# Add resources to Server +res1 = HTTP_Server.add_resource('/resource1', value = "Hello Client!") +res2 = HTTP_Server.add_resource('/resource2') + +# Register resource request handlers +res1.register_request_handler(HTTP_Server.GET) +res2.register_request_handler(HTTP_Server.GET | HTTP_Server.POST | HTTP_Server.PUT | HTTP_Server.DELETE, callback=server_callback) + +``` + +## Initialization + +#### HTTP_Server.init(port=80, max_uri=100, keyfile=None, certfile=None) + +Initialize the HTTP_Server module. +The arguments are: +* `port` is the port where the HTTP_Server module listens. If not set, the default port is 80. HTTPS (HTTP Secure or HTTP over TLS/SSL) mode is activated in case of 443 port setting. In this case, `keyfile` and `certfile` must be placed into `/flash/cert/` folder on the device. +* `max_uri` is the maximum allowed resources on the Server. +* `keyfile` is the Private Key path for SSL Certificate. +* `certfile` is the Certification File path for SSL Certificate. + +For example you can create an HTTP Server: +```python +# Init HTTP Server +HTTP_Server.init() +``` +or an HTTPS: +```python +# Init HTTPS Server +HTTP_Server.init(port=443, keyfile='/flash/cert/prvtkey.pem', certfile='/flash/cert/cacert.pem') +``` +_Note: Self-signed Certificate can be gemerated by using OpenSSL command line tool:_ +``` +openssl req -newkey rsa:2048 -nodes -keyout prvtkey.pem -x509 -days 365 -out cacert.pem -subj "/CN=Pycom HTTPS Server" +``` + +## Methods: + +#### HTTP_Server.deinit() + +Disables and deinitiates HTTP_Server module. + +#### HTTP_Server.add_resource(uri, *, content_type=HTTP_Server.TEXT, value=0) + +Creates a `HTTP_Resource` object, which can be defined with its `uri`, and can be registered with different request handlers. + +* `uri` is the unique identifier path to the resource, `uri` string must be started with '/'. +* `content_type` is the representation type of the resource. +* `value` is the default value of the resource. If not given, it is initialised to decimal 0. + +#### HTTP_Server.get_resource(uri, *) + +Returns with the `HTTP_Resource` object defined by `uri` argument. + +* `uri` is the full path of the resource to be returned. + +#### HTTP_Server.list_resource(uri_start=None) + +Returns with the list of uri starts with `uri_start` strings, registered on the Server. + +* `uri_start` is start string of uri to be returned. If not set, all the existing resources are returned. + +#### HTTP_Server.remove_resource(uri, *) + +Removes the `HTTP_Resource` defined by the `uri` argument, and unregister its handler. + +* `uri` is the full path of the resource to be removed. + +## Class resource + +The resource class represents a resource in the scope of the HTTP\_Server module. A new resource can be created with the `HTTP_Server.add_resource` function or with a POST Client request. + +#### Class methods + +The following methods are defined in the scope of the `HTTP_Resource` class. + +#### resource.value(value) +Get or set the `value` for resource. + +* Calling method without argument gets the currently set `value` as string. +* `value` is the new value to update the current value with. + +#### resource.register_request_handler(method, *, callback) + +Registers the servers operation on the given resource. Calling multiple times this function, only the most recent methods are registered. +* `method` is the indication of the desired action to be performed for a given resource. It can be `HTTP_Server.GET`, `HTTP_Server.POST`, `HTTP_Server.PUT`, `HTTP_Server.DELETE`, or any combination at the same time. Calling method again with different method, unregisters recent settings, and resets the new one(s). +* `callback` can be assigned to the resource, it contains information about Client's request: + * `uri` is the full path of the resource to be requested. + * `method` indicates the request method. + * `headers` is a dictionary contains all the request headers as key-value pairs. Size of `headers` depends on the number of headers received. + * `body` is the message body or payload transmitted in request. + * `new_uri` is the full path of newly created `uri`. It is empty except in case of successful Post request. + * `status` is the status code sent back by the Server in the response. + +```python +# The callback that handles the requests of the Server +def server_callback(uri, method, headers, body, new_uri, status): + # Define callback method + pass + +# Create a resource +res = HTTP_Server.add_resource('/res') + +# Register a GET request handler for resource +res.register_request_handler(HTTP_Server.GET) + +# Register GET and POST request handler for resource with a callback +res.register_request_handler(HTTP_Server.GET | HTTP_Server.POST, callback=server_callback) +``` +--- +_Note: Registering a request handler means that `HTTP_Server` automatically handles basic HTTP requests. Currently four methods are supported:_ +* _GET: Server serves the requested resources value. Successful GET: `200 OK`._ +* _PUT: Server updates the value of the requested resource (creating resource with PUT request is not supported). Successful PUT: `204 No Content`. `Content-Location` header in response contains the updated reource's URI._ +* _POST: Server creates a new resource. New resource will be the subresource of the requested one, and a 5 digit ID is generated by the Server, and the value of the resource will be the payload of the request. Successful POST: `201 Created`. `Location` header in response contains the created reource's URI._ +* _DELETE: Server deletes the requested resource. Successful DELETE: `204 No Content`._ + +_Currently handled status codes by Server: `200`, `201`, `204`, `404`, `405`, `406`, `408`, `413`, `415`, `500` and `507`_ + + +--- + +## Constants +* HTTP_Server methods: `HTTP_Serveer.GET`, `HTTP_Server.POST`, `HTTP_Server.PUT`, `HTTP_Server.DELETE` +* HTTP_Server media types: + + `HTTP_Server.TEXT`: HyperText Markup Language (HTML): "text/html" + + `HTTP_Server.XML`: XML format: "text/xml" + + `HTTP_Server.PLAIN`: Text format: "text/plain" + + `HTTP_Server.JSON`: Json format: "application/json" + + `HTTP_Server.OCTET`: Binary data: "application/octet-stream" + + `HTTP_Server.APP_XML`: Json format: "application/xml diff --git a/content/tutorials/all/https.md b/content/tutorials/all/https.md index bf44611c..01f01bd5 100644 --- a/content/tutorials/all/https.md +++ b/content/tutorials/all/https.md @@ -6,6 +6,150 @@ aliases: - chapter/tutorials/all/https --- +At present, basic HTTP/S Server and Client functionality is available. More features will be implemented in the near future. This page will be updated in line with these features. + +Full info on HTTP/S Server/Client can be found within [HTTPS page](http://localhost:1313/firmwareapi/pycom/network/https/) page of the Firmware API Reference. + +## HTTP Server + +In the following example, an HTTP webserver will be created, using [`HTTP_Server`](http://localhost:1313/firmwareapi/pycom/network/https/server/) module. The color (`RED`, `GREEN`, `BLUE` or `OFF`) of the LED can be modified through a web or mobile client: + +```python +import pycom +from network import WLAN +from network import HTTP_Server + +# The callback that handles the responses generated from the requests sent to a HTTP/S Server +def server_callback(uri, method, headers, body, new_uri, status): + if(uri == '/buttons/red'): + pycom.rgbled(0x7f0000) + if (uri == '/buttons/green'): + pycom.rgbled(0x007f00) + if (uri == '/buttons/blue'): + pycom.rgbled(0x0000ff) + if (uri == '/buttons/off'): + pycom.rgbled(0x000000) + +# Connect to the network +wlan = WLAN(mode=WLAN.STA) +wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) +while not wlan.isconnected(): + pass +print(wlan.ifconfig()) + +# Initiate LED as turned off +pycom.heartbeat(0) +pycom.rgbled(0x000000) + +# Initilise an HTTP Server +HTTP_Server.init() + +# Define html values +main_html = 'Choose the Pycom RGB Color
' +main_html += '' +main_html += '' +main_html += '' +main_html += '' +redirect_html = '' + +# Create resources +button = HTTP_Server.add_resource('/buttons', value=main_html) +button_red = HTTP_Server.add_resource('/buttons/red', value=redirect_html) +button_green = HTTP_Server.add_resource('/buttons/green', value=redirect_html) +button_blue = HTTP_Server.add_resource('/buttons/blue', value=redirect_html) +button_off = HTTP_Server.add_resource('/buttons/off', value=redirect_html) + +# Register resource request handlers +button.register_request_handler(HTTP_Server.GET, callback=server_callback) +button_red.register_request_handler(HTTP_Server.GET, callback=server_callback) +button_green.register_request_handler(HTTP_Server.GET, callback=server_callback) +button_blue.register_request_handler(HTTP_Server.GET, callback=server_callback) +button_off.register_request_handler(HTTP_Server.GET, callback=server_callback) +``` + +## HTTPS Server + +To create an HTTPS (HTTP Secure or HTTP over TLS/SSL) Server using `HTTP_Server` module, a `keyfile` and a `certfile` must be generated, and placed into `/flash/cert/` folder on the device. The simplest is to create a self-signed certificate, which can be generated by using OpenSSL command line tool: + +``` +openssl req -newkey rsa:2048 -nodes -keyout prvtkey.pem -x509 -days 365 -out cacert.pem -subj "/CN=Pycom HTTPS Server" +``` + +To enable HTTPS capabilities, port must be set to `443`, `keyfile` and `certfile` paths must be added: + +```python +# Init HTTPS Server +HTTP_Server.init(port=443, keyfile='/flash/cert/prvtkey.pem', certfile='/flash/cert/cacert.pem') +``` + +After initiating an HTTPS Server, it will automatically handle the whole SSL handshake process, in case of a request is coming from a Client. From this point, there is no difference between the HTTP and HTTPS Server methods usage. + +## HTTP Client + +For testing the capabilities of [`HTTP_Client`](http://localhost:1313/firmwareapi/pycom/network/https/client/) module a great way is to use Request & Response Service of http://httpbin.org/. In the example below, a quick example demonstrates using of `GET`, `POST`, `PUT` and `DELETE` methods: + +```python +from network import WLAN +from network import HTTP_Client + +# The callback that handles the responses generated from the requests sent to a HTTP/S Server +def client_callback(status, headers, body): + print("Status Code: {}".format(status)) + for key, value in headers.items(): + print(key, ":", value) + print("Body: {}".format(body)) + +# Connect to the network +wlan = WLAN(mode=WLAN.STA) +wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) +while not wlan.isconnected(): + pass +print(wlan.ifconfig()) + +# Initialize HTTP Client +HTTP_Client.init("http://httpbin.org/get", callback=client_callback) +# Send request with GET method +HTTP_Client.send_request(method=HTTP_Client.GET) + +# Set POST +HTTP_Client.url("http://httpbin.org/post") +HTTP_Client.send_request(method=HTTP_Client.POST, body='post data') + +# Set PUT +HTTP_Client.url("http://httpbin.org/put") +HTTP_Client.send_request(method=HTTP_Client.PUT, body='put data') + +# Set DELETE +HTTP_Client.url("http://httpbin.org/delete") +HTTP_Client.send_request(method=HTTP_Client.DELETE) + +# Optionally deinit Client +HTTP_Client.deinit() +``` + +#### Basic authentication +Basic authentication is supported by Client after username and password are set. In this case `Authorization` request header is filled up properly with encoded username and password as value: +```python +# Initialize HTTP Client with user and pass +HTTP_Client.init("http://httpbin.org/basic-auth/http_user/http_pass", auth=('http_user', 'http_pass'), callback=client_handler) +# Send request +HTTP_Client.send_request() +``` + +## HTTPS Client +To create an HTTPS (HTTP Secure or HTTP over TLS/SSL) Client using `HTTP_Client` module, the only thing that needs to be done is to be set the correct `url` in init method starting with `https://`. From that point, the whole SSL handshake process will be handled automatically after a request send. Request & Response Service of https://httpbin.org/ can be used for testing. + +```python +# Initialize HTTP Client +HTTP_Client.init("http://httpbin.org/get", callback=client_callback) +``` + +```python +# Initialize HTTPS Client +HTTP_Client.init("https://httpbin.org/get", callback=client_callback) +``` + +## HTTP with socket Basic connection using `ssl.wrap_socket()`. ```python