Simple HTTP(S) service to support GET and PUT operations.
It can be used as a simple file transfer service.

Configuration
-------------

The Hopi service needs a directory which it could serve. The <DocumentRoot> element of the service configuration xml contains the path of this directory.

    <Service name="hopi" id="hopi">
        <DocumentRoot>./testdir</DocumentRoot>
    </Service>

The Hopi service has a special mode called SlaveMode, which is needed by the Shepherd service to use this Hopi service as a transfer service.

    <Service name="hopi" id="hopi3">
        <DocumentRoot>./shepherd_transfer3</DocumentRoot>
        <SlaveMode>1</SlaveMode>
    </Service>
    
If the SlaveMode is not "1" or not specified, then it os off.

The Hopi service could handle GET and PUT requests.
We can configure HED this way:


    <Component name="tcp.service" id="tcp">
        <next id="http"/>
        <tcp:Listen>
            <tcp:Port>60000</tcp:Port>
        </tcp:Listen>
    </Component>
    <Component name="http.service" id="http">
        <next id="hopi">GET</next>
        <next id="hopi">PUT</next>
    </Component>
    <Service name="hopi" id="hopi">
        <DocumentRoot>./testdir</DocumentRoot>
    </Service>

Here the component called "http.service" is the HTTP MCC, a message chain component which could handle HTTP request and route them to other components or service.
So actually the Hopi service is not dealing with HTTP itself, because the HTTP part is handled by the HTTP MCC. The Hopi service just handles the content.
This configuration means that no other services or components can receive HTTP/GET or HTTP/PUT request on this port.

The other configuration option is using a Plexer:

    <Component name="tcp.service" id="tcp">
        <next id="http"/>
        <tcp:Listen>
            <tcp:Port>60000</tcp:Port>
        </tcp:Listen>
    </Component>
    <Component name="http.service" id="http">
        <next id="plexer">GET</next>
        <next id="plexer">PUT</next>
    </Component>
    <Plexer name="plexer.service" id="plexer">
        <next id="hopi">^/prb</next>
    </Plexer>
    <Service name="hopi" id="hopi">
        <DocumentRoot>./testdir</DocumentRoot>
    </Service>
    
With this configuration the Hopi service is accessible with the path '/prb'.

Operation in normal mode
------------------------

If the HED is listening on localhost:60000 and the Hopi service is wired to the HTTP MMC without the plexer, you can GET this URL: http://localhost:60000, and you will get a directory listing of the specified DocumentRoot directory: './testdir'.

    $ curl http://localhost:60000
    <HTML>
    <HEAD>Directory list of '/'</HEAD>
    <BODY><UL>
    <LI><a href="/abc">abc</a></LI>
    <LI><a href="/dir">dir</a></LI>
    </UL></BODY></HTML>

If 'testdir/abc' is a file, you can get its content from  http://localhost:60000/abc

    $ cat testdir/abc 
    This file is called 'abc'.
    $ curl http://localhost:60000/abc
    This file is called 'abc'.

If 'testdir/dir' is a subdirectory, you can get its contents using http://localhost:60000/dir

    $ ls testdir/dir/
    ghi
    $ curl http://localhost:60000/dir
    <HTML>
    <HEAD>Directory list of '/dir'</HEAD>
    <BODY><UL>
    <LI><a href="/dir/ghi">ghi</a></LI>
    </UL></BODY></HTML>

You can PUT a file to the server. e.g. if you PUT to http://localhost:60000/dir/new_file

    $ cat file_to_put 
    This file will be put to the server.
    $ curl -T file_to_put http://localhost:60000/dir/new_file
    $ ls testdir/dir/
    ghi       new_file  
    $ cat testdir/dir/new_file 
    This file will be put to the server.
    $ curl http://localhost:60000/dir/new_file
    This file will be put to the server.

If you request a PUT to an already existing file, the Hopi service will overwrite it if it has write-permission.

    $ curl http://localhost:60000/abc
    This file is called 'abc'.
    $ curl -T file_to_put http://localhost:60000/abc
    $ curl http://localhost:60000/abc
    This file will be put to the server.

If we use a Plexer, and the Hopi service has a path of '/prb':

    $ curl http://localhost:60000/prb
    <HTML>
    <HEAD>Directory list of ''</HEAD>
    <BODY><UL>
    <LI><a href="/prb/abc">abc</a></LI>
    <LI><a href="/prb/dir">dir</a></LI>
    </UL></BODY></HTML>
    $ curl http://localhost:60000/prb/dir/ghi
    This file is called 'ghi'.
    $ curl http://localhost:60000/prb/dir/new_file
    This file will be put to the server.
    $ cat file_to_put 
    This file will be put to the server.
    $ curl -T file_to_put http://localhost:60000/prb/new_here
    $ curl http://localhost:60000/prb/new_here
    This file will be put to the server.
    $ curl http://localhost:60000/prb/new_here
    $ cat testdir/new_here 
    This file will be put to the server.

Operation in slave mode
-----------------------
In slave mode there are these differencies:
    - no directory listing
    - PUT is only allowed to existing files (which overwrites the existing file)
    - immediately after the GET or PUT request the file on the server is removed! (unlinked)
    
This does not make much sense first, but this is the way how it is used by the Shepherd service:

The Shepherd service uses two seperate directories: one for storing all the files ('./store') and one for the file transfers ('./transfer'). The store directory always contains all the files the Shepherd stores, the transfer directory is empty at the beginning.
If a client asks for a file called 'file1', this file is in the store directory ('./store/file'), and the Shepherd service creates a hardlink into the transfer directory (e.g. './transfer/abc') and sets this file read-only. If the Hopi service has the path '/prb' and has the DocumentRoot './transfer' then after the hardlink is created, we have this URL for this file: http://localhost:60000/prb/abc Now we can give this URL to the client. Then the client GETs this URL and gets the file, and the Hopi removes (unlinks) this file, which makes this http://localhost:60000/prb/abc URL invalid, and the file is still there in the store directory. Now if some other user wants this file, the Shepperd creates an other hardlink, e.g. './transfer/qwe' and new we have an URL http://localhost:60000/prb/qwe
So these are one-time URLs.
If a client wants to upload a new file, then the Shepperd creates an empty file in the store, e.g. './store/file2' and creates a hardlink, e.g. './transfer/oiu', and make it writable, and now we have a URL http://localhost:60000/prb/oiu which is an existing file, and the client is able to do a PUT to this URL. When the client PUT the file there, the Hopi service in slave mode immediately removes the uploaded file from the transfer dir, but because it has a hardlink in the store dir, so the file will be there as './store/file2'.

So this is the way the Shepherd service integrates with a slave mode Hopi service.


Known limitation
----------------

The current version of the Hopi service (2008. july 10.) is reads the whole file into memory when PUTing or GETing a file.
