c语言 断点续传

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <locale.h>
#include <dirent.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sbase.h>
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
#ifdef HAVE_BZ2LIB
#include <bzlib.h>
#endif
#include "iniparser.h"
#include "http.h"
#include "mime.h"
#include "trie.h"
#include "stime.h"
#include "logger.h"
#define XHTTPD_VERSION       "0.0.1"
#define HTTP_RESP_OK            "HTTP/1.1 200 OK"
#define HTTP_BAD_REQUEST        "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n"
#define HTTP_NOT_FOUND          "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n" 
#define HTTP_NOT_MODIFIED       "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n"
#define HTTP_NO_CONTENT         "HTTP/1.1 206 No Content\r\nContent-Length: 0\r\n\r\n"
#define LL(x) ((long long)x)
#define UL(x) ((unsigned long int)x)
static SBASE *sbase = NULL;
static SERVICE *service = NULL;
static dictionary *dict = NULL;
static char *httpd_home = "/tmp/xhttpd/html";
static char *http_indexes[HTTP_INDEX_MAX];
static int http_indexes_view = 0;
static int nindexes = 0;
static char *http_default_charset = "UTF-8";
static int httpd_compress = 1;
static char *httpd_compress_cachedir = "/tmp/xhttpd/cache";
static HTTP_VHOST httpd_vhosts[HTTP_VHOST_MAX];
static int nvhosts = 0;
static int httpd_proxy_timeout = 0;
static void *namemap = NULL;
static void *hostmap = NULL;
static void *urlmap = NULL;
static void *http_headers_map = NULL;
static void *logger = NULL;

/* mkdir recursive */
int xhttpd_mkdir(char *path, int mode)
{
    char *p = NULL, fullpath[HTTP_PATH_MAX];
    int ret = 0, level = -1;
    struct stat st;

    if(path)
    {
        strcpy(fullpath, path);
        p = fullpath;
        while(*p != '\0')
        {
            if(*p == '/' )
            {
                while(*p != '\0' && *p == '/' && *(p+1) == '/')++p;
                if(level > 0)
                {
                    *p = '\0';
                    memset(&st, 0, sizeof(struct stat));
                    ret = stat(fullpath, &st);
                    if(ret == 0 && !S_ISDIR(st.st_mode)) return -1;
                    if(ret != 0 && mkdir(fullpath, mode) != 0) return -1;
                    *p = '/';
                }
                level++;
            }
            ++p;
        }
        return 0;
    }
    return -1;
}

/* xhttpd packet reader */
int xhttpd_packet_reader(CONN *conn, CB_DATA *buffer)
{
    return buffer->ndata;
}

