8

I am trying to send a BREW request to a server implementing the Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0, defined in RFC 2324), which is a protocol built on top of HTTP, using cURL.

I would like to BREW a coffee with Cream and a shot of Vanilla syrup, but so far all my requests have all been rejected by the server.

Confusingly, the RFC says the Content-Type must be set to "application/coffee-pot-command" (Section 2.11), but also says Content-Type must be set to "message/coffeepot" (Section 4, and additionally says the content of "message/coffeepot" must contain a coffee-message-body, defined as coffee-message-body = "start" | "stop"). I've gone with "application/coffee-pot-command", purely become it comes first (but I've tried both).

Next, I have added an "Accept-Additions" header, specifying the milk-type as "Cream" and the syrup-type as "Vanilla". The RFC outlines a BNF for this header:

   Accept-Additions = "Accept-Additions" ":"
                      #( addition-range [ accept-params ] )

    addition-type   = ( "*"
                      | milk-type
                      | syrup-type
                      ) *( ";" parameter )
    milk-type       = ( "Cream" | "Half-and-half" | "Whole-milk"
                      | "Part-Skim" | "Skim" | "Non-Dairy" )
    syrup-type      = ( "Vanilla" | "Almond" | "Raspberry"
                      | "Chocolate" )

My best guess for this part would be I would need to add the following header:

--header "Accept-Additions: Skim;1,Vanilla;1"

Although I am not experienced with interpreting BNF so this could be completely wrong.

All this has lead me to build the following request with cURL:

curl -X POST https://theserver.com --header "Content-Type:application/coffee-pot-command" --header "Accept-Additions:Skim;1,Vanilla;1"

However the server keeps telling me "Invalid drink, cannot brew". Where am I going wrong?

There is a section called "The "coffee" URI scheme" which contains more BNF:

coffee-url  =  coffee-scheme ":" [ "//" host ]
                ["/" pot-designator ] ["?" additions-list ]

coffee-scheme = ( "koffie"                      ; Afrikaans, Dutch
               | "coffee"                   ; English
               )

But I have no idea how to interpret this. Do I need to add "coffee:/pot-0" somewhere? If so how does one do that in cURL? Is this what I am missing? I am aware the RFC is an April Fools, but the server is real. A free coffee to whoever can help.

ajq88
  • 305
  • 1
  • 14
  • Did you try just getting the bare miniumum to work, maybe `--header "Accept-Additions: Skim"` ? Good luck. – shellter Nov 05 '20 at 16:44
  • Ah, and I now notice `https://my.server.com`. So you're running this at home? What do the logs tell you? Also see if `curl -v ...` sheds any light. – shellter Nov 05 '20 at 17:27
  • 3
    RFC 2324 has been obsoleted by RFC 7168. The RFC notes that there is some conflict between the purpose of brewing Tea and brewing Coffee; see section 2.3.3 .It seems that despite the title the RFC assumption is a Tea pot for brewing that beverage. If coffee is desired the content-type "message/coffeepot" should be used. National Barista Day March 1st 2023 might be a nice date release an updated RFC that better accommodates the use of modern Barista machines. – Alan Carlyle Sep 02 '22 at 09:02
  • The funniest part is that nowadays with all the IOT stuff, BREW requests could actually become useful and widely used at some point, if someone releases kitchen appliances who support it. Seems like a great candidate for a hardware open source project. – ThaJay Jun 29 '23 at 14:46

1 Answers1

0

I know this is an old question but I am attempting to implement an HTCPCP server and I have also found that the RFC is contradictory, incomplete or seemingly inaccurate in some areas. After wrestling with the RFC I think the appropriate way to structure your request using curl is:

curl 'theserver.com` \
     --request 'BREW' \
     --header  'Scheme: coffee' \
     --header  'Content-Type: message/coffeepot' \
     --header  'Accept-Additions: milk-type/Cream, syrup-type/Vanilla' \
     --data    'coffee-message-body=start'

The above syntax may or may not line up with the implementation on the server you are (were?) attempting to interact with but I've explained my reasoning below.

Rationale

It is clear from section 2 and section 3 of RFC2324 that a valid HTCPCP URL should start with the coffee: scheme, not with https:.

The scheme in section 3 is a simplified version of the General Syntax of URI's from RFC2068 and seems to restrict HTCPCP to only accept the net-path form of URIs; "//" followed by a network location. Local paths (like file:/ paths in HTTP) are not allowed. Therefore, to make a request to an HTCPCP server at theserver.com, the URL must start with coffee://theserver.com (or an internationalised equivalent such as koffie://theserver.com for Afrikaans/Dutch).

