::obexTop, Main, Index
The obex
namespace contains the Client and Server classes which implement the Generic Object Exchange Profile (GOEP)
on which all other OBEX profiles are based. These classes may be used to access or provide any OBEX based service but require the application to have more knowledge of the profile with which that service is compliant. The profile-specific classes are easier to use in that regard.
Client | Implements GOEP client functionality. |
Server | Implements GOEP server functionality. |
OBEX operationsobex, Top, Main, Index
An OBEX operation consists of the client making one of the requests in the table below by calling the method of the same name. This is the request phase during which multiple packets may be exchanged with the server. The exchange then enters the response phase in which the server responds to the request via another multiple packet exchange. A single request may be in progress on a single transport connection at a time.
connect | Initiate a conversation and establish context. |
disconnect | Terminate a conversation. |
put | Send an object to the server. |
get | Retrieve an object from the server. |
setpath | Sets the object directory location on the server. |
session | Used for reliable session support over unreliable transports. Not supported by the obex package. |
abort | Special request sent to abort an ongoing request. |
The normal mode of operation consists of a sequence of requests starting with a connect
, ending with a disconnect
, and one or more of the other requests in between. Note that the connect
and disconnect
are optional for some some servers which will accept the put
and get
requests without a preceding connect
.
Packet transfer modelobex, Top, Main, Index
The OBEX session protocol allows for one request at a time. This one request may result in multiple packets in both directions that need to be processed for the request to be completed. Rather than carrying out this communications itself, the Client and Server objects depend on the application itself to do the actual packet transfer. This makes the implementation independent of the channels, whether synchronous or event-driven I/O is used and so on. For all it knows, the data is transferred by encapsulating in E-mail.
Generating requestsobex, Top, Main, Index
From the client side, a request to connect looks as below:
obex::Client create Client lassign [client connect] step data while {$step eq "continue"} { if {[string length $data]} { ...send $data to server... } set reply [...get data from server..] lassign [client input $reply] } if {$step eq "done"} { # Operation completed. Check if successful. if {[client status] eq "success"} { ... handle success ... } else { ... handle error ... } } else { # assert $step == "failed". Operation could not be completed. ... Handle bad news ... }
Although this fragment used the connect
operation, the model is exactly the same for other operations such as get
, put
etc. All the methods that implement these operations return a pair consisting of the next step to take and optionally data to send to the server. The application then sends data, if any, to the server. Then if the step value was continue
, application needs to read additional data and feed whatever it gets (at least one byte) to the Client.input method. This step is repeated as long as the input
method returns continue
. At any state, a method may return done
indicating all communication is over and the request completed or failed
indicated the request could not be completed. Note that done
only indicates the operation was completed, not that it was successful. More on this in Request completion status.
The above illustrates the conceptual model but of course the application may choose to do the equivalent non-sequentially via the event loop and non-blocking I/O.
Request completion statusobex, Top, Main, Index
The completion of a request is indicated by a return value of done
, writable
or failed
from the operation methods.
The value writable
is only returned in a PUT
streaming operation to indicate the next chunk of the data stream may be sent. See Client.put_stream for details.
The value failed
indicates a complete response was not received from the server. The cause may be protocol version incompatibility, protocol errors, loss of connectivity and so on.
The value done
indicates a full and valid response was received from the server. However, this does not mean that the request itself was successful as the server response may indicate failure or some other status. This status can be checked with the Client.status method which returns one of the following values: success
, informational
, redirect
, clienterror
, servererror
, databaseerror
or protocolerror
.
Each request completion status value corresponds to one of several OBEX response codes from the server. The actual response code may be obtained with the Client.status_detail method. The ResponseCode
and ResponseCodeName
dictionary keys returned by the method contain the numeric and mnemonic values.
A status of success
includes the following response codes (mnemonic values shown):
ok | Success. |
created | Object was created. |
accepted | Request accepted. |
nonauthoritative | Non-authoritative information. |
nocontent | No content. |
resetcontent | Reset content. |
partialcontent | Partial content. |
A status of informational
includes the following response codes:
continue | Client should send next packet in the request. This is internally handled by the package. |
A status of redirect
includes the following response codes and indicates the resource or object is available elsewhere or by some other means.
multiplechoices | Multiple choices. |
movedpermanently | Moved permanently. |
movedtemporarily | Moved temporarily. |
seeother | See other. |
notmodified | Not modified. |
useproxy | Use proxy. |
A status of protocolerror
includes the following response codes:
protocolerror | Generated internally by the obex package if a protocol error occured. It does not actually map to a OBEX response. |
A status of clienterror
indicates an error by the client in its request. It includes the following response codes:
badrequest | Bad request. Server could not understand request. |
unauthorized | Unauthorized. |
paymentrequired | Payment required. |
forbidden | Forbidden. Request understood but denied. |
notfound | Not found. |
methodnotallowed | Method not allowed. |
notacceptable | Request not acceptable. |
proxyauthenticationrequired | Proxy authentication required. |
requesttimeout | Request timed out. |
conflict | Conflict. |
gone | Gone. |
lengthrequired | Length required. |
preconditionfailed | Precondition failed. |
requestedentitytoolarge | Requested entity too large. |
requesturltoolarge | Request URL too large. |
unsupportedmediatype | Unsupported media. |
A status of servererror
indicates an error on the server in responding to a request and includes the following response codes:
internalservererror | Internal server error. |
notimplemented | Not implemented. |
badgateway | Bad gateway. |
serviceunavailable | Service unavailable. |
gatewaytimeout | Gateway timed out. |
httpversionnotsupported | Version not supported. |
A status of databaseerror
includes the following response codes:
databasefull | Database full. |
databaselocked | Database locked. |
Synchronous completionobex, Top, Main, Index
As a convenience most suitable for interactive use, the Client.await method can be used instead of the above idiom to synchronously wait for a request to complete. The equivalent of the above example would be
set status [client await $chan [client connect]] if {$status eq "done"} { ... } else { ... }
This runs the "continue" loop shown previously internally until the request succeeds or fails. The disadvantage of this method is that it will block the event loop until completion and offers no protection against timeouts, a non-responsive server and other such errors.
Channel configurationobex, Top, Main, Index
OBEX is a binary protocol. Any channels used to pass data should therefore be configured to be in binary mode. Moreover, because OBEX packets are small and never have more than one outstanding, buffering should be turned off.
chan configure $chan -translation binary -buffering none
Generating responsesobex, Top, Main, Index
[TBD] Server not implemented
OBEX headersobex, Top, Main, Index
The actual object itself, and any related metadata about it, is transferred in OBEX packets as a sequence of headers. For example, in a file transfer using get
operations, the request may contain a Name
header specifying the requested file while the response would include Body
and Timestamp
headers containing the file content and time of creation respectively.
The headers that allowed a OBEX conversation and the context in which they are used are defined by the profile followed by the application.
A header consists of two parts:
- The header identifier which specifies both the type and the semantics of the header.
- The header value whose format is fully defined by the header identifier.
Header values may be a string, a binary (sequence of bytes), a 8-bit value or a 32-bit value. When passing header values into obex
commands, the caller has to ensure the value is formatted appropriately. For strings and integers, this is straightforward. For byte sequences, caller must ensure the value is generated as a binary using the binary format
or encoding convertto
commands, read from a binary channel and so on.
The table below shows the header identifiers.
AppParameters | Byte sequence. Used by layered applications to include additional information in a request or response. The value is a byte sequence of (tag,length,value) triples where tag and length are one byte each. Tags and semantics are defined by the application. |
AuthChallenge | Byte sequence. Authentication challenge. |
AuthResponse | Byte sequence. Authentication response. |
Body | Byte sequence. A chunk of the object content. |
ConnectionId | 32-bit. The connection id used when multiplexing multiple OBEX connections over one transport connection. |
Count | 32-bit. Number of objects involved in the operation. |
CreatorId | 32-bit. Unsigned integer that identifies the creator of an object. |
Description | String. Describes the object or provides additional information about the operation, errors etc. |
EndOfBody | Byte sequence. The last chunk of the object content. |
Http | Byte sequence. This has the same format as HTTP 1.x headers and should be parsed as HTTP headers with the same semantics. |
Length | 32-bit. Length of object in bytes. |
Name | String. Name of the object, e.g. a file name. |
ObjectClass | Byte sequence. Similar in function to the Type header except the scope of the semantics are specific to the layered application. |
SessionParameters | Byte sequence. Parameters in session commands. |
SessionSequenceNumber | 8-bit. Used for sequencing packets in a session. |
Target | Byte sequence. Specifies the service to process a request. Must be the first header in a request packet if present and cannot be used together with the ConnectionId header within a request. |
Timestamp | Byte sequence. Represents time of last modification of the object. This should be in ISO 8601 format as YYYYMMDDTHHMMSS for local time and YYYYMMDDTHHMMSSZ for UTC. Note this is a byte sequence and not a string. |
Timestamp4 | 32-bit. Represents time of last modification as number of seconds since January 1, 1970. |
Type | Byte sequence. Describes the type of the object in the same manner as HTTP's Content-Header header. The value is a byte sequence of ASCII characters terminated by a null, not a string. |
WanUuid | Byte sequence. Only used in stateless networks environments where the OBEX server resides on network client with the OBEX client residing on the network server. The OBEX server (the network client) then includes this in all responses. |
Who | Byte sequence. Similar to the Target header except that while Target in a request identifies the desired service, Who in a response identifies the service generating the response. |
Classesobex, Top, Main, Index
Client [::obex]obex, Top, Main, Index
Method summary
constructor | Constructor for the class. |
abort | Generates a Obex ABORT request. |
await | Synchronously completes an ongoing operation. |
bodies | See Helper.bodies |
clear | Clears error state if any. |
connect | Generates a Obex connect request. |
connected | Returns 1 if the client has an OBEX connection active. |
disconnect | Generates a Obex disconnect request. |
get | Generates a Obex GET request. |
headers | See Helper.headers |
idle | Returns 1 if another request can be issued, otherwise 0. |
input | Process data from the remote server. |
put | Generates a Obex PUT request. |
put_delete | Generates a Obex PUT request to delete an object. |
put_stream | Generates a Obex PUT request with content provided in chunks. |
reset | Resets state of the object. |
response | Returns the last response received from the server. |
session | Generate a OBEX session request. |
setpath | Generates a Obex SETPATH request. |
state | Returns the state of the client. |
status | Returns the status of the last response received. |
status_detail | Returns the detailed status of the last response received. |
Mixins
Subclasses
constructor [::obex::Client]Client, Top, Main, Index
Client new ?args?
Parameters
method constructor {args} { namespace path [linsert [namespace path] end ::obex ::obex::core] my reset if {[llength [self next]]} { next {*}$args } }
abort [::obex::Client]Client, Top, Main, Index
Generates a Obex ABORT
request.
Parameters
headers | List of alternating header names and values. Optional, default "" . |
Description
It is the caller's responsibility to ensure the value associated with the header is formatted as described in OBEX headers and that the supplied headers if valid for ABORT
requests. The ConnectionId
header is automatically generated as needed and should not be included by the caller.
Return value
Returns a list of one or two elements, the first of which is either continue
or failed
, and the second, if present, is data to be sent to the server. See input for details.
method abort {{headers {}}} { # Generates a Obex `ABORT` request. # headers - List of alternating header names and values. # # It is the caller's responsibility to ensure the value associated # with the header is formatted as described in [OBEX headers] and # that the supplied headers if valid for `ABORT` requests. # The `ConnectionId` header is automatically generated as needed # and should not be included by the caller. # # Returns a list of one or two elements, the first of which is either # `continue` or `failed`, and the second, if present, is data to be sent # to the server. See [input] for details. my BeginRequest abort set state(headers_out) [header encoden $headers] set packet [my OutgoingPacket 0xff 0] # Check if all headers were accomodated if {[llength $state(headers_out)]} { # Not all headers fit. Abort request must be a single packet my RaiseError "Headers too long for abort request." } return [list continue $packet] }
await [::obex::Client]Client, Top, Main, Index
Synchronously completes an ongoing operation.
Parameters
chan | A Tcl channel for communicating with the server. This must not be multiplexed with other requests. |
action_state | The return value from a method that returns an action state such as connect, disconnect, get, put, put_delete, put_stream, setpath, abort or input. |
Description
The method places the passed channel into binary blocking mode and uses it to communicate with the server to complete all remaining steps required for the operation to complete. The original modes are restored before returning.
Return value
Returns done
, writable
or failed
. See input. Note the method will not return continue
.
method await {chan action_state} { # Synchronously completes an ongoing operation. # chan - A Tcl channel for communicating with the server. This # must not be multiplexed with other requests. # action_state - The return value from a method that returns # an action state such as [connect], [disconnect], [get], # [put], [put_delete], [put_stream], [setpath], [abort] or [input]. # # The method places the passed channel into binary blocking mode and # uses it to communicate with the server to complete all remaining # steps required for the operation to complete. The original modes are # restored before returning. # # Returns `done`, `writable` or `failed`. See [input]. Note the # method will not return `continue`. set chan_config [chan configure $chan] chan configure $chan -blocking 1 -buffering none -translation binary try { return [my Await $chan $action_state] } finally { # Restore original config. Note -encoding and -eofchar # need explicitly set as -translation binary above # changes them but not changed back by -translation below. chan configure $chan -blocking [dict get $chan_config -blocking] -buffering [dict get $chan_config -buffering] -encoding [dict get $chan_config -encoding] -translation [dict get $chan_config -translation] -eofchar [dict get $chan_config -eofchar] } }
clear [::obex::Client]Client, Top, Main, Index
Clears error state if any.
Description
The object is restored to an idle state readying it for another request. The command will raise an error if called while a request is in progress.
method clear {} { # Clears error state if any. # # The object is restored to an idle state readying it for another # request. The command will raise an error if called while a request # is in progress. my ResetRequest set state(state) IDLE return }
connect [::obex::Client]Client, Top, Main, Index
Generates a Obex connect request.
Parameters
headers | List of alternating header names and values. Optional, default "" . |
Description
It is the caller's responsibility to ensure the value associated with the header is formatted as described in OBEX headers and that the supplied headers if any are acceptable in connect
request. The following headers are commonly used in connects: Target
, Who
, Count
, Length
and Description
.
The method should not be called multiple times without an intervening call to disconnect.
Return value
Returns a list of one or two elements, the first of which is either continue
or failed
, and the second, if present, is data to be sent to the server. See input for details.
method connect {{headers {}}} { # Generates a Obex connect request. # headers - List of alternating header names and values. # # It is the caller's responsibility to ensure the value associated # with the header is formatted as described in [OBEX headers] and # that the supplied headers if any are acceptable in `connect` request. # The following headers are commonly used in connects: # `Target`, `Who`, `Count`, `Length` and `Description`. # # The method should not be called multiple times without an # intervening call to [disconnect]. # # Returns a list of one or two elements, the first of which is either # `continue` or `failed`, and the second, if present, is data to be sent # to the server. See [input] for details. if {$state(connected)} { error "Already connected." } my BeginRequest connect set state(headers_out) [header encoden $headers] # Packet is opcode 0x80, 2 bytes length, version (1.0->0x10), # flags (0), 2 bytes max len (proposed) set extra [binary format cucuSu 0x10 0 65535] set packet [my OutgoingPacket 0x80 0 $extra] if {[llength $state(headers_out)]} { # Not all headers fit. Connect request must be a single packet my RaiseError "Headers too long for connect request." } return [list continue $packet] }
connected [::obex::Client]Client, Top, Main, Index
Returns 1 if the client has an OBEX connection active.
Return value
Returns 1 if the client has an OBEX connection active.
method connected {} { # Returns 1 if the client has an OBEX connection active. return $state(connected) }
disconnect [::obex::Client]Client, Top, Main, Index
Generates a Obex disconnect request.
Parameters
headers | List of alternating header names and values. Optional, default "" . |
Description
It is the caller's responsibility to ensure the value associated with the header is formatted as described in OBEX headers and that the supplied headers if valid for disconnect
requests. The ConnectionId
header is automatically generated as needed and shoould not be included by the caller.
Return value
Returns a list of one or two elements, the first of which is either continue
or failed
, and the second, if present, is data to be sent to the server. See input for details.
method disconnect {{headers {}}} { # Generates a Obex disconnect request. # headers - List of alternating header names and values. # # It is the caller's responsibility to ensure the value associated # with the header is formatted as described in [OBEX headers] and # that the supplied headers if valid for `disconnect` requests. # The `ConnectionId` header is automatically generated as needed # and shoould not be included by the caller. # # Returns a list of one or two elements, the first of which is either # `continue` or `failed`, and the second, if present, is data to be sent # to the server. See [input] for details. if {!$state(connected)} { error "Not connected." } my BeginRequest disconnect set state(headers_out) [header encoden $headers] set packet [my OutgoingPacket 0x81 0] # Check if all headers were accomodated if {[llength $state(headers_out)]} { # Not all headers fit. Disconnect request must be a single packet my RaiseError "Headers too long for disconnect request." } set state(connected) false return [list continue $packet] }
get [::obex::Client]Client, Top, Main, Index
Generates a Obex GET
request.
Parameters
headers | List of alternating header names and values. Optional, default "" . |
Description
It is the caller's responsibility to ensure the value associated with the header is formatted as described in OBEX headers and that the supplied headers if any are acceptable in put
request. The following headers are commonly used in put operations: Name
, Type
, Http
, Timestamp
and Description
.
Return value
Returns a list of one or two elements, the first of which is either continue
or failed
, and the second, if present, is data to be sent to the server. See input for details.
method get {{headers {}}} { # Generates a Obex `GET` request. # headers - List of alternating header names and values. # # It is the caller's responsibility to ensure the value associated # with the header is formatted as described in [OBEX headers] and # that the supplied headers if any are acceptable in `put` request. # The following headers are commonly used in put operations: # `Name`, `Type`, `Http`, `Timestamp` and `Description`. # # Returns a list of one or two elements, the first of which is either # `continue` or `failed`, and the second, if present, is data to be sent # to the server. See [input] for details. my BeginRequest get set state(headers_out) [header encoden $headers] return [list continue [my OutgoingPacket 0x03 0]] }
idle [::obex::Client]Client, Top, Main, Index
Returns 1 if another request can be issued, otherwise 0.
Return value
Returns 1 if another request can be issued, otherwise 0.
method idle {} { # Returns 1 if another request can be issued, otherwise 0. return [expr {$state eq "IDLE"}] }
input [::obex::Client]Client, Top, Main, Index
Process data from the remote server.
Parameters
data | Binary data as received from remote server. |
Description
The method takes as input data received from the server as part of the response to a request. The return value from the method is a list of one or two elements. The first element is one of the following:
done | The full response has been received. The application can then call any of the retrieval methods or initiate another request. If the second element is present and and not empty, it is data to be sent to the server. The application can call other methods to retrieve the result of the request. The application may also call methods to initiate the next request. |
continue | The response has only been partially received. If the second element is present and not empty, it is data to be sent to the server. In either case, the application should read more data from the server and invoke the input method again passing it the read data. |
writable | This value is only returned if the current operation was a streaming put operation initiated with put_stream. It indicates that put_stream should be called again to send the next chunk of data. |
failed | The request failed. See Request completion status for dealing with errors and failures. If the second element is present and not empty, it is data to be sent to the server. In either case, the application must not invoke additional requests without first calling the reset method. |
This method will raise an exception if no request is currently outstanding.
Return value
Returns a list of one or two elements, the first being one of done
, failed
, continue
or writable
and the second optional element being data to send to the server.
method input {data} { # Process data from the remote server. # data - Binary data as received from remote server. # The method takes as input data received from the server as part of # the response to a request. The return value from the method is a list # of one or two elements. The first element is one of the following: # `done` - The full response has been received. The application # can then call any of the retrieval methods or initiate # another request. If the second element is present and # and not empty, it is data to be sent to the server. # The application can call other methods to retrieve # the result of the request. The application may also # call methods to initiate the next request. # `continue` - The response has only been partially received. If # the second element is present and not empty, it is # data to be sent to the server. In either case, the # application should read more data from the server # and invoke the `input` method again passing it the # read data. # `writable` - This value is only returned if the current operation # was a streaming `put` operation initiated with # [put_stream]. It indicates that [put_stream] should # be called again to send the next chunk of data. # `failed` - The request failed. See [Request completion status] # for dealing with errors and failures. If # the second element is present and not empty, it is # data to be sent to the server. In either case, the # application must not invoke additional requests # without first calling the [reset] method. # # This method will raise an exception if no request is currently # outstanding. # # Returns a list of one or two elements, the first being one of # `done`, `failed`, `continue` or `writable` and the second optional # element being data to send to the server. my AssertState BUSY # Append new data to existing append state(input) $data if {! [response decode $state(input) $state(op) response]} { return continue; # Incomplete packet, need more input } # TBD - should this be a protocol error if input was longer than packet # Possibly a response followed by an ABORT? set state(input) [string range $state(input) [dict get $response PacketLength] end] # If we have a connection id, the incoming one must match if present if {[info exists connection_id]} { if {![header find $response(Headers) ConnectionId conn_id] || $connection_id != $conn_id} { # TBD - ignore mismatches for now } } # Save as latest response set state(response) $response # For multipart responses, collect headers lappend state(headers_in) {*}[dict get $response Headers] # Do request-specific processing return [switch -exact -- $state(op) { connect { my ConnectResponseHandler } disconnect { my ResponseHandler false } put { my ResponseHandler true } get { my ResponseHandler true } setpath { my ResponseHandler false } session { error "Internal error: session request."} abort { my ResponseHandler false } default { error "Unexpected request opcode $state(op)." } }] }
put [::obex::Client]Client, Top, Main, Index
Generates a Obex PUT
request.
Parameters
content | Content to be sent to server as-is. This must be formatted appropriately based on the Type header, Http or ObjectClass headers passed in. If none of these are present, the server may interpret $content in any manner it chooses, possibly looking at the Name header if present, some default handling or even rejecting the request. |
headers | List of alternating header names and values. Optional, default "" . |
Description
It is the caller's responsibility to ensure the value associated with the header is formatted as described in OBEX headers and that the supplied headers if any are acceptable in put
request. The following headers are commonly used in put operations: Name
, Type
, Http
, Timestamp
and Description
. The headers Body
, EndOfBody
, Length
and ConnectionId
are automatically generated and should not be passed in.
Return value
Returns a list of one or two elements, the first of which is either continue
or failed
, and the second, if present, is data to be sent to the server. See input for details.
method put {content {headers {}}} { # Generates a Obex `PUT` request. # content - Content to be sent to server as-is. This must be formatted # appropriately based on the `Type` header, `Http` or `ObjectClass` # headers passed in. If none of these are present, # the server may interpret $content in any # manner it chooses, possibly looking at the `Name` header if present, # some default handling or even rejecting the request. # headers - List of alternating header names and values. # # It is the caller's responsibility to ensure the value associated # with the header is formatted as described in [OBEX headers] and # that the supplied headers if any are acceptable in `put` request. # The following headers are commonly used in put operations: # `Name`, `Type`, `Http`, `Timestamp` and `Description`. # The headers `Body`, `EndOfBody`, `Length` and `ConnectionId` # are automatically generated and should not be passed in. # # Returns a list of one or two elements, the first of which is either # `continue` or `failed`, and the second, if present, is data to be sent # to the server. See [input] for details. # TBD - maybe break up content into body headers assuming body space # is packet size - packet header - connection id header. That would # simplify Put method my BeginRequest put lappend headers Length [string length $content] {*}[my SplitContent $content] set state(headers_out) [header encoden $headers] return [list continue [my OutgoingPacket 0x02 0]] }
put_delete [::obex::Client]Client, Top, Main, Index
Generates a Obex PUT
request to delete an object.
Parameters
headers | List of alternating header names and values. Optional, default "" . |
Description
It is the caller's responsibility to ensure the value associated with the header is formatted as described in OBEX headers and that the supplied headers if any are acceptable in put
request. The following headers are commonly used in put operations: Name
, Type
, Http
, Timestamp
and Description
. The headers Body
, EndOfBody
, Length
should not be present in a delete operation and should not be passed in. Moreover, ConnectionId
header is automatically generated and should not be passed in.
Return value
Returns a list of one or two elements, the first of which is either continue
or failed
, and the second, if present, is data to be sent to the server. See input for details.
method put_delete {{headers {}}} { # Generates a Obex `PUT` request to delete an object. # headers - List of alternating header names and values. # # It is the caller's responsibility to ensure the value associated # with the header is formatted as described in [OBEX headers] and # that the supplied headers if any are acceptable in `put` request. # The following headers are commonly used in put operations: # `Name`, `Type`, `Http`, `Timestamp` and `Description`. # The headers `Body`, `EndOfBody`, `Length` should not be present # in a delete operation and should not be passed in. Moreover, # `ConnectionId` header is automatically generated and should not # be passed in. # # Returns a list of one or two elements, the first of which is either # `continue` or `failed`, and the second, if present, is data to be sent # to the server. See [input] for details. my BeginRequest put set state(headers_out) [header encoden $headers] return [list continue [my OutgoingPacket 0x02 0]] }
put_stream [::obex::Client]Client, Top, Main, Index
Generates a Obex PUT
request with content provided in chunks.
Parameters
chunk | A content chunk to be sent. An empty string indicates end of the stream. This must be formatted appropriately based on the Type header, Http or ObjectClass headers passed in the initial call to put_stream . If none of these are present, the server may interpret content in any manner it chooses, possibly looking at the Name header if present, some default handling or even rejecting the request. |
headers | List of alternating header names and values. Should be empty on all calls except the first. Optional, default "" . |
Description
This is similar to the put method except that it permits the caller to present the data to be sent in chunks instead of all at once. The application provides each chunk of the content in repeated calls to put_stream
. This is more memory-efficient for large files. Only the first call initiating the PUT
operation may specify headers. Passing an empty string indicates end of the content.
Streaming operation is achieved through the following sequence of calls:
- Streaming is initiated through a call to
put_stream
which normally returnscontinue
with data to send to the server. - The input method is called with the response from the server. This returns
writable
indicating more data can be sent. The application then callsput_stream
again with the next chunk. - This
input
,put_stream
sequence is repeated until there is no more data to send at which time the application should callput_stream
with an empty chunk.
It is the caller's responsibility to ensure the value associated with the header is formatted as described in OBEX headers and that the supplied headers if any are acceptable in put
request. The following headers are commonly used in put operations: Name
, Type
, Http
, Timestamp
and Description
. The headers Body
, EndOfBody
, and ConnectionId
are automatically generated and should not be passed in. This method does not automatically add a Length
header since the length is not known a priori. However, some server implementations require the Length
header and therefore should be passed in as part of $headers
in such cases.
Return value
Returns a list of one or two elements, the first of which is either continue
or failed
, and the second, if present, is data to be sent to the server. See input for details.
method put_stream {chunk {headers {}}} { # Generates a Obex `PUT` request with content provided in chunks. # chunk - A content chunk to be sent. An empty string indicates end # of the stream. This must be formatted # appropriately based on the `Type` header, `Http` or `ObjectClass` # headers passed in the initial call to `put_stream`. # If none of these are present, # the server may interpret content in any # manner it chooses, possibly looking at the `Name` header if present, # some default handling or even rejecting the request. # headers - List of alternating header names and values. Should # be empty on all calls except the first. # # This is similar to the [put] method except that it permits the # caller to present the data to be sent in chunks instead of all at # once. The application provides each chunk of the content in repeated # calls to `put_stream`. # This is more memory-efficient for large files. Only the first # call initiating the `PUT` operation may specify headers. Passing # an empty string indicates end of the content. # # Streaming operation is achieved through the following sequence of # calls: # # * Streaming is initiated through a call to `put_stream` which # normally returns `continue` with data to send to the server. # * The [input] method is called with the response from the server. # This returns `writable` indicating more data can be sent. The # application then calls `put_stream` again with the next chunk. # * This `input`, `put_stream` sequence is repeated until there # is no more data to send at which time the application should # call `put_stream` with an empty chunk. # # It is the caller's responsibility to ensure the value associated # with the header is formatted as described in [OBEX headers] and # that the supplied headers if any are acceptable in `put` request. # The following headers are commonly used in put operations: # `Name`, `Type`, `Http`, `Timestamp` and `Description`. # The headers `Body`, `EndOfBody`, and `ConnectionId` # are automatically generated and should not be passed in. # This method does not automatically add a `Length` header since # the length is not known a priori. However, some server implementations # require the `Length` header and therefore should be passed in # as part of $headers in such cases. # # Returns a list of one or two elements, the first of which is either # `continue` or `failed`, and the second, if present, is data to be sent # to the server. See [input] for details. if {$state(state) eq "IDLE"} { my BeginRequest put } else { my AssertState STREAMING if {[llength $headers] != 0} { my RaiseError "Headers only allowed in first chunk of a data stream." } } if {[string length $chunk] != 0} { # Note no Length header as unknown! lappend headers {*}[my SplitContent $chunk] set state(streaming) 1 } else { lappend headers EndOfBody {} set state(streaming) 0 # TBD - do we need to guard against more put_stream calls # after EndOfBody is sent? } set state(headers_out) [header encoden $headers] set state(state) BUSY return [list continue [my OutgoingPacket 0x02 0]] }
reset [::obex::Client]Client, Top, Main, Index
Resets state of the object.
Description
The object is placed in the same state as when it was newly constructed. All state information is lost.
method reset {} { # Resets state of the object. # # The object is placed in the same state as when it was newly constructed. # All state information is lost. # Connection specific state set state(state) IDLE set state(connected) false unset -nocomplain state(connection_id) unset -nocomplain state(connection_header) unset -nocomplain state(target) unset -nocomplain state(who) set state(max_packet_len) 255; # Assume min unless remote tells otherwise # Request specific state my ResetRequest }
response [::obex::Client]Client, Top, Main, Index
Returns the last response received from the server.
Description
The return value is a dictionary in the same form as returned by the ::obex::core::response decode command.
Return value
Returns the last response received from the server.
method response {} { # Returns the last response received from the server. # # The return value is a dictionary in the same form as returned by # the [::obex::core::response decode] command. return $state(response) }
session [::obex::Client]Client, Top, Main, Index
Generate a OBEX session
request.
Parameters
headers | List of alternating header names and values. Optional, default "" . |
Description
This command is not implemented.
method session {{headers {}}} { # Generate a OBEX `session` request. # headers - List of alternating header names and values. # This command is not implemented. error "Sessions not implemented in this release." }
setpath [::obex::Client]Client, Top, Main, Index
Generates a Obex SETPATH
request.
Parameters
headers | List of alternating header names and values. Optional, default "" . |
-nocreate | Do no create folder if it does not exist. |
-parent | Apply operation at the parent's level. |
Description
It is the caller's responsibility to ensure the value associated with the header is formatted as described in OBEX headers and that the supplied headers if valid for SETPATH
requests. The ConnectionId
header is automatically generated as needed and shoould not be included by the caller.
Return value
Returns a list of one or two elements, the first of which is either continue
or failed
, and the second, if present, is data to be sent to the server. See input for details.
method setpath {{headers {}} args} { # Generates a Obex `SETPATH` request. # headers - List of alternating header names and values. # -parent - Apply operation at the parent's level. # -nocreate - Do no create folder if it does not exist. # # It is the caller's responsibility to ensure the value associated # with the header is formatted as described in [OBEX headers] and # that the supplied headers if valid for `SETPATH` requests. # The `ConnectionId` header is automatically generated as needed # and shoould not be included by the caller. # # Returns a list of one or two elements, the first of which is either # `continue` or `failed`, and the second, if present, is data to be sent # to the server. See [input] for details. my BeginRequest setpath set flags 0 set constants 0 foreach opt $args { switch -exact -- $opt { -parent { set flags [expr {$flags | 1}] } -nocreate { set flags [expr {$flags | 2}] } default { error "Unknown option \"$opt\"." } } } set state(headers_out) [header encoden $headers] set packet [my OutgoingPacket 0x85 0 [binary format cucu $flags $constants]] if {[llength $state(headers_out)]} { # Not all headers fit. setpath request must be a single packet my RaiseError "Headers too long for setpath request." } return [list continue $packet] }
state [::obex::Client]Client, Top, Main, Index
Returns the state of the client.
Description
The returned state is in the form of a dictionary with the following elements:
State | Client state; one of IDLE , BUSY or ERROR |
Connected | 0/1 depending on whether connected or not. |
ConnectionId | The connection id. Only present if connected and remote server sent a connection id. |
MaxPacketLength | Maximum packet length negotiated. |
ErrorMessage | If present, the last error seen. |
Return value
Returns the state of the client.
method state {} { # Returns the state of the client. # # The returned state is in the form of a dictionary with the following # elements: # State - Client state; one of `IDLE`, `BUSY` or `ERROR` # Connected - 0/1 depending on whether connected or not. # ConnectionId - The connection id. Only present if connected # **and** remote server sent a connection id. # MaxPacketLength - Maximum packet length negotiated. # ErrorMessage - If present, the last error seen. set l [list State $state(state) Connected $state(connected) MaxPacketLength $state(max_packet_len)] if {[info exists state(connection_id)]} { lappend l ConnectionId $state(connection_id) } if {[info exists state(error_message)]} { lappend l ErrorMessage $state(error_message) } return $l }
status [::obex::Client]Client, Top, Main, Index
Returns the status of the last response received.
Description
The returned value is one of success
, informational
, redirect
, clienterror
, servererror
, databaseerror
or protocolerror
. See ::obex::Request completion status.
The command will raise an error if no response has been received for any request.
Return value
Returns the status of the last response received.
method status {} { # Returns the status of the last response received. # # The returned value is one of # `success`, `informational`, `redirect`, # `clienterror`, `servererror`, `databaseerror` or `protocolerror`. # See [::obex::Request completion status]. # # The command will raise an error if no response has been received # for any request. return [dict get $state(response) ResponseStatus] }
status_detail [::obex::Client]Client, Top, Main, Index
Returns the detailed status of the last response received.
Description
The returned dictionary has the following keys:
ResponseStatus | The generic status category. |
ResponseCode | The numeric response code from server. |
ResponseCodeName | Mnemonic form of ResponseCode |
ErrorMessage | Additional human readable error status message. This key may not be present. |
For more information on values for the above keys, see ::obex::Request completion status.
The command will raise an error if no response has been received for any request.
Return value
Returns the detailed status of the last response received.
method status_detail {} { # Returns the detailed status of the last response received. # # The returned dictionary has the following keys: # ResponseStatus - The generic status category. # ResponseCode - The numeric response code from server. # ResponseCodeName - Mnemonic form of `ResponseCode` # ErrorMessage - Additional human readable error status message. This # key may not be present. # # For more information on values for the above keys, see # [::obex::Request completion status]. # # The command will raise an error if no response has been received # for any request. dict with state(response) { lappend status ResponseStatus $ResponseStatus ResponseCode $ResponseCode ResponseCodeName [response::ResponseCodeName $ResponseCode] if {[info exists state(error_message)]} { lappend status ErrorMessage $state(error_message) } } return $status }
Helper [::obex]obex, Top, Main, Index
Method summary
bodies | Get the data content in a request or response. |
headers | Retrieves the content of headers of a given type. |
Subclasses
bodies [::obex::Helper]Helper, Top, Main, Index
Get the data content in a request or response
Description
The data content is transferred in Obex through headers of type Body
and EndOfBody
. This method returns the values received through these headers as a list. The content is in binary form and needs to be appropriately interpreted depending on the application specifics. Note the content may be fragmented at arbitrary boundaries during transmission and so the returned values may need to be concatenated before operations like UTF-8 decoding.
Return value
Returns list of binary strings received through Body
and EndOfBody
headers.
method bodies {} { # Get the data content in a request or response # # The data content is transferred in Obex through headers of type `Body` # and `EndOfBody`. This method returns the values received through these # headers as a list. The content is in binary form and needs to be # appropriately interpreted depending on the application specifics. Note # the content may be fragmented at arbitrary boundaries during # transmission and so the returned values may need to be concatenated # before operations like UTF-8 decoding. # # Returns list of binary strings received through `Body` and `EndOfBody` # headers. set bodies {} foreach {name val} $state(headers_in) { if {$name in {Body EndOfBody}} { lappend bodies $val } } return $bodies }
headers [::obex::Helper]Helper, Top, Main, Index
Retrieves the content of headers of a given type.
Parameters
name | Name of the header. |
Return value
Returns a list each element of which is the value of a header
method headers {name} { # Retrieves the content of headers of a given type. # name - name of the header # Returns a list each element of which is the value of a header # return [header findall $state(headers_in)] }
Server [::obex]obex, Top, Main, Index
Method summary
constructor | Constructor for the class. |
bodies | See Helper.bodies |
get_request | Not documented. |
headers | See Helper.headers |
input | Process data received from a client. |
reset | Resets state of the object. |
respond | Not documented. |
respond_content | Generate a response containing content. |
state | Returns the state of the server. |
Mixins
constructor [::obex::Server]Server, Top, Main, Index
Server new ?args?
Parameters
method constructor {args} { namespace path [linsert [namespace path] end ::obex ::obex::core] my reset if {[llength [self next]]} { next {*}$args } }
get_request [::obex::Server]Server, Top, Main, Index
method get_request {} { return $state(request) }
input [::obex::Server]Server, Top, Main, Index
Process data received from a client.
Parameters
data | Binary data as received from client. |
Description
The method takes as input data received from a client. The return value from the method is a list of one or two of one or two elements. The first element is one of done
continue
or failed
. The semantics depend on whether the request is in the request phase or the response phase.
In the request phase,
done | The request was completed without need for an explicit response from the application. If the the second element is present and not empty, it is data to be sent to the client. |
respond | The full request has been received. The application should then call one of the response methods to reply to the client. If the second element is present and not empty, it is data to be sent to the client. The application can call other methods to retrieve the request. |
continue | The request has only been partially received. If the second element is present and not empty, it is data to be sent to the client. In either case, the application should read more data from the client and again invoke the input method passing it the read data. |
failed | The request has failed. See Request completion status for dealing with errors and failures. If the second element is present and not empty, it is data to be sent to the client. In either case, the application must not use this instance to accept additional requests without first calling the reset method. |
In the response phase,
done | The full response has been sent to the client. If the second element is present and not empty, it is data to be sent to the client. The application can then process a new request using this object. |
continue | The response has only been partially sent. If the second element is present and not empty, it is data to be sent to the client. In either case, the application should read more data from the client and invoke the input method again passing it the read data. |
failed | The request has failed. See Request completion status for dealing with errors and failures. If the second element is present and not empty, it is data to be sent to the client. In either case, the application must not use this instance to accept additional requests without first calling the reset method. |
method input {data} { # Process data received from a client. # data - Binary data as received from client. # The method takes as input data received from a client. # The return value from the method is a list of one or two # of one or two elements. The first element is one of `done` # `continue` or `failed`. The semantics depend on whether # the request is in the **request** phase or the **response** # phase. # # In the **request** phase, # `done` - The request was completed without need for an explicit # response from the application. If the # the second element is present and not empty, it is data # to be sent to the client. # `respond` - The full request has been received. The application should # then call one of the response methods to reply to the client. If # the second element is present and not empty, it is data # to be sent to the client. The application can call other # methods to retrieve the request. # `continue` - The request has only been partially received. If the # second element is present and not empty, it is data to be # sent to the client. In either case, the application should # read more data from the client and again invoke the [input] # method passing it the read data. # `failed` - The request has failed. See [Request completion status] for dealing # with errors and failures. If the second element is present # and not empty, it is data to be sent to the client. In # either case, the application must not use this instance # to accept additional requests without first calling the # [reset] method. # # In the **response** phase, # `done` - The full response has been sent to the client. # If the second element is present and not empty, it is data # to be sent to the client. The application can then # process a new request using this object. # `continue` - The response has only been partially sent. If the # second element is present and not empty, it is data to be # sent to the client. In either case, the application should # read more data from the client and invoke the [input] method # again passing it the read data. # `failed` - The request has failed. See [Request completion status] for dealing # with errors and failures. If the second element is present # and not empty, it is data to be sent to the client. In # either case, the application must not use this instance # to accept additional requests without first calling the # [reset] method. # switch -exact -- $state(state) { IDLE - REQUEST { my RequestPhaseInput $data} RESPOND { my ResponsePhaseInput $data} ERROR { error "Method must not be called after an error without calling the reset method first."} default { error "Internal error: unknown state $state(state)"} } }
reset [::obex::Server]Server, Top, Main, Index
Resets state of the object.
Description
The object is placed in the same state as when it was newly constructed. All state information is lost.
method reset {} { # Resets state of the object. # # The object is placed in the same state as when it was newly constructed. # All state information is lost. # Connection specific state set state(state) IDLE unset -nocomplain state(connection_id); # Set when connect comes in unset -nocomplain state(connection_header) set state(max_packet_len) 255; # Assume min until remote tells otherwise # Request specific state my ResetRequest }
respond [::obex::Server]Server, Top, Main, Index
Parameters
status | Not documented. |
headers | Not documented. Optional, default "" . |
method respond {status {headers {}}} { my AssertState RESPOND set state(headers_out) [header encoden $headers] set status [response::ResponseCode $status] set state(response_code) $status # CONNECT and DISCONNECT need special handling. set op [dict get $state(request) OpName] if {$op eq "connect"} { set state(max_packet_len) [dict get $state(request) MaxLength] set state(connection_id) [GenerateId] set state(connection_header) [header encode ConnectionId $state(connection_id)] set state(headers_out) $headers # Packet is opcode 0x80, 2 bytes length, version (1.0->0x10), # flags (0), 2 bytes max len set extra_fields [binary format cucuSu 0x10 0 $state(max_packet_len)] } elseif {$op eq "disconnect"} { set extra_fields "" unset -nocomplain state(connection_id) unset -nocomplain state(connection_header) set state(max_packet_len) 255 } else { set extra_fields "" } # TBD - assume a single packet response set status [expr {$status | 0x80}] set packet [my OutgoingPacket $status 1 $extra_fields] if {[llength $state(headers_out)]} { # TBD - don't know exactly how multipacket responses work when # status is not continue. my RaiseError "Response does not fit in a packet." } return [list done $packet] }
respond_content [::obex::Server]Server, Top, Main, Index
Generate a response containing content.
Parameters
content | Content to include in the response to the client. |
headers | List of alternating header names and values. Optional, default "" . |
method respond_content {content {headers {}}} { # Generate a response containing content. # content - content to include in the response to the client. # headers - List of alternating header names and values. # my AssertState RESPOND lappend headers Length [string length $content] {*}[my SplitContent $content] set state(headers_out) [header encoden $headers] set state(response_code) $status set packet [my OutgoingPacket $state(response_code) 1] if {[llength $state(headers_out)]} { return [list continue $packet] } else { return [list done $packet] } }
state [::obex::Server]Server, Top, Main, Index
Returns the state of the server.
Description
The returned state is in the form of a dictionary with the following elements:
State | Server state; one of IDLE , REQUEST , RESPOND or ERROR |
Connected | 0/1 depending on whether connected or not. |
ConnectionId | The connection id. Only present if connected. |
MaxPacketLength | Maximum packet length negotiated. |
ErrorMessage | If present, the last error seen. |
Return value
Returns the state of the server.
method state {} { # Returns the state of the server. # # The returned state is in the form of a dictionary with the following # elements: # State - Server state; one of `IDLE`, `REQUEST`, `RESPOND` or `ERROR` # Connected - 0/1 depending on whether connected or not. # ConnectionId - The connection id. Only present if connected. # MaxPacketLength - Maximum packet length negotiated. # ErrorMessage - If present, the last error seen. set l [list State $state(state) MaxPacketLength $state(max_packet_len)] if {[info exists state(connection_id)]} { lappend l Connected 1 ConnectionId $state(connection_id) } else { lappend l Connected 0 } if {[info exists state(error_message)]} { lappend l ErrorMessage $state(error_message) } return $l }