11. File I/O and Operating System Access

Previous chapter

Next chapter

A GemStone application will generally need to interact with services provided outside of GemStone—for example, to work with text files, includes fileing out and in source code and object representations. Other capabilities that rely on the operating system include communiting with other processes via sockets.

This chapter explains how to access and update disk files, execute operatating system operations, and communicate over sockets to other machines.

Accessing Files
describes the protocol provided by class GsFile to open and close files, read their contents, and write to them.

Executing Operating System Commands
how to execute operating system commands from GemStone.

File In and File Out
filing out your application source code.

PassiveObject
describes the mechanism that GemStone provides for storing the objects that represent your data.

Creating and Using Sockets
describes the protocol provided by class GsSocket and GsSecureSocket to create operating system sockets and exchange data between two independent interface processes.

11.1 Accessing Files

The class GsFile provides the protocol to create and access operating system files. This section provides a few examples of the more common operations for text files. For a complete description of the functionality available, including the set of messages for manipulating binary files, see the comment for the class GsFile in the image.

Instances of GsFile understand most protocol common to Streams.

Specifying Files

Many of the methods in the class GsFile take as arguments a file specification, which is any string that constitutes a legal file specification in the operating system under which GemStone is running. Wildcard characters are legal in a file specification if they are legal in the operating system.

Many of the methods in the class GsFile distinguish between files on the client versus the server machine. In this context, the term client refers to the machine on which the interface is executing, and the server refers to the machine on which the Gem is executing. (This may not necessarily be the same machine on which the Stone is executing.) In the case of a linked interface, the interface and the Gem execute as a single process, so the client machine and the server machine are the same. In the case of an RPC interface, the interface and the Gem are separate processes, and the client machine can be different from the server machine.

Specifying Files Using Environment Variables

If you supply an environment variable instead of a full path when using the methods described in this chapter, the way in which the environment variable is expanded depends upon whether the process is running on the client or the server machine.

  • If you are running a linked interface or you are using methods that create processes on the server, the environment variables accessed by your GemStone Smalltalk methods are those defined in the shell under which the Gem process is running.
  • If you are running an RPC interface and using methods that create processes on a separate client machine, the environment variables are instead those defined by the remote user account on the client machine on which the application process is running.

NOTE
If you do not want to worry about these details, supply full path names and avoid the use of environment variables. This allows your application to work uniformly across different environments.

The examples in this section use a UNIX path as a file specification.

Creating a File

You can create a new operating system file from GemStone Smalltalk using several class methods for GsFile. Example 11.1 creates a file named aFileName in the current directory on the client machine.

Example 11.1
| myFile myFilePath | 
myFilePath := 'aFileName'. 
myFile := GsFile openWrite: myFilePath. 
"Here would go code to write data to the file"
myFile close
 

Example 11.2 creates a file named aFileName in the current directory on the server.

Example 11.2
myFile := GsFile openWriteOnServer: mySpec
"Here would go code to write data to the file"
myFile close
 

These methods return the instance of GsFile that was created, or nil if an error occurred. Common errors include invalid paths or insufficient permissions. To determine the specific problem, use the techniques described under GsFile Errors.

Opening a File

GsFile provides a wide variety of protocol to open files. For a complete set of methods, see the image. These methods return the GsFile instance if successful, or nil if an error occurs.

Table 11.1 GsFile Class Methods to Open Files

Method

Description

openRead:

openReadCompressed:

Opens a file on the client machine for reading, replacing the existing contents.

openWrite:

openWriteCompressed:

 

Opens a file on the client machine for writing. Creates a new file if one does not exist, or truncates an existing file to 0.

openAppend:

Opens a file on the client machine for writing, appending the new contents instead of replacing the existing contents. Creates the file if it does not exist.

openReadOnServer:

openReadOnServerCompressed:

Opens a file on the server for reading, replacing the existing contents.

openWriteOnServer:

openWriteOnServerCompressed:

 

Opens a file on the server for writing. Creates a new file if one does not exist, or truncates an existing file to 0.

openAppendOnServer:

Opens a file on the server for reading, appending the new contents instead of replacing the existing contents.

GsFile close

Closes the receiver.

closeAll

Closes all open GsFile instances on the client machine except stdin, stdout, and stderr.

closeAllOnServer

Closes all open GsFile instances on the server except stdin, stdout, and stderr.

Closing a File or Files

The following methods close the current instance, or multiple open files:

Table 11.2 GsFile Method Summary

Method

Description

GsFile >> close

Closes the receiver.

GsFile class >> closeAll

Closes all open GsFile instances on the client machine except stdin, stdout, and stderr.

GsFile class >>
closeAllOnServer

Closes all open GsFile instances on the server except stdin, stdout, and stderr.

Your operating system limits the number of files a process can concurrently access. Using GemStone classes to open, read or write, and close files does not lift your application’s responsibility for closing open files. Make sure you write and close files as soon as possible.

Writing to a File

After you have opened a file for writing, you can add new contents to it in several ways. For example, the instance methods addAll: and nextPutAll: take strings as arguments and write the string to the end of the file specified by the receiver. The method add: takes a single character as argument and writes the character to the end of the file. And various methods such as cr, lf, and ff write specific characters to the end of the file—in this case, a carriage return, a line feed, and a form feed character, respectively.

For example, the following code writes the two strings specified to the file myFile.txt, separated by end-of-line characters.

Example 11.3
myFile := GsFile openWrite: 'myFileName'.
myFile nextPutAll: 'All of us are in the gutter,'.
myFile cr.
myFile nextPutAll: 'but some of us are looking at the stars.'.
myFile close.
myFile := GsFile openRead: 'myFileName'.
myFile contents.
%
 
GsFile closeAll.
%
 

These methods return the number of bytes that were written to the file, or nil if an error occurs.

Writing Extended Characters To a File

Characters outside the base ASCII range of 0...255 require multiple bytes to represent. Instance of traditional and Unicode Strings handle characters with larger values than can be held by the given class, by transparently morphing into a class that can hold larger Characters. This is discussed in Chapter 5, “String Classes and Collation”.

Most GsFile protocol can read and write byte values only, and do not handle Characters with values larger than 255, nor instances of DoubleByteString, QuadByteString, Unicode16 and Unicode32 that include Characters over 255.

To write a string containing extended characters to a file, it must be explicitly encoded into UTF-8 before writing to the file. You can do this by using

GsFile >> nextPutAsUtf8: aString

This writes the given string (or instsance of Uft8) to the file encoded as UTF-8.

You can also encode a string into UTF-8 using String >> encodeAsUTF8. This creates an instance of Uft8, a type of ByteArray holding the UTF-8 encoded equivalent of the data. This can be written to the GsFile normally.

For example, the Euro character € has the Unicode value U+20AC.

Example 11.4 Writing the Extended Character € to a File
| myfile str | 
myfile := GsFile openWrite: 'extendedCharacterExample.txt'.
str := String new.
str add: 'How to write a Euro character '.
str add: (Character withValue: 16r20AC).
str add: ' to a file'; lf.
myfile nextPutAsUtf8: str.
myfile close.
 

To read a file containing data encoded in UTF-8, use the method

GsFile >> contentsAsUtf8

This returns the contents of the entire file as an instance of Utf8, which contains byte values. The method Utf8 >> decodeFromUTF8 can them be used to decode the contents into an instance of the appropriate Unicode String class.

Note that when reading files containing Characters with codePoints larger than 127. you must be aware of the whether the file is encoded in order to decode appropriately. GsFile reads the bytes and does not distinguish between encoded or un-encoded contents.

Reading from a File

Instances of GsFile can be accessed in many of the same ways as instances of Stream subclasses. Like streams, GsFile instances also include the notion of a position, or pointer into the file. When you first open a file, the pointer is positioned at the beginning of the file. Reading or writing elements of the file ordinarily repositions the pointer as if you were processing elements of a stream.

A variety of methods allow you to read some or all of the contents of a file from within GemStone Smalltalk. For example, the contents method (at the end of Example 11.3) returns the entire contents of the specified file and positions the pointer at the end of the file.

In Example 11.5, next: into: takes the 12 characters after the current pointer position and places them into the specified string object. It then advances the pointer by 12 characters.

Example 11.5
| result |
result := String new.
myFile := GsFile openRead: 'myFileName'.
myFile next: 12 into: result.
myFile close
result.
 

Positioning

You can also reposition the pointer without reading characters, or peek at characters without repositioning the pointer. The method:

GsFile peek

allows you to view the next character in the file without advancing the pointer.

To advance the pointer without reading the intervening characters, use:

GsFile skip: anInteger

Testing Files

The class GsFile provides a variety of methods that allow you to determine facts about a file.

To test for existence of a file, use:

GsFile >> exists: aFileNameString
GsFile >> existsOnServer: aFileNameString

These methods returns true if the file exists, false if it does not, and nil if an error occurred.

Renaming Files

Files on the client or server can be renamed or moved. For example:

GsFile rename: '/tmp/myfile.txt' to: '/tmp/newname.txt'.
 
GsFile renameFileOnServer: '$GEMSTONE/data/system.conf' to:
'/users/david/mysystem.conf'.

Removing Files

To remove a file from the client machine, use an expression of the form:

GsFile closeAll.
GsFile removeClientFile: mySpec.

To remove a file from the server machine, use the method removeServerFile: instead. These methods return the receiver or nil if an error occurred.

Examining a Directory

To get a list of the names of files in a directory, send GsFile the message contentsOfDirectory: aFileSpec onClient: aBoolean. This message acts very much like the UNIX ls command, returning an array of file specifications for all entries in the directory.

If the argument to the onClient: keyword is true, GemStone searches on the client machine. If the argument is false, it searches on the server instead.

For example:

Example 11.6
GsFile contentsOfDirectory: '$GEMSTONE/examples/admin' onClient: false
%
an Array
  #1 /dbf/gsadmin/GS6432/examples/admin/.
  #2 /dbf/gsadmin/GS6432/examples/admin/..
  #3 /dbf/gsadmin/GS6432/examples/admin/onlinebackup.sh
  #4 /dbf/gsadmin/GS6432/examples/admin/archivelogs.sh 
 

If the argument is a directory name, this message returns the full pathnames of all files in the directory, as shown in Example 11.6. However, if the argument is a filename, this message returns the full pathnames of all files in the current directory that match the filename. The argument can contain wildcard characters such as *. The following example shows a different use of this message.

GsFile contentsOfDirectory: '$GEMSTONE/ver*' onClient: false
%
an Array
  #1 /dbf/gsadmin/GS6432/version.txt

If you wish to distinguish between files and directories, you can use the message contentsAndTypesOfDirectory:onClient: instead. This method returns an array of pairs of elements. After the name of the directory element, a value of true indicates a file; a value of false indicates a directory. For example:

All the above methods, like most GsFile methods, return nil if an error occurs.

GsFile Errors

GsFile operations return nil in cases where an error occurs during the operation. For this reason, most GsFile operations should check for nil return. There are separate methods to check for errors within file operations on server files and client files.

To check for errors in an operation on a server file, the method is GsFile >> serverErrorString. It is nil if no error has occurred. This error is available until the next GsFile operation is executed.

Example 11.7
| myFile | 
myFile := GsFile openReadOnServer: 'nonexistentfile'. 
myFile isNil 
  ifTrue: [GsFile serverErrorString]
  ifFalse: ['Succesfully opened'].
%
errno=2,ENOENT, The file or directory specified cannot be found
 

To check for similar errors for a client file, use the method lastErrorString. For example:

Example 11.8
| myFile | 
myFile := GsFile openRead: 'privatefile'. 
myFile isNil 
  ifTrue: [GsFile lastErrorString]
  ifFalse: ['Succesfully opened'].
%
errno=13,EACCES, Authorization failure (permission denied)
 

11.2 Executing Operating System Commands

Simple Commands

System also understands the message performOnServer: aString, which causes the UNIX shell commands given in aString to execute in a subprocess of the current GemStone process. The output of the commands is returned as a GemStone Smalltalk string. For example:

System performOnServer: 'date'
%
Mon Mar 10 15:19:56 PDT 2014

The commands in aString can have exactly the same form as a shell script; for example, new lines or semicolons can separate commands, and the character “\” can be used as an escape character. The string returned is whatever an equivalent shell command writes to stdout. If the command or commands cannot be executed successfully by the subprocess, the interpreter halts and GemStone returns an error message.

The GemStone (reverse) privilege NoPerformOnServer controls the ability to execute this method. If a user account is given this privilege, that user cannot execute performOnServer:.

More complex interactions

