Your opinion will help to improve our service
Leave a feedback >Make sure that you can compile and run core tests as described at Configure, Build and Install.
This sample provides some basic information on how to configure the service and how to setup testing. If you are eager to prototype and experiment, consider the Easy - library for single file prototyping instead.
Typical HTTP server application in userver consists of the following parts:
int main()
- startup code.Let's write a simple server that responds with
"Hello, unknown user!\n"
on every request to /hello
URL without name
argument;"Hello, <name of the user>!\n"
on every request to /hello
URL with ?name=<name of the user>
.This sample also contains information on how to add unit tests, benchmarks and functional tests.
Our application logic is straightforward:
The "say_hello.hpp" contains a signe function declaration, so that the implementation details are hidden and the header is lightweight to include:
HTTP handlers must derive from server::handlers::HttpHandlerBase
and have a name, that is obtainable at compile time via kName
variable and is obtainable at runtime via HandlerName()
.
The primary functionality of the handler should be located in HandleRequest
function. Return value of this function is the HTTP response body. If an exception exc
derived from server::handlers::CustomHandlerException
is thrown from the function then the HTTP response code will be set to exc.GetCode()
and exc.GetExternalErrorBody()
would be used for HTTP response body. Otherwise if an exception exc
derived from std::exception
is thrown from the function then the HTTP response code will be set to 500
.
Handle*
functions are invoked concurrently on the same instance of the handler class. Use synchronization primitives or do not modify shared data in Handle*
.Now we have to configure the service by providing task_processors
and default_task_processor
options for the components::ManagerControllerComponent and configuring each component in components
section:
components_manager:
task_processors: # Task processor is an executor for coroutine tasks
main-task-processor: # Make a task processor for CPU-bound coroutine tasks.
worker_threads: 4 # Process tasks in 4 threads.
fs-task-processor: # Make a separate task processor for filesystem bound tasks.
worker_threads: 1
default_task_processor: main-task-processor # Task processor in which components start.
components: # Configuring components that were registered via component_list
server:
listener: # configuring the main listening socket...
port: 8080 # ...to listen on this port and...
task_processor: main-task-processor # ...process incoming requests on this task processor.
logging:
fs-task-processor: fs-task-processor
loggers:
default:
file_path: '@stderr'
level: debug
overflow_behavior: discard # Drop logs if the system is too busy to write them down.
handler-hello-sample: # Finally! Our handler.
path: /hello # Registering handler by URL '/hello'.
method: GET,POST # It will only reply to GET (HEAD) and POST requests.
task_processor: main-task-processor # Run it on CPU bound task processor
Note that all the components and handlers have their static options additionally described in docs.
Finally, we add our component to the components::MinimalServerComponentList()
, and start the server with static configuration file passed from command line.
You can either pass argc, argv to utils::DaemonRun()
to parse config yaml and config vars filepaths from arguments, or you may use embedded config file.
Sometimes it is handy to embed file(s) content into the binary to avoid additional filesystem reads. You may use it with userver_embed_file()
cmake function. It generates cmake target which can be linked into your executable target.
Cmake part looks like the following:
C++ part looks simple - include the generated header and use utils::FindResource()
function to get the embedded file contents:
The build scripts consist of the following parts:
OBJECTS
target with built sources that are used across unit tests, benchmarks and the service itself: test
directory as a directory with tests for testsuite: To build the sample, execute the following build steps at the userver root directory:
The sample could be started by running make start-userver-samples-hello_service
. The command would invoke testsuite start target that sets proper paths in the configuration files and starts the service.
To start the service manually run ./samples/hello_service/userver-samples-hello_service -c </path/to/static_config.yaml>
.
static_config.yaml
and file from samples
directory into build directory.Now you can send a request to your server from another terminal:
$ curl 127.0.0.1:8080/hello Hello, unknown user!
Unit tests could be implemented with one of UTEST macros in the following way:
Functional tests for the service could be implemented using the service_client fixture from pytest_userver.plugins.core in the following way:
Do not forget to add the plugin in conftest.py:
See the full example at: