30 Star 75 Fork 28

liexusong / bolt

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
connection.c 15.55 KB
一键复制 编辑 原始数据 按行查看 历史
xusong.lie 提交于 2015-09-08 11:56 . update GC
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
/*
* Bolt - The Realtime Image Compress System
* Copyright (c) 2015 - 2016, Liexusong <280259971@qq.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "bolt.h"
#include "connection.h"
#define BOLT_MAX_FREE_CONNECTIONS 1024
static int
bolt_connection_process_request(bolt_connection_t *c);
static int
bolt_connection_http_parse_url(struct http_parser *parser,
const char *at, size_t len);
static int
bolt_connection_http_parse_field(struct http_parser *p,
const char *at, size_t len);
static int
bolt_connection_http_parse_value(struct http_parser *p,
const char *at, size_t len);
void
bolt_connection_recv_handler(int sock, short event, void *arg);
void
bolt_connection_send_handler(int sock, short event, void *arg);
static int freeconn_count;
static void *freeconn_list[BOLT_MAX_FREE_CONNECTIONS];
static struct http_parser_settings http_parser_callbacks = {
.on_message_begin = NULL,
.on_url = bolt_connection_http_parse_url,
.on_header_field = bolt_connection_http_parse_field,
.on_header_value = bolt_connection_http_parse_value,
.on_headers_complete = NULL,
.on_body = NULL,
.on_message_complete = NULL
};
char bolt_error_400_page[] =
"<html>"
"<head><title>400 Bad Request</title></head>"
"<body bgcolor=\"white\">"
"<center><h1>400 Bad Request</h1></center>"
"<hr><div align=\"center\">Bolt " BOLT_VERSION "</div>"
"</body>"
"</html>";
char bolt_error_404_page[] =
"<html>"
"<head><title>404 Not Found</title></head>"
"<body bgcolor=\"white\">"
"<center><h1>404 Not Found</h1></center>"
"<hr><div align=\"center\">Bolt " BOLT_VERSION "</div>"
"</body>"
"</html>";
char bolt_error_500_page[] =
"<html>"
"<head><title>500 Internal Server Error</title></head>"
"<body bgcolor=\"white\">"
"<center><h1>500 Internal Server Error</h1></center>"
"<hr><div align=\"center\">Bolt " BOLT_VERSION "</div>"
"</body>"
"</html>";
int
bolt_init_connections()
{
freeconn_count = 0;
return 0;
}
int
bolt_connection_install_revent(bolt_connection_t *c,
void (*handler)(int, short, void *))
{
if (!c->revset) {
event_set(&c->revent, c->sock,
EV_READ|EV_PERSIST, handler, c);
event_base_set(service->ebase, &c->revent);
if (event_add(&c->revent, NULL) == -1) {
bolt_log(BOLT_LOG_ERROR,
"Failed to install read event, socket(%d)", c->sock);
return -1;
}
c->revset = 1;
}
return 0;
}
int
bolt_connection_install_wevent(bolt_connection_t *c,
void (*handler)(int, short, void *))
{
if (!c->wevset) {
event_set(&c->wevent, c->sock,
EV_WRITE|EV_PERSIST, handler, c);
event_base_set(service->ebase, &c->wevent);
if (event_add(&c->wevent, NULL) == -1) {
bolt_log(BOLT_LOG_ERROR,
"Failed to install write event, socket(%d)", c->sock);
return -1;
}
c->wevset = 1;
}
return 0;
}
void
bolt_connection_remove_revent(bolt_connection_t *c)
{
if (c->revset) {
if (event_del(&c->revent) == 0) {
c->revset = 0;
}
}
}
void
bolt_connection_remove_wevent(bolt_connection_t *c)
{
if (c->wevset) {
if (event_del(&c->wevent) == 0) {
c->wevset = 0;
}
}
}
bolt_connection_t *
bolt_create_connection(int sock)
{
bolt_connection_t *c;
if (freeconn_count > 0) {
c = freeconn_list[--freeconn_count];
} else {
c = malloc(sizeof(*c));
if (c == NULL) {
bolt_log(BOLT_LOG_ERROR,
"Not enough memory to alloc connection object");
return NULL;
}
}
c->sock = sock;
c->http_code = 200;
c->recv_state = BOLT_HTTP_STATE_START;
c->keepalive = 0;
c->revset = 0;
c->wevset = 0;
c->parse_field = BOLT_PARSE_FIELD_START;
c->parse_error = 0;
c->header_only = 0;
c->rpos = c->rbuf;
c->rend = c->rbuf + BOLT_RBUF_SIZE;
c->rlast = c->rbuf;
c->icache = NULL;
c->headers.tms = 0;
http_parser_init(&c->hp, HTTP_REQUEST);
c->hp.data = c;
if (bolt_connection_install_revent(c,
bolt_connection_recv_handler) == -1)
{
bolt_free_connection(c);
return NULL;
}
return c;
}
void
bolt_free_connection(bolt_connection_t *c)
{
close(c->sock);
bolt_connection_remove_revent(c);
bolt_connection_remove_wevent(c);
if (c->icache) {
c->icache->refcount--;
c->icache = NULL;
}
if (freeconn_count < BOLT_MAX_FREE_CONNECTIONS) {
freeconn_list[freeconn_count++] = c;
} else {
free(c);
}
}
static int
bolt_connection_recv_completed(bolt_connection_t *c)
{
char last;
while (c->rlast < c->rpos) {
last = *c->rlast;
switch (c->recv_state) {
case BOLT_HTTP_STATE_START:
if (last == BOLT_CR) {
c->recv_state = BOLT_HTTP_STATE_CR;
}
break;
case BOLT_HTTP_STATE_CR:
if (last == BOLT_LF) {
c->recv_state = BOLT_HTTP_STATE_CRLF;
} else {
c->recv_state = BOLT_HTTP_STATE_START;
}
break;
case BOLT_HTTP_STATE_CRLF:
if (last == BOLT_CR) {
c->recv_state = BOLT_HTTP_STATE_CRLFCR;
} else {
c->recv_state = BOLT_HTTP_STATE_START;
}
break;
case BOLT_HTTP_STATE_CRLFCR:
if (last == BOLT_LF) {
c->recv_state = BOLT_HTTP_STATE_CRLFCRLF;
} else {
c->recv_state = BOLT_HTTP_STATE_START;
}
break;
default:
c->recv_state = BOLT_HTTP_STATE_START;
break;
}
c->rlast++;
}
if (c->recv_state == BOLT_HTTP_STATE_CRLFCRLF) {
return 0;
}
return -1;
}
void
bolt_connection_keepalive(bolt_connection_t *c)
{
if (c->http_code == 200) {
c->icache->refcount--;
c->icache = NULL;
}
c->http_code = 200;
c->recv_state = BOLT_HTTP_STATE_START;
c->parse_field = BOLT_PARSE_FIELD_START;
c->keepalive = 0;
c->parse_error = 0;
c->header_only = 0;
c->rpos = c->rbuf;
c->rlast = c->rbuf;
c->headers.tms = 0;
http_parser_init(&c->hp, HTTP_REQUEST);
c->hp.data = c;
bolt_connection_remove_wevent(c);
bolt_connection_install_revent(c,
bolt_connection_recv_handler);
}
void
bolt_connection_recv_handler(int sock, short event, void *arg)
{
bolt_connection_t *c = (bolt_connection_t *)arg;
int nbytes, remain, retval;
if (!c || c->sock != sock) {
return;
}
remain = c->rend - c->rpos;
if (remain <= 0) {
bolt_free_connection(c);
bolt_log(BOLT_LOG_ERROR,
"Connection header too big, socket(%d)", c->sock);
return;
}
nbytes = read(c->sock, c->rpos, remain);
if (nbytes < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
bolt_free_connection(c);
bolt_log(BOLT_LOG_ERROR,
"Connection read error, socket(%d)", c->sock);
}
return;
} else if (nbytes == 0) {
bolt_free_connection(c);
return;
}
c->rpos += nbytes;
if (bolt_connection_recv_completed(c) == 0) {
retval = http_parser_execute(&c->hp, &http_parser_callbacks,
c->rbuf, c->rlast - c->rbuf);
if (c->hp.method != HTTP_GET) {
bolt_free_connection(c);
bolt_log(BOLT_LOG_ERROR,
"Connection request method not `GET', socket(%d)",
c->sock);
return;
}
c->keepalive = http_should_keep_alive(&c->hp);
/* Process connection request */
if (bolt_connection_process_request(c) == -1) {
bolt_free_connection(c);
}
}
}
void
bolt_connection_send_handler(int sock, short event, void *arg)
{
bolt_connection_t *c = (bolt_connection_t *)arg;
int nsend, nbytes;
if (!c || c->sock != sock) {
return;
}
nsend = c->wend - c->wpos;
nbytes = write(c->sock, c->wpos, nsend);
if (nbytes < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
bolt_free_connection(c);
bolt_log(BOLT_LOG_ERROR,
"Connection write error, socket(%d)", c->sock);
}
return;
} else if (nbytes == 0) {
bolt_free_connection(c);
return;
}
c->wpos += nbytes;
if (c->wpos >= c->wend) {
if (c->send_state == BOLT_SEND_HEADER_STATE && !c->header_only) {
switch (c->http_code) {
case 200:
c->wpos = (char *)c->icache->cache;
c->wend = c->wpos + c->icache->size;
break;
case 400:
c->wpos = bolt_error_400_page;
c->wend = c->wpos + sizeof(bolt_error_400_page) - 1;
break;
case 404:
c->wpos = bolt_error_404_page;
c->wend = c->wpos + sizeof(bolt_error_404_page) - 1;
break;
case 500:
default:
c->wpos = bolt_error_500_page;
c->wend = c->wpos + sizeof(bolt_error_500_page) - 1;
break;
}
c->send_state = BOLT_SEND_CONTENT_STATE;
} else {
if (c->keepalive) { /* keepalive? */
bolt_connection_keepalive(c);
} else {
bolt_free_connection(c);
}
}
}
}
void
bolt_connection_begin_send(bolt_connection_t *c)
{
int nsend;
switch (c->http_code) {
case 200:
nsend = snprintf(c->wbuf, BOLT_WBUF_SIZE,
"HTTP/1.1 200 OK\r\n"
"Content-Type: image/jpeg\r\n"
"Content-Length: %d\r\n"
"Last-Modified: %s\r\n"
"Server: Bolt\r\n\r\n",
c->icache->size,
c->icache->datetime);
break;
case 304:
nsend = snprintf(c->wbuf, BOLT_WBUF_SIZE,
"HTTP/1.1 304 Not Modified\r\n"
"Server: Bolt\r\n\r\n");
break;
case 400:
nsend = snprintf(c->wbuf, BOLT_WBUF_SIZE,
"HTTP/1.1 400 Bad Request\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %d\r\n"
"Server: Bolt\r\n\r\n",
sizeof(bolt_error_400_page) - 1);
break;
case 404:
nsend = snprintf(c->wbuf, BOLT_WBUF_SIZE,
"HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %d\r\n"
"Server: Bolt\r\n\r\n",
sizeof(bolt_error_404_page) - 1);
break;
case 500:
default:
nsend = snprintf(c->wbuf, BOLT_WBUF_SIZE,
"HTTP/1.1 500 Internal Server Error\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %d\r\n"
"Server: Bolt\r\n\r\n",
sizeof(bolt_error_500_page) - 1);
break;
}
c->wpos = c->wbuf;
c->wend = c->wbuf + nsend;
c->send_state = BOLT_SEND_HEADER_STATE;
bolt_connection_install_wevent(c,
bolt_connection_send_handler);
}
static int
bolt_connection_process_request(bolt_connection_t *c)
{
bolt_cache_t *cache;
bolt_wait_queue_t *waitq;
int pass = 0, send = 0;
if (c->parse_error != 0) {
bolt_log(BOLT_LOG_ERROR,
"Header was invaild when parsed, socket(%d)", c->sock);
return -1;
}
pthread_mutex_lock(&service->cache_lock);
if (jk_hash_find(service->cache_htb, c->filename,
c->fnlen, (void **)&cache) == JK_HASH_OK)
{
/* Move cache to LRU tail */
list_del(&cache->link);
list_add_tail(&cache->link, &service->gc_lru);
if (cache->time == c->headers.tms) {
c->http_code = 304;
c->header_only = 1;
} else {
c->http_code = 200;
c->icache = cache;
cache->refcount++;
cache->last = service->current_time;
}
send = 1;
} else {
if (jk_hash_find(service->waiting_htb, c->filename,
c->fnlen, (void **)&waitq) == JK_HASH_ERR)
{
/* Free by bolt_wakeup_handler() */
waitq = malloc(sizeof(*waitq));
if (NULL == waitq) {
pthread_mutex_unlock(&service->cache_lock);
bolt_log(BOLT_LOG_ERROR,
"Not enough memory to alloc wait queue");
return -1;
}
INIT_LIST_HEAD(&waitq->wait_conns);
jk_hash_insert(service->waiting_htb,
c->filename, c->fnlen, waitq, 0);
pass = 1;
}
list_add(&c->link, &waitq->wait_conns);
}
pthread_mutex_unlock(&service->cache_lock);
if (pass && bolt_worker_pass_task(c) == -1) {
return -1;
}
if (send) {
bolt_connection_begin_send(c);
}
/*
* Remove read event here,
* because we don't need read when processing request.
*/
bolt_connection_remove_revent(c);
return 0;
}
static int
bolt_connection_http_parse_url(struct http_parser *p,
const char *at, size_t len)
{
bolt_connection_t *c = p->data;
char *start, *end;
if (len > BOLT_FILENAME_LENGTH) {
c->parse_error = 1;
return -1;
}
start = (char *)at;
end = (char *)at + len;
while (start < end && *start == '/') start++;
len = end - start;
if (len == 0) {
c->parse_error = 1;
return -1;
}
memcpy(c->filename, "/", 1);
memcpy(c->filename + 1, start, len);
c->fnlen = len + 1;
return 0;
}
static int
bolt_connection_http_parse_field(struct http_parser *p,
const char *at, size_t len)
{
bolt_connection_t *c = p->data;
if (!strncasecmp(at, "If-Modified-Since", len)) {
c->parse_field = BOLT_PARSE_FIELD_IF_MODIFIED_SINCE;
}
return 0;
}
static int
bolt_connection_http_parse_value(struct http_parser *p,
const char *at, size_t len)
{
bolt_connection_t *c = p->data;
if (c->parse_field == BOLT_PARSE_FIELD_IF_MODIFIED_SINCE) {
c->headers.tms = bolt_parse_time(at, len);
}
return 0;
}
C
1
https://gitee.com/liexusong/bolt.git
git@gitee.com:liexusong/bolt.git
liexusong
bolt
bolt
master

搜索帮助