System >> performOnServer: can execute arbitrary OS code on the server, but only operates synchronously; Smalltalk will block until the command has completed.

To provide an asynchronous perform, and to allow Smalltalk to read from stdout or write to stdin, you can use the class GsHostProcess.

To use this, use the class method fork:, passing the command line you wish to execute. This will return immediately with an instance of GsHostProcess with sockets on stdin, stdout, and stderr. You can use socket protocol to read from or write to these sockets.

Note that pathname resolution is not provided. You must fully qualify executable paths.

For example:

run
| hostprocess |
hostprocess := GsHostProcess fork: '/bin/date'.
hostprocess stdout read: 1024
%
Tue Mar 11 11:03:14 PDT 2014

11.3 File In and File Out

To archive your application or transfer GemStone classes to another repository you can file out GemStone Smalltalk source code for classes and methods to a text file. To port your application to another repository, you can file in that text file, and the source code for your classes and methods is immediately available in the new repository.

Fileout

Methods in behavior allow you to file out a class, category, or method. For example, to file out a single class named Customer:

| myFile | 
myFile := GsFile openWrite: 'CustomerClassFileout.gs'. 
myFile isNil 
  ifTrue: [^GsFile serverErrorString].
Customer fileOutClassOn: myFile.
myFile close.
%

Using ClassOrganizer, you can file out all the classes and methods in a SymbolDictionary, ordered correctly for filein. For example, to file out UserGlobals:

| myFile | 
myFile := GsFile openWrite: 'UserGlobalsFileout.gs'. 
myFile isNil 
  ifTrue: [^GsFile serverErrorString].
ClassOrganizer new fileOutClassesAndMethodsInDictionary:
	UserGlobals on: myFile.
myFile close.
%

File out can also be done using the topaz command fileout. See the Topaz User’s Guide for more information.

Filein

File in is done using topaz input command, or facilities provided by GBS.

For example, to file in the fileout of UserGlobals from the previous example:

topaz 1> input UserGlobalsFileout.gs

Handling strings with extended characters

GsFile cannot directly write characters with codepoints over 255. If your class or method names, code, or comments includes any characters with codepoints over 255, you will need to encode as UTF-8 in order to file out using GsFile, as is described under Writing Extended Characters To a File.

In topaz, the fileformat command allows you to file out and file in as UTF-8.

11.4 PassiveObject

To archive your data, you can passivate objects themselves to a file. Objects representing your data are stored into a serialized, text-based form by the GemStone class PassiveObject. PassiveObject starts with a root object and traces through its instance variables, and their instance variables, recursively until it reaches special objects (instances of SmallInteger, Character, Boolean, SmallDouble, or UndefinedObject), or classes that can be reduced to special objects (strings and numbers that are not integers), creating a representation of the object that preserves all of the values required to re-create it. The resulting network of object descriptions can be written to a file, stream, or string. Each file can hold only one network—you cannot append additional networks to an existing passive object file, stream, or string.

A few objects and aspects of objects are not preserved:

The relationship between two objects is conserved only so long as they are described in the same network. Similarly, if two separate objects A and B both refer to the same third object C, then making A and B passive in two separate operations will result in duplicating the object C, which will be represented in both A’s and B’s network. Because the resulting network of objects can be quite large anyway, you want to avoid such unnecessary duplication. For this reason, it is usually a good idea to create one collection to hold all the objects you wish to preserve before invoking one of the PassiveObject methods.

In addition, since object identity is not preserved, behavior that depends on identity may not work as expected. For example, for objects that implement = using ==, the re-activated object will not be = to the original.

The class PassiveObject implements the method passivate: anObject toStream: aGsFileOrStream to write objects out to a stream or a file. To write the object AllEmployees out to the file allEmployees.obj in the current directory, execute an expression of the form shown in Example 11.9.

Example 11.9
| empFile |
empFile := GsFile openWriteOnServer: 'allEmployees.obj'.
PassiveObject passivate: AllEmployees toStream: empFile.
empFile close.
 

The class PassiveObject implements the method newOnStream: aGsFileOrStream to read objects from a stream or file into a repository. The method activate then restores the object to its previous form.

The following example reads the file allEmployees.obj into a GemStone repository:

Example 11.10
| empFile passivatedEmployees |
empFile := GsFile openReadOnServer: 'allEmployees.obj'.
passivatedEmployees := PassiveObject newOnStream: empFile.
AllEmployees := passivatedEmployees activate.
empFile close.
 

