Просмотр исходного кода

Merge branch 'dev' of https://github.com/ithewei/libhv into dev

ithewei 5 лет назад
Родитель
Сommit
5020f05ca7

+ 3 - 2
README.md

@@ -8,7 +8,7 @@ but simpler apis and richer protocols.
 
 ## Features
 
-- cross-platform (Linux, Windows, Mac)
+- cross-platform (Linux, Windows, Mac, Solaris)
 - event-loop (IO, timer, idle)
 - ENABLE_IPV6
 - WITH_OPENSSL
@@ -100,7 +100,8 @@ bin/curl -v localhost:8080/v1/api/echo -d "hello,world!"
 bin/curl -v localhost:8080/v1/api/query?page_no=1\&page_size=10
 bin/curl -v localhost:8080/v1/api/kv   -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
 bin/curl -v localhost:8080/v1/api/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
-bin/curl -v localhost:8080/v1/api/form -F "file=@LICENSE"
+bin/curl -v localhost:8080/v1/api/form -F "user=admin pswd=123456"
+bin/curl -v localhost:8080/v1/api/upload -F "file=@LICENSE"
 
 bin/curl -v localhost:8080/v1/api/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
 bin/curl -v localhost:8080/v1/api/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'

+ 26 - 24
base/hlog.c

@@ -47,6 +47,7 @@ struct logger_s {
     FILE*               fp_;
     char                cur_logfile[256];
     time_t              last_logfile_ts;
+    int                 can_write_cnt;
 
     hmutex_t            mutex_; // thread-safe
 };
@@ -65,6 +66,7 @@ static void logger_init(logger_t* logger) {
     logger->enable_fsync = 1;
     logger_set_file(logger, DEFAULT_LOG_FILE);
     logger->last_logfile_ts = 0;
+    logger->can_write_cnt = -1;
     hmutex_init(&logger->mutex_);
 }
 
