소스 검색

Make the http client independent of events and use my iobuf.

master
Richard Nyberg 16 년 전
부모
커밋
b5d78b066a
3개의 변경된 파일152개의 추가작업 그리고 155개의 파일을 삭제
  1. +58
    -6
      btpd/http_tr_if.c
  2. +80
    -140
      misc/http_client.c
  3. +14
    -9
      misc/http_client.h

+ 58
- 6
btpd/http_tr_if.c 파일 보기

@@ -1,6 +1,7 @@
#include "btpd.h" #include "btpd.h"


#include <http_client.h> #include <http_client.h>
#include <iobuf.h>


#define MAX_DOWNLOAD (1 << 18) // 256kB #define MAX_DOWNLOAD (1 << 18) // 256kB


@@ -9,14 +10,23 @@ static const char *m_tr_events[] = { "started", "stopped", "completed", "" };
struct http_tr_req { struct http_tr_req {
struct torrent *tp; struct torrent *tp;
struct http_req *req; struct http_req *req;
struct evbuffer *buf; struct iobuf buf;
struct event rdev;
struct event wrev;
nameconn_t nc;
int sd;
enum tr_event event; enum tr_event event;
}; };


static void static void
http_tr_free(struct http_tr_req *treq) http_tr_free(struct http_tr_req *treq)
{ {
evbuffer_free(treq->buf); if (treq->sd != -1) {
btpd_ev_del(&treq->rdev);
btpd_ev_del(&treq->wrev);
close(treq->sd);
}
iobuf_free(&treq->buf);
free(treq); free(treq);
} }


@@ -111,16 +121,16 @@ http_cb(struct http_req *req, struct http_response *res, void *arg)
http_tr_free(treq); http_tr_free(treq);
break; break;
case HTTP_T_DATA: case HTTP_T_DATA:
if (treq->buf->off + res->v.data.l > MAX_DOWNLOAD) { if (treq->buf.off + res->v.data.l > MAX_DOWNLOAD) {
tr_result(treq->tp, TR_RES_FAIL, -1); tr_result(treq->tp, TR_RES_FAIL, -1);
http_tr_cancel(treq); http_tr_cancel(treq);
break; break;
} }
if (evbuffer_add(treq->buf, res->v.data.p, res->v.data.l) != 0) if (!iobuf_write(&treq->buf, res->v.data.p, res->v.data.l))
btpd_err("Out of memory.\n"); btpd_err("Out of memory.\n");
break; break;
case HTTP_T_DONE: case HTTP_T_DONE:
if (parse_reply(treq->tp, treq->buf->buffer, treq->buf->off, if (parse_reply(treq->tp, treq->buf.buf, treq->buf.off,
treq->event != TR_EV_STOPPED, &interval) == 0) treq->event != TR_EV_STOPPED, &interval) == 0)
tr_result(treq->tp, TR_RES_OK, interval); tr_result(treq->tp, TR_RES_OK, interval);
else else
@@ -132,11 +142,47 @@ http_cb(struct http_req *req, struct http_response *res, void *arg)
} }
} }


static void
sd_wr_cb(int sd, short type, void *arg)
{
struct http_tr_req *treq = arg;
if (http_write(treq->req, sd) && http_want_write(treq->req))
btpd_ev_add(&treq->wrev, NULL);
}

static void
sd_rd_cb(int sd, short type, void *arg)
{
struct http_tr_req *treq = arg;
if (http_read(treq->req, sd) && http_want_read(treq->req))
btpd_ev_add(&treq->rdev, NULL);
}

static void
nc_cb(void *arg, int error, int sd)
{
struct http_tr_req *treq = arg;
if (error) {
tr_result(treq->tp, TR_RES_FAIL, -1);
http_cancel(treq->req);
http_tr_free(treq);
} else {
treq->sd = sd;
event_set(&treq->wrev, sd, EV_WRITE, sd_wr_cb, treq);
event_set(&treq->rdev, sd, EV_READ, sd_rd_cb, treq);
if (http_want_read(treq->req))
btpd_ev_add(&treq->rdev, NULL);
if (http_want_write(treq->req))
btpd_ev_add(&treq->wrev, NULL);
}
}

struct http_tr_req * struct http_tr_req *
http_tr_req(struct torrent *tp, enum tr_event event, const char *aurl) http_tr_req(struct torrent *tp, enum tr_event event, const char *aurl)
{ {
char e_hash[61], e_id[61], ip_arg[INET_ADDRSTRLEN + 4], url[512], qc; char e_hash[61], e_id[61], ip_arg[INET_ADDRSTRLEN + 4], url[512], qc;
const uint8_t *peer_id = btpd_get_peer_id(); const uint8_t *peer_id = btpd_get_peer_id();
struct http_url *http_url;


qc = (strchr(aurl, '?') == NULL) ? '?' : '&'; qc = (strchr(aurl, '?') == NULL) ? '?' : '&';


@@ -166,16 +212,22 @@ http_tr_req(struct torrent *tp, enum tr_event event, const char *aurl)
free(treq); free(treq);
return NULL; return NULL;
} }
if ((treq->buf = evbuffer_new()) == NULL) treq->buf = iobuf_init(4096);
if (treq->buf.error)
btpd_err("Out of memory.\n"); btpd_err("Out of memory.\n");
treq->tp = tp; treq->tp = tp;
treq->event = event; treq->event = event;
treq->sd = -1;
http_url = http_url_get(treq->req);
treq->nc = btpd_name_connect(http_url->host, http_url->port, nc_cb, treq);
return treq; return treq;
} }


void void
http_tr_cancel(struct http_tr_req *treq) http_tr_cancel(struct http_tr_req *treq)
{ {
if (treq->sd == -1)
btpd_name_connect_cancel(treq->nc);
http_cancel(treq->req); http_cancel(treq->req);
http_tr_free(treq); http_tr_free(treq);
} }

+ 80
- 140
misc/http_client.c 파일 보기

@@ -1,25 +1,16 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>

#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>


#include <event.h> #include "iobuf.h"
#include <evdns.h>

#include "subr.h" #include "subr.h"
#include "http_client.h" #include "http_client.h"


#define TIMEOUT (& (struct timeval) { 45, 0 })

struct http_url * struct http_url *
http_url_parse(const char *url) http_url_parse(const char *url)
{ {
@@ -88,14 +79,11 @@ http_url_free(struct http_url *url)
} }


struct http_req { struct http_req {
enum {
HTTP_RESOLVE, HTTP_CONNECT, HTTP_WRITE, HTTP_RECEIVE, HTTP_PARSE
} state;
enum { enum {
PS_HEAD, PS_CHUNK_SIZE, PS_CHUNK_DATA, PS_CHUNK_CRLF, PS_ID_DATA PS_HEAD, PS_CHUNK_SIZE, PS_CHUNK_DATA, PS_CHUNK_CRLF, PS_ID_DATA
} pstate; } pstate;


int sd; int parsing;
int cancel; int cancel;
int chunked; int chunked;
long length; long length;
@@ -104,8 +92,8 @@ struct http_req {
void *arg; void *arg;


struct http_url *url; struct http_url *url;
struct evbuffer *buf; struct iobuf rbuf;
struct event ev; struct iobuf wbuf;
}; };


static void static void
@@ -113,12 +101,8 @@ http_free(struct http_req *req)
{ {
if (req->url != NULL) if (req->url != NULL)
http_url_free(req->url); http_url_free(req->url);
if (req->buf != NULL) iobuf_free(&req->rbuf);
evbuffer_free(req->buf); iobuf_free(&req->wbuf);
if (req->sd > 0) {
event_del(&req->ev);
close(req->sd);
}
free(req); free(req);
} }


@@ -235,57 +219,57 @@ again:
case PS_HEAD: case PS_HEAD:
if (len == 0) if (len == 0)
goto error; goto error;
if ((end = evbuffer_find(req->buf, "\r\n\r\n", 4)) != NULL) if ((end = iobuf_find(&req->rbuf, "\r\n\r\n", 4)) != NULL)
dlen = 4; dlen = 4;
else if ((end = evbuffer_find(req->buf, "\n\n", 2)) != NULL) else if ((end = iobuf_find(&req->rbuf, "\n\n", 2)) != NULL)
dlen = 2; dlen = 2;
else { else {
if (req->buf->off < (1 << 15)) if (req->rbuf.off < (1 << 15))
return 1; return 1;
else else
goto error; goto error;
} }
if (evbuffer_add(req->buf, "", 1) != 0) if (!iobuf_write(&req->rbuf, "", 1))
goto error; goto error;
req->buf->off--; req->rbuf.off--;
if (!headers_parse(req, req->buf->buffer, end)) if (!headers_parse(req, req->rbuf.buf, end))
goto error; goto error;
if (req->cancel) if (req->cancel)
goto cancel; goto cancel;
evbuffer_drain(req->buf, end - (char *)req->buf->buffer + dlen); iobuf_consumed(&req->rbuf, end - (char *)req->rbuf.buf + dlen);
goto again; goto again;
case PS_CHUNK_SIZE: case PS_CHUNK_SIZE:
assert(req->chunked); assert(req->chunked);
if (len == 0) if (len == 0)
goto error; goto error;
if ((end = evbuffer_find(req->buf, "\n", 1)) == NULL) { if ((end = iobuf_find(&req->rbuf, "\n", 1)) == NULL) {
if (req->buf->off < 20) if (req->rbuf.off < 20)
return 1; return 1;
else else
goto error; goto error;
} }
errno = 0; errno = 0;
req->length = strtol(req->buf->buffer, &numend, 16); req->length = strtol(req->rbuf.buf, &numend, 16);
if (req->length < 0 || numend == (char *)req->buf->buffer || errno) if (req->length < 0 || numend == (char *)req->rbuf.buf || errno)
goto error; goto error;
if (req->length == 0) if (req->length == 0)
goto done; goto done;
evbuffer_drain(req->buf, end - (char *)req->buf->buffer + 1); iobuf_consumed(&req->rbuf, end - (char *)req->rbuf.buf + 1);
req->pstate = PS_CHUNK_DATA; req->pstate = PS_CHUNK_DATA;
goto again; goto again;
case PS_CHUNK_DATA: case PS_CHUNK_DATA:
if (len == 0) if (len == 0)
goto error; goto error;
assert(req->length > 0); assert(req->length > 0);
dlen = min(req->buf->off, req->length); dlen = min(req->rbuf.off, req->length);
if (dlen > 0) { if (dlen > 0) {
res.type = HTTP_T_DATA; res.type = HTTP_T_DATA;
res.v.data.l = dlen; res.v.data.l = dlen;
res.v.data.p = req->buf->buffer; res.v.data.p = req->rbuf.buf;
req->cb(req, &res, req->arg); req->cb(req, &res, req->arg);
if (req->cancel) if (req->cancel)
goto cancel; goto cancel;
evbuffer_drain(req->buf, dlen); iobuf_consumed(&req->rbuf, dlen);
req->length -= dlen; req->length -= dlen;
if (req->length == 0) { if (req->length == 0) {
req->pstate = PS_CHUNK_CRLF; req->pstate = PS_CHUNK_CRLF;
@@ -297,15 +281,15 @@ again:
if (len == 0) if (len == 0)
goto error; goto error;
assert(req->length == 0); assert(req->length == 0);
if (req->buf->off < 2) if (req->rbuf.off < 2)
return 1; return 1;
if (req->buf->buffer[0] == '\r' && req->buf->buffer[1] == '\n') if (req->rbuf.buf[0] == '\r' && req->rbuf.buf[1] == '\n')
dlen = 2; dlen = 2;
else if (req->buf->buffer[0] == '\n') else if (req->rbuf.buf[0] == '\n')
dlen = 1; dlen = 1;
else else
goto error; goto error;
evbuffer_drain(req->buf, dlen); iobuf_consumed(&req->rbuf, dlen);
req->pstate = PS_CHUNK_SIZE; req->pstate = PS_CHUNK_SIZE;
goto again; goto again;
case PS_ID_DATA: case PS_ID_DATA:
@@ -314,17 +298,17 @@ again:
else if (len == 0) else if (len == 0)
goto error; goto error;
if (req->length < 0) if (req->length < 0)
dlen = req->buf->off; dlen = req->rbuf.off;
else else
dlen = min(req->buf->off, req->length); dlen = min(req->rbuf.off, req->length);
if (dlen > 0) { if (dlen > 0) {
res.type = HTTP_T_DATA; res.type = HTTP_T_DATA;
res.v.data.p = req->buf->buffer; res.v.data.p = req->rbuf.buf;
res.v.data.l = dlen; res.v.data.l = dlen;
req->cb(req, &res, req->arg); req->cb(req, &res, req->arg);
if (req->cancel) if (req->cancel)
goto cancel; goto cancel;
evbuffer_drain(req->buf, dlen); iobuf_consumed(&req->rbuf, dlen);
if (req->length > 0) { if (req->length > 0) {
req->length -= dlen; req->length -= dlen;
if (req->length == 0) if (req->length == 0)
@@ -346,129 +330,85 @@ cancel:
return 0; return 0;
} }


static void struct http_url *
http_read_cb(int sd, short type, void *arg) http_url_get(struct http_req *req)
{ {
struct http_req *req = arg; return req->url;
if (type == EV_TIMEOUT) {
http_error(req);
return;
}
int nr = evbuffer_read(req->buf, sd, 1 << 14);
if (nr < 0) {
if (nr == EAGAIN)
goto more;
else {
http_error(req);
return;
}
}
req->state = HTTP_PARSE;
if (!http_parse(req, nr))
return;
req->state = HTTP_RECEIVE;
more:
if (event_add(&req->ev, TIMEOUT) != 0)
http_error(req);
} }


static void int
http_write_cb(int sd, short type, void *arg) http_want_read(struct http_req *req)
{ {
struct http_req *req = arg; return 1;
if (type == EV_TIMEOUT) { }

int
http_want_write(struct http_req *req)
{
return req->wbuf.off > 0;
}

int
http_read(struct http_req *req, int sd)
{
if (!iobuf_accommodate(&req->rbuf, 4096)) {
http_error(req); http_error(req);
return; return 0;
}
int nw = evbuffer_write(req->buf, sd);
if (nw == -1) {
if (errno == EAGAIN)
goto out;
else
goto error;
} }
out: ssize_t nr = read(sd, req->rbuf.buf + req->rbuf.off, 4096);
if (req->buf->off != 0) { if (nr < 0 && errno == EAGAIN)
if (event_add(&req->ev, TIMEOUT) != 0) return 1;
goto error; else if (nr < 0) {
http_error(req);
return 0;
} else { } else {
req->state = HTTP_RECEIVE; req->rbuf.off += nr;
event_set(&req->ev, req->sd, EV_READ, http_read_cb, req); req->parsing = 1;
if (event_add(&req->ev, TIMEOUT) != 0) if (http_parse(req, nr)) {
goto error; req->parsing = 0;
return 1;
} else
return 0;
} }
return;
error:
http_error(req);
}

static int
http_connect(struct http_req *req, struct in_addr inaddr)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(req->url->port);
addr.sin_addr = inaddr;
req->state = HTTP_CONNECT;
if ((req->sd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
goto error;
if (set_nonblocking(req->sd) != 0)
goto error;
if ((connect(req->sd, (struct sockaddr *)&addr, sizeof(addr)) != 0
&& errno != EINPROGRESS))
goto error;
event_set(&req->ev, req->sd, EV_WRITE, http_write_cb, req);
if (event_add(&req->ev, TIMEOUT) != 0)
goto error;
return 1;
error:
return 0;
} }


static void int
http_dnscb(int result, char type, int count, int ttl, void *addrs, void *arg) http_write(struct http_req *req, int sd)
{ {
struct http_req *req = arg; assert(req->wbuf.off > 0);
if (req->cancel) ssize_t nw =
http_free(req); write(sd, req->wbuf.buf, req->wbuf.off);
else if (result == DNS_ERR_NONE && type == DNS_IPv4_A && count > 0) { if (nw < 0 && errno == EAGAIN)
int addri = rand_between(0, count - 1); return 1;
struct in_addr inaddr; else if (nw < 0) {
bcopy(addrs + addri * sizeof(struct in_addr), &inaddr,
sizeof(struct in_addr));
if (!http_connect(req, inaddr))
http_error(req);
} else
http_error(req); http_error(req);
return 0;
} else {
iobuf_consumed(&req->wbuf, nw);
return 1;
}
} }


int int
http_get(struct http_req **out, const char *url, const char *hdrs, http_get(struct http_req **out, const char *url, const char *hdrs,
http_cb_t cb, void *arg) http_cb_t cb, void *arg)
{ {
struct in_addr addr;
struct http_req *req = calloc(1, sizeof(*req)); struct http_req *req = calloc(1, sizeof(*req));
if (req == NULL) if (req == NULL)
return 0; return 0;
req->sd = -1;
req->cb = cb; req->cb = cb;
req->arg = arg; req->arg = arg;
req->url = http_url_parse(url); req->url = http_url_parse(url);
if (req->url == NULL) if (req->url == NULL)
goto error; goto error;
if ((req->buf = evbuffer_new()) == NULL) req->rbuf = iobuf_init(4096);
goto error; req->wbuf = iobuf_init(1024);
if (evbuffer_add_printf(req->buf, "GET %s HTTP/1.1\r\n" if (!iobuf_print(&req->wbuf, "GET %s HTTP/1.1\r\n"
"Host: %s:%hu\r\n" "Host: %s:%hu\r\n"
"Accept-Encoding:\r\n" "Accept-Encoding:\r\n"
"Connection: close\r\n" "Connection: close\r\n"
"%s" "%s"
"\r\n", req->url->uri, req->url->host, req->url->port, hdrs) == -1) "\r\n", req->url->uri, req->url->host, req->url->port, hdrs))
goto error;
if (inet_aton(req->url->host, &addr) == 1) {
if (!http_connect(req, addr))
goto error;
} else if (evdns_resolve_ipv4(req->url->host, 0, http_dnscb, req) != 0)
goto error; goto error;
if (out != NULL) if (out != NULL)
*out = req; *out = req;
@@ -481,7 +421,7 @@ error:
void void
http_cancel(struct http_req *req) http_cancel(struct http_req *req)
{ {
if (req->state == HTTP_RESOLVE || req->state == HTTP_PARSE) if (req->parsing)
req->cancel = 1; req->cancel = 1;
else else
http_free(req); http_free(req);


+ 14
- 9
misc/http_client.h 파일 보기

@@ -1,6 +1,15 @@
#ifndef BTPD_HTTP_CLIENT_H #ifndef BTPD_HTTP_CLIENT_H
#define BTPD_HTTP_CLIENT_H #define BTPD_HTTP_CLIENT_H


struct http_url {
char *host;
char *uri;
uint16_t port;
};

struct http_url *http_url_parse(const char *url);
void http_url_free(struct http_url *url);

struct http_response { struct http_response {
enum { enum {
HTTP_T_ERR, HTTP_T_CODE, HTTP_T_HEADER, HTTP_T_DATA, HTTP_T_DONE HTTP_T_ERR, HTTP_T_CODE, HTTP_T_HEADER, HTTP_T_DATA, HTTP_T_DONE
@@ -19,20 +28,16 @@ struct http_response {
} v; } v;
}; };


struct http_url {
char *host;
char *uri;
uint16_t port;
};

struct http_req; struct http_req;

typedef void (*http_cb_t)(struct http_req *, struct http_response *, void *); typedef void (*http_cb_t)(struct http_req *, struct http_response *, void *);


struct http_url *http_url_parse(const char *url);
void http_url_free(struct http_url *url);
int http_get(struct http_req **out, const char *url, const char *hdrs, int http_get(struct http_req **out, const char *url, const char *hdrs,
http_cb_t cb, void *arg); http_cb_t cb, void *arg);
void http_cancel(struct http_req *req); void http_cancel(struct http_req *req);
struct http_url *http_url_get(struct http_req *req);
int http_want_read(struct http_req *req);
int http_want_write(struct http_req *req);
int http_read(struct http_req *req, int sd);
int http_write(struct http_req *req, int sd);


#endif #endif

||||||
x
 
000:0
불러오는 중...
취소
저장