mirror of
https://github.com/LCTT/TranslateProject.git
synced 2024-12-29 21:41:00 +08:00
322 lines
8.9 KiB
Markdown
322 lines
8.9 KiB
Markdown
|
[#]: subject: "Boost the power of C with these open source libraries"
|
|||
|
[#]: via: "https://opensource.com/article/22/5/libsoup-gobject-c"
|
|||
|
[#]: author: "Joël Krähemann https://opensource.com/users/joel2001k"
|
|||
|
[#]: collector: "lkxed"
|
|||
|
[#]: translator: " "
|
|||
|
[#]: reviewer: " "
|
|||
|
[#]: publisher: " "
|
|||
|
[#]: url: " "
|
|||
|
|
|||
|
Boost the power of C with these open source libraries
|
|||
|
======
|
|||
|
GObject and libsoup do a lot of work for you, so you can turn your attention to inventing amazing applications in C.
|
|||
|
|
|||
|
![Why and how to handle exceptions in Python Flask][1]
|
|||
|
(Image by: Image from Unsplash.com, Creative Commons Zero)
|
|||
|
|
|||
|
The [GLib Object System (GObject)][2] is a library providing a flexible and extensible object-oriented framework for C. In this article, I demonstrate using the 2.4 version of the library.
|
|||
|
|
|||
|
The GObject libraries extend the ANSI C standard, with typedefs for common types such as:
|
|||
|
|
|||
|
* gchar: a character type
|
|||
|
* guchar: an unsigned character type
|
|||
|
* gunichar: a fixed 32 bit width unichar type
|
|||
|
* gboolean: a boolean type
|
|||
|
* gint8, gint16, gint32, gint64: 8, 16, 32, and 64 bit integers
|
|||
|
* guint8, guint16, guint32, guint64: unsigned 8, 16, 32, and 64 bit integers
|
|||
|
* gfloat: an IEEE Standard 754 single precision floating point number
|
|||
|
* gdouble: an IEEE Standard 754 double precision floating point number
|
|||
|
* gpointer: a generic pointer type
|
|||
|
|
|||
|
### Function pointers
|
|||
|
|
|||
|
GObject also introduces a type and object system with classes and interfaces. This is possible because the ANSI C language understands function pointers.
|
|||
|
|
|||
|
To declare a function pointer, you can do this:
|
|||
|
|
|||
|
```c
|
|||
|
void (*my_callback)(gpointer data);
|
|||
|
```
|
|||
|
|
|||
|
But first, you need to assign the `my_callback` variable:
|
|||
|
|
|||
|
```c
|
|||
|
void my_callback_func(gpointer data)
|
|||
|
{
|
|||
|
//do something
|
|||
|
}
|
|||
|
|
|||
|
my_callback = my_callback_func;
|
|||
|
```
|
|||
|
|
|||
|
The function pointer `my_callback` can be invoked like this:
|
|||
|
|
|||
|
```c
|
|||
|
gpointer data;
|
|||
|
data = g_malloc(512 * sizeof(gint16));
|
|||
|
my_callback(data);
|
|||
|
```
|
|||
|
|
|||
|
### Object classes
|
|||
|
|
|||
|
The GObject base class consists of 2 structs (`GObject` and `GObjectClass` ) which you inherit to implement your very own objects.
|
|||
|
|
|||
|
You embed GObject and GObjectClass as the first struct field:
|
|||
|
|
|||
|
```c
|
|||
|
struct _MyObject
|
|||
|
{
|
|||
|
GObject gobject;
|
|||
|
//your fields
|
|||
|
};
|
|||
|
|
|||
|
struct _MyObjectClass
|
|||
|
{
|
|||
|
GObjectClass gobject;
|
|||
|
//your class methods
|
|||
|
};
|
|||
|
|
|||
|
GType my_object_get_type(void);
|
|||
|
```
|
|||
|
|
|||
|
The object’s implementation contains fields, which might be exposed as properties. GObject provides a solution to private fields, too. This is actually a struct in the C source file, instead of the header file. The class usually contains function pointers only.
|
|||
|
|
|||
|
An interface can’t be derived from another interface and is implemented as following:
|
|||
|
|
|||
|
```c
|
|||
|
struct _MyInterface
|
|||
|
{
|
|||
|
GInterface ginterface;
|
|||
|
//your interface methods
|
|||
|
};
|
|||
|
```
|
|||
|
|
|||
|
Properties are accessed by `g_object_get()` and `g_object_set()` function calls. To get a property, you must provide the return location of the specific type. It’s recommended that you initialize the return location first:
|
|||
|
|
|||
|
```c
|
|||
|
gchar *str
|
|||
|
|
|||
|
str = NULL;
|
|||
|
|
|||
|
g_object_get(gobject,
|
|||
|
"my-name", &str,
|
|||
|
NULL);
|
|||
|
```
|
|||
|
|
|||
|
Or you might want to set the property:
|
|||
|
|
|||
|
```c
|
|||
|
g_object_set(gobject,
|
|||
|
"my-name", "Anderson",
|
|||
|
NULL);
|
|||
|
```
|
|||
|
|
|||
|
### The libsoup HTTP library
|
|||
|
|
|||
|
The `libsoup` project provides an HTTP client and server library for GNOME. It uses GObjects and the glib main loop to integrate with GNOME applications, and also has a synchronous API for use in command-line tools. First, create a `libsoup` session with an authentication callback specified. You can also make use of cookies.
|
|||
|
|
|||
|
```c
|
|||
|
SoupSession *soup_session;
|
|||
|
SoupCookieJar *jar;
|
|||
|
|
|||
|
soup_session = soup_session_new_with_options(SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_BASIC,
|
|||
|
SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_DIGEST,
|
|||
|
NULL);
|
|||
|
|
|||
|
jar = soup_cookie_jar_text_new("cookies.txt",
|
|||
|
FALSE);
|
|||
|
|
|||
|
soup_session_add_feature(soup_session, jar);
|
|||
|
g_signal_connect(soup_session, "authenticate",
|
|||
|
G_CALLBACK(my_authenticate_callback), NULL);
|
|||
|
```
|
|||
|
|
|||
|
Then you can create a HTTP GET request like the following:
|
|||
|
|
|||
|
```c
|
|||
|
SoupMessage *msg;
|
|||
|
SoupMessageHeaders *response_headers;
|
|||
|
SoupMessageBody *response_body;
|
|||
|
guint status;
|
|||
|
GError *error;
|
|||
|
|
|||
|
msg = soup_form_request_new("GET",
|
|||
|
"http://127.0.0.1:8080/my-xmlrpc",
|
|||
|
NULL);
|
|||
|
|
|||
|
status = soup_session_send_message(soup_session,
|
|||
|
msg);
|
|||
|
|
|||
|
response_headers = NULL;
|
|||
|
response_body = NULL;
|
|||
|
|
|||
|
g_object_get(msg,
|
|||
|
"response-headers", &response_headers,
|
|||
|
"response-body", &response_body,
|
|||
|
NULL);
|
|||
|
|
|||
|
g_message("status %d", status);
|
|||
|
cookie = NULL;
|
|||
|
soup_message_headers_iter_init(&iter,
|
|||
|
response_headers);
|
|||
|
|
|||
|
while(soup_message_headers_iter_next(&iter, &name, &value)){
|
|||
|
g_message("%s: %s", name, value);
|
|||
|
}
|
|||
|
|
|||
|
g_message("%s", response_body->data);
|
|||
|
if(status == 200){
|
|||
|
cookie = soup_cookies_from_response(msg);
|
|||
|
while(cookie != NULL){
|
|||
|
char *cookie_name;
|
|||
|
cookie_name = soup_cookie_get_name(cookie->data);
|
|||
|
//parse cookies
|
|||
|
cookie = cookie->next;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
The authentication callback is called as the web server asks for authentication.
|
|||
|
|
|||
|
Here’s a function signature:
|
|||
|
|
|||
|
```c
|
|||
|
#define MY_AUTHENTICATE_LOGIN "my-username"
|
|||
|
#define MY_AUTHENTICATE_PASSWORD "my-password"
|
|||
|
|
|||
|
void my_authenticate_callback(SoupSession *session,
|
|||
|
SoupMessage *msg,
|
|||
|
SoupAuth *auth,
|
|||
|
gboolean retrying,
|
|||
|
gpointer user_data)
|
|||
|
{
|
|||
|
g_message("authenticate: ****");
|
|||
|
soup_auth_authenticate(auth,
|
|||
|
MY_AUTHENTICATE_LOGIN,
|
|||
|
MY_AUTHENTICATE_PASSWORD);
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### A libsoup server
|
|||
|
|
|||
|
For basic HTTP authentication to work, you must specify a callback and server context path. Then you add a handler with another callback.
|
|||
|
|
|||
|
This example listens to any IPv4 address on localhost port 8080:
|
|||
|
|
|||
|
```c
|
|||
|
SoupServer *soup_server;
|
|||
|
SoupAuthDomain *auth_domain;
|
|||
|
GSocket *ip4_socket;
|
|||
|
GSocketAddress *ip4_address;
|
|||
|
MyObject *my_object;
|
|||
|
GError *error;
|
|||
|
|
|||
|
soup_server = soup_server_new(NULL);
|
|||
|
auth_domain = soup_auth_domain_basic_new(SOUP_AUTH_DOMAIN_REALM, "my-realm",
|
|||
|
SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, my_xmlrpc_server_auth_callback,
|
|||
|
SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA, my_object,
|
|||
|
SOUP_AUTH_DOMAIN_ADD_PATH, "my-xmlrpc",
|
|||
|
NULL);
|
|||
|
|
|||
|
soup_server_add_auth_domain(soup_server, auth_domain);
|
|||
|
soup_server_add_handler(soup_server,
|
|||
|
"my-xmlrpc",
|
|||
|
my_xmlrpc_server_callback,
|
|||
|
my_object,
|
|||
|
NULL);
|
|||
|
|
|||
|
ip4_socket = g_socket_new(G_SOCKET_FAMILY_IPV4,
|
|||
|
G_SOCKET_TYPE_STREAM,
|
|||
|
G_SOCKET_PROTOCOL_TCP,
|
|||
|
&error);
|
|||
|
|
|||
|
ip4_address = g_inet_socket_address_new(g_inet_address_new_any(G_SOCKET_FAMILY_IPV4),
|
|||
|
8080);
|
|||
|
error = NULL;
|
|||
|
g_socket_bind(ip4_socket,
|
|||
|
ip4_address,
|
|||
|
TRUE,
|
|||
|
&error);
|
|||
|
error = NULL;
|
|||
|
g_socket_listen(ip4_socket, &error);
|
|||
|
|
|||
|
error = NULL;
|
|||
|
soup_server_listen_socket(soup_server,
|
|||
|
ip4_socket, 0, &error);
|
|||
|
```
|
|||
|
|
|||
|
In this example code, there are two callbacks. One handles authentication, and the other handles the request itself.
|
|||
|
|
|||
|
Suppose you want a web server to allow a login with the credentials username **my-username** and the password **my-password**, and to set a session cookie with a random unique user ID (UUID) string.
|
|||
|
|
|||
|
```c
|
|||
|
gboolean my_xmlrpc_server_auth_callback(SoupAuthDomain *domain,
|
|||
|
SoupMessage *msg,
|
|||
|
const char *username,
|
|||
|
const char *password,
|
|||
|
MyObject *my_object)
|
|||
|
{
|
|||
|
if(username == NULL || password == NULL){
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
if(!strcmp(username, "my-username") &&
|
|||
|
!strcmp(password, "my-password")){
|
|||
|
SoupCookie *session_cookie;
|
|||
|
GSList *cookie;
|
|||
|
gchar *security_token;
|
|||
|
cookie = NULL;
|
|||
|
|
|||
|
security_token = g_uuid_string_random();
|
|||
|
session_cookie = soup_cookie_new("my-srv-security-token",
|
|||
|
security_token,
|
|||
|
"localhost",
|
|||
|
"my-xmlrpc",
|
|||
|
-1);
|
|||
|
|
|||
|
cookie = g_slist_prepend(cookie,
|
|||
|
session_cookie);
|
|||
|
soup_cookies_to_request(cookie,
|
|||
|
msg);
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
A handler for the context path **my-xmlrpc**:
|
|||
|
|
|||
|
```c
|
|||
|
void my_xmlrpc_server_callback(SoupServer *soup_server,
|
|||
|
SoupMessage *msg,
|
|||
|
const char *path,
|
|||
|
GHashTable *query,
|
|||
|
SoupClientContext *client,
|
|||
|
MyObject *my_object)
|
|||
|
{
|
|||
|
GSList *cookie;
|
|||
|
cookie = soup_cookies_from_request(msg);
|
|||
|
//check cookies
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### A more powerful C
|
|||
|
|
|||
|
I hope my examples show how the GObject and libsoup projects give C a very real boost. Libraries like these extend C in a literal sense, and by doing so they make C more approachable. They do a lot of work for you, so you can turn your attention to inventing amazing applications in the simple, direct, and timeless C language.
|
|||
|
|
|||
|
--------------------------------------------------------------------------------
|
|||
|
|
|||
|
via: https://opensource.com/article/22/5/libsoup-gobject-c
|
|||
|
|
|||
|
作者:[Joël Krähemann][a]
|
|||
|
选题:[lkxed][b]
|
|||
|
译者:[译者ID](https://github.com/译者ID)
|
|||
|
校对:[校对者ID](https://github.com/校对者ID)
|
|||
|
|
|||
|
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
|||
|
|
|||
|
[a]: https://opensource.com/users/joel2001k
|
|||
|
[b]: https://github.com/lkxed
|
|||
|
[1]: https://opensource.com/sites/default/files/lead-images/computer_code_programming_laptop.jpg
|
|||
|
[2]: https://docs.gtk.org/gobject/concepts.html
|