@@ -149,7 +151,7 @@ static void ts_logfile(const char* filepath, time_t ts, char* buf, int len) {
 
 static FILE* shift_logfile(logger_t* logger) {
     time_t ts_now = time(NULL);
-    int interval_days = logger->last_logfile_ts == 0 ? 0 : (ts_now+s_gmtoff) / SECONDS_PER_DAY - (logger->last_logfile_ts+s_gmtoff) / SECONDS_PER_DAY;;
+    int interval_days = logger->last_logfile_ts == 0 ? 0 : (ts_now+s_gmtoff) / SECONDS_PER_DAY - (logger->last_logfile_ts+s_gmtoff) / SECONDS_PER_DAY;
     if (logger->fp_ == NULL || interval_days > 0) {
         // close old logfile
         if (logger->fp_) {
@@ -161,9 +163,9 @@ static FILE* shift_logfile(logger_t* logger) {
         }
 
         if (logger->remain_days >= 0) {
+            char rm_logfile[256] = {0};
             if (interval_days >= logger->remain_days) {
                 // remove [today-interval_days, today-remain_days] logfile
-                char rm_logfile[256] = {0};
                 for (int i = interval_days; i >= logger->remain_days; --i) {
                     time_t ts_rm  = ts_now - i * SECONDS_PER_DAY;
                     ts_logfile(logger->filepath, ts_rm, rm_logfile, sizeof(rm_logfile));
@@ -172,7 +174,6 @@ static FILE* shift_logfile(logger_t* logger) {
             }
             else {
                 // remove today-remain_days logfile
-                char rm_logfile[256] = {0};
                 time_t ts_rm  = ts_now - logger->remain_days * SECONDS_PER_DAY;
                 ts_logfile(logger->filepath, ts_rm, rm_logfile, sizeof(rm_logfile));
                 remove(rm_logfile);
@@ -187,15 +188,14 @@ static FILE* shift_logfile(logger_t* logger) {
         logger->last_logfile_ts = ts_now;
     }
 
-    // ftruncate
-    // NOTE; estimate can_write_cnt to avoid frequent fseek/ftell
-    static int s_can_write_cnt = 0;
-    if (logger->fp_ && --s_can_write_cnt < 0) {
+    // NOTE: estimate can_write_cnt to avoid frequent fseek/ftell
+    if (logger->fp_ && --logger->can_write_cnt < 0) {
         fseek(logger->fp_, 0, SEEK_END);
         long filesize = ftell(logger->fp_);
         if (filesize > logger->max_filesize) {
             fclose(logger->fp_);
             logger->fp_ = NULL;
+            // ftruncate
             logger->fp_ = fopen(logger->cur_logfile, "w");
             // reopen with O_APPEND for multi-processes
             if (logger->fp_) {
@@ -204,7 +204,7 @@ static FILE* shift_logfile(logger_t* logger) {
             }
         }
         else {
-            s_can_write_cnt = (logger->max_filesize - filesize) / logger->bufsize;
+            logger->can_write_cnt = (logger->max_filesize - filesize) / logger->bufsize;
         }
     }
 
@@ -215,21 +215,6 @@ int logger_print(logger_t* logger, int level, const char* fmt, ...) {
     if (level < logger->level)
         return -10;
 
-    const char* pcolor = "";
-    const char* plevel = "";
-#define XXX(id, str, clr) \
-    case id: plevel = str; pcolor = clr; break;
-
-    switch (level) {
-        LOG_LEVEL_MAP(XXX)
-    }
-#undef XXX
-
-    if (!logger->enable_color) {
-        pcolor = "";
-    }
-
-    // lock logger->buf
     int year,month,day,hour,min,sec,ms;
 #ifdef _WIN32
     SYSTEMTIME tm;
@@ -255,12 +240,29 @@ int logger_print(logger_t* logger, int level, const char* fmt, ...) {
     sec      = tm->tm_sec;
     ms       = tv.tv_usec/1000;
 #endif
+
+    const char* pcolor = "";
+    const char* plevel = "";
+#define XXX(id, str, clr) \
+    case id: plevel = str; pcolor = clr; break;
+
+    switch (level) {
+        LOG_LEVEL_MAP(XXX)
+    }
+#undef XXX
+
+    if (!logger->enable_color) {
+        pcolor = "";
+    }
+
+    // lock logger->buf
     hmutex_lock(&logger->mutex_);
     char* buf = logger->buf;
     int bufsize = logger->bufsize;
     int len = snprintf(buf, bufsize, "%s[%04d-%02d-%02d %02d:%02d:%02d.%03d][%s] ",
         pcolor,
-        year, month, day, hour, min, sec, ms, plevel);
+        year, month, day, hour, min, sec, ms,
+        plevel);
 
     va_list ap;
     va_start(ap, fmt);

+ 1 - 1
base/hmutex.h

@@ -193,7 +193,7 @@ END_EXTERN_C
 #ifdef __cplusplus
 #include <mutex>
 #include <condition_variable>
-using std::mutex;
+// using std::mutex;
 // NOTE: test std::timed_mutex incorrect in some platforms, use htimed_mutex_t
 // using std::timed_mutex;
 using std::condition_variable;

+ 2 - 0
base/hplatform.h

@@ -38,6 +38,8 @@
 #elif defined(__OpenBSD__)
     #define OS_OPENBSD
     #define OS_BSD
+#elif defined(sun) || defined(__sun) || defined(__sun__)
+    #define OS_SOLARIS
 #else
     #error "Unsupported operating system platform!"
 #endif

+ 3 - 3
base/hsocket.c

@@ -15,7 +15,7 @@ const char* socket_strerror(int err) {
 #endif
 }
 
-int Resolver(const char* host, sockaddr_un* addr) {
+int Resolver(const char* host, sockaddr_u* addr) {
     if (inet_pton(AF_INET, host, &addr->sin.sin_addr) == 1) {
         addr->sa.sa_family = AF_INET; // host is ipv4, so easy ;)
         return 0;
@@ -60,7 +60,7 @@ int Bind(int port, const char* host, int type) {
         WSAStartup(MAKEWORD(2,2), &wsadata);
     }
 #endif
-    sockaddr_un localaddr;
+    sockaddr_u localaddr;
     socklen_t addrlen = sizeof(localaddr);
     memset(&localaddr, 0, addrlen);
     int ret = sockaddr_assign(&localaddr, host, port);
@@ -109,7 +109,7 @@ error:
 
 int Connect(const char* host, int port, int nonblock) {
     // Resolver -> socket -> nonblocking -> connect
-    sockaddr_un peeraddr;
+    sockaddr_u peeraddr;
     socklen_t addrlen = sizeof(peeraddr);
     memset(&peeraddr, 0, addrlen);
     int ret = sockaddr_assign(&peeraddr, host, port);

+ 12 - 12
base/hsocket.h

@@ -51,23 +51,23 @@ typedef union {
     struct sockaddr     sa;
     struct sockaddr_in  sin;
     struct sockaddr_in6 sin6;
-} sockaddr_un;
+} sockaddr_u;
 
 // @param host: domain or ip
 // @retval 0:succeed
-int Resolver(const char* host, sockaddr_un* addr);
+int Resolver(const char* host, sockaddr_u* addr);
 
-static inline socklen_t sockaddrlen(sockaddr_un* addr) {
+static inline socklen_t sockaddrlen(sockaddr_u* addr) {
     if (addr->sa.sa_family == AF_INET) {
         return sizeof(struct sockaddr_in);
     }
     else if (addr->sa.sa_family == AF_INET6) {
         return sizeof(struct sockaddr_in6);
     }
-    return sizeof(sockaddr_un);
+    return sizeof(sockaddr_u);
 }
 
-static inline const char* sockaddr_ip(sockaddr_un* addr, char *ip, int len) {
+static inline const char* sockaddr_ip(sockaddr_u* addr, char *ip, int len) {
     if (addr->sa.sa_family == AF_INET) {
         return inet_ntop(AF_INET, &addr->sin.sin_addr, ip, len);
     }
@@ -77,7 +77,7 @@ static inline const char* sockaddr_ip(sockaddr_un* addr, char *ip, int len) {
     return ip;
 }
 
-static inline uint16_t sockaddr_port(sockaddr_un* addr) {
+static inline uint16_t sockaddr_port(sockaddr_u* addr) {
     uint16_t port = 0;
     if (addr->sa.sa_family == AF_INET) {
         port = htons(addr->sin.sin_port);
@@ -88,7 +88,7 @@ static inline uint16_t sockaddr_port(sockaddr_un* addr) {
     return port;
 }
 
-static inline void sockaddr_set_port(sockaddr_un* addr, int port) {
+static inline void sockaddr_set_port(sockaddr_u* addr, int port) {
     if (addr->sa.sa_family == AF_INET) {
         addr->sin.sin_port = ntohs(port);
     }
@@ -100,12 +100,12 @@ static inline void sockaddr_set_port(sockaddr_un* addr, int port) {
 //#define INET_ADDRSTRLEN   16
 //#define INET6_ADDRSTRLEN  46
 #define SOCKADDR_STRLEN     64 // ipv4:port | [ipv6]:port
-#define SOCKADDR_STR(addr, buf) sockaddr_str((sockaddr_un*)addr, buf, sizeof(buf))
-// NOTE: typeof(addr)=[sockaddr*, sockaddr_in*, sockaddr_in6*, sockaddr_un*]
+#define SOCKADDR_STR(addr, buf) sockaddr_str((sockaddr_u*)addr, buf, sizeof(buf))
+// NOTE: typeof(addr)=[sockaddr*, sockaddr_in*, sockaddr_in6*, sockaddr_u*]
 // char buf[SOCKADDR_STRLEN] = {0};
 // SOCKADDR_STR(addr, buf);
 
-static inline const char* sockaddr_str(sockaddr_un* addr, char* buf, int len) {
+static inline const char* sockaddr_str(sockaddr_u* addr, char* buf, int len) {
     char ip[SOCKADDR_STRLEN] = {0};
     uint16_t port = 0;
     if (addr->sa.sa_family == AF_INET) {
@@ -121,13 +121,13 @@ static inline const char* sockaddr_str(sockaddr_un* addr, char* buf, int len) {
     return buf;
 }
 
-static inline void sockaddr_print(sockaddr_un* addr) {
+static inline void sockaddr_print(sockaddr_u* addr) {
     char buf[SOCKADDR_STRLEN] = {0};
     sockaddr_str(addr, buf, sizeof(buf));
     puts(buf);
 }
 
-static inline int sockaddr_assign(sockaddr_un* addr, const char* host, int port) {
+static inline int sockaddr_assign(sockaddr_u* addr, const char* host, int port) {
     if (host) {
         int ret = Resolver(host, addr);
         if (ret != 0) return ret;

+ 3 - 1
base/htime.c

@@ -41,7 +41,7 @@ unsigned int gettick() {
 #endif
 }
 
-unsigned long long gethrtime() {
+unsigned long long gethrtime_us() {
 #ifdef OS_WIN
     static LONGLONG s_freq = 0;
     if (s_freq == 0) {
@@ -55,6 +55,8 @@ unsigned long long gethrtime() {
         return (unsigned long long)(count.QuadPart / (double)s_freq * 1000000);
     }
     return 0;
+#elif defined(OS_SOLARIS)
+    return gethrtime() / 1000;
 #elif HAVE_CLOCK_GETTIME
     struct timespec ts;
     clock_gettime(CLOCK_MONOTONIC, &ts);

+ 2 - 1
base/htime.h

@@ -72,8 +72,9 @@ static inline unsigned long long timestamp_ms() {
 void msleep(unsigned int ms);
 // ms
 unsigned int gettick();
+
 // us
-unsigned long long gethrtime();
+unsigned long long gethrtime_us();
 
 datetime_t datetime_now();
 time_t     datetime_mktime(datetime_t* dt);

+ 1 - 1
docs/apis.md

@@ -34,7 +34,7 @@
 - datetime_fmt
 - gmtime_fmt
 - gettick
-- gethrtime
+- gethrtime_us
 - sleep
 - msleep
 - usleep

+ 112 - 1
event/evport.c

@@ -2,25 +2,136 @@
 
 #ifdef EVENT_PORT
 
+#include "hplatform.h"
+#include "hdef.h"
+#include "hevent.h"
+
 #include <port.h>
 
+#define EVENTS_INIT_SIZE     64
+
+typedef struct evport_ctx_s {
+    int port;
+    int capacity;
+    int nevents;
+    port_event_t* events;
+} evport_ctx_t;
+
+static void evport_ctx_resize(evport_ctx_t* evport_ctx, int size) {
+    int bytes = sizeof(port_event_t) * size;
+    int oldbytes = sizeof(port_event_t) * evport_ctx->capacity;
+    evport_ctx->events = (port_event_t*)safe_realloc(evport_ctx->events, bytes, oldbytes);
+    evport_ctx->capacity = size;
+}
+
 int iowatcher_init(hloop_t* loop) {
+    if (loop->iowatcher) return 0;
+    evport_ctx_t* evport_ctx;
+    SAFE_ALLOC_SIZEOF(evport_ctx);
+    evport_ctx->port = port_create();
+    evport_ctx->capacity = EVENTS_INIT_SIZE;
+    evport_ctx->nevents = 0;
+    int bytes = sizeof(port_event_t) * evport_ctx->capacity;
+    SAFE_ALLOC(evport_ctx->events, bytes);
+    loop->iowatcher = evport_ctx;
     return 0;
 }
 
 int iowatcher_cleanup(hloop_t* loop) {
+    if (loop->iowatcher == NULL) return 0;
+    evport_ctx_t* evport_ctx = (evport_ctx_t*)loop->iowatcher;
+    close(evport_ctx->port);
+    SAFE_FREE(evport_ctx->events);
+    SAFE_FREE(loop->iowatcher);
     return 0;
 }
 
 int iowatcher_add_event(hloop_t* loop, int fd, int events) {
+    if (loop->iowatcher == NULL) {
+        iowatcher_init(loop);
+    }
+    evport_ctx_t* evport_ctx = (evport_ctx_t*)loop->iowatcher;
+    hio_t* io = loop->ios.ptr[fd];
+
+    int evport_events = 0;
+    if (io->events & HV_READ) {
+        evport_events |= POLLIN;
+    }
+    if (io->events & HV_WRITE) {
+        evport_events |= POLLOUT;
+    }
+    if (events & HV_READ) {
+        evport_events |= POLLIN;
+    }
+    if (events & HV_WRITE) {
+        evport_events |= POLLOUT;
+    }
+    port_associate(evport_ctx->port, PORT_SOURCE_FD, fd, evport_events, NULL);
+    if (io->events == 0) {
+        if (evport_ctx->nevents == evport_ctx->capacity) {
+            evport_ctx_resize(evport_ctx, evport_ctx->capacity * 2);
+        }
+        ++evport_ctx->nevents;
+    }
     return 0;
 }
 
 int iowatcher_del_event(hloop_t* loop, int fd, int events) {
+    evport_ctx_t* evport_ctx = (evport_ctx_t*)loop->iowatcher;
+    if (evport_ctx == NULL) return 0;
+    hio_t* io = loop->ios.ptr[fd];
+
+    int evport_events = 0;
+    if (io->events & HV_READ) {
+        evport_events |= POLLIN;
+    }
+    if (io->events & HV_WRITE) {
+        evport_events |= POLLOUT;
+    }
+    if (events & HV_READ) {
+        evport_events &= ~POLLIN;
+    }
+    if (events & HV_WRITE) {
+        evport_events &= ~POLLOUT;
+    }
+    if (evport_events == 0) {
+        port_dissociate(evport_ctx->port, PORT_SOURCE_FD, fd);
+        --evport_ctx->nevents;
+    } else {
+        port_associate(evport_ctx->port, PORT_SOURCE_FD, fd, evport_events, NULL);
+    }
     return 0;
 }
 
 int iowatcher_poll_events(hloop_t* loop, int timeout) {
-    return 0;
+    evport_ctx_t* evport_ctx = (evport_ctx_t*)loop->iowatcher;
+    if (evport_ctx == NULL) return 0;
+    struct timespec ts, *tp;
+    if (timeout == INFINITE) {
+        tp = NULL;
+    } else {
+        ts.tv_sec = timeout / 1000;
+        ts.tv_nsec = (timeout % 1000) * 1000000;
+        tp = &ts;
+    }
+    unsigned nevents = 1;
+    port_getn(evport_ctx->port, evport_ctx->events, evport_ctx->capacity, &nevents, tp);
+    for (int i = 0; i < nevents; ++i) {
+        int fd = evport_ctx->events[i].portev_object;
+        int revents = evport_ctx->events[i].portev_events;
+        hio_t* io = loop->ios.ptr[fd];
+        if (io) {
+            if (revents & POLLIN) {
+                io->revents |= HV_READ;
+            }
+            if (revents & POLLOUT) {
+                io->revents |= HV_WRITE;
+            }
+            EVENT_PENDING(io);
+        }
+        // Upon retrieval, the event object is no longer associated with the port.
+        iowatcher_add_event(loop, fd, io->events);
+    }
+    return nevents;
 }
 #endif

+ 2 - 2
event/hevent.c

@@ -68,14 +68,14 @@ void hio_set_type(hio_t* io, hio_type_e type) {
 
 void hio_set_localaddr(hio_t* io, struct sockaddr* addr, int addrlen) {
     if (io->localaddr == NULL) {
-        SAFE_ALLOC(io->localaddr, sizeof(sockaddr_un));
+        SAFE_ALLOC(io->localaddr, sizeof(sockaddr_u));
     }
     memcpy(io->localaddr, addr, addrlen);
 }
 
 void hio_set_peeraddr (hio_t* io, struct sockaddr* addr, int addrlen) {
     if (io->peeraddr == NULL) {
-        SAFE_ALLOC(io->peeraddr, sizeof(sockaddr_un));
+        SAFE_ALLOC(io->peeraddr, sizeof(sockaddr_u));
     }
     memcpy(io->peeraddr, addr, addrlen);
 }

+ 9 - 9
event/hloop.c

@@ -175,7 +175,7 @@ static void hloop_init(hloop_t* loop) {
     hmutex_init(&loop->custom_events_mutex);
     // NOTE: init start_time here, because htimer_add use it.
     loop->start_ms = timestamp_ms();
-    loop->start_hrtime = loop->cur_hrtime = gethrtime();
+    loop->start_hrtime = loop->cur_hrtime = gethrtime_us();
 }
 
 static void hloop_cleanup(hloop_t* loop) {
@@ -268,7 +268,7 @@ int hloop_run(hloop_t* loop) {
         }
     }
     loop->status = HLOOP_STATUS_STOP;
-    loop->end_hrtime = gethrtime();
+    loop->end_hrtime = gethrtime_us();
     if (loop->flags & HLOOP_FLAG_AUTO_FREE) {
         hloop_cleanup(loop);
         SAFE_FREE(loop);
@@ -296,7 +296,7 @@ int hloop_resume(hloop_t* loop) {
 }
 
 void hloop_update_time(hloop_t* loop) {
-    loop->cur_hrtime = gethrtime();
+    loop->cur_hrtime = gethrtime_us();
     if (ABS((int64_t)hloop_now(loop) - (int64_t)time(NULL)) > 1) {
         // systemtime changed, we adjust start_ms
         loop->start_ms = timestamp_ms() - (loop->cur_hrtime - loop->start_hrtime) / 1000;
@@ -473,12 +473,12 @@ static void hio_socket_init(hio_t* io) {
     nonblocking(io->fd);
     // fill io->localaddr io->peeraddr
     if (io->localaddr == NULL) {
-        SAFE_ALLOC(io->localaddr, sizeof(sockaddr_un));
+        SAFE_ALLOC(io->localaddr, sizeof(sockaddr_u));
     }
     if (io->peeraddr == NULL) {
-        SAFE_ALLOC(io->peeraddr, sizeof(sockaddr_un));
+        SAFE_ALLOC(io->peeraddr, sizeof(sockaddr_u));
     }
-    socklen_t addrlen = sizeof(sockaddr_un);
+    socklen_t addrlen = sizeof(sockaddr_u);
     int ret = getsockname(io->fd, io->localaddr, &addrlen);
     printd("getsockname fd=%d ret=%d errno=%d\n", io->fd, ret, socket_errno());
     // NOTE:
@@ -487,7 +487,7 @@ static void hio_socket_init(hio_t* io) {
     // tcp_client/udp_client peeraddr set by hio_setpeeraddr
     if (io->io_type == HIO_TYPE_TCP || io->io_type == HIO_TYPE_SSL) {
         // tcp acceptfd
-        addrlen = sizeof(sockaddr_un);
+        addrlen = sizeof(sockaddr_u);
         ret = getpeername(io->fd, io->peeraddr, &addrlen);
         printd("getpeername fd=%d ret=%d errno=%d\n", io->fd, ret, socket_errno());
     }
@@ -695,7 +695,7 @@ hio_t* create_tcp_server (hloop_t* loop, const char* host, int port, haccept_cb
 }
 
 hio_t* create_tcp_client (hloop_t* loop, const char* host, int port, hconnect_cb connect_cb) {
-    sockaddr_un peeraddr;
+    sockaddr_u peeraddr;
     socklen_t addrlen = sizeof(peeraddr);
     memset(&peeraddr, 0, addrlen);
     int ret = sockaddr_assign(&peeraddr, host, port);
@@ -727,7 +727,7 @@ hio_t* create_udp_server(hloop_t* loop, const char* host, int port) {
 
 // @client: Resolver -> socket -> hio_get -> hio_set_peeraddr
 hio_t* create_udp_client(hloop_t* loop, const char* host, int port) {
-    sockaddr_un peeraddr;
+    sockaddr_u peeraddr;
     socklen_t addrlen = sizeof(peeraddr);
     memset(&peeraddr, 0, addrlen);
     int ret = sockaddr_assign(&peeraddr, host, port);

+ 2 - 0
event/iowatcher.h

@@ -20,6 +20,8 @@
 #define EVENT_KQUEUE
 #elif defined(OS_BSD)
 #define EVENT_KQUEUE
+#elif defined(OS_SOLARIS)
+#define EVENT_PORT
 #else
 #define EVENT_SELECT
 #endif

+ 8 - 8
event/nio.c

@@ -45,7 +45,7 @@ static void nio_accept(hio_t* io) {
     //printd("nio_accept listenfd=%d\n", io->fd);
     socklen_t addrlen;
 accept:
-    addrlen = sizeof(sockaddr_un);
+    addrlen = sizeof(sockaddr_u);
     int connfd = accept(io->fd, io->peeraddr, &addrlen);
     hio_t* connio = NULL;
     if (connfd < 0) {
@@ -59,7 +59,7 @@ accept:
             goto accept_error;
         }
     }
-    addrlen = sizeof(sockaddr_un);
+    addrlen = sizeof(sockaddr_u);
     getsockname(connfd, io->localaddr, &addrlen);
     connio = hio_get(io->loop, connfd);
     // NOTE: inherit from listenio
@@ -105,7 +105,7 @@ accept_error:
 
 static void nio_connect(hio_t* io) {
     //printd("nio_connect connfd=%d\n", io->fd);
-    socklen_t addrlen = sizeof(sockaddr_un);
+    socklen_t addrlen = sizeof(sockaddr_u);
     int ret = getpeername(io->fd, io->peeraddr, &addrlen);
     if (ret < 0) {
         io->error = socket_errno();
@@ -113,7 +113,7 @@ static void nio_connect(hio_t* io) {
         goto connect_failed;
     }
     else {
-        addrlen = sizeof(sockaddr_un);
+        addrlen = sizeof(sockaddr_u);
         getsockname(io->fd, io->localaddr, &addrlen);
         /*
         char localaddrstr[SOCKADDR_STRLEN] = {0};
@@ -176,7 +176,7 @@ read:
     case HIO_TYPE_UDP:
     case HIO_TYPE_IP:
     {
-        socklen_t addrlen = sizeof(sockaddr_un);
+        socklen_t addrlen = sizeof(sockaddr_u);
         nread = recvfrom(io->fd, buf, len, 0, io->peeraddr, &addrlen);
     }
         break;
@@ -239,7 +239,7 @@ write:
         break;
     case HIO_TYPE_UDP:
     case HIO_TYPE_IP:
-        nwrite = sendto(io->fd, buf, len, 0, io->peeraddr, sizeof(sockaddr_un));
+        nwrite = sendto(io->fd, buf, len, 0, io->peeraddr, sizeof(sockaddr_u));
         break;
     default:
         nwrite = write(io->fd, buf, len);
@@ -324,7 +324,7 @@ static void connect_timeout_cb(htimer_t* timer) {
 }
 
 int hio_connect(hio_t* io) {
-    int ret = connect(io->fd, io->peeraddr, sizeof(sockaddr_un));
+    int ret = connect(io->fd, io->peeraddr, sizeof(sockaddr_u));
 #ifdef OS_WIN
     if (ret < 0 && socket_errno() != WSAEWOULDBLOCK) {
 #else
@@ -370,7 +370,7 @@ try_write:
             break;
         case HIO_TYPE_UDP:
         case HIO_TYPE_IP:
-            nwrite = sendto(io->fd, buf, len, 0, io->peeraddr, sizeof(sockaddr_un));
+            nwrite = sendto(io->fd, buf, len, 0, io->peeraddr, sizeof(sockaddr_u));
             break;
         default:
             nwrite = write(io->fd, buf, len);

+ 19 - 0
examples/httpd/http_api_test.h

@@ -11,6 +11,7 @@
     XXX("/kv",      POST,   http_api_kv)        \
     XXX("/json",    POST,   http_api_json)      \
     XXX("/form",    POST,   http_api_form)      \
+    XXX("/upload",  POST,   http_api_upload)    \
     XXX("/grpc",    POST,   http_api_grpc)      \
     \
     XXX("/test",    POST,   http_api_test)      \
@@ -86,6 +87,24 @@ inline int http_api_form(HttpRequest* req, HttpResponse* res) {
     return 0;
 }
 
+inline int http_api_upload(HttpRequest* req, HttpResponse* res) {
+    if (req->content_type != MULTIPART_FORM_DATA) {
+        res->status_code = HTTP_STATUS_BAD_REQUEST;
+        return 0;
+    }
+    FormData file = req->form["file"];
+    string filepath("html/uploads/");
+    filepath += file.filename;
+    FILE* fp = fopen(filepath.c_str(), "w");
+    if (fp) {
+        hlogi("Save as %s", filepath.c_str());
+        fwrite(file.content.data(), 1, file.content.size(), fp);
+        fclose(fp);
+    }
+    response_status(res, 0, "OK");
+    return 0;
+}
+
 inline int http_api_grpc(HttpRequest* req, HttpResponse* res) {
     if (req->content_type != APPLICATION_GRPC) {
         res->status_code = HTTP_STATUS_BAD_REQUEST;

+ 4 - 1
html/downloads/scripts/getting_started.sh

@@ -35,7 +35,10 @@ read -n1
 bin/curl -v localhost:8080/v1/api/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
 
 read -n1
-bin/curl -v localhost:8080/v1/api/form -F "file=@LICENSE"
+bin/curl -v localhost:8080/v1/api/form -F "user=admin pswd=123456"
+
+read -n1
+bin/curl -v localhost:8080/v1/api/upload -F "file=@LICENSE"
 
 read -n1
 bin/curl -v localhost:8080/v1/api/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'

+ 1 - 0
html/uploads/upload.sh

@@ -0,0 +1 @@
+bin/curl -v localhost:8080/v1/api/upload -F "file=@LICENSE"

+ 3 - 1
http/HttpMessage.cpp

@@ -342,7 +342,9 @@ int HttpMessage::ParseBody() {
             return false;
         }
         boundary += strlen("boundary=");
-        return parse_multipart(body, form, boundary);
+        string strBoundary(boundary);
+        strBoundary = trim_pairs(strBoundary, "\"\"\'\'");
+        return parse_multipart(body, form, strBoundary.c_str());
     }
     case X_WWW_FORM_URLENCODED:
         return parse_query_params(body.c_str(), kv);

+ 5 - 4
http/http_content.cpp

@@ -141,7 +141,8 @@ struct multipart_parser_userdata {
                 StringList kv = split(trim(str, " "), '=');
                 if (kv.size() == 2) {
                     const char* key = kv.begin()->c_str();
-                    const char* value = trim_pairs(*(kv.begin()+1), "\"\"").c_str();
+                    string value = *(kv.begin() + 1);
+                    value = trim_pairs(value, "\"\"\'\'");
                     if (strcmp(key, "name") == 0) {
                         name = value;
                     }
@@ -169,21 +170,21 @@ static int on_header_field(multipart_parser* parser, const char *at, size_t leng
     multipart_parser_userdata* userdata = (multipart_parser_userdata*)multipart_parser_get_data(parser);
     userdata->handle_header();
     userdata->state = MP_HEADER_FIELD;
-    userdata->header_field.insert(userdata->header_field.size(), at, length);
+    userdata->header_field.append(at, length);
     return 0;
 }
 static int on_header_value(multipart_parser* parser, const char *at, size_t length) {
     //printf("on_header_value:%.*s\n", (int)length, at);
     multipart_parser_userdata* userdata = (multipart_parser_userdata*)multipart_parser_get_data(parser);
     userdata->state = MP_HEADER_VALUE;
-    userdata->header_value.insert(userdata->header_value.size(), at, length);
+    userdata->header_value.append(at, length);
     return 0;
 }
 static int on_part_data(multipart_parser* parser, const char *at, size_t length) {
     //printf("on_part_data:%.*s\n", (int)length, at);
     multipart_parser_userdata* userdata = (multipart_parser_userdata*)multipart_parser_get_data(parser);
     userdata->state = MP_PART_DATA;
-    userdata->part_data.insert(userdata->part_data.size(), at, length);
+    userdata->part_data.append(at, length);
     return 0;
 }
 static int on_part_data_begin(multipart_parser* parser) {

+ 2 - 2
http/server/HttpServer.cpp

@@ -230,8 +230,8 @@ static void on_accept(hio_t* io) {
     HttpHandler* handler = new HttpHandler;
     handler->service = (HttpService*)hevent_userdata(io);
     handler->files = &s_filecache;
-    sockaddr_ip((sockaddr_un*)hio_peeraddr(io), handler->ip, sizeof(handler->ip));
-    handler->port = sockaddr_port((sockaddr_un*)hio_peeraddr(io));
+    sockaddr_ip((sockaddr_u*)hio_peeraddr(io), handler->ip, sizeof(handler->ip));
+    handler->port = sockaddr_port((sockaddr_u*)hio_peeraddr(io));
     handler->io = io;
     hevent_set_userdata(io, handler);
 }

+ 3 - 3
protocol/icmp.c

@@ -27,7 +27,7 @@ int ping(const char* host, int cnt) {
     //min_rtt = MIN(rtt, min_rtt);
     //max_rtt = MAX(rtt, max_rtt);
     // gethostbyname -> socket -> setsockopt -> sendto -> recvfrom -> closesocket
-    sockaddr_un peeraddr;
+    sockaddr_u peeraddr;
     socklen_t addrlen = sizeof(peeraddr);
     memset(&peeraddr, 0, addrlen);
     int ret = Resolver(host, &peeraddr);
@@ -67,7 +67,7 @@ int ping(const char* host, int cnt) {
         icmp_req->icmp_seq = ++seq;
         icmp_req->icmp_cksum = 0;
         icmp_req->icmp_cksum = checksum((uint8_t*)icmp_req, sendbytes);
-        start_hrtime = gethrtime();
+        start_hrtime = gethrtime_us();
         addrlen = sockaddrlen(&peeraddr);
         int nsend = sendto(sockfd, sendbuf, sendbytes, 0, &peeraddr.sa, addrlen);
         if (nsend < 0) {
@@ -82,7 +82,7 @@ int ping(const char* host, int cnt) {
             continue;
         }
         ++recv_cnt;
-        end_hrtime = gethrtime();
+        end_hrtime = gethrtime_us();
         // check valid
         bool valid = false;
         int iphdr_len = ipheader->ihl * 4;

+ 3 - 1
readme_cn.md

@@ -99,7 +99,8 @@ bin/curl -v localhost:8080/v1/api/echo -d "hello,world!"
 bin/curl -v localhost:8080/v1/api/query?page_no=1\&page_size=10
 bin/curl -v localhost:8080/v1/api/kv   -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
 bin/curl -v localhost:8080/v1/api/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
-bin/curl -v localhost:8080/v1/api/form -F "file=@LICENSE"
+bin/curl -v localhost:8080/v1/api/form -F "user=admin pswd=123456"
+bin/curl -v localhost:8080/v1/api/upload -F "file=@LICENSE"
 
 bin/curl -v localhost:8080/v1/api/test -H "Content-Type:application/x-www-form-urlencoded" -d 'bool=1&int=123&float=3.14&string=hello'
 bin/curl -v localhost:8080/v1/api/test -H "Content-Type:application/json" -d '{"bool":true,"int":123,"float":3.14,"string":"hello"}'
@@ -291,6 +292,7 @@ sudo echo-servers/benchmark.sh
 - EVENT_POLL
 - EVENT_EPOLL   (linux only)
 - EVENT_KQUEUE  (mac/bsd)
+- EVENT_PORT    (solaris)
 - EVENT_IOCP    (windows only)
 
 ### http