It is often the case that applications - be they client applications, or server applications - need to make network connections via standard internet protocols. VisualWorks supports a wide variety of such protocols, including HTTP and HTTPS. It's becoming more and more common for applications to expose what some call REST (REpresentational State Transfer) as a way of opening up some of their internal API's to the world.
What, do you ask is REST? Simply put, it's an HTTP based interface to an application. While that sounds complicated, it's not. For instance - searching my web log for information on REST could take the form of this HTTP based query:
http://www.cincomsmalltalk.com/blog/blogView?search=REST&searchTitle=true&searchText=false. The interesting thing is, the results of a query like this need not be parsed solely by a web browser - they can be parsed by an application that doesn't even expose the results directly to the end user. Now, this is a simple example - the query above is a GET, and the results come back in the HTML body as text. What if you have a more complex interface?
Using my weblog again as an example, let's look at exposing POST interfaces. In the weblog and RSS world, there's growing interest in being able to trace posts, comments, and related posts on other web logs. Two examples of this are Trackback and Pingback. Both are interfaces that accept HTTP POSTS from clients. Handling those in a VisualWorks server is easy; you just create a servlet, a post handler, and grab the form elements. But what about the client end?
In the RSS/Web log world, a web log should not only be able to receive these notifications, but be able to send them. How do you create a POST in code? Very easily, as it turns out. Using the NetClients libraries, here's an example of creating and sending a trackback POST:
doPostTBFor: tbToSendUrl from: entry
| client content |
client := HttpClient new.
content := self postTBFor: tbToSendUrl from: entry.
request := HttpRequest method: 'POST' url: tbToSendUrl.
request referer: entry getPermaUrl.
request contents: content.
request contentType: 'application/x-www-form-urlencoded'.
^[client executeRequest: request] on: self httpExceptions
do: [:ex | ex return].
In this example, #postTBFor:from: looks like this:
postTBFor: baseUrl from: entry
| stream settings excerptLength myUrl |
stream := WriteStream on: (String new: 500).
settings := BlogSaver default settings.
excerptLength := entry blogText size min: settings excerptLength.
stream nextPutAll: 'title=', (entry getHTMLEncodingFor: entry title).
myUrl := settings mainLink, '?entry=', entry timestamp asSeconds printString, '&showComments=true'.
stream nextPutAll: '&url=', myUrl.
stream nextPutAll: '&excerpt=', (entry getHTMLEncodingFor: (entry blogText copyFrom: 1 to: excerptLength)).
stream nextPutAll: '&blog_name=', (entry getHTMLEncodingFor: settings blogName).
What I've done here is created a POST object, set the type to 'application/x-www-form-urlencoded' - which will allow the remote end to interpret it as a form being posted - and then placed the URL-Encoded form data in as the contents. That's the second method. Note that the code is simple streaming; I just create the URL-Encoded data manually. If this is something you need to do a lot of, you could pretty easily add convenience protocol to automate this.
In any case, using the above two methods, my web log sends trackbacks and pingbacks whenever I refer to an enabled site in a post. It's all pretty simple - a few lines of simple Smalltalk code, and you have an interface into any site on the web!