/* xhttpd index view */
int xhttpd_index_view(CONN *conn, HTTP_REQ *http_req, char *file, char *root, char *end)
{
    char buf[HTTP_BUF_SIZE], url[HTTP_PATH_MAX], *p = NULL, *e = NULL, *pp = NULL;
    struct dirent *ent = NULL;
    unsigned char *s = NULL;
    struct stat st = {0};
    int len = 0, n = 0, keepalive = 0;
    DIR *dirp = NULL;

    if(conn && file && root && end && (dirp = opendir(file)))
    {
        if((p = pp = (char *)calloc(1, HTTP_INDEXES_MAX)))
        {
            p += sprintf(p, "<html><head><title>Indexes Of %s</title>"
                    "<head><body><h1 align=center>xhttpd</h1>", root);
            if(*end == '/') --end;
            while(end > root && *end != '/')--end;
            if(end == root)
                p += sprintf(p, "<a href='/' >/</a><br>");
            else if(end > root)
            {
                *end = '\0';
                p += sprintf(p, "<a href='%s/' >..</a><br>", root);
            }
            p += sprintf(p, "<hr noshade><table><tr align=left>"
                    "<th width=500>Name</th><th width=200>Size</th>"
                    "<th>Last-Modified</th></tr>");
            end = p;
            while((ent = readdir(dirp)) != NULL)
            {
                if(ent->d_name[0] != '.' && ent->d_reclen > 0)
                {
                    p += sprintf(p, "<tr>");
                    s = (unsigned char *)ent->d_name;
                    e = url;
                    while(*s != '\0') 
                    {
                        if(*s == 0x20 && *s > 127)
                        {
                            e += sprintf(e, "%%%02x", *s);
                        }else *e++ = *s++;
                    }
                    *e = '\0';
                    if(ent->d_type == DT_DIR)
                    {
                        p += sprintf(p, "<td><a href='%s/' >%s/</a></td>", 
                                url, ent->d_name);
                    }
                    else
                    {
                        p += sprintf(p, "<td><a href='%s' >%s</a></td>", 
                                url, ent->d_name);
                    }
                    sprintf(buf, "%s/%s", file, ent->d_name);
                    if(ent->d_type != DT_DIR && lstat(buf, &st) == 0)
                    {
                        if(st.st_size >= (off_t)HTTP_BYTE_G)
                            p += sprintf(p, "<td> %.1fG </td>", 
                                    (double)st.st_size/(double) HTTP_BYTE_G);
                        else if(st.st_size >= (off_t)HTTP_BYTE_M)
                            p += sprintf(p, "<td> %lldM </td>", 
                                    LL(st.st_size/(off_t)HTTP_BYTE_M));
                        else if(st.st_size >= (off_t)HTTP_BYTE_K)
                            p += sprintf(p, "<td> %lldK </td>", 
                                    LL(st.st_size/(off_t)HTTP_BYTE_K));
                        else 
                            p += sprintf(p, "<td> %lldB </td>", LL(st.st_size));

                    }
                    else p += sprintf(p, "<td> - </td>");
                    p += sprintf(p, "<td>");
                    p += strdate(st.st_mtime, p);
                    p += sprintf(p, "</td>");
                    p += sprintf(p, "</tr>");
                }
            }
            p += sprintf(p, "</table>");
            if(end != p) p += sprintf(p, "<hr noshade>");
            p += sprintf(p, "<em></body></html>");
            len = (p - pp);
            p = buf;
            p += sprintf(p, "HTTP/1.1 200 OK\r\nContent-Length:%lld\r\n"
                    "Content-Type: text/html; charset=%s\r\n",
                    LL(len), http_default_charset);
            if((n = http_req->headers[HEAD_GEN_CONNECTION]) > 0)
            {
                p += sprintf(p, "Connection: %s\r\n", http_req->hlines + n);
                if((strncasecmp(http_req->hlines + n, "close", 5)) !=0 )
                    keepalive = 1;
            }
            else 
            {
                p += sprintf(p, "Connection: close\r\n");
            }
            p += sprintf(p, "Date: ");p += GMTstrdate(time(NULL), p);p += sprintf(p, "\r\n");
            p += sprintf(p, "Server: xhttpd/%s\r\n\r\n", XHTTPD_VERSION);
            conn->push_chunk(conn, buf, (p - buf));
            conn->push_chunk(conn, pp, len);
            //fprintf(stdout, "buf:%s pp:%s\n", buf, pp);
            free(pp);
            pp = NULL;
            if(!keepalive) conn->over(conn);
        }
        closedir(dirp);
        return 0;
    }
    else
    {
        fprintf(stderr, "%s::%d open dir:%s file:%p root:%p end:%p failed, %s\n", __FILE__, __LINE__, file, file, root, end, strerror(errno));
    }
    return -1;
}
#ifdef HAVE_ZLIB
int xhttpd_gzip(unsigned char **zstream, unsigned char *in, int inlen, time_t mtime)
{
    unsigned char *c = NULL, *out = NULL;
    unsigned long crc = 0;
    int outlen = 0;
    z_stream z = {0};

    if(in && inlen > 0)
    {
        z.zalloc = Z_NULL;
        z.zfree = Z_NULL;
        z.opaque = Z_NULL;
        if(deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 
                    -MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK)
        {
            return -1;
        }
        z.next_in = (unsigned char *)in;
        z.avail_in = inlen;
        z.total_in = 0;
        outlen = (inlen * 1.1) + 12 + 18;
        if((*zstream = out = (unsigned char *)calloc(1, outlen)))
        {
            c = out;
            c[0] = 0x1f;
            c[1] = 0x8b;
            c[2] = Z_DEFLATED;
            c[3] = 0; /* options */
            c[4] = (mtime >>  0) & 0xff;
            c[5] = (mtime >>  8) & 0xff;
            c[6] = (mtime >> 16) & 0xff;
            c[7] = (mtime >> 24) & 0xff;
            c[8] = 0x00; /* extra flags */
            c[9] = 0x03; /* UNIX */
            z.next_out = out + 10;
            z.avail_out = outlen - 10 - 8;
            z.total_out = 0;
            if(deflate(&z, Z_FINISH) != Z_STREAM_END)
            {
                deflateEnd(&z);
                free(*zstream);
                *zstream = NULL;
                return -1;
            }
            //crc
            crc = http_crc32(in, inlen);
            c = *zstream + 10 + z.total_out;
            c[0] = (crc >>  0) & 0xff;
            c[1] = (crc >>  8) & 0xff;
            c[2] = (crc >> 16) & 0xff;
            c[3] = (crc >> 24) & 0xff;
            c[4] = (z.total_in >>  0) & 0xff;
            c[5] = (z.total_in >>  8) & 0xff;
            c[6] = (z.total_in >> 16) & 0xff;
            c[7] = (z.total_in >> 24) & 0xff;
            outlen = (10 + 8 + z.total_out);
            if(deflateEnd(&z) != Z_OK)
            {
                free(*zstream);
                *zstream = NULL;
                return -1;
            }
            return outlen;
        }
    }
    return -1;
}

