Http_helper
Table of Contents
Preface
Introduction Constants Instance Variables External Objects Public Spin Methods... Private Spin Methods...
get_request
Constant Strings get_status parse end_all _http _html_page newline flush parse_ip socket_reset socket_open socket_close License |
HTTP Helper: Easy W5100 Interface┌───────────────────────────────────────┐ │ HTTP Helper │ │ Author: Phil Pilgrim │ │(c) Copyright 2013 Bueno Systems, Inc. │ │ See end of file for terms of use. │ └───────────────────────────────────────┘ This object provides an easy-to-use wrapper for Parallax's W5100_Indirect_Driver. Revision History 2013.10.04: v1.0 Initial release. Contact Information Phil Pilgrim: propeller@phipi.com IntroductionThis object provides a wrapper for the W5100_Indirect_Driver as used on Parallax's Spinneret ethernet module. It enables the easy retrieval of client requests with their parsed-out parameters, along with formatting and buffering for the HTTP responses.ConstantsSOURCE CODE... BUF_SIZE = 512 LF = $0a CR = $0d QU = $22 #0, W5100_DATA0,W5100_DATA1,W5100_DATA2,W5100_DATA3,W5100_DATA4,W5100_DATA5,W5100_DATA6,W5100_DATA7 { }, W5100_ADDR0,W5100_ADDR1,W5100_WR ,W5100_RD ,W5100_CS ,W5100_INT ,W5100_RST ,W5100_SEN BUFFER_SIZE = 2048 Instance VariablesSOURCE CODE... byte macaddr[6], data[BUFFER_SIZE] byte buffer[BUF_SIZE] byte nparams word port, params[33], bufptr long gateway, subnet, ip, client, timeout External ObjectsSOURCE CODE... w5100 : "W5100_Indirect_Driver.spin" Public Spin MethodsStart and StopStart and stop the HTTP server.start(_ip, _gateway, _subnet, _mac)Start the HTTP server.Parameters:
_ip: pointer to a string containing the IPV4 address of the server
_gateway: pointer to a string containing the IPV4 address of the internet gateway
_subnet: pointer to a string containing the IPV4 subnet designator
_mac: MAC address of the server hardware
Return: port number, as contained in _ip or 80 (default)
Example: srv.start(string("192.168.0.50:3456"), string("192.168.0.4"), string("255.255.255.0"), string("00 08 DC 16 F0 13"))
Start the server at IP location 192.168.0.50 and port 3456.
Internet gateway is at 192.168.0.4.
Subnet is 255.255.255.0.
Hardware MAC address is 00 08 DC 16 F0 13.
Return value is 3456 (port number).
SOURCE CODE... PUB start(_ip, _gateway, _subnet, _mac) | i, digit, part stop port := 0 ip := parse_ip(_ip) ifnot (port) port := 80 subnet := parse_ip(_subnet) gateway := parse_ip(_gateway) client := 0 i~ repeat case digit := byte[_mac++] "0" .. "9" : digit -= "0" "A" .. "F" : digit += 10 - "A" "a" .. "f" : digit += 10 - "a" other : digit~~ if (digit => 0) part := part << 4 | digit if (i++ & 1) macaddr[(i - 1) >> 1] := part until i == 12 w5100.StartINDIRECT(W5100_DATA0, W5100_ADDR0, W5100_ADDR1, W5100_CS, W5100_RD, W5100_WR, W5100_RST, W5100_SEN) w5100.InitAddresses(true, @macaddr, @gateway, @subnet, @ip) socket_open timeout := clkfreq * 5 return port stopStop the HTTP server.Return: none
Example: srv.stop
Stop the HTTP server.
SOURCE CODE... PUB stop w5100.StopINDIRECT Request handlingProcess incoming client requests.echo_requestEcho the client request as a plain text page.Return: none
Example: srv.echo_request
Wait for a request from the client and return it as a plain page.
SOURCE CODE... PUB echo_request | req repeat until get_request begin_plain_page str(@data) end_plain_page requestWait for a request, parse the address line, and return a pointer to the parsed address (sans ? arguments).Return: pointer to the parsed address.
Example: req := srv.request
Wait for a client request, parse it, and return a pointer to the address portion.
SOURCE CODE... PUB request repeat until get_request return parse(@data) request0Poll for a request. If one has come in, parse the address line, and return a pointer to the parsed address (sans ? arguments). If no request, return zero.Return: pointer to the parsed address, or zero if no request.
Example: if (req := srv.request0)
Assign the parsed address pointer to req. If there was a request, proceed with the if statement's consequent.
SOURCE CODE... PUB request0 if (get_request) return parse(@data) else return false param(straddr)Return the value of a chosen parameter from a processed GET request.Parameters:
straddr: pointer to a string containing the parameter name.
Return: pointer to a string containing the value of the parameter.
Example: value_addr := srv.param(string("name"))
Return a pointer to a sting containing the value of the name parameter in the request.
For example, if the request line were /index?name=Sam&phone=555-1234, the call would return
the address of a string containing "Sam".
SOURCE CODE... PUB param(straddr) | i repeat i from 1 to nparams - 1 step 2 if strcomp(straddr, params[i]) return params[i + 1] return 0 Page sendingSend response pages in various formats.begin_plain_pageBegin a plain text page, i.e. buffer the HTTP header for a text/plain response.Return: none
Example: srv.begin_plain_page
Begin a plain text page. Content is expected to follow.
SOURCE CODE... PUB begin_plain_page _http str(@plain_http) begin_custom_contentBegin a page with custom (non-HTML, non-text/plain) content.Return: none
Example: srv.begin_custom_content
Buffer a simple two-line HTTP header, including "200 OK". "Content-type" header line
and actual content is expected to follow.
SOURCE CODE... PUB begin_custom_content _http begin_plain_html_page(title, refresh, background)Start an HTML page as plain text. (Useful for debugging.)Parameters:
title: Page title.
refresh: Time is seconds for automatic refresh, or zero for no refresh.
background: Pointer to a string containing the page background color, or zero for default white.
Return: none
Example: srv.begin_plain_html_page(string("My Page"), 2, string("red"))
Start an HTML page as plain text which would otherwise have the title "My Page", refresh
every two seconds, and have a red background.
SOURCE CODE... PUB begin_plain_html_page(title, refresh, background) _http str(@plain_http) _html_page(title, refresh, background) begin_html_page(title, refresh, background)Start an HTML page, including the <html> and <body> tags.Parameters:
title: Page title.
refresh: Time is seconds for automatic refresh, or zero for no refresh.
background: Pointer to a string containing the page background color, or zero for default white.
Return: none
Example: srv.begin_html_page(string("My Page"), 0, string("#ffff00"))
Start an HTML page having the title "My Page", with no automatic refresh, and with a yellow background.
SOURCE CODE... PUB begin_html_page(title, refresh, background) _http str(@html_http) _html_page(title, refresh, background) not_foundSend a simple, plain-text "404 Not Found" page.Return: none
Example: srv.not_found
Send a "404 Not Found" page.
SOURCE CODE... PUB not_found str(@error_404) end_all end_plain_pageEnd and send a plain text page after the content has been buffered.Return: none
Example: srv.end_plain_page
End and send the buffered plain text page.
SOURCE CODE... PUB end_plain_page end_all end_custom_contentEnd and send custom content after the content has been buffered.Return: none
Example: srv.end_custom_content
End and send the buffered custom content.
SOURCE CODE... PUB end_custom_content end_all end_html_pageEnd and send an HTML page after the content has been buffered. Includes the </body> and </html> tags.Return: What gets returned, or 'none'.
Example: srv.end_html_page
End and send the buffered HTML page.
SOURCE CODE... PUB end_html_page newline str(string(" </body>", 10, "</html>", 10)) end_all str(straddr)Buffer for sending to the client the zero-terminated string pointed to by the parameter.Parameters:
straddr: pointer to a zero-terminated string of data to send to the client.
Return: none
Example: srv.str(@index_page)
Buffer the string located at address @index_page.
SOURCE CODE... PUB str(straddr) array(straddr, strsize(straddr)) outnz(char)Buffer a single character for sending to the client, but only if it's non-zero.Parameters:
char: the character to send
Return: none
Example: srv.outnz(next_char)
Buffer the character next_char unless next_char == 0.
SOURCE CODE... PUB outnz(char) if (char) out(char) out(char)Buffer a single character to send to the client.Parameters:
char: the character to send
Return: none
Example: srv.out(srv#LF)
Buffer a linefeed ($0a).
SOURCE CODE... PUB out(char) array(@char, 1) array(addr, count)Buffer an entire byte array of characters to send to the client.Parameters:
addr: address of the array's first element.
count: the number of bytes to buffer, beginning at addr.
Return: none
Example: srv.array(@data, 16)
Buffer 16 bytes of data beginning at address @data.
SOURCE CODE... PUB array(addr, count) if (bufptr + count > BUF_SIZE) flush if (bufptr + count > BUF_SIZE) w5100.txTCP(0, addr, count) else bytemove(@buffer + bufptr, addr, count) bufptr += count Private Spin Methodsget_requestCheck status and retrieve a request if one is available. REturn the packet size, or zero for no request.SOURCE CODE... PRI get_request : packet_size | time ifnot w5100.SocketTCPestablished(0) return ifnot (lookdown(get_status : $14, $16, $17, $11)) socket_reset return time := cnt repeat until (cnt - time > timeout) if (packet_size := W5100.rxTCP(0, @data)) data[packet_size]~ return socket_reset return get_statusReturn the current server status.Return: What gets returned, or 'none'.
Example: Sample method call.
Explain what the sample call does.
Next lines, if needed...
SOURCE CODE... PRI get_status : status w5100.readIND(w5100#_S0_SR, @status, 1) parse(dataddr)Parse the request line, replacing quoted characters with their ASCII equivalants and separating out GET parameter into their key/value pairs.SOURCE CODE... PRI parse(dataddr) | char, i, getptr, putptr, varend nparams~ repeat while ((char := byte[dataddr++]) and char <> LF and char <> CR) if(lookdown(char : " ", "?", "&", "=")) byte[dataddr - 1]~ params[nparams++] := dataddr repeat i from 0 to nparams - 1 getptr := putptr := params[i] varend := getptr + strsize(getptr) repeat while getptr < varend case char := byte[getptr++] "+": byte[putptr++] := " " "%": repeat 2 char := char << 4 | lookdownz(byte[getptr++] : "0" .. "9", "A" .. "F") byte[putptr++] := char other: byte[putptr++] := char byte[putptr]~ return params[0] end_allFlush the buffer into the server, send the TCP packet, close the socket, then reopen it.SOURCE CODE... PRI end_all flush w5100.txTCP(0, 0, 0) socket_close socket_open _httpBuffer the beginning HTTP header.SOURCE CODE... PRI _http bufptr~ str(@http) _html_page(title, refresh, background)Buffer the HTML page header.SOURCE CODE... PRI _html_page(title, refresh, background) str(@html_head) if (title) str(string(LF, " <title>")) str(title) str(string("</title>")) if (refresh) str(string(LF, " <meta http-equiv=",QU,"refresh",QU," content=",QU)) str(refresh) str(string(QU,">")) str(@html_body) if (background) str(string(" style=",QU,"background-color: ")) str(background) out(QU) str(string(">")) newline newlineBuffer a linefeed.SOURCE CODE... PRI newline out(LF) flushFlush the local buffer into the W5100 server.SOURCE CODE... PRI flush if (bufptr) w5100.txTCP(0, @buffer, bufptr) bufptr~ parse_ip(_ip)Parse a stringified IP address into its 32-bit long equivalent.SOURCE CODE... PRI parse_ip(_ip) : retip | part, digit, char part~ repeat if ((char := byte[_ip++]) => "0" and char =< "9") part := part * 10 + char - "0" else retip := retip >> 8 | (part <# 255) << 24 part~ if (char == 0 or char == ":") quit if (char == ":") repeat while (byte[_ip] => "0" and byte[_ip] =< "9") port := port * 10 + byte[_ip++] - "0" socket_resetClose, then open the socket.SOURCE CODE... PRI socket_reset socket_close socket_open socket_openOpen a socket for listening.SOURCE CODE... PRI socket_open w5100.SocketOpen(0, W5100#_TCPPROTO, port, port, @client) waitcnt(clkfreq / 4 + cnt) w5100.SocketTCPlisten(0) waitcnt(clkfreq / 4 + cnt) socket_closeClose the open socket.SOURCE CODE... PRI socket_close w5100.SocketTCPdisconnect(0) waitcnt(clkfreq / 40 + cnt) w5100.SocketClose(0) Constant StringsSOURCE CODE... http byte "HTTP/1.1 200 OK",10 'Initial HTTP header for successful request. byte "Connection: close",10 byte 0 error_404 byte "HTTP/1.1 404 Not Found",10 'HTTP header for "content not found." byte "Connection: close",10 byte "Content-type: text/plain",10,10 byte "404 Not Found." byte 0 plain_http byte "Content-type: text/plain",10,10 'HTTP headr for plain text. byte 0 html_http byte "Content-type: text/html",10,10 'HTTP header for HTML. byte 0 html_head byte "<html xmlns=",QU,"http://www.w3.org/1999/xhtml",QU," dir=",QU,"ltr",QU," lang=",QU,"en",QU,">",10 'Beginning of HTML page. byte " <head>",10 byte " <meta http-equiv=",QU,"Content-Type",QU," content=",QU,"text/html; charset=ISO-8859-1",QU," />" byte 0 html_body byte 10," </head>",10 'Beginning of HTML body. byte " <body " byte 0 License┌──────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────┤ │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. │ └──────────────────────────────────────────────────────────────────────────────────────┘ |