That being said, curl does not allow you to specify an arbitrary scheme so I moved it into its own HTTP 'Scheme' Header which comes out as HTTP_SCHEME in Rack. On my server, I manually change the url_scheme to the HTTP_SCHEME value if HTTP_SCHEME is set.

The next part of the URI is optional unless there is more than one pot. For a server with multiple pots you need to append /pot-# where # is the pot number. Assuming zero-indexed pots, the second pot would be accessible at coffee://theserver.com/pot-1.

The final (also optional) part of the URI is the additions. The RFC does not provide examples (and section 3 may contradict section 2.2.2.1) but the the way I read section 3 is that addition requests should be passed as parameter/value pairs appended to the URL. If so, your requested milk type of cream and syrup type of vanilla would mean the correct additions-list string on the end of the URL is milk-type=Cream&syrup-type=Vanilla.

Combining all that together the final URL (assuming the HTCPCP server doesn't have multiple pots available) is theserver.com?milk-type=Cream&syrup-type=Vanilla

If multiple pots are available, the second pot at that address (again assuming pots are zero-indexed) would be theserver.com/pot-1?milk-type=Cream&syrup-type=Vanilla

The downside to this approach, and perhaps the reason that section 2.2.2.1 exists, is that it doesn't seem to allow for substitute additions. If my first choice of milk is Cream but I'd also be happy with Whole-milk, how do I specify (and prioritise) that using parameter/value pairs?

A logical answer to me is that we request our desired additions in the URL but we include acceptable additions (including any allowed substitutions) using the Accept-Additions header. Then again, a properly crafted Accept-Additions header renders the additions in the URL completely redundant. In my implementation I think I will return a 400 Bad Request error if the additions-list is present in the URL but clashes with the accepted additions in the header.

Once again, the instructions in the RFC on how to set this header seems to have typographical errors and it lacks examples but given it seems to be closely modelled on 2068 (and explicitly mentions the Accept request-header field), I think the intention is to copy the accept-params prioritisation system from 2068.

Let's say I like white coffee, the more fat the better, but I'd rather have black coffee than ingest soy, rice, or almond "milk". In that case my milk-type preferences might be:

  1. Cream
  2. Whole-milk
  3. Half-and-half, Part-skim or Skim
  4. No milk

To indicate this using 2068 quality values syntax, the header could be:

Accept-Additions: milk-type/Cream,
                  milk-type/Skim; q=0.5,
                  milk-type/none; q=0.25,
                  milk-type/Whole-milk; q=0.75,
                  milk-type/Half-and-half; q=0.5,
                  milk-type/Part-skim; q=0.5

The order is irrelevant as the preference comes from the qvalues. Note well I've borrowed the all lowercase none value/syntax from the Accept-Ranges header of the HTTP spec.

In your case, if you only want a vanilla coffee with cream, I suspect the header should be:

Accept-Additions: milk-type/Cream, syrup-type/Vanilla

Regarding the contradictory statements about media type, section 4 specifically deals with Media Type whereas section 2.1.1 only mentions it as an aside. Furthermore, the language in section 4 is stronger than section 2.1.1 so the appropriate media type is therefore message/coffeepot.

It should also be noted that the BREW method is preferred to POST and the body needs to contain coffee-message-body = "start" | "stop" so the final curl command that combines all those pieces and starts brewing a vanilla coffee with cream (accepting no substitutes) would be:

curl 'theserver.com?milk-type=Cream&syrup-type=Vanilla` \
     -X 'BREW' \
     -H 'Scheme: coffee' \
     -H 'Content-Type: message/coffeepot' \
     -H 'Accept-Additions: milk-type/Cream, syrup-type/Vanilla' \
     -d 'coffee-message-body=start'

If you agree that the additions-list in the URL is redundant, the command (this time using -- parameters for curl) would be:

curl 'theserver.com` \
     --request 'BREW' \
     --header  'Scheme: coffee' \
     --header  'Content-Type: message/coffeepot' \
     --header  'Accept-Additions: milk-type/Cream, syrup-type/Vanilla' \
     --data    'coffee-message-body=start'

Hopefully you'll get back a 2xx return code and the coffee will start brewing. If Cream or Vanilla aren't available you should get back a 406 Not Acceptable return code and the body of the response should contain the currently available additions.

Matthew
  • 1,300
  • 12
  • 30