/* deflate */
int xhttpd_deflate(unsigned char **zstream, unsigned char *in, int inlen)
{
    unsigned char *out = NULL;
    z_stream z = {0};
    int outlen = 0;

    if(in && inlen > 0)
    {
        z.zalloc = Z_NULL;
        z.zfree = Z_NULL;
        z.opaque = Z_NULL;
        if(deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 
                    -MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK)
        {
            return -1;
        }
        z.next_in = (unsigned char *)in;
        z.avail_in = inlen;
        z.total_in = 0;
        outlen = (inlen * 1.1) + 12 + 18;
        if((*zstream = out = (unsigned char *)calloc(1, outlen)))
        {
            z.next_out = out;
            z.avail_out = outlen;
            z.total_out = 0;
            if(deflate(&z, Z_FINISH) != Z_STREAM_END)
            {
                deflateEnd(&z);
                free(*zstream);
                *zstream = NULL;
                return -1;
            }
            outlen = z.total_out;
            if(deflateEnd(&z) != Z_OK)
            {
                free(*zstream);
                *zstream = NULL;
                return -1;
            }
            return outlen;
        }
    }
    return -1;
}
#endif
#ifdef HAVE_BZ2LIB
int xhttpd_bzip2(unsigned char **zstream, unsigned char *in, int inlen)
{
    unsigned char *out = NULL;
    bz_stream bz = {0};
    int outlen = 0;

    if(in && inlen > 0)
    {
        bz.bzalloc = NULL;
        bz.bzfree = NULL;
        bz.opaque = NULL;
        if(BZ2_bzCompressInit(&bz, 9, 0, 0) != BZ_OK)
        {
            return -1;
        }
        bz.next_in = (char *)in;
        bz.avail_in = inlen;
        bz.total_in_lo32 = 0;
        bz.total_in_hi32 = 0;
        outlen = (inlen * 1.1) + 12;
        if((*zstream = out = (unsigned char *)calloc(1, outlen)))
        {
            bz.next_out = (char *)out;
            bz.avail_out = outlen;
            bz.total_out_lo32 = 0;
            bz.total_out_hi32 = 0;
            if(BZ2_bzCompress(&bz, BZ_FINISH) != BZ_STREAM_END)
            {
                BZ2_bzCompressEnd(&bz);
                free(*zstream);
                *zstream = NULL;
                return -1;
            }
            if(bz.total_out_hi32)
            {
                free(*zstream);
                *zstream = NULL;
                return -1;
            }
            outlen = bz.total_out_lo32;
            if(BZ2_bzCompressEnd(&bz) != BZ_OK)
            {
                free(*zstream);
                *zstream = NULL;
                return -1;
            }
        }
    }
    return -1;
}
#endif

/* httpd file compress */
int xhttpd_compress_handler(CONN *conn, HTTP_REQ *http_req, char *host, int is_need_compress, int mimeid,
        char *file, char *root, off_t from, off_t to, struct stat *st)
{
    char zfile[HTTP_PATH_MAX], zoldfile[HTTP_PATH_MAX], linkfile[HTTP_PATH_MAX], 
         buf[HTTP_BUF_SIZE], *encoding = NULL, *outfile = NULL, *p = NULL;
    unsigned char *block = NULL, *in = NULL, *zstream = NULL;
    int fd = 0, inlen = 0, zlen = 0, i = 0, id = 0, keepalive = 0;
    off_t offset = 0, len = 0;
    struct stat zst = {0};

    if(is_need_compress)
    {
        if(httpd_compress)
        {
            for(i = 0; i < HTTP_ENCODING_NUM; i++)
            {
                if(is_need_compress & ((id = (1 << i))))
                {
                    encoding = (char *)http_encodings[i];
                    if(from == 0 && to == st->st_size)
                    {
                        sprintf(linkfile, "%s/%s%s.%s",  httpd_compress_cachedir,
                                host, root, encoding);
                    }
                    else
                    {
                        sprintf(linkfile, "%s/%s%s-%lld-%lld.%s", httpd_compress_cachedir, 
                                host, root, LL(from), LL(to), encoding);
                    }
                    sprintf(zfile, "%s.%lu", linkfile, UL(st->st_mtime));
                    if(access(zfile, F_OK) == 0)
                    {
                        lstat(zfile, &zst);
                        outfile = zfile;
                        from = 0;
                        len = zst.st_size;
                        goto OVER;
                    }
                    else
                    {
                        if(access(linkfile, F_OK))
                        {
                            xhttpd_mkdir(linkfile, 0755);
                        }
                        else 
                        {
                            if(readlink(linkfile, zoldfile, (HTTP_PATH_MAX - 1)))
                                unlink(zoldfile);
                            unlink(linkfile);
                        }
                        goto COMPRESS;
                    }
                    break;
                }
            }
        }
COMPRESS:
        offset = (from/(off_t)HTTP_MMAP_MAX) * HTTP_MMAP_MAX;
        len = to - offset;
        if(len < HTTP_MMAP_MAX && (fd = open(file, O_RDONLY)) > 0)
        {
            if((block = (unsigned char *)mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0)))
            {
                in = block + (from%(off_t)HTTP_MMAP_MAX);
                inlen = to - from;

#ifdef HAVE_ZLIB
                if(is_need_compress & HTTP_ENCODING_DEFLATE)
                {
                    if((zlen = xhttpd_deflate(&zstream, in, inlen)) <= 0)
                        goto err;
                    encoding = "deflate";
                    //compressed |= HTTP_ENCODING_DEFLATE;
                }
                else if(is_need_compress & HTTP_ENCODING_GZIP)
                {
                    if((zlen = xhttpd_gzip(&zstream, in, inlen, 
                                    st->st_mtime)) <= 0) goto err;
                    encoding = "gzip";
                    //compressed |= HTTP_ENCODING_GZIP;

                }
                /*
                   else if(is_need_compress & HTTP_ENCODING_COMPRESS)
                   {
                   compressed |= HTTP_ENCODING_COMPRESS;
                   }
                   */
#endif  
#ifdef HAVE_BZ2LIB
                if(encoding == NULL && is_need_compress & HTTP_ENCODING_BZIP2)
                {
                    if((zlen = xhttpd_bzip2(&zstream, in, inlen)) <= 0) goto err;
                    encoding = "bzip2";
                    //compressed |= HTTP_ENCODING_BZIP2;
                }
#endif
                munmap(block, len);
                block = NULL;
            }
            close(fd);
            if(encoding == NULL) goto err;
            //write to cache file
            if(httpd_compress && zstream && zlen > 0)
            {
                if((fd = open(zfile, O_CREAT|O_WRONLY, 0644)) > 0)
                {
                    if(symlink(zfile, linkfile) != 0 || write(fd, zstream, zlen) <= 0 )
                    {
                        FATAL_LOGGER(logger, "symlink/write to %s failed, %s", 
                                linkfile, strerror(errno));
                    }
                    close(fd); 
                }
            }
        }
OVER:
        p = buf;
        if(from > 0)
        {
            p += sprintf(p, "HTTP/1.1 206 Partial Content\r\nAccept-Ranges: bytes\r\n"
                    "Content-Range: bytes %lld-%lld/%lld\r\n", 
                    LL(from), LL(to - 1), LL(st->st_size));
        }
        else
            p += sprintf(p, "HTTP/1.1 200 OK\r\nAccept-Ranges: bytes\r\n");
        p += sprintf(p, "Content-Type: %s; charset=%s\r\n",
                http_mime_types[mimeid].s, http_default_charset);
        /*
        if((n = http_req->headers[HEAD_GEN_CONNECTION]) > 0)
        {
            p += sprintf(p, "Connection: %s\r\n", http_req->hlines + n);
        }
        */
        p += sprintf(p, "Last-Modified:");
        p += GMTstrdate(st->st_mtime, p);
        p += sprintf(p, "%s", "\r\n");//date end
        if(zstream && zlen > 0) len = zlen;
        if(encoding) p += sprintf(p, "Content-Encoding: %s\r\n", encoding);
        p += sprintf(p, "Content-Length: %lld\r\n", LL(len));
        p += sprintf(p, "Date: ");p += GMTstrdate(time(NULL), p);p += sprintf(p, "\r\n");
        p += sprintf(p, "Connection: close\r\n");
        p += sprintf(p, "Server: xhttpd/%s\r\n\r\n", XHTTPD_VERSION);
        conn->push_chunk(conn, buf, (p - buf));
        if(zstream && zlen > 0)
        {
            conn->push_chunk(conn, zstream, zlen);
        }
        else
        {
            conn->push_file(conn, outfile, from, len);
        }
        if(zstream) free(zstream);
        if(!keepalive)conn->over(conn);
        return 0;
    }
err:
    return -1;
}

/* xhttpd bind proxy */
int xhttpd_bind_proxy(CONN *conn, char *host, int port) 
{
    CONN *new_conn = NULL;
    SESSION session = {0};
    char *ip = NULL;

    if(conn && host && port > 0)
    {
        if(ip)
        {
            session.packet_type = PACKET_PROXY;
            session.timeout = httpd_proxy_timeout;
            if((new_conn = service->newproxy(service, conn, -1, -1, ip, port, &session)))
            {
                new_conn->start_cstate(new_conn);
                return 0;
            }
        }
    }
    return -1;
}

你可能感兴趣的:(断点续传)