28 Star 77 Fork 27

liexusong / bolt

Create your Gitee Account
Explore and code with more than 6 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Without author's permission, this code is only for learning and cannot be used for other purposes.
Clone or download
connection.c 15.55 KB
Copy Edit Web IDE Raw Blame History
xusong.lie authored 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;
}

Comment ( 0 )

Sign in for post a comment

C
1
https://git.oschina.net/liexusong/bolt.git
git@git.oschina.net:liexusong/bolt.git
liexusong
bolt
bolt
master

Search

103611 48b8ff67 1899542 103622 4d02230c 1899542