We welcome third party contributions H2O. The most straightforward way to do so, is to fork the project and submit a PR. If the change you're proposing is substantial, it might be a good idea to open an issue in the issue tracker in order to discuss it first.
By submitting a pull request, you agree to license the submitted code under the MIT License. If you do not own the copyright of the code that is being submitted, please clarify the name of the copyright holder and the license under which the copyrighted material can be used so that we can review if it could be incorporated as part of the H2O project.
H2O is built on a multitude of platforms: *BSD, Mac OS, Solaris and Linux. It uses a dialect of C close to c89 and c99 intended to compile on most gcc functions, and we avoid GNU extensions.
PRs are automatically built in Travis, and a test suite is run over the code. When possible, please add test coverage to the code that you're submitting.
H2O uses clang-format
in order to maintain a uniform coding style on the code base, running
the tool before submitting might make the review process smoother. The
formatting command can be invoked over all the repository by the following
command: make -f misc/regen.mk clang-format-all
To summarize, the coding style resembles the Linux kernel's with the difference that it uses 4 spaces rather than one tab for indentation.
Here's a .vimrc
snippet that would use this style:
set tabstop=4
set shiftwidth=4
set softtabstop=4
set expandtab
Structs are prefixed with st_
and suffixed with _t
. Structs that are
found in public headers are typedef'ed and prefixed with h2o_
, and
suffixed with _t
:
/* public struct */
typedef struct st_h2o_conn_t h2o_conn_t;
struct st_h2o_conn_t {
....
};
/* private struct */
struct st_on_client_hello_ptls_t {
...
};
As with structs, publicly visible functions are prefixed with h2o_
,
such as h2o_process_request
, whereas private functions don't use one:
get_ocsp_response
.
Goto labels use upper case, and are indented with the matching code:
void fn(const char *err)
{
if (global) {
if (err == NULL)
goto Ok;
Ok:
this_is_ok();
}
}
The H2O code base tends to use subclassing in order to pass contextual
information. For example, both h2o_http2_conn_t
and struct st_h2o_http1_conn_t
are subclasses of h2o_conn_t
. They do so by
defining a super
member of the struct of type h2o_conn_t
, and
returning that outside their respective modules. The struct is then cast
back to the specilized subclasses when returning to their modules.
This is in turn used so that both the HTTP/1 and HTTP/2 connections are
able to expose a common interface for get_sockname
, for example:
/* http2 */
static socklen_t get_sockname(h2o_conn_t *_conn, struct sockaddr *sa)
{
h2o_http2_conn_t *conn = (void *)_conn;
...
}
/* http1 */
static socklen_t get_sockname(h2o_conn_t *_conn, struct sockaddr *sa)
{
struct st_h2o_http1_conn_t *conn = (void *)_conn;
...
}
This way upper layers can act on an h2o_conn_t
without exposing the
details of h2o_http2_conn_t
or struct st_h2o_http1_conn_t
.
Another technique commonly used in the H2O code base is offset
calculations. This allows to better performance and reduced public interfaces, while
still being able to pass objects of different types. H2O
uses the H2O_STRUCT_FROM_MEMBER
macro (which in turn uses
offsetof
) in order to
compute the offset of a member in a struct. This way, given a pointer to
a member, we can obtain a pointer to the enclosing struct.
Linked lists (h2o_linklist_t
) and timers (h2o_timeout_entry_t
)
are typical users of the technique.
Here's an example demonstrating how to pass context alongside a timer pointer:
struct st_mycontext_t {
int nr_timer_hits;
h2o_timeout_entry_t timer;
};
void timer(h2o_timeout_entry_t *t)
{
struct st_mycontext_t *mc = H2O_STRUCT_FROM_MEMBER(struct st_mycontext_t, timer, t);
mc->nr_timer_hits++;
}
void f(struct st_mycontext_t *mc)
{
mc->timer.cb = timer;
mc->nr_timer_hits = 0;
h2o_timeout_link(loop, io_timeout, &mc->timer);
}
H2O uses two main facilities for tests: unit tests in C and integration tests written mostly in perl.
Unit tests are found under t/00unit
directory, and use picotest
as a testing framework.
Integration tests are found under the t/
directory, and are any file
ending with .t
extension. The test suite uses Test::More
as a testing
framework. t/Util.pm
offers facilities like running curl for all
supported protocols, spawning H2O or instantiating a backend server using
Plackup.
The easiest way of running the tests is to use the pre-built Docker image. The following command pulls the image from Docker Hub, and runs the tests using the h2o source repository in the current directory.
make -f misc/docker-ci/check.mk pull all
Both tests can be run with make check
. Some tests require to be run as root, and can be run with make check-as-root
. You might need dependencies such as curl or nghttp2, or
some perl modules. You can refer to the misc/docker-ci/Dockerfile
file for an example of how to install the dependencies.
Single tests can be run manually by running perl t/<test>.t
, in which
case, you might have to set the environment variable H2O_ROOT
to the
root of the H2O repository since some tests rely on it, and also
BINARY_DIR
to the binary directory if you are building them off-the-source.
H2O is part of Google's OSS-Fuzz project, and as such H2O is continuously
fuzzed. Fuzzers are build when cmake
is passed the -DBUILD_FUZZER=ON
flag, they use libFuzzer. Anything
that parses input from the network is a good candidate for fuzzing.
The preferred way of importing third-party code is to store them in dedicated directories under deps/
.
In case you plan to merge them into master you should use the helper script misc/dump-github-repository.pl
to check out a repository. Else you can simply copy the files or check them out as git submodule.
See also https://github.com/h2o/h2o/wiki/Importing-third-party-code
For adding mruby modules their directory name should start with mruby-
.
All modules that match Dir.glob("../mruby-*/mrbgem.rake")
then get automatically linked.
The H2O repository contains source docs in the form of .mt
template
files, as well the generated docs. The source docs can be found under
srcdoc/
and the generated ones can be found under doc/
.
The .mt
files, use a templating DSL built on top of
Text::MicroTemplate
The DSL defines methods such as code
, example
, directive
which
are used to write the documentation. All those directives are defined in
makedoc.pl
.
Docs are generated by running make
in the doc/
directory.