Examples and use streams rather than files to actually move the data. This is useful, as streams do not create temporary objects that occupy large amounts of memory before the garbage collector can reclaim their storage.

11.5 Creating and Using Sockets

Sockets open a connection between two processes, allowing a two-way exchange of data. The class GsSocket provides a mechanism for manipulating operating system sockets from within GemStone Smalltalk.

Methods in the class GsSocket do not use the terms client and server in the same way as the methods in class GsFile. Instead, these terms refer to the roles that two processes play with respect to the socket: the server process creates the socket, binds it to a port number, and listens for the client, while the client connects to an already created socket. Both client and server are processes created (or spawned) by a Gem process.

In addition to standard sockets created by GsSocket, you can create secure SSL sockets using the class GsSecureSocket. GsSecureSocket is a subclass of GsSocket that adds protocol to specify certificates and require authentication.

Both GsSocket and GsSecureSocket contain class methods clientExample and serverExample, and GsSecureSocket contains additional class methods clientExample2 and serverExample2. These methods provide examples of how to create a socket connection between two sessions. The example methods work together; they require two separate sessions running from two independently executing interfaces, one running the server example and one running the client example. You can execute these methods from Topaz or from GemBuilder for Smalltalk, but note that serverExample, which should be started first, will take control of the interface until the clientExample completes the socket connection.

GsSocket

GsSocket is the class representing a basic socket.

Establishing the connection

To setup a socket connection, you create instances of GsSocket in both the client and server processes.

1. On the server side, create an instance of GsSocket, and call makeServerAtPort: This creates a listening socket on the given port.

To have the operating system select a port, use a wildcard bind using makeServer:, or pass nil as the port argument. You will then need to determine the port that the client should connect at using the port method.

2. On the client side, create an instance of a GsSocket and call one of the following:

  • connectTo: for a connection to a process on the same host
  • connectTo:on: if the server is on a different machine
  • connectTo:on:timeoutMs: to specify a timeout for the connection

Provided there was a listening server socket setup as in step 1, this will initiate the connection to the server.

3. The server then does an accept, or acceptTimeoutMs: (to specify a timeout) . This returns a new instance of GsSocket for the client connection.

Note that the server side has two sockets; a listening socket and the established socket with the client.

Communication on the socket

Each process can write and read to the socket using protocol such as write: and read:. See the image methods in the categories Reading and Writing for specific methods.

Writes and reads are of byte objects such as String or ByteArray. Read operations are for a specified number of bytes, and return the actual number of bytes read if fewer bytes were available (if fewer bytes were written to the socket by the peer).

Closing the socket

When completed, the client should close its socket and the server close the listening and established sockets. This is done by simply sending close to the sockets.

Socket Configuration

Socket configuration can be done using the method

GsSocket >> option:put:

See the comments in this method for details on socket configuration.

The most common option is blocking.

Blocking

Sockets can be made blocking or non-blocking, and the blocking status checked, using the following methods:

GsSocket >> makeNonBlocking
GsSocket >> makeBlocking
GsSocket >> isNonBlocking
GsSocket >> isBlocking

GsSecureSocket

GsSecureSocket creates a secure socket using Secure Sockets Layer (SSL), providing access to the open-source OpenSSL library. This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)

To create a secure socket, you create instances of the GsSecureSocket class and first establish the connection as a regular socket. Then, further protocol authenticates the connection to make the socket secure.

Secure Sockets include class level setup of certificates and types of authentication that may be done outside of specific socket operations.

Set up certificates and private keys

GsSecureSocket instances must be configured with the CA certificates, private key files, and passphrases (if needed), to allow them to complete the secure handshake.

These can be configured in the class GsSecureSocket, so they will apply to all instances of GsSecureSocket. In this case they must be configured before the instance of GsSecureSocket is created.

Alternatively, you can create the instance of GsSecureSocket, and send instance methods to configure the certificates and private keys.

Generating certificates

To use secure sockets, you will need to have certificates, CA certificates, private key files and passphrases, such as sufficient for your security requirements. These may be provided by your organization.

The GemStone distribution includes example certificates, and a script that will allow you to generate certificates. The script is provided here:

