src/tcp_server.c
branchnew-lib-errors
changeset 219 cefec18b8268
parent 218 5229a5d098b2
equal deleted inserted replaced
218:5229a5d098b2 219:cefec18b8268
     1 #include "tcp_internal.h"
       
     2 #include "sock_internal.h"
       
     3 #include "log.h"
       
     4 
       
     5 #include <unistd.h>
       
     6 
       
     7 /*
       
     8  * Service methods
       
     9  */
       
    10 void tcp_server__deinit (service_t *service)
       
    11 {
       
    12     struct tcp_server *serv = service_check(service, &tcp_server_type);
       
    13 
       
    14     tcp_server_deinit(serv);
       
    15 }
       
    16 
       
    17 /*
       
    18  * Service type
       
    19  */
       
    20 const struct service_type tcp_server_type = {
       
    21     .base_type = {
       
    22         .parent = &service_type_type,
       
    23     },
       
    24     .methods = {
       
    25         .deinit = tcp_server__deinit,
       
    26     },
       
    27 };
       
    28 
       
    29 /*
       
    30  * We got a new client, build a transport for it and give it to the user
       
    31  */
       
    32 static err_t tcp_server_client (struct tcp_server *serv, evutil_socket_t sock, error_t *err)
       
    33 {
       
    34     struct tcp_transport *trans;
       
    35     
       
    36     // create a new transport for it, this also makes it nonblocking
       
    37     if (tcp_transport_create(&trans, &serv->base_service.info.trans_info, sock, err))
       
    38         goto error;
       
    39 
       
    40     // make it connected
       
    41     // this will call transport_callbacks::on_connect, which is all the user needs
       
    42     if (tcp_transport_connected(trans, err))
       
    43         goto error;
       
    44 
       
    45     // ok
       
    46     return SUCCESS;
       
    47 
       
    48 error:
       
    49     // cleanup
       
    50     if (trans)
       
    51         tcp_transport_destroy(trans);
       
    52 
       
    53     return ERROR_CODE(err);    
       
    54 }
       
    55 
       
    56 /*
       
    57  * Libevent callback
       
    58  */
       
    59 static void tcp_server_on_accept (evutil_socket_t sock, short what, void *arg)
       
    60 {
       
    61     struct tcp_server *serv = arg;
       
    62     evutil_socket_t client_sock;
       
    63     error_t err;
       
    64 
       
    65     (void) what;
       
    66 
       
    67     // accept as a new client connection
       
    68     if ((client_sock = accept(sock, NULL, NULL)) < 0 && errno != EAGAIN)
       
    69         JUMP_SET_ERROR_ERRNO(&err, ERR_ACCEPT);
       
    70 
       
    71     // spurious read event?
       
    72     if (client_sock < 0)
       
    73         return;
       
    74 
       
    75     // handle it
       
    76     if (tcp_server_client(serv, client_sock, &err))
       
    77         goto error;
       
    78 
       
    79     // ok
       
    80     return;
       
    81 
       
    82 error:
       
    83     if (client_sock >= 0)
       
    84         EVUTIL_CLOSESOCKET(client_sock);
       
    85 
       
    86     // faaail
       
    87     service_error(&serv->base_service, &err);
       
    88 }
       
    89 
       
    90 /*
       
    91  * Attempts to construct a listen()'d socket with the given addr, and return it
       
    92  *
       
    93  * @param addr the addrinfo to try and create a socket for
       
    94  * @param err returned error info
       
    95  * @return listening socket, or -err_t on error
       
    96  */
       
    97 static int tcp_server_sock_addr (struct addrinfo *addr, error_t *err)
       
    98 {
       
    99     evutil_socket_t sock;
       
   100 
       
   101     // create the sock
       
   102     if ((sock = tcp_sock_create(addr, err)) < 0)
       
   103         goto error;
       
   104 
       
   105     // bind it
       
   106     if (bind(sock, addr->ai_addr, addr->ai_addrlen) < 0)
       
   107         JUMP_SET_ERROR_ERRNO(err, ERR_BIND);
       
   108 
       
   109     // listen
       
   110     if (listen(sock, TCP_SERVER_BACKLOG) < 0)
       
   111         JUMP_SET_ERROR_ERRNO(err, ERR_LISTEN);
       
   112     
       
   113     // ok, valid socket
       
   114     return sock;
       
   115 
       
   116 error:
       
   117     if (sock >= 0)
       
   118         // cleanup
       
   119         EVUTIL_CLOSESOCKET(sock);
       
   120     
       
   121     return -ERROR_CODE(err);
       
   122 }
       
   123 
       
   124 /*
       
   125  * Construct a listen()'d socket with the given resolver result, and return it.
       
   126  *
       
   127  * @param rr the resolver lookup result to create a socket for
       
   128  * @param err returned error info
       
   129  * @return listening socket, or -err_t on error
       
   130  */
       
   131 static int tcp_server_sock (struct resolve_result *rr, error_t *err)
       
   132 {
       
   133     struct addrinfo *addr;
       
   134     evutil_socket_t sock;
       
   135 
       
   136     // try each addrinfo
       
   137     while ((addr = resolve_result_next(rr))) {
       
   138         // attempt to construct given socket
       
   139         if ((sock = tcp_server_sock_addr(addr, err)) < 0)
       
   140             // log an informative error warning
       
   141             log_warn_error(err, "%s", resolve_addr_text(addr)); 
       
   142 
       
   143         else
       
   144             // got a valid socket 
       
   145             break;
       
   146     }
       
   147     
       
   148     if (sock >= 0)
       
   149         // valid socket
       
   150         return sock;
       
   151 
       
   152     else
       
   153         // some error occured
       
   154         return -ERROR_CODE(err);
       
   155 }
       
   156 
       
   157 err_t tcp_server_listen (struct tcp_server *serv, const char *interface, const char *service, error_t *err)
       
   158 {
       
   159     struct resolve_result rr;
       
   160     evutil_socket_t sock;
       
   161 
       
   162     // get the global event_base
       
   163     struct event_base *ev_base = _sock_stream_ctx.ev_base;
       
   164 
       
   165     // init the resolver
       
   166     resolve_result_init(&rr);
       
   167 
       
   168     // resolve the interface/service
       
   169     if (resolve_addr(&rr, interface, service, SOCK_STREAM, AI_PASSIVE, err))
       
   170         return ERROR_CODE(err);
       
   171     
       
   172     // create the socket
       
   173     if ((sock = tcp_server_sock(&rr, err)) < 0)
       
   174         goto error;
       
   175 
       
   176     // deinit lookup results
       
   177     resolve_result_deinit(&rr);
       
   178 
       
   179     // make it nonblocking
       
   180     if (evutil_make_socket_nonblocking(sock))
       
   181         JUMP_SET_ERROR_STR(err, ERR_MISC, "evutil_make_socket_nonblocking");
       
   182     
       
   183     // construct event for the sock
       
   184     if ((serv->ev = event_new(ev_base, sock, EV_READ | EV_PERSIST, tcp_server_on_accept, serv)) == NULL)
       
   185         JUMP_SET_ERROR(err, ERR_EVENT_NEW);
       
   186 
       
   187     // add it
       
   188     if (event_add(serv->ev, NULL))
       
   189         JUMP_SET_ERROR(err, ERR_EVENT_ADD);
       
   190 
       
   191     // ok
       
   192     return SUCCESS;
       
   193 
       
   194 error:
       
   195     // deinit results just to be sure
       
   196     resolve_result_deinit(&rr);
       
   197 
       
   198     if (sock >= 0 && !serv->ev)
       
   199         // need to close socket ourselves, because we couldn't register our event for it
       
   200         EVUTIL_CLOSESOCKET(sock);
       
   201     
       
   202     // general cleanup
       
   203     tcp_server_deinit(serv);
       
   204     
       
   205     return ERROR_CODE(err);
       
   206 }
       
   207 
       
   208 void tcp_server_deinit (struct tcp_server *serv)
       
   209 {
       
   210     if (serv->ev) {
       
   211         // ignore errors
       
   212         event_del(serv->ev);
       
   213 
       
   214         // ignore errors
       
   215         close(event_get_fd(serv->ev));
       
   216         
       
   217         // release event
       
   218         event_free(serv->ev);
       
   219         
       
   220         // invalidate
       
   221         serv->ev = NULL;
       
   222     }
       
   223 }
       
   224 
       
   225 static void tcp_server_destroy (struct tcp_server *serv)
       
   226 {
       
   227     tcp_server_deinit(serv);
       
   228 
       
   229     free(serv);
       
   230 }
       
   231 
       
   232 /*
       
   233  * Public interface
       
   234  */
       
   235 err_t tcp_listen (const struct service_info *info, service_t **service_ptr, 
       
   236         const char *interface, const char *service, error_t *err)
       
   237 {
       
   238     struct tcp_server *serv;
       
   239 
       
   240     // alloc
       
   241     if ((serv = calloc(1, sizeof(*serv))) == NULL)
       
   242         return SET_ERROR(err, ERR_MEM);
       
   243 
       
   244     // init service
       
   245     service_init(&serv->base_service, &tcp_server_type, info);
       
   246 
       
   247     // init ourselves
       
   248     if (tcp_server_listen(serv, interface, service, err))
       
   249         goto error;
       
   250 
       
   251     // ok
       
   252     *service_ptr = &serv->base_service;
       
   253 
       
   254     return SUCCESS;
       
   255 
       
   256 error:
       
   257     tcp_server_destroy(serv);
       
   258 
       
   259     return ERROR_CODE(err);
       
   260 }