Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/tom-seddon/yhs
Embeddable HTTP server.
https://github.com/tom-seddon/yhs
Last synced: 28 days ago
JSON representation
Embeddable HTTP server.
- Host: GitHub
- URL: https://github.com/tom-seddon/yhs
- Owner: tom-seddon
- Created: 2012-06-04T01:41:17.000Z (over 12 years ago)
- Default Branch: master
- Last Pushed: 2018-05-07T21:10:05.000Z (over 6 years ago)
- Last Synced: 2024-05-02T19:22:02.272Z (7 months ago)
- Language: C
- Size: 445 KB
- Stars: 80
- Watchers: 4
- Forks: 13
- Open Issues: 2
-
Metadata Files:
- Readme: README.org
Awesome Lists containing this project
- AwesomeCppGameDev - yhs
README
#+OPTIONS: toc:nil num:nil author:nil email:nil creator:nil timestamp:nil ^:nil
#+TITLE: yocto HTTP serverThe yocto HTTP server is a small embeddable web server, with a
convenient public domain licence. Use it to add a web server to your
program for debugging, introspection or remote control.You specify the paths of "files" and "folders" you want to make
available, and callbacks to be called when they are requested, and the
yocto HTTP server handles the rest. When your callback is called, you
can use convenient stdio-style functions to send text or binary data
to the browser, or transmit image data.Of course, if you just want some files serving from folders on disk,
the yocto HTTP server will do that.Also, WebSockets.
The yocto HTTP server has been written for ease of embedding and ease
of use, under the assumption that it will be used as a development and
debugging aid. Security and performance were not design goals.* Installation
iOS, Mac OS X and Windows (VC++) are supported. It can be built as
C89, C99 or C++.1. Add =yhs.c= and =yhs.h= to your project;
2. Include =yhs.h= in files that need it;
3. Add function calls as described below;
4. Add =#ifdef= (etc.) to make very sure you won't ship with it
running;5. PROFIT.
* Use
This file provides a conversational overview. Please consult the
header file as well.** Start, update and stop server
A particular server (serving a particular tree of "files" on a
particular port) is represented by a =yhsServer= pointer:: yhsServer *server;
Create one using =yhs_new_server=, supplying port:
: server = yhs_new_server(80);
You can name your server, if you like. Its name will appear in any
error pages.: yhs_set_server_name(server,"my amazing server");
Each time round your main loop, call =yhs_update= to keep the server
ticking over:: yhs_update(server);
When you're done, call =yhs_delete_server= to free up the server and
its resources:: yhs_delete_server(server);
: server=NULL;** Adding things to serve
Use =yhs_add_res_path_handler= to add a callback (see below) for a
particular path:: yhs_add_res_path_handler(server,"/res/",&handle_root,NULL);
The argument for =context= is stored and made available to the
callback.Paths not ending in =/= are considered files, and their callback will
be called when a request is made for that exact path.Paths ending in =/= are considered folders, meaning the callback will
be called for any file in that folder (at whatever depth), if there
isn't a closer-matching folder or file handler for it.If there's no handler added for the root folder =/=, =GET= requests
for =/= will be responded to automatically with a contents page.The server will respond to any other unhandled path with a 404 page.
** Serving things
The handler callback has the following signature:
: extern "C" typedef void (*yhsResPathHandlerFn)(yhsRequest *re);
=re= points to the (opaque) request object. There are various
functions to get details about the request:- =yhs_get_path= retrieves the specified path, and
=yhs_get_path_handler_relative= retrieves the part that's relative
to the path supplied to =yhs_add_res_path_handler=.- =yhs_get_method= and =yhs_get_method_str= retrieve the HTTP
method. =yhs_get_method= returns one of the values from the (not
exhaustive) =yhsMethod= enum, and =yhs_get_method_str= returns the
actual method name string.- =yhs_find_header_field= allows the request header fields to be
queried.You can also use =yhs_get_handler_context= and =yhs_get_handler_path=
to retrieve the values supplied to =yhs_add_res_path_handler=.On entry to the callback, any content is available for reading (if you
want it), and the server is ready for your callback to provide a
response, as described below.Once you have sent the response, just return from the callback and
appropriate action will be taken automatically. If your callback
doesn't provide any response, the server will automatically provide a
404 page.(You can respond to a =HEAD= request in exactly the same way as a
=GET= request. The server checks for =HEAD= specially, and will
discard any response body in that case, leaving just the headers.)*** Data response
Use =yhs_begin_data_response= to start a data response, supplying MIME type
of data being sent:: yhs_begin_data_response(re,"text/html");
Then use =yhs_text= (works like =printf=) to send raw text:
: yhs_text(re,"Hello
%d
",rand());Also available are =yhs_textv= (works like =vprintf=), =yhs_text=
(works like =fputs=), =yhs_data= (works a bit like =fwrite=), and
=yhs_data_byte= (works a bit like =fputc=).If you're responding with HTML, there are a set of convenience
functions, =yhs_html_text*=, which can add in HTML escapes and
optionally replace =\n= with =
=.: yhs_html_text(re,YHS_HEF_BR,random_text);
These functions perform a bit of buffering, so don't be afraid to
write single bytes or chars.Between calling =yhs_begin_data_response= and =yhs_text= (or similar), you
can add extra HTTP header fields to the response using
=yhs_header_field=:: yhs_header_field(re,"X-Powered-By","C");
(=yhs_begin_data_response= will already have added an appropriate
=Content-Type= field.)*** Image response
Use =yhs_begin_image_response= to start an image response. Supply width,
height and bytes per pixel of image:: yhs_begin_image_response(re,256,256,3);
Then for each pixel -- and you must supply every pixel -- call
=yhs_pixel= to specify red, green, blue and alpha:: for(int y=0;y<256;++y) {
: for(int x=0;x<256;++x)
: yhs_pixel(re,rand()&255,rand()&255,rand()&255,255);
: }Do please note that the PNGs are not compressed.
Between calling =yhs_begin_image_response= and =yhs_text= (or similar), you
can add extra HTTP header fields to the response using
=yhs_header_field=:: yhs_header_field(re,"X-Powered-By","C");
(=yhs_begin_image_response= will already have added an appropriate
=Content-Type= field.)*** Error response
Call =yhs_error_response= to generate an HTTP error page. Provide
the HTTP status line, e.g., "200 OK".*** 303 See Other response
Use =yhs_see_other_response= to direct the browser to =GET= a
different URL.*** Serving a tree of files
The server is primarily designed for serving data using the callbacks,
but you can use the supplied =yhs_file_server_handler= handler to
supply a tree of local files. You might use this for icons, say, or
Javascript.When adding the file server handler, supply the local path as the
context pointer:: yhs_add_res_path_handler(server,"/resources/",&yhs_file_server_handler,(void *)"./web_resources/");
If a folder is requested rather than a file, the server will respond
with a simple files listing page.** Deferred responses
You may want to put off responding to a request, if it can't be
conveniently responded to in the middle of the server update. You can
call =yhs_defer_response= to do this.Requests with deferred responses are held in a list, so you can work
through them later. You can maintain one list of all such requests, or
have multiple lists.Each list is represented by a =yhsRequest *=, holding a pointer to the
head. It should start out NULL.: yhsRequest *list=NULL;
To defer a response, pass the request you're dealing with, and a
pointer to the list head pointer:: yhs_defer_response(re,&list);
This allocates a copy of the current request, adds it to the list, and
invalidates =*re=. (=yhs_defer_response= may fail and return 0, if the
allocation fails; in that case, the list will be unchanged, and the
server will end up producing a 404. So most of the time, you probably
won't need to check.)Then later, work through the list and make progress with each response
using the functions above. Then, to advance your current item pointer
to the next request in the list, use =yhs_next_request_ptr= to leave
the response in progress or =yhs_end_deferred_response= to finish it
up and remove it from the list.The expected code is along these lines:
: yhsRequest **cur=&list;
: while(*cur) {
: /* do stuff to **cur */
: if(/* finished with **cur */)
: yhs_end_deferred_response(cur);
: else
: yhs_next_request_ptr(cur);
: }** Content
If the request has content associated with it, use =yhs_get_content=
to retrieve it. Check for associated content by looking for the
=Content-Length= header field by hand, or use
=yhs_get_content_details= to do the check. =yhs_get_content_details=
will retrieve =Content-Length= as an =int=, and find any
=Content-Type= field supplied too.You can retrieve the content all in one go, or in parts.
** Forms
Helpers are provided for processing data from =POST= method forms in
=application/x-www-form-urlencoded= format. (=GET= forms, and
=multipart/form-data=, are not specifically catered for.)In the handler, use =yhs_read_form_content=:
: int is_form_data_ok=yhs_read_form_content(re);
: if(!is_form_data_ok) {
: /* error (probably unlikely) */
: return;
: }This allocates some memory to save off the form data. This memory is
freed automatically when the response finishes.You can (try to) retrieve a control's value by control name, using
=yhs_find_control_value=:: const char *value=yhs_find_control_value(re,"value name");
The result is =NULL= if the value doesn't exist.
You can also iterate through all the names and values available:
: for(size_t i=0;i