1ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// Copyright 2013 The Chromium Authors. All rights reserved.
2ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
3ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// found in the LICENSE file.
4ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
5ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#ifndef CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_
6ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#define CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_
7ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
8ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include <string>
9ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include <vector>
10ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
11ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/basictypes.h"
12ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/values.h"
133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include "cloud_print/gcp20/prototype/local_print_job.h"
14ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "net/server/http_server.h"
15ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "net/server/http_server_request_info.h"
16ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
17ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochclass GURL;
18ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
19ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// HTTP server for receiving .
20ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochclass PrivetHttpServer: public net::HttpServer::Delegate {
21ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch public:
22ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // TODO(maksymb): Move this enum to some namespace instead of this class.
23ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  enum RegistrationErrorStatus {
24ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    REG_ERROR_OK,
25ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
26a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    REG_ERROR_INVALID_PARAMS,
27ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    REG_ERROR_DEVICE_BUSY,
28ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    REG_ERROR_PENDING_USER_ACTION,
29ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    REG_ERROR_USER_CANCEL,
30ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    REG_ERROR_CONFIRMATION_TIMEOUT,
31ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    REG_ERROR_INVALID_ACTION,
323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    REG_ERROR_OFFLINE,
33ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    REG_ERROR_SERVER_ERROR
34ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  };
35ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
36ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // TODO(maksymb): Move this struct to some namespace instead of this class.
37ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  struct DeviceInfo {
38ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    DeviceInfo();
39ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    ~DeviceInfo();
40ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
41ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    std::string version;
42a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string name;
43a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string description;
44a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string url;
45a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string id;
46a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string device_state;
47a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string connection_state;
48ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    std::string manufacturer;
49a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string model;
50a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string serial_number;
51a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string firmware;
52a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    int uptime;
53a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::string x_privet_token;
54a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
55ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    std::vector<std::string> api;
56a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    std::vector<std::string> type;
57ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  };
58ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
59ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  class Delegate {
60ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch   public:
61ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    virtual ~Delegate() {}
62ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
63ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // Invoked when registration is starting.
64ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    virtual RegistrationErrorStatus RegistrationStart(
65ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        const std::string& user) = 0;
66ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
67ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // Invoked when claimtoken is needed.
68ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    virtual RegistrationErrorStatus RegistrationGetClaimToken(
69ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        const std::string& user,
70ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        std::string* token,
71ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        std::string* claim_url) = 0;
72ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
73ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // Invoked when registration is going to be completed.
74ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    virtual RegistrationErrorStatus RegistrationComplete(
75ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        const std::string& user,
76ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        std::string* device_id) = 0;
77ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
78ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // Invoked when client asked for cancelling the registration.
79ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    virtual RegistrationErrorStatus RegistrationCancel(
80ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        const std::string& user) = 0;
81ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
82ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // Invoked for receiving server error details.
83ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    virtual void GetRegistrationServerError(std::string* description) = 0;
84ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
85a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    // Invoked when /privet/info is called.
86ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    virtual void CreateInfo(DeviceInfo* info) = 0;
87a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Invoked for checking wether /privet/register should be exposed.
89a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    virtual bool IsRegistered() const = 0;
90a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
913551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Invoked for checking wether /privet/printer/* should be exposed.
923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    virtual bool IsLocalPrintingAllowed() const = 0;
933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
94a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    // Invoked when XPrivetToken has to be checked.
95a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    virtual bool CheckXPrivetTokenHeader(const std::string& token) const = 0;
963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Invoked for getting capabilities.
98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    virtual const base::DictionaryValue& GetCapabilities() = 0;
993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1003551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Invoked for creating a job.
1013551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    virtual LocalPrintJob::CreateResult CreateJob(
1023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        const std::string& ticket,
1033551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        std::string* job_id,
1043551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        int* expires_in,
1053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        // TODO(maksymb): Use base::TimeDelta for timeouts
1063551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        int* error_timeout,
1073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        std::string* error_description) = 0;
1083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Invoked for simple local printing.
1103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    virtual LocalPrintJob::SaveResult SubmitDoc(
1113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        const LocalPrintJob& job,
1123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        std::string* job_id,
1133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        int* expires_in,
1143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        std::string* error_description,
1153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        int* timeout) = 0;
1163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Invoked for advanced local printing.
1183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    virtual LocalPrintJob::SaveResult SubmitDocWithId(
1193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        const LocalPrintJob& job,
1203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        const std::string& job_id,
1213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        int* expires_in,
1223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        std::string* error_description,
1233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        int* timeout) = 0;
1243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Invoked for getting job status.
1263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    virtual bool GetJobState(const std::string& job_id,
1273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                             LocalPrintJob::Info* info) = 0;
128ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  };
129ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
130ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Constructor doesn't start server.
131ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  explicit PrivetHttpServer(Delegate* delegate);
132ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
133ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Destroys the object.
134ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  virtual ~PrivetHttpServer();
135ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
136ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Starts HTTP server: start listening port |port| for HTTP requests.
137ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  bool Start(uint16 port);
138ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
139ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Stops HTTP server.
140ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  void Shutdown();
141ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
142ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch private:
143ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // net::HttpServer::Delegate methods:
1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  virtual void OnConnect(int connection_id) OVERRIDE {}
145ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  virtual void OnHttpRequest(
146ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      int connection_id,
147ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      const net::HttpServerRequestInfo& info) OVERRIDE;
148ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  virtual void OnWebSocketRequest(
149ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      int connection_id,
150ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      const net::HttpServerRequestInfo& info) OVERRIDE;
151ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  virtual void OnWebSocketMessage(int connection_id,
152ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                                  const std::string& data) OVERRIDE;
153ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  virtual void OnClose(int connection_id) OVERRIDE;
154ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
155a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // Sends error as response. Invoked when request method is invalid.
156a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  void ReportInvalidMethod(int connection_id);
157a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
158a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // Returns |true| if |request| should be done with correct |method|.
159a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // Otherwise sends |Invalid method| error.
160a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // Also checks support of |request| by this server.
161558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  bool ValidateRequestMethod(int connection_id,
162558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch                             const std::string& request,
163a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch                             const std::string& method);
164a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
165ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Processes http request after all preparations (XPrivetHeader check,
166ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // data handling etc.)
1673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  net::HttpStatusCode ProcessHttpRequest(
1683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      const GURL& url,
1693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      const net::HttpServerRequestInfo& info,
1703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      std::string* response);
171ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
172ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Pivet API methods. Return reference to NULL if output should be empty.
173ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  scoped_ptr<base::DictionaryValue> ProcessInfo(
174ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      net::HttpStatusCode* status_code) const;
1753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> ProcessCapabilities(
1773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      net::HttpStatusCode* status_code) const;
1783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> ProcessCreateJob(
1803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      const GURL& url,
1813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      const std::string& body,
1823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      net::HttpStatusCode* status_code) const;
1833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> ProcessSubmitDoc(
1853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      const GURL& url,
1863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      const net::HttpServerRequestInfo& info,
1873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      net::HttpStatusCode* status_code) const;
1883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> ProcessJobState(
1903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      const GURL& url,
1913551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      net::HttpStatusCode* status_code) const;
1923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
193ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  scoped_ptr<base::DictionaryValue> ProcessRegister(
194ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      const GURL& url,
1953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      net::HttpStatusCode* status_code) const;
196ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
1973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // Processes current status and depending on it replaces (or not)
198ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // |current_response| with error or empty response.
199ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  void ProcessRegistrationStatus(
200ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      RegistrationErrorStatus status,
201ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      scoped_ptr<base::DictionaryValue>* current_response) const;
202ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
203ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Port for listening.
204ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
205ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  uint16 port_;
206ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
207ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Contains encapsulated object for listening for requests.
2081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  scoped_ptr<net::HttpServer> server_;
209ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
210ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  Delegate* delegate_;
211ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
212ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  DISALLOW_COPY_AND_ASSIGN(PrivetHttpServer);
213ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch};
214ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
215ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#endif  // CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_
216