static void postal_http_router (SoupServer *server, SoupMessage *message, const gchar *path, GHashTable *query, SoupClientContext *client, gpointer user_data) { PostalHttpPrivate *priv; PostalHttp *http = user_data; ENTRY; g_assert(SOUP_IS_SERVER(server)); g_assert(SOUP_IS_MESSAGE(message)); g_assert(path); g_assert(client); g_assert(POSTAL_IS_HTTP(http)); priv = http->priv; g_object_set_data_full(G_OBJECT(message), "http", g_object_ref(http), g_object_unref); if (!url_router_route(priv->router, server, message, path, query, client)) { soup_message_set_status(message, SOUP_STATUS_NOT_FOUND); } EXIT; }
/** * soup_server_get_port: * @server: a #SoupServer * * Gets the TCP port that @server is listening on. This is most useful * when you did not request a specific port (or explicitly requested * %SOUP_ADDRESS_ANY_PORT). * * Return value: the port @server is listening on. **/ guint soup_server_get_port (SoupServer *server) { g_return_val_if_fail (SOUP_IS_SERVER (server), 0); return SOUP_SERVER_GET_PRIVATE (server)->port; }
/** * soup_server_add_handler: * @server: a #SoupServer * @path: the toplevel path for the handler * @callback: callback to invoke for requests under @path * @user_data: data for @callback * @destroy: destroy notifier to free @user_data * * Adds a handler to @server for requests under @path. See the * documentation for #SoupServerCallback for information about * how callbacks should behave. * * If @path is %NULL or "/", then this will be the default handler for * all requests that don't have a more specific handler. Note though * that if you want to handle requests to the special "*" URI, you * must explicitly register a handler for "*"; the default handler * will not be used for that case. **/ void soup_server_add_handler (SoupServer *server, const char *path, SoupServerCallback callback, gpointer user_data, GDestroyNotify destroy) { SoupServerPrivate *priv; SoupServerHandler *hand; g_return_if_fail (SOUP_IS_SERVER (server)); g_return_if_fail (callback != NULL); priv = SOUP_SERVER_GET_PRIVATE (server); /* "" was never documented as meaning the same this as "/", * but it effectively was. We have to special case it now or * otherwise it would match "*" too. */ if (path && (!*path || !strcmp (path, "/"))) path = NULL; hand = g_slice_new0 (SoupServerHandler); hand->path = g_strdup (path); hand->callback = callback; hand->destroy = destroy; hand->user_data = user_data; soup_server_remove_handler (server, path); if (path) soup_path_map_add (priv->handlers, path, hand); else priv->default_handler = hand; }
/** * soup_server_unpause_message: * @server: a #SoupServer * @msg: a #SoupMessage associated with @server. * * Resumes I/O on @msg. Use this to resume after calling * soup_server_pause_message(), or after adding a new chunk to a * chunked response. * * I/O won't actually resume until you return to the main loop. **/ void soup_server_unpause_message (SoupServer *server, SoupMessage *msg) { g_return_if_fail (SOUP_IS_SERVER (server)); g_return_if_fail (SOUP_IS_MESSAGE (msg)); soup_message_io_unpause (msg); }
/** * soup_server_is_https: * @server: a #SoupServer * * Checks whether @server is running plain http or https. * * In order for a server to run https, you must set the * %SOUP_SERVER_SSL_CERT_FILE and %SOUP_SERVER_SSL_KEY_FILE properties * to provide it with an SSL certificate to use. * * Return value: %TRUE if @server is serving https. **/ gboolean soup_server_is_https (SoupServer *server) { SoupServerPrivate *priv; g_return_val_if_fail (SOUP_IS_SERVER (server), 0); priv = SOUP_SERVER_GET_PRIVATE (server); return (priv->ssl_cert_file && priv->ssl_key_file); }
/** * soup_server_get_listener: * @server: a #SoupServer * * Gets @server's listening socket. You should treat this as * read-only; writing to it or modifiying it may cause @server to * malfunction. * * Return value: the listening socket. **/ SoupSocket * soup_server_get_listener (SoupServer *server) { SoupServerPrivate *priv; g_return_val_if_fail (SOUP_IS_SERVER (server), NULL); priv = SOUP_SERVER_GET_PRIVATE (server); return priv->listen_sock; }
/** * soup_server_get_async_context: * @server: a #SoupServer * * Gets @server's async_context. This does not add a ref to the * context, so you will need to ref it yourself if you want it to * outlive its server. * * Return value: @server's #GMainContext, which may be %NULL **/ GMainContext * soup_server_get_async_context (SoupServer *server) { SoupServerPrivate *priv; g_return_val_if_fail (SOUP_IS_SERVER (server), NULL); priv = SOUP_SERVER_GET_PRIVATE (server); return priv->async_context; }
/** * soup_server_remove_auth_domain: * @server: a #SoupServer * @auth_domain: a #SoupAuthDomain * * Removes @auth_domain from @server. **/ void soup_server_remove_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain) { SoupServerPrivate *priv; g_return_if_fail (SOUP_IS_SERVER (server)); priv = SOUP_SERVER_GET_PRIVATE (server); priv->auth_domains = g_slist_remove (priv->auth_domains, auth_domain); g_object_unref (auth_domain); }
/** * soup_server_quit: * @server: a #SoupServer * * Stops processing for @server. Call this to clean up after * soup_server_run_async(), or to terminate a call to soup_server_run(). * * @server is still in a working state after this call; you can start * and stop a server as many times as you want. **/ void soup_server_quit (SoupServer *server) { SoupServerPrivate *priv; g_return_if_fail (SOUP_IS_SERVER (server)); priv = SOUP_SERVER_GET_PRIVATE (server); g_signal_handlers_disconnect_by_func (priv->listen_sock, G_CALLBACK (new_connection), server); if (priv->loop) g_main_loop_quit (priv->loop); }
void web_setup (WebFixture *fix, gconstpointer data) { if (!PORT) PORT = 52853; fix->server = soup_server_new (SOUP_SERVER_PORT, PORT, NULL); while (!SOUP_IS_SERVER (fix->server)) { PORT++; fix->server = soup_server_new (SOUP_SERVER_PORT, PORT, NULL); } soup_server_add_handler (fix->server, "/feeds/", feed_server, NULL, NULL); soup_server_add_handler (fix->server, "/video/", video_server, NULL, NULL); soup_server_add_handler (fix->server, "/redirect/", redirect_server, NULL, NULL); soup_server_run_async (fix->server); }
/** * soup_server_run: * @server: a #SoupServer * * Starts @server, causing it to listen for and process incoming * connections. Unlike soup_server_run_async(), this creates a * #GMainLoop and runs it, and it will not return until someone calls * soup_server_quit() to stop the server. **/ void soup_server_run (SoupServer *server) { SoupServerPrivate *priv; g_return_if_fail (SOUP_IS_SERVER (server)); priv = SOUP_SERVER_GET_PRIVATE (server); if (!priv->loop) { priv->loop = g_main_loop_new (priv->async_context, TRUE); soup_server_run_async (server); } if (priv->loop) g_main_loop_run (priv->loop); }
static SoupServerHandler * soup_server_get_handler (SoupServer *server, const char *path) { SoupServerPrivate *priv; SoupServerHandler *hand; g_return_val_if_fail (SOUP_IS_SERVER (server), NULL); priv = SOUP_SERVER_GET_PRIVATE (server); if (path) { hand = soup_path_map_lookup (priv->handlers, path); if (hand) return hand; if (!strcmp (path, "*")) return NULL; } return priv->default_handler; }
static void postal_http_handle_v1_users_user_devices (UrlRouter *router, SoupServer *server, SoupMessage *message, const gchar *path, GHashTable *params, GHashTable *query, SoupClientContext *client, gpointer user_data) { const gchar *user; PostalHttp *http = user_data; ENTRY; g_assert(router); g_assert(SOUP_IS_SERVER(server)); g_assert(SOUP_IS_MESSAGE(message)); g_assert(path); g_assert(params); g_assert(g_hash_table_contains(params, "user")); g_assert(client); g_assert(POSTAL_IS_HTTP(http)); user = g_hash_table_lookup(params, "user"); soup_server_pause_message(server, message); if (message->method == SOUP_METHOD_GET) { postal_service_find_devices(http->priv->service, user, get_int_param(query, "offset"), get_int_param(query, "limit"), NULL, /* TODO */ devices_get_cb, g_object_ref(message)); EXIT; } soup_message_set_status(message, SOUP_STATUS_METHOD_NOT_ALLOWED); soup_server_unpause_message(server, message); EXIT; }
/** * soup_server_run_async: * @server: a #SoupServer * * Starts @server, causing it to listen for and process incoming * connections. * * The server actually runs in @server's #GMainContext. It will not * actually perform any processing unless the appropriate main loop is * running. In the simple case where you did not set the server's * %SOUP_SERVER_ASYNC_CONTEXT property, this means the server will run * whenever the glib main loop is running. **/ void soup_server_run_async (SoupServer *server) { SoupServerPrivate *priv; g_return_if_fail (SOUP_IS_SERVER (server)); priv = SOUP_SERVER_GET_PRIVATE (server); if (!priv->listen_sock) { if (priv->loop) { g_main_loop_unref (priv->loop); priv->loop = NULL; } return; } g_signal_connect (priv->listen_sock, "new_connection", G_CALLBACK (new_connection), server); return; }
/** * soup_server_remove_handler: * @server: a #SoupServer * @path: the toplevel path for the handler * * Removes the handler registered at @path. **/ void soup_server_remove_handler (SoupServer *server, const char *path) { SoupServerPrivate *priv; SoupServerHandler *hand; g_return_if_fail (SOUP_IS_SERVER (server)); priv = SOUP_SERVER_GET_PRIVATE (server); if (!path || !*path || !strcmp (path, "/")) { if (priv->default_handler) { unregister_handler (priv->default_handler); free_handler (priv->default_handler); priv->default_handler = NULL; } return; } hand = soup_path_map_lookup (priv->handlers, path); if (hand && !strcmp (path, hand->path)) { unregister_handler (hand); soup_path_map_remove (priv->handlers, path); } }
static void postal_http_handle_v1_users_user_badge (UrlRouter *router, SoupServer *server, SoupMessage *message, const gchar *path, GHashTable *params, GHashTable *query, SoupClientContext *client, gpointer user_data) { const gchar *user; PostalHttp *http = user_data; JsonNode *node = NULL; GError *error = NULL; guint badge; ENTRY; g_assert(router); g_assert(SOUP_IS_SERVER(server)); g_assert(SOUP_IS_MESSAGE(message)); g_assert(path); g_assert(params); g_assert(g_hash_table_contains(params, "user")); g_assert(client); g_assert(POSTAL_IS_HTTP(http)); user = g_hash_table_lookup(params, "user"); if (message->method == SOUP_METHOD_PUT) { if (!(node = postal_http_parse_body(message, &error)) || !JSON_NODE_HOLDS_VALUE(node)) { if (!error) { error = g_error_new(JSON_PARSER_ERROR, JSON_PARSER_ERROR_UNKNOWN, _("JSON must contain integer.")); } postal_http_reply_error(http, message, error); GOTO(cleanup); } badge = json_node_get_int(node); g_object_set_data(G_OBJECT(message), "http", http); postal_service_set_user_badge(http->priv->service, user, badge, NULL, postal_http_set_user_badge_cb, g_object_ref(message)); soup_server_pause_message(server, message); EXIT; } soup_message_set_status(message, SOUP_STATUS_METHOD_NOT_ALLOWED); cleanup: g_clear_error(&error); if (node) { json_node_free(node); } EXIT; }
static void postal_http_handle_v1_notify (UrlRouter *router, SoupServer *server, SoupMessage *message, const gchar *path, GHashTable *params, GHashTable *query, SoupClientContext *client, gpointer user_data) { PostalNotification *notif; const gchar *collapse_key = NULL; const gchar *str; PostalHttp *http = user_data; JsonObject *aps; JsonObject *c2dm; JsonObject *gcm; JsonObject *object; JsonArray *devices; JsonArray *users; GPtrArray *devices_ptr; GPtrArray *users_ptr; JsonNode *node; GError *error = NULL; guint count; guint i; g_assert(SOUP_IS_SERVER(server)); g_assert(SOUP_IS_MESSAGE(message)); g_assert(path); g_assert(client); g_assert(POSTAL_IS_HTTP(http)); if (message->method != SOUP_METHOD_POST) { soup_message_set_status(message, SOUP_STATUS_METHOD_NOT_ALLOWED); return; } soup_server_pause_message(server, message); if (!(node = postal_http_parse_body(message, &error))) { postal_http_reply_error(http, message, error); g_error_free(error); return; } if (!JSON_NODE_HOLDS_OBJECT(node) || !(object = json_node_get_object(node)) || !json_object_has_member(object, "aps") || !(node = json_object_get_member(object, "aps")) || !JSON_NODE_HOLDS_OBJECT(node) || !(aps = json_object_get_object_member(object, "aps")) || !json_object_has_member(object, "c2dm") || !(node = json_object_get_member(object, "c2dm")) || !JSON_NODE_HOLDS_OBJECT(node) || !(c2dm = json_object_get_object_member(object, "c2dm")) || !json_object_has_member(object, "gcm") || !(node = json_object_get_member(object, "gcm")) || !JSON_NODE_HOLDS_OBJECT(node) || !(gcm = json_object_get_object_member(object, "gcm")) || !json_object_has_member(object, "users") || !(node = json_object_get_member(object, "users")) || !JSON_NODE_HOLDS_ARRAY(node) || !(users = json_object_get_array_member(object, "users")) || !json_object_has_member(object, "devices") || !(node = json_object_get_member(object, "devices")) || !JSON_NODE_HOLDS_ARRAY(node) || !(devices = json_object_get_array_member(object, "devices"))) { error = g_error_new(postal_json_error_quark(), 0, "Missing or invalid fields in JSON payload."); postal_http_reply_error(http, message, error); json_node_free(node); g_error_free(error); return; } if (json_object_has_member(object, "collapse_key") && (node = json_object_get_member(object, "collapse_key")) && JSON_NODE_HOLDS_VALUE(node)) { collapse_key = json_node_get_string(node); } notif = g_object_new(POSTAL_TYPE_NOTIFICATION, "aps", aps, "c2dm", c2dm, "collapse-key", collapse_key, "gcm", gcm, NULL); count = json_array_get_length(users); users_ptr = g_ptr_array_sized_new(count); for (i = 0; i < count; i++) { node = json_array_get_element(users, i); if (json_node_get_value_type(node) == G_TYPE_STRING) { str = json_node_get_string(node); g_ptr_array_add(users_ptr, (gchar *)str); } } g_ptr_array_add(users_ptr, NULL); count = json_array_get_length(devices); devices_ptr = g_ptr_array_sized_new(count); g_ptr_array_set_free_func(devices_ptr, g_free); for (i = 0; i < count; i++) { node = json_array_get_element(devices, i); if (json_node_get_value_type(node) == G_TYPE_STRING) { str = json_node_get_string(node); g_ptr_array_add(devices_ptr, g_strdup(str)); } } g_ptr_array_add(devices_ptr, NULL); postal_service_notify(http->priv->service, notif, (gchar **)users_ptr->pdata, (gchar **)devices_ptr->pdata, NULL, /* TODO: Cancellable/Timeout? */ postal_http_notify_cb, g_object_ref(message)); g_ptr_array_unref(devices_ptr); g_ptr_array_unref(users_ptr); json_node_free(node); g_object_unref(notif); }
gboolean url_router_route (UrlRouter *router, SoupServer *server, SoupMessage *message, const gchar *path, GHashTable *query, SoupClientContext *client) { UrlNodeData *data; const gchar *cur; const gchar *end; GHashTable *params = NULL; gboolean ret = FALSE; GNode *node = (GNode *)router; gchar *part; g_return_val_if_fail(router, FALSE); g_return_val_if_fail(SOUP_IS_SERVER(server), FALSE); g_return_val_if_fail(SOUP_IS_MESSAGE(message), FALSE); g_return_val_if_fail(path, FALSE); g_return_val_if_fail(client, FALSE); cur = path; if (*path != '/') { return FALSE; } cur++; while (node && *cur) { if (!(node = node->children)) { break; } end = cur; while (*end && *end != '/') end++; for (; node; node = node->next) { data = node->data; if (data->catchall) { if (!params) { params = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); } part = g_strndup(cur, end - cur); g_hash_table_insert(params, (gchar *)data->key + 1, part); break; } else if (url_node_data_match(data, cur, end - cur)) { break; } } cur = end; if (*cur == '/') cur++; } if (node && (data = node->data) && data->handler) { data->handler(router, server, message, path, params, query, client, data->handler_data); ret = TRUE; } if (params) { g_hash_table_unref(params); } return ret; }
static void postal_http_handle_v1_users_user_devices_device (UrlRouter *router, SoupServer *server, SoupMessage *message, const gchar *path, GHashTable *params, GHashTable *query, SoupClientContext *client, gpointer user_data) { PostalDevice *pdev; const gchar *user; const gchar *device; PostalHttp *http = user_data; GError *error = NULL; JsonNode *node; ENTRY; g_assert(router); g_assert(SOUP_IS_SERVER(server)); g_assert(SOUP_IS_MESSAGE(message)); g_assert(path); g_assert(params); g_assert(g_hash_table_contains(params, "device")); g_assert(g_hash_table_contains(params, "user")); g_assert(client); g_assert(POSTAL_IS_HTTP(http)); device = g_hash_table_lookup(params, "device"); user = g_hash_table_lookup(params, "user"); if (message->method == SOUP_METHOD_GET) { postal_service_find_device(http->priv->service, user, device, NULL, /* TODO */ postal_http_find_device_cb, g_object_ref(message)); soup_server_pause_message(server, message); EXIT; } else if (message->method == SOUP_METHOD_DELETE) { pdev = g_object_new(POSTAL_TYPE_DEVICE, "device-token", device, "user", user, NULL); postal_service_remove_device(http->priv->service, pdev, NULL, /* TODO */ postal_http_remove_device_cb, g_object_ref(message)); soup_server_pause_message(server, message); g_object_unref(pdev); EXIT; } else if (message->method == SOUP_METHOD_PUT) { if (!(node = postal_http_parse_body(message, &error))) { postal_http_reply_error(http, message, error); soup_server_unpause_message(server, message); g_error_free(error); EXIT; } pdev = postal_device_new(); if (!postal_device_load_from_json(pdev, node, &error)) { postal_http_reply_error(http, message, error); soup_server_unpause_message(server, message); json_node_free(node); g_error_free(error); g_object_unref(pdev); EXIT; } postal_device_set_device_token(pdev, device); postal_device_set_user(pdev, user); json_node_free(node); g_object_set_data_full(G_OBJECT(message), "device", g_object_ref(pdev), g_object_unref); postal_service_add_device(http->priv->service, pdev, NULL, /* TODO */ postal_http_add_device_cb, g_object_ref(message)); soup_server_pause_message(server, message); g_object_unref(pdev); EXIT; } else { soup_message_set_status(message, SOUP_STATUS_METHOD_NOT_ALLOWED); EXIT; } g_assert_not_reached(); }