$GEMSTONE/examples/openssl/make_example_certs.sh

The GemStone distribution also includes the openssl executable:

$GEMSTONE/bin/openssl

This is the version of openssl that GemStone uses; using this, rather than any version that may be present on your system, is recommended. For details on the openssl interface, see http://www.openssl.org/docs/apps/openssl.html.

Enable or disable certificate validation

By default, client sockets do not validate server connections, but by default server sockets validate client connection. This is the usual case, and if this is your preferred behavior you do not need to explicitly enable or disable validation.

If validation on either server socket or client socket is disabled, the methods that configure the certificates, CA certificates, private key files and passphrases do not need to be executed.

Class methods that configure validation must be executed prior to the creation of the GsSecureSocket instance.

Server Sockets

By default, server sockets validate all connection requests. To disable validation, use the methods:

GsSecureSocket enableCertificateVerificationOnServer
aGsSecureServerSocket enableCertificateVerification

Parallel methods exist to re-enable validation after validation is disabled.

Client Sockets

By default, client sockets do not validate connections from the server. To enable validation, use the methods:

GsSecureSocket enableCertificateVerificationOnClient
aGsSecureClientSocket enableCertificateVerification

Parallel methods exist to disable validation after validation is enabled.

Setup CA certificates

When socket validation is to be done, the Certificate Authority (CA) certificates should be setup prior to creating instance of GsSecureSocket.

Server Sockets

By default, server sockets validate client connections. Before creating a server socket, the CA certificate used to validate client connections should be loaded using the following method:.

GsSecureSocket class >> 
	useCACertificateFileForServers: certfile 

The CA certificate file must be in PEM format. It may contain more than one certificate. If this method returns false, an error occurred and the certificate was not successfully loaded.

An example CA certificate file is provided here:

$GEMSTONE/examples/openssl/certs/serverCA.pem'
Client Sockets

By default, certificates are not verified on the client, so the client CA certificate file does not need to be loaded. If you need to enable client validation, load the client CA certificate file using the following method.

GsSecureSocket class >> 
	useCACertificateFileForClients: certfile

Setup certificate, private key, and passphrase

The certificate, private key, and private key passphrase can be setup by class methods to apply to all instances, or by sending messages to the instance of GsSecureSocket.

Method variants are provided that allow you to pass in the certificate and private key either as file path and name, or as a string. If a string is used, it must exactly match the contents of the corresponding certificate file (including white space, line feeds, etc.), or the strings will not be accepted.

Both certificate and private key must be in PEM format, and the private key must match the certificate. The same file may be specified for the certificate file and the private key file.

The certificate may contain a certificate chain or a single certificate.

If the private key requires a passphrase, it must be specified as a string. If the private key does not require a passphrase, the argument is expected to be nil.

Class setup for server sockets

To specify the server certificates, private key file, and passphrase (if required), that will be used for all secure server sockets that are created after these methods are invoked, use the methods:

GsSecureSocket class >> useServerCertificateFile: certfile 
	withPrivateKeyFile: keyFile privateKeyPassphraseFile: strOrNil
GsSecureSocket class >> useServerCertificate: certString
	withPrivateKey: keyString privateKeyPassphraseFile: strOrNil

An example file is provided here:

$GEMSTONE/examples/openssl/certs/server.pem
Class setup for client sockets

By default, certificates are not verified on the client, so the certificate and private key do not need to be setup.

If you need to enable client validation, the follow methods specify the client certificates, private key file, and passphrase, that will be used to validate server connections for secure client sockets that are created after these methods are invoked:

GsSecureSocket class >> useClientCertificateFile: certfile
	withPrivateKeyFile: keyFile privateKeyPassphraseFile: strOrNil
GsSecureSocket class >> useClientCertificate: certString
	withPrivateKey: keyString privateKeyPassphraseFile: strOrNil
Instance setup for client or server sockets

You can specify the certificate, private key, and passphrase for a single specific instance of GsSecureSocket (either a server socket or a client socket), using instance methods:

GsSecureSocket >> useCertificateFile: certfile 
	withPrivateKeyFile: keyFile privateKeyPassphraseFile: strOrNil
GsSecureSocket >> useCertificate: certString withPrivateKey: keyString
	privateKeyPassphraseFile: strOrNil

Setup the Cipher list

The list of ciphers that are acceptable to use can be configured, either on the class side for servers and clients, or for specific instances of GsSecureSocket client or server sockets.

The cipher list is specified as a formatted string. See http://www.openssl.org/docs/apps/ciphers.html for details on the format of this string (as well as other information on ciphers).

For example, to use all ciphers except NULL ciphers and anonymous Diffie-Hellman (DH), and sort by strength, use the following string:

'ALL:!ADH:@STRENGTH'

To configure the cipher list for all instances of GsSecureSocket, use the following methods. These methods return true if the specification finds one or more usable ciphers, false if no usable ciphers match the specification.

GsSecureSocket class >> 
	setClientCipherListFromString: aString
GsSecureSocket class >> 
	setServerCipherListFromString: aString

To configure the cipher list for a specific instance of a server socket or client socket, the ciphers must be set before secureConnect:/secureAccept are executed. This method returns true if the specification finds one or more usable ciphers, false if no usable ciphers match the specification, and nil if the operation has no affect because the receiver is already connected.

GsSecureSocket class >> 
	setCipherListFromString: aString

Once an instance of GsSecureSocket is successfully connected, you can fetch the cipher in use using:

GsSecureSocket >> fetchCipherDescription

Establishing the connection

Rather than creating instances using GsSocket class >> new, with GsSecureSocket sockets are instantiated using newClient and newServer.

To establish the socket connection, as with regular GsSocket,

1. The server creates the socket using newServer, and calls makeServerAtPort: on an unused port, to create the server listener socket on that port.

2. The client creates the socket using newClient, and calls connectTo:, specify the same port as in Step 1.

3. The server socket calls accept, which creates the connected socket on the given port.

This establishes the standard socket, but the connection is not secure. Another client-server interaction is required to make this a secure socket.

At this point, you can setup specific certificates and ciphers that will apply to these sockets only, as described in the preceding sections. This is needed if you have not previously set up certificates and ciphers that apply to all GsSecureSocket connections.

Then continue with the process that makes the socket secure:

4. The client socket calls secureConnect

5. The server socket calls secureAccept

If these methods return true, then the connection is secure. To determine if you have a secure connection, use the method:

GsSecureSocket >> hasSecureConnection

Communication on the socket

At this point reads and writes are done as for standard sockets.

Closing the socket

You can either close the socket connection entirely, or close the secure connection and remain connected for normal (not secure) communication.

To close the socket entirely, use

GsSecureSocket >> close

Which performs both the secure close and the regular close.

Note that the secure close requires a handshake. If the socket is blocking, and the peer does not respond, then the close will hang. To close the socket, we recommend first making it non blocking:

mySecureSocket makeNonBlocking.
mySecureSocket close.

To close only the secure socket and leave the connection available for non-secure communication, you can use the method

GsSecureSocket >> secureClose

Which must be executed by both sockets on the connection. You can then call close later, to close the connection entirely.

Error handling

GsSocket

The following methods are implemented both for the class and instance of GsSocket. For errors in GsSocket class methods, use the class side error methods, and for errors in GsSocket instance methods, use the instance methods

lastErrorString
Returns a String containing information about the last error or nil if no error has occurred. Clears the error information.

lastErrorCode
Returns an integer representing the last OS error or nil if no error has occurred. Does not clear the error information

lastErrorSymbol
Returns a Symbol representing the last OS error or nil if no error has occurred. Does not clears the error information.

GsSecureSocket

If one of the calls returns nil or false, you can determine the last error from an SSL function called from an instance method using the instance method:

GsSecureSocket >> fetchLastIoErrorString
This fetches and clears the error string from a call to SSL functions for connect, accept, read, or write.

On the class side, the following methods return error strings for any SSL function call errors:

GsSecureSocket class >> fetchErrorStringArray
Returns an Array of error strings generated by the OpenSSL package. The errors returned are cleared from the SSL error queue. The array is ordered from oldest to newest error.

GsSecureSocket class >> fetchLastCertificateVerificationErrorForClient
GsSecureSocket class >> fetchLastCertificateVerificationErrorForServer

These methods fetches and clears a string representing the last certificate verification error logged by, respectively, a client SSL socket or a server SSL socket.

To clear the error queue, use the method

GsSecureSocket class >> clearErrorQueue

 

Previous chapter

Next chapter