TranslateProject/sources/tech/20220505 Boost the power of C with these open source libraries.md

322 lines
8.9 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[#]: 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 objects 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 cant 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. Its 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.
Heres 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