mirror of
https://github.com/lxsang/ant-http
synced 2025-07-27 11:09:48 +02:00
add h2 implement
This commit is contained in:
@ -23,6 +23,7 @@ THE SOFTWARE.
|
||||
*/
|
||||
#include "dictionary.h"
|
||||
#include "utils.h"
|
||||
#include "list.h"
|
||||
|
||||
dictionary_t dict()
|
||||
{
|
||||
@ -66,8 +67,13 @@ chain_t __put_el_with_key(dictionary_t dic, const char* key)
|
||||
if(dic->map == NULL) return NULL;
|
||||
if ((np = dlookup(dic,key)) == NULL) { /* not found */
|
||||
np = (chain_t) malloc(sizeof(*np));
|
||||
if (np == NULL || (np->key = strdup(key)) == NULL)
|
||||
if (np == NULL)
|
||||
return NULL;
|
||||
if((np->key = strdup(key)) == NULL)
|
||||
{
|
||||
free(np);
|
||||
return NULL;
|
||||
}
|
||||
np->value = NULL;
|
||||
hashval = hash(key, dic->cap);
|
||||
np->next = dic->map[hashval];
|
||||
@ -77,18 +83,43 @@ chain_t __put_el_with_key(dictionary_t dic, const char* key)
|
||||
// found
|
||||
return np;
|
||||
}
|
||||
chain_t dput(dictionary_t dic,const char* key, void* value)
|
||||
|
||||
static void free_ditem_value(void* value, antd_dict_item_type_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ANTD_DI_HEAP:
|
||||
if(value)
|
||||
free(value);
|
||||
break;
|
||||
case ANTD_DI_LIST:
|
||||
if(value)
|
||||
list_free((list_t*)&value);
|
||||
break;
|
||||
case ANTD_DI_DIC:
|
||||
if(value)
|
||||
freedict(value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
chain_t insert(dictionary_t dic,const char* key, void* value, antd_dict_item_type_t type)
|
||||
{
|
||||
chain_t np = __put_el_with_key(dic,key);
|
||||
if(np == NULL)
|
||||
{
|
||||
if(value) free(value);
|
||||
free_ditem_value(value, type);
|
||||
return NULL;
|
||||
}
|
||||
if(np->value && value) free(np->value);
|
||||
if(np->value && value) free_ditem_value(np->value, np->type);
|
||||
np->type = type;
|
||||
np->value = value;
|
||||
return np;
|
||||
}
|
||||
|
||||
|
||||
|
||||
chain_t dremove(dictionary_t dic, const char* key)
|
||||
{
|
||||
if(dic->map == NULL) return 0;
|
||||
@ -131,7 +162,7 @@ void free_association(chain_t * asoc)
|
||||
if(a->key)
|
||||
{
|
||||
free(a->key);
|
||||
if(a->value) free(a->value);
|
||||
if(a->value) free_ditem_value(a->value, a->type);
|
||||
}
|
||||
free(a);
|
||||
}
|
||||
|
@ -25,18 +25,24 @@ THE SOFTWARE.
|
||||
#define DICTIONARY_H
|
||||
|
||||
#define DHASHSIZE 16
|
||||
#define dput(d,k,v) (insert(d,k,v,ANTD_DI_HEAP))
|
||||
#define dput_static(d,k,v) (insert(d,k,v,ANTD_DI_STATIC))
|
||||
#define dput_list(d,k,v) (insert(d,k,v,ANTD_DI_LIST))
|
||||
#define dput_dict(d,k,v) (insert(d,k,v,ANTD_DI_DIC))
|
||||
#define for_each_assoc(assoc, dic) \
|
||||
for(unsigned int i = 0; i < dic->cap; i++) \
|
||||
for(assoc = dic->map[i];assoc!= NULL; assoc = assoc->next)
|
||||
|
||||
typedef enum{ANTD_DI_STATIC, ANTD_DI_HEAP, ANTD_DI_LIST, ANTD_DI_DIC} antd_dict_item_type_t;
|
||||
|
||||
/**
|
||||
* Dictionary for header
|
||||
*/
|
||||
typedef struct __assoc{
|
||||
struct __assoc *next;
|
||||
char *key;
|
||||
antd_dict_item_type_t type;
|
||||
void* value;
|
||||
//char *value;
|
||||
} * chain_t;
|
||||
|
||||
typedef chain_t* map_t;
|
||||
@ -51,7 +57,7 @@ dictionary_t dict();
|
||||
dictionary_t dict_n(unsigned int n);
|
||||
chain_t dlookup(dictionary_t,const char*);
|
||||
void* dvalue(dictionary_t, const char*);
|
||||
chain_t dput(dictionary_t,const char*, void*);
|
||||
chain_t insert(dictionary_t,const char*, void*, antd_dict_item_type_t type);
|
||||
chain_t dremove(dictionary_t, const char*);
|
||||
void freedict(dictionary_t);
|
||||
|
||||
|
101
lib/h2.c
Normal file
101
lib/h2.c
Normal file
@ -0,0 +1,101 @@
|
||||
#include "h2.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
void* antd_h2_preface_ck(void* data)
|
||||
{
|
||||
char buf[25];
|
||||
antd_request_t* rq = (antd_request_t*) data;
|
||||
int count = antd_recv(rq->client,buf,24);
|
||||
if(count != 24)
|
||||
{
|
||||
// TODO servers MUST treat an invalid connection preface as a
|
||||
// connection error (Section 5.4.1) of type PROTOCOL_ERROR
|
||||
ERROR("Unable to read preface for client %d: [%s]",rq->client->sock,buf);
|
||||
return antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
}
|
||||
buf[24] = '\0';
|
||||
if(strcmp(buf, H2_CONN_PREFACE) != 0)
|
||||
{
|
||||
ERROR("Connection preface is not correct for client %d: [%s]",rq->client->sock,buf);
|
||||
// TODO servers MUST treat an invalid connection preface as a
|
||||
// connection error (Section 5.4.1) of type PROTOCOL_ERROR
|
||||
return antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
}
|
||||
return antd_create_task(antd_h2_handle, (void *)rq, NULL, rq->client->last_io);
|
||||
}
|
||||
|
||||
void* antd_h2_handle(void* data)
|
||||
{
|
||||
antd_request_t* rq = (antd_request_t*) data;
|
||||
antd_task_t* task;
|
||||
if(rq->client->flags & CLIENT_FL_READABLE)
|
||||
{
|
||||
task = antd_create_task(antd_h2_read,(void *)rq, NULL, rq->client->last_io);
|
||||
task->priority++;
|
||||
schedule_task(task);
|
||||
}
|
||||
if(rq->client->flags & CLIENT_FL_WRITABLE)
|
||||
{
|
||||
task = antd_create_task(antd_h2_write,(void *)rq, NULL, rq->client->last_io);
|
||||
task->priority++;
|
||||
schedule_task(task);
|
||||
}
|
||||
|
||||
task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
task->priority++;
|
||||
return task;
|
||||
}
|
||||
|
||||
static int antd_h2_read_frame(antd_client_t* cl, antd_h2_frame_t* frame)
|
||||
{
|
||||
uint8_t tmp;
|
||||
frame->length = 0;
|
||||
frame->type = 0;
|
||||
frame->flags = 0;
|
||||
frame->identifier= 0;
|
||||
if( antd_recv(cl,& frame->length,24) != 24) return 0;
|
||||
printf("length is %d\n", frame->length);
|
||||
// TODO:
|
||||
// Values greater than 2^14 (16,384) MUST NOT be
|
||||
// sent unless the receiver has set a larger value for
|
||||
// SETTINGS_MAX_FRAME_SIZE.
|
||||
if( antd_recv(cl,& frame->type,8) != 8) return 0;
|
||||
printf("type is %d\n", frame->type);
|
||||
if( antd_recv(cl,& frame->flags,8) != 8) return 0;
|
||||
if( antd_recv(cl,& tmp,1) != 1) return 0;
|
||||
// identifier
|
||||
if( antd_recv(cl,& frame->identifier,31) != 31) return 0;
|
||||
frame->data = (uint8_t*) malloc(frame->length);
|
||||
if(!frame->data) return 0;
|
||||
if( antd_recv(cl,frame->data, frame->length) != frame->length)
|
||||
{
|
||||
free(frame->data);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void* antd_h2_read(void* data)
|
||||
{
|
||||
antd_h2_frame_t frame;
|
||||
antd_request_t* rq = (antd_request_t*) data;
|
||||
antd_task_t* task;
|
||||
if(!antd_h2_read_frame(rq->client, &frame))
|
||||
{
|
||||
// TODO: frame error
|
||||
printf("error reading frame\n");
|
||||
ERROR("Unable to read frame from client %d",rq->client->sock);
|
||||
task = antd_create_task(NULL, (void *)rq, NULL, time(NULL));
|
||||
task->priority++;
|
||||
return task;
|
||||
}
|
||||
// verify frame
|
||||
printf("Frame type: %d\n", frame.type & 0xff);
|
||||
return antd_create_task(NULL, data, NULL, time(NULL));
|
||||
}
|
||||
void* antd_h2_write(void* data)
|
||||
{
|
||||
printf("write task\n");
|
||||
return antd_create_task(NULL, data, NULL, time(NULL));
|
||||
}
|
57
lib/h2.h
Normal file
57
lib/h2.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef HTTP2_H
|
||||
#define HTTP2_H
|
||||
#include "handle.h"
|
||||
#include "hpack.h"
|
||||
|
||||
#define H2_CONN_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
|
||||
#define H2_FRM_DATA 0x0
|
||||
#define H2_FRM_HEADER 0x1
|
||||
#define H2_FRM_PRIORITY 0x2
|
||||
#define H2_FRM_RST_STREAM 0x3
|
||||
#define H2_FRM_SETTINGS 0x4
|
||||
#define H2_FRM_PUSH_PROMISE 0x5
|
||||
#define H2_FRM_PING 0x6
|
||||
#define H2_FRM_GOAWAY 0x7
|
||||
#define H2_FRM_WINDOW_UPDATE 0x8
|
||||
#define H2_FRM_CONTINUATION 0x9
|
||||
|
||||
/**
|
||||
* Struct that holds a
|
||||
* h2 connection
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
} antd_h2_conn_t;
|
||||
|
||||
/**
|
||||
* Struct that holds a
|
||||
* h2 stream
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
} antd_h2_stream_t;
|
||||
|
||||
/**
|
||||
* a H2 frame
|
||||
*/
|
||||
typedef struct {
|
||||
// 24 bits length
|
||||
unsigned int length;
|
||||
// 8 bits type
|
||||
uint8_t type;
|
||||
// 8 bits flags
|
||||
uint8_t flags;
|
||||
// 31 bits identifier
|
||||
unsigned int identifier;
|
||||
uint8_t* data;
|
||||
} antd_h2_frame_t;
|
||||
|
||||
void* antd_h2_read(void* rq);
|
||||
void* antd_h2_write(void* rq);
|
||||
|
||||
void* antd_h2_preface_ck(void* rq);
|
||||
|
||||
void* antd_h2_handle(void* rq);
|
||||
|
||||
#endif
|
19
lib/handle.c
19
lib/handle.c
@ -1,4 +1,5 @@
|
||||
#include "handle.h"
|
||||
#include "handle.h"
|
||||
|
||||
#define HTML_TPL "<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><h2>%s</h2></BODY></HTML>"
|
||||
|
||||
static const char* S_100 = "Continue";
|
||||
@ -82,6 +83,11 @@ int compressable(char* ctype)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void schedule_task(antd_task_t* task)
|
||||
{
|
||||
UNUSED(task);
|
||||
}
|
||||
|
||||
void htdocs(antd_request_t* rq, char* dest)
|
||||
{
|
||||
dictionary_t xheader = (dictionary_t)dvalue(rq->request, "REQUEST_HEADER");
|
||||
@ -210,7 +216,7 @@ void antd_send_header(void* cl, antd_response_header_t* res)
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = Z_NO_FLUSH;
|
||||
//client->status = Z_NO_FLUSH;
|
||||
dput(res->header,"Content-Encoding", strdup("gzip"));
|
||||
}
|
||||
}
|
||||
@ -224,7 +230,7 @@ void antd_send_header(void* cl, antd_response_header_t* res)
|
||||
}
|
||||
else
|
||||
{
|
||||
client->status = Z_NO_FLUSH;
|
||||
//client->status = Z_NO_FLUSH;
|
||||
dput(res->header,"Content-Encoding", strdup("deflate"));
|
||||
}
|
||||
}
|
||||
@ -282,6 +288,7 @@ int antd_send(void *src, const void* data_in, int len_in)
|
||||
antd_client_t * source = (antd_client_t *) src;
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
int status = (source->flags & CLIENT_FL_COMPRESSION_END)?Z_NO_FLUSH:Z_FINISH;
|
||||
if(source->zstream && source->z_level != ANTD_CNONE)
|
||||
{
|
||||
antd_compress_t current_zlevel = source->z_level;
|
||||
@ -296,7 +303,7 @@ int antd_send(void *src, const void* data_in, int len_in)
|
||||
{
|
||||
zstream->avail_out = BUFFLEN;
|
||||
zstream->next_out = buf;
|
||||
if(deflate(zstream, source->status) == Z_STREAM_ERROR)
|
||||
if(deflate(zstream,status) == Z_STREAM_ERROR)
|
||||
{
|
||||
source->z_level = current_zlevel;
|
||||
data = NULL;
|
||||
@ -643,9 +650,9 @@ int antd_close(void* src)
|
||||
//TODO: send finish data to the socket before quit
|
||||
if(source->zstream)
|
||||
{
|
||||
if(source->status == Z_NO_FLUSH && source->z_level != ANTD_CNONE)
|
||||
if(!(source->flags & CLIENT_FL_COMPRESSION_END) && source->z_level != ANTD_CNONE)
|
||||
{
|
||||
source->status = Z_FINISH;
|
||||
source->flags |= CLIENT_FL_COMPRESSION_END;
|
||||
antd_send(source, "", 0);
|
||||
}
|
||||
deflateEnd(source->zstream);
|
||||
|
23
lib/handle.h
23
lib/handle.h
@ -21,6 +21,7 @@
|
||||
#include "dictionary.h"
|
||||
#include "list.h"
|
||||
#include "ini.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
#define SERVER_NAME "Antd"
|
||||
#define IS_POST(method) (strcmp(method,"POST")== 0)
|
||||
@ -37,7 +38,12 @@
|
||||
|
||||
typedef enum {ANTD_CGZ, ANTD_CDEFL, ANTD_CNONE} antd_compress_t;
|
||||
|
||||
//extern config_t server_config;
|
||||
// define the client flag
|
||||
#define CLIENT_FL_ACCEPTED 0x01
|
||||
#define CLIENT_FL_COMPRESSION_END 0x02
|
||||
#define CLIENT_FL_HTTP_1_1 0x04
|
||||
#define CLIENT_FL_READABLE 0x08
|
||||
#define CLIENT_FL_WRITABLE 0x10
|
||||
|
||||
typedef struct {
|
||||
unsigned int port;
|
||||
@ -50,9 +56,9 @@ typedef struct {
|
||||
typedef struct{
|
||||
int sock;
|
||||
void* ssl;
|
||||
int status;
|
||||
uint8_t flags;
|
||||
time_t last_io;
|
||||
// compress
|
||||
// compress option
|
||||
antd_compress_t z_level;
|
||||
void* zstream;
|
||||
} antd_client_t;
|
||||
@ -72,7 +78,7 @@ typedef struct
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
typedef struct {
|
||||
//int port;
|
||||
char *plugins_dir;
|
||||
char *plugins_ext;
|
||||
@ -108,14 +114,15 @@ typedef struct {
|
||||
int raw_body;
|
||||
} plugin_header_t;
|
||||
|
||||
|
||||
int __attribute__((weak)) require_plugin(const char*);
|
||||
// some functions that allows access to server
|
||||
// private data
|
||||
int __attribute__((weak)) require_plugin(const char*);
|
||||
void __attribute__((weak)) htdocs(antd_request_t* rq, char* dest);
|
||||
void __attribute__((weak)) dbdir(char* dest);
|
||||
void __attribute__((weak)) tmpdir(char* dest);
|
||||
void __attribute__((weak)) plugindir(char* dest);
|
||||
|
||||
int __attribute__((weak)) compressable(char* ctype);
|
||||
int __attribute__((weak)) compressable(char* ctype);
|
||||
void __attribute__((weak)) schedule_task(antd_task_t* task);
|
||||
|
||||
void set_nonblock(int socket);
|
||||
//void set_block(int socket);
|
||||
|
1
lib/hpack.c
Normal file
1
lib/hpack.c
Normal file
@ -0,0 +1 @@
|
||||
#include "hpack.h"
|
6
lib/hpack.h
Normal file
6
lib/hpack.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef HPACK_H
|
||||
#define HPACK_H
|
||||
|
||||
// HPACK header compression implementation
|
||||
|
||||
#endif
|
@ -155,8 +155,9 @@ void antd_scheduler_init(antd_scheduler_t* scheduler, int n)
|
||||
scheduler->status = 1;
|
||||
scheduler->workers_queue = NULL;
|
||||
scheduler->pending_task = 0 ;
|
||||
scheduler->validate_data = 0;
|
||||
scheduler->validate_data = NULL;
|
||||
scheduler->destroy_data = NULL;
|
||||
scheduler->task_ready = NULL;
|
||||
// init semaphore
|
||||
scheduler->scheduler_sem = sem_open("scheduler", O_CREAT, 0600, 0);
|
||||
if (scheduler->scheduler_sem == SEM_FAILED)
|
||||
@ -323,7 +324,7 @@ int antd_task_schedule(antd_scheduler_t* scheduler)
|
||||
}
|
||||
// has the task now
|
||||
// validate the task
|
||||
if(scheduler->validate_data && difftime( time(NULL), it->task->access_time) > MAX_VALIDITY_INTERVAL)
|
||||
if(scheduler->validate_data && ! scheduler->validate_data(it->task))
|
||||
{
|
||||
// data task is not valid
|
||||
LOG("Task data is not valid, task will be killed");
|
||||
@ -336,6 +337,16 @@ int antd_task_schedule(antd_scheduler_t* scheduler)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check if the task is ready
|
||||
if(scheduler->task_ready && !scheduler->task_ready(it->task))
|
||||
{
|
||||
// task is not ready, put it back to the queue
|
||||
antd_add_task(scheduler, it->task);
|
||||
free(it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// task is ready for execute, now figure out how it will be executed
|
||||
// check the type of task
|
||||
if(it->task->type == LIGHT || scheduler->n_workers <= 0)
|
||||
{
|
||||
@ -355,7 +366,7 @@ int antd_task_schedule(antd_scheduler_t* scheduler)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
void antd_wait(antd_scheduler_t* scheduler)
|
||||
void antd_scheduler_wait(antd_scheduler_t* scheduler)
|
||||
{
|
||||
int stat;
|
||||
while(scheduler->status)
|
||||
|
@ -9,7 +9,7 @@
|
||||
#define NORMAL_PRIORITY ((int)((N_PRIORITY - 1) / 2))
|
||||
#define LOW_PRIORITY (N_PRIORITY - 1)
|
||||
#define HIGH_PRIORITY 0
|
||||
#define MAX_VALIDITY_INTERVAL 20 // 10 s for task validity
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LIGHT,
|
||||
@ -92,7 +92,8 @@ typedef struct
|
||||
default to NULL
|
||||
*/
|
||||
void* (*destroy_data)(void*);
|
||||
int validate_data;
|
||||
int (*validate_data)(antd_task_t*);
|
||||
int (*task_ready)(antd_task_t*);
|
||||
} antd_scheduler_t;
|
||||
|
||||
/*
|
||||
@ -133,7 +134,7 @@ int antd_task_schedule(antd_scheduler_t *);
|
||||
/*
|
||||
wait for event
|
||||
*/
|
||||
void antd_wait(antd_scheduler_t *);
|
||||
void antd_scheduler_wait(antd_scheduler_t *);
|
||||
|
||||
antd_callback_t* callback_of( void* (*callback)(void*) );
|
||||
#endif
|
2
lib/ws.c
2
lib/ws.c
@ -403,7 +403,7 @@ int ws_client_connect(ws_client_t* wsclient, port_config_t pcnf)
|
||||
}
|
||||
// will be free
|
||||
wsclient->antdsock->sock = sock;
|
||||
wsclient->antdsock->status = 0;
|
||||
wsclient->antdsock->flags = 0;
|
||||
wsclient->antdsock->last_io = time(NULL);
|
||||
wsclient->antdsock->zstream = NULL;
|
||||
#ifdef USE_OPENSSL
|
||||
|
Reference in New Issue
Block a user