# yhs **Repository Path**: studvc/yhs ## Basic Information - **Project Name**: yhs - **Description**: Embeddable HTTP server. - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-01-05 - **Last Updated**: 2021-01-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #+OPTIONS: toc:nil num:nil author:nil email:nil creator:nil timestamp:nil ^:nil #+TITLE: yocto HTTP server The 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