/* Called when headers are complete but body may not be. * Request is thus in use from another thread once headers * are complete. */ static void hwf_connection_container_on_incoming_message(HioConnection *connection, HioIncoming *incoming) { HrtTask *task; g_assert(HWF_IS_REQUEST_CONTAINER(incoming)); task = hrt_task_create_task(connection->task); hrt_debug("Created task %p for incoming request %p", task, incoming); /* execute the request handler */ hrt_task_add_immediate(task, on_request_run, g_object_ref(incoming), (GDestroyNotify) g_object_unref); /* don't quit the connection task while request tasks are pending */ hrt_task_add_subtask(connection->task, task, on_request_completed, g_object_ref(incoming), (GDestroyNotify) g_object_unref); g_object_unref(task); }
static gboolean on_server_socket_accepted(HioServer *server, int fd, void *data) { HwfContainer *container = HWF_CONTAINER(data); HrtTask *task; GValue value = { 0, }; hrt_debug("Creating connection for accepted socket %d", fd); task = hrt_task_runner_create_task(container->runner); g_value_init(&value, HJS_TYPE_RUNTIME_SPIDERMONKEY); g_value_set_object(&value, container->runtime); hrt_task_add_arg(task, "runtime", &value); g_value_unset(&value); hio_connection_process_socket(HWF_TYPE_CONNECTION_CONTAINER, task, fd); /* task will be ref'd by the watchers added by hwf_connection_process_socket() */ g_object_unref(task); return TRUE; }
static void hwf_request_container_add_header(HioRequestHttp *http, HrtBuffer *name, HrtBuffer *value) { /* FIXME */ hrt_debug("%s", G_STRFUNC); }
/* request has been sufficiently parsed (usually, it has all headers * but not the body if any) to go ahead and start working. */ void hwf_request_container_execute(HwfRequestContainer *request, HrtTask *request_task) { HioResponseHttp *response; HrtBuffer *buffer; hrt_debug("Executing request %p in task %p", request, request_task); response = hio_request_http_get_response(HIO_REQUEST_HTTP(request)); hio_response_http_send_headers(response); buffer = hrt_buffer_new_static_utf8_locked("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"><TITLE>HELLO WORLD</TITLE><P>This is a web page.</P>"); hio_response_http_write(response, buffer); hrt_buffer_unref(buffer); hio_response_http_close(response); }
static gboolean on_request_completed(HrtTask *task, HrtWatcherFlags flags, void *data) { HwfRequestContainer *request; HioResponseHttp *response; request = HWF_REQUEST_CONTAINER(data); hrt_debug("Task %p request %p completed", task, request); /* Automatically send headers and close the body, * if it wasn't done already. */ response = hio_request_http_get_response(HIO_REQUEST_HTTP(request)); hio_response_http_send_headers(response); hio_response_http_close(response); return FALSE; }
static HioRequestHttp* hwf_connection_container_create_request(HioConnectionHttp *http, const char *method, int major_version, int minor_version, const char *path, const char *query_string) { HioRequestHttp *request; GValue value = { 0, }; request = hio_request_http_new(HWF_TYPE_REQUEST_CONTAINER, method, major_version, minor_version, path, query_string); g_value_init(&value, HJS_TYPE_RUNTIME); if (!hrt_task_get_arg(HIO_CONNECTION(http)->task, "runtime", &value, NULL)) g_error("Task didn't have a runtime set on it"); hwf_request_container_set_runtime(HWF_REQUEST_CONTAINER(request), g_value_get_object(&value)); g_value_unset(&value); hrt_debug("Created request %s %d.%d '%s' query '%s'", hio_request_http_get_method(request), hio_request_http_get_major_version(request), hio_request_http_get_minor_version(request), hio_request_http_get_path(request), hio_request_http_get_query_string(request)); return request; }
gboolean hio_server_listen_tcp(HioServer *hio_server, const char *host, int port, GError **error) { struct addrinfo hints; struct addrinfo *ai_iter; struct addrinfo *ai; socklen_t addr_len; struct sockaddr_storage addr; char buf[10]; int result; GIOChannel *io_channel; int fd; if (g_atomic_int_get(&hio_server->fd) >= 0) { g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "Server open already"); return FALSE; } memset(&hints, '\0', sizeof(hints)); hints.ai_family = AF_INET; hints.ai_protocol = IPPROTO_TCP; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; /* AI_NUMERICHOST needed on numeric hosts... */ snprintf(buf, sizeof(buf), "%d", port); buf[sizeof(buf)-1] = '\0'; ai = NULL; result = getaddrinfo(host, &buf[0], &hints, &ai); if (result != 0 || ai == NULL) { g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "getaddrinfo(): %s", gai_strerror(result)); return FALSE; } /* for now we don't listen on multiple, just use first */ for (ai_iter = ai; ai_iter != NULL; ai_iter = ai_iter->ai_next) { fd = socket(ai_iter->ai_family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); if (fd < 0) { freeaddrinfo(ai); g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "socket(): %s", strerror(errno)); return FALSE; } if (bind(fd, (struct sockaddr*) ai_iter->ai_addr, ai_iter->ai_addrlen) < 0) { freeaddrinfo(ai); close(fd); g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "bind(): %s", strerror(errno)); return FALSE; } if (listen(fd, 75) < 0) { freeaddrinfo(ai); close(fd); g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "listen(): %s", strerror(errno)); return FALSE; } break; } freeaddrinfo(ai); addr_len = sizeof(addr); result = getsockname(fd, (struct sockaddr*) &addr, &addr_len); if (result < 0) { g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "getsockname(): %s", strerror(errno)); close(fd); return FALSE; } if (port == 0) { /* see what port we got */ result = getnameinfo((struct sockaddr*)&addr, addr_len, NULL, 0, buf, sizeof(buf), NI_NUMERICHOST); buf[sizeof(buf) - 1] = '\0'; if (result < 0) { g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "getnameinfo(): %s", strerror(errno)); close(fd); return FALSE; } hio_server->port = atoi(buf); if (hio_server->port == 0) { g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "invalid port %s", buf); close(fd); return FALSE; } } else { hio_server->port = port; } io_channel = g_io_channel_unix_new(fd); hio_server->on_new_connections_id = add_io_watch(hio_server->main_context, io_channel, G_IO_IN, on_new_connections, hio_server); g_io_channel_unref(io_channel); g_atomic_int_set(&hio_server->fd, fd); hrt_debug("Socket fd %d now open host '%s' port %u", fd, host, hio_server->port); return TRUE; }
static gboolean on_new_connections(GIOChannel *source, GIOCondition condition, gpointer data) { HioServer *hio_server = HIO_SERVER(data); struct sockaddr addr; socklen_t addrlen; int client_fd; gboolean accepted; if (!(condition & G_IO_IN)) return TRUE; addrlen = sizeof(addr); /* accept as many things as we can in a loop, this reduces * main loop iterations */ while (TRUE) { int listen_fd = g_atomic_int_get(&hio_server->fd); if (listen_fd < 0) { /* socket was closed */ break; } client_fd = accept4(listen_fd, &addr, &addrlen, SOCK_CLOEXEC | SOCK_NONBLOCK); if (client_fd < 0) { if (errno == EINTR) { continue; } else if (errno == EWOULDBLOCK || errno == EAGAIN) { /* nothing else to accept */ break; } else if (errno == EBADF) { /* we closed the socket */ hrt_debug("socket was invalid when we called accept4()"); break; } else { hrt_message("accept4(): %s", strerror(errno)); break; } } hrt_debug("accepted new socket %d", client_fd); accepted = FALSE; g_signal_emit(G_OBJECT(hio_server), signals[SOCKET_ACCEPTED], 0, client_fd, &accepted); /* If nobody else has returned true, we just close the new connection. */ if (!accepted) { shutdown(client_fd, SHUT_RDWR); close(client_fd); hrt_debug("nobody wanted new socket %d so we closed it", client_fd); } } return TRUE; }