114 lines
4.5 KiB
Plaintext
114 lines
4.5 KiB
Plaintext
in libowfat, every descriptor has a state associated with it, and it
|
|
contains a cookie that is set by gatling. That cookie is a pointer to a
|
|
state gatling uses for the connection. The libowfat state is invisible
|
|
from gatling; the gatling state is called struct http_data and is
|
|
defined in gatling.h.
|
|
|
|
Its most important fields are:
|
|
|
|
t (the state for gatling's statemachine, type: enum conntype)
|
|
r (a buffer that holds the incoming request)
|
|
iob (an iob that holds the outgoing data)
|
|
buddy (see below)
|
|
|
|
r is an array (variable length buffer type defined in libowfat)
|
|
iob is a structure from libowfat that holds a linked list of buffers
|
|
and/or file descriptors that are supposed to be sent out over a file
|
|
descriptor. You can use iob_send to send it directly, or iob_write to
|
|
send it via a write() callback; gatling uses this to send data with SSL
|
|
encryption.
|
|
|
|
For some requests, more than one connection is needed. FTP has a
|
|
control and a data connection. For these, the descriptor of the other
|
|
connection is stored in the buddy member of the http_state.
|
|
|
|
Here is the state flow for typical requests:
|
|
|
|
HTTP:
|
|
# main()
|
|
- starts out as HTTPSERVER6 or HTTPSERVER4 (IPv4 vs IPv6).
|
|
- gets a read event if a connection comes in.
|
|
# accept_server_connection()
|
|
- calls accept() and makes a new context for that connection,
|
|
sets its type to HTTPREQUEST
|
|
|
|
# main()
|
|
- gets a read event if data comes in. Reads until it sees a \r\n\r\n
|
|
or the size limit is reached (8192 bytes currently)
|
|
# read events are handled in main() - search for io_canread()
|
|
# handle_read_misc()
|
|
# the \r\n\r\n detection is in header_complete()
|
|
# httpresponse()
|
|
# http_openfile()
|
|
- constructs http response header, adds it with iob_addbuf_free
|
|
- adds file content with iob_addfile
|
|
- uses io_dontwantread and io_wantwrite to switch state machine to writing
|
|
|
|
# main()
|
|
- gets a write event
|
|
# write events are handled in main() - search for io_canwrite()
|
|
# handle_write_misc()
|
|
- calls iob_send() (or iob_write() for SSL mode)
|
|
- repeat from "gets a write event" until all data was written
|
|
- if client indicated HTTP keep-alive, switch back to HTTPREQUEST and
|
|
read mode
|
|
otherwise close connection with cleanup()
|
|
|
|
|
|
|
|
HTTP with CGI:
|
|
- same as plain HTTP until inside http_openfile().
|
|
# proxy_connection
|
|
- check for the ".proxy" file and go through the list of regexes set
|
|
on command line with -C and -O to see if the request is a CGI or
|
|
proxy. Same code used for both.
|
|
|
|
Now, in addition to the connection from browser to gatling, we need a
|
|
second connection from gatling to the proxy server / CGI. For proxy
|
|
mode, we just call socket(), but for CGI we use a "fork slave". gatling
|
|
forks right at the start if CGI mode is enabled, and if we want to
|
|
create a CGI process, we do not create it ourselves (to save the
|
|
overhead of having to close all the open descriptors) but we send a
|
|
message to the "fork slave" we created at the beginning. That process
|
|
then forks and execs the CGI and returns the descriptor to the CGI to us.
|
|
As part of this message, we also pass the HTTP request header to
|
|
the fork slave, which writes it to the CGI.
|
|
|
|
# still in proxy_connection
|
|
- create a new context for the connection to the CGI
|
|
- change state of http connection from HTTPREQUEST to HTTPPOST
|
|
- set state of cgi connection to PROXYPOST
|
|
|
|
if request is a POST:
|
|
- if some POST data came in with the http request:
|
|
- put POST data into cgi_connection->r
|
|
- set cgi_connection->still_to_copy to Content-Length
|
|
- set http_connection->still_to_copy to Content-Length - sizeof(already copied data)
|
|
- io_dontwantread(http_connection), io_wantwrite(cgi_connection)
|
|
|
|
# main()
|
|
- receive write event on the cgi connection (PROXYPOST)
|
|
# handle_write_proxypost
|
|
- write data from buddy->r to socket, deduct from still_to_copy
|
|
|
|
|
|
if request is a GET:
|
|
- io_dontwantread(http_connection), io_wantread(cgi_connection)
|
|
|
|
writemoredata:
|
|
# main()
|
|
- receive read event on the cgi connection (PROXYPOST)
|
|
# handle_read_proxypost()
|
|
# proxy_is_readable()
|
|
- read data from cgi connection into stack buffer
|
|
- malloc a buffer for response header plus buffer contents
|
|
- iob_addbuf_free it to http_connection->iob
|
|
- io_dontwantread(cgi_connection), io_wantwrite(http_connection)
|
|
|
|
# main()
|
|
- receive write event on the http connection (HTTPPOST)
|
|
# handle_write_httppost()
|
|
- iob_write/iob_send data to http connection
|
|
- if not done: io_dontwantread(http_connection), io_wantread(cgi_connection), goto writemoredata;
|
|
|