Commit b69ac914 authored by noamsml@chromium.org's avatar noamsml@chromium.org

Class to list only printers that support local printing

An adapter for PrivetDeviceLister that checks /privet/info and outputs only
devices that support /privet/printer/submitdoc.

BUG=311390

Review URL: https://codereview.chromium.org/48113019

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233340 0039d316-1c4b-4281-b951-d872f2087c98
parent 6da4ffab
...@@ -17,6 +17,12 @@ const char kPrivetKeyTimeout[] = "timeout"; ...@@ -17,6 +17,12 @@ const char kPrivetKeyTimeout[] = "timeout";
const char kPrivetActionNameInfo[] = "info"; const char kPrivetActionNameInfo[] = "info";
const char kPrivetInfoPath[] = "/privet/info";
const char kPrivetRegisterPath[] = "/privet/register";
const char kPrivetCapabilitiesPath[] = "/privet/capabilities";
const char kPrivetSubmitdocPath[] = "/privet/printer/submitdoc";
const char kPrivetCreatejobPath[] = "/privet/printer/createjob";
const char kPrivetErrorDeviceBusy[] = "device_busy"; const char kPrivetErrorDeviceBusy[] = "device_busy";
const char kPrivetErrorPendingUserAction[] = "pending_user_action"; const char kPrivetErrorPendingUserAction[] = "pending_user_action";
const char kPrivetErrorInvalidXPrivetToken[] = "invalid_x_privet_token"; const char kPrivetErrorInvalidXPrivetToken[] = "invalid_x_privet_token";
...@@ -28,8 +34,9 @@ const char kPrivetActionGetClaimToken[] = "getClaimToken"; ...@@ -28,8 +34,9 @@ const char kPrivetActionGetClaimToken[] = "getClaimToken";
const char kPrivetActionComplete[] = "complete"; const char kPrivetActionComplete[] = "complete";
const char kPrivetActionCancel[] = "cancel"; const char kPrivetActionCancel[] = "cancel";
extern const char kPrivetDefaultDeviceType[] = "_privet._tcp.local"; const char kPrivetDefaultDeviceType[] = "_privet._tcp.local";
extern const char kPrivetSubtypeTemplate[] = "%s._sub._privet._tcp.local"; const char kPrivetSubtypeTemplate[] = "%s._sub._privet._tcp.local";
const char kPrivetSubtypePrinter[] = "_printer";
const char kPrivetTxtKeyName[] = "ty"; const char kPrivetTxtKeyName[] = "ty";
const char kPrivetTxtKeyDescription[] = "note"; const char kPrivetTxtKeyDescription[] = "note";
......
...@@ -18,6 +18,12 @@ extern const char kPrivetKeyTimeout[]; ...@@ -18,6 +18,12 @@ extern const char kPrivetKeyTimeout[];
extern const char kPrivetActionNameInfo[]; extern const char kPrivetActionNameInfo[];
extern const char kPrivetInfoPath[];
extern const char kPrivetRegisterPath[];
extern const char kPrivetCapabilitiesPath[];
extern const char kPrivetSubmitdocPath[];
extern const char kPrivetCreatejobPath[];
extern const char kPrivetErrorDeviceBusy[]; extern const char kPrivetErrorDeviceBusy[];
extern const char kPrivetErrorPendingUserAction[]; extern const char kPrivetErrorPendingUserAction[];
extern const char kPrivetErrorInvalidXPrivetToken[]; extern const char kPrivetErrorInvalidXPrivetToken[];
...@@ -31,6 +37,7 @@ extern const char kPrivetActionCancel[]; ...@@ -31,6 +37,7 @@ extern const char kPrivetActionCancel[];
extern const char kPrivetDefaultDeviceType[]; extern const char kPrivetDefaultDeviceType[];
extern const char kPrivetSubtypeTemplate[]; extern const char kPrivetSubtypeTemplate[];
extern const char kPrivetSubtypePrinter[];
const double kPrivetMaximumTimeScaling = 1.2; const double kPrivetMaximumTimeScaling = 1.2;
......
...@@ -18,12 +18,6 @@ const char kUrlPlaceHolder[] = "http://host/"; ...@@ -18,12 +18,6 @@ const char kUrlPlaceHolder[] = "http://host/";
const char kPrivetRegisterActionArgName[] = "action"; const char kPrivetRegisterActionArgName[] = "action";
const char kPrivetRegisterUserArgName[] = "user"; const char kPrivetRegisterUserArgName[] = "user";
const char kPrivetInfoPath[] = "/privet/info";
const char kPrivetRegisterPath[] = "/privet/register";
const char kPrivetCapabilitiesPath[] = "/privet/capabilities";
const char kPrivetSubmitdocPath[] = "/privet/printer/submitdoc";
const char kPrivetCreatejobPath[] = "/privet/printer/createjob";
const char kPrivetURLKeyUser[] = "user"; const char kPrivetURLKeyUser[] = "user";
const char kPrivetURLKeyJobname[] = "jobname"; const char kPrivetURLKeyJobname[] = "jobname";
const char kPrivetURLKeyOffline[] = "offline"; const char kPrivetURLKeyOffline[] = "offline";
...@@ -241,7 +235,7 @@ void PrivetRegisterOperationImpl::OnPrivetInfoDone( ...@@ -241,7 +235,7 @@ void PrivetRegisterOperationImpl::OnPrivetInfoDone(
PrivetInfoOperation* operation, PrivetInfoOperation* operation,
int http_code, int http_code,
const base::DictionaryValue* value) { const base::DictionaryValue* value) {
// TODO (noamsml): Simplify error case. // TODO(noamsml): Simplify error case.
if (!value) { if (!value) {
delegate_->OnPrivetRegisterError(this, delegate_->OnPrivetRegisterError(this,
kPrivetActionNameInfo, kPrivetActionNameInfo,
......
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/local_discovery/privet_local_printer_lister.h"
#include <string>
#include "chrome/browser/local_discovery/privet_constants.h"
#include "chrome/browser/local_discovery/privet_device_lister_impl.h"
#include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h"
namespace local_discovery {
struct PrivetLocalPrinterLister::DeviceContext {
public:
DeviceContext() {
}
~DeviceContext() {
}
scoped_ptr<PrivetHTTPResolution> privet_resolution;
scoped_ptr<PrivetHTTPClient> privet_client;
scoped_ptr<PrivetInfoOperation> info_operation;
DeviceDescription description;
bool has_local_printing;
};
PrivetLocalPrinterLister::PrivetLocalPrinterLister(
ServiceDiscoveryClient* service_discovery_client,
net::URLRequestContextGetter* request_context,
Delegate* delegate) : delegate_(delegate) {
privet_lister_.reset(new PrivetDeviceListerImpl(service_discovery_client,
this,
kPrivetSubtypePrinter));
privet_http_factory_ = PrivetHTTPAsynchronousFactory::CreateInstance(
service_discovery_client,
request_context);
}
PrivetLocalPrinterLister::~PrivetLocalPrinterLister() {
}
void PrivetLocalPrinterLister::Start() {
privet_lister_->Start();
privet_lister_->DiscoverNewDevices(false);
}
void PrivetLocalPrinterLister::DeviceChanged(
bool added,
const std::string& name,
const DeviceDescription& description) {
DeviceContextMap::iterator i = device_contexts_.find(name);
if (i != device_contexts_.end()) {
if (i->second->has_local_printing) {
// This line helps with the edge case of a device description changing
// during the /privet/info call.
i->second->description = description;
delegate_->LocalPrinterChanged(added, name, description);
}
} else {
linked_ptr<DeviceContext> context(new DeviceContext);
context->has_local_printing = false;
context->description = description;
context->privet_resolution = privet_http_factory_->CreatePrivetHTTP(
name,
description.address,
base::Bind(&PrivetLocalPrinterLister::OnPrivetResolved,
base::Unretained(this)));
device_contexts_[name] = context;
context->privet_resolution->Start();
}
}
void PrivetLocalPrinterLister::DeviceCacheFlushed() {
device_contexts_.clear();
delegate_->LocalPrinterCacheFlushed();
}
void PrivetLocalPrinterLister::OnPrivetResolved(
scoped_ptr<PrivetHTTPClient> http_client) {
DeviceContextMap::iterator i = device_contexts_.find(http_client->GetName());
DCHECK(i != device_contexts_.end());
i->second->info_operation = http_client->CreateInfoOperation(this);
i->second->privet_client = http_client.Pass();
i->second->info_operation->Start();
}
void PrivetLocalPrinterLister::OnPrivetInfoDone(
PrivetInfoOperation* operation,
int http_code,
const base::DictionaryValue* json_value) {
bool has_local_printing = false;
const base::ListValue* api_list = NULL;
if (json_value && json_value->GetList(kPrivetInfoKeyAPIList, &api_list)) {
for (size_t i = 0; i < api_list->GetSize(); i++) {
std::string api;
api_list->GetString(i, &api);
if (api == kPrivetSubmitdocPath) {
has_local_printing = true;
}
}
}
std::string name = operation->GetHTTPClient()->GetName();
DeviceContextMap::iterator i = device_contexts_.find(name);
DCHECK(i != device_contexts_.end());
i->second->has_local_printing = has_local_printing;
if (has_local_printing) {
delegate_->LocalPrinterChanged(true, name, i->second->description);
}
}
void PrivetLocalPrinterLister::DeviceRemoved(const std::string& device_name) {
DeviceContextMap::iterator i = device_contexts_.find(device_name);
if (i != device_contexts_.end()) {
bool has_local_printing = i->second->has_local_printing;
device_contexts_.erase(i);
if (has_local_printing) {
delegate_->LocalPrinterRemoved(device_name);
}
}
}
} // namespace local_discovery
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_LOCAL_DISCOVERY_PRIVET_LOCAL_PRINTER_LISTER_H_
#define CHROME_BROWSER_LOCAL_DISCOVERY_PRIVET_LOCAL_PRINTER_LISTER_H_
#include <map>
#include <string>
#include "base/memory/linked_ptr.h"
#include "chrome/browser/local_discovery/privet_device_lister.h"
#include "chrome/browser/local_discovery/privet_http.h"
#include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h"
#include "chrome/common/local_discovery/service_discovery_client.h"
#include "net/url_request/url_request_context.h"
namespace local_discovery {
// This is an adapter to PrivetDeviceLister that finds printers and checks if
// they support Privet local printing.
class PrivetLocalPrinterLister : PrivetDeviceLister::Delegate,
PrivetInfoOperation::Delegate {
public:
class Delegate {
public:
virtual ~Delegate() {}
virtual void LocalPrinterChanged(bool added,
const std::string& name,
const DeviceDescription& description) = 0;
virtual void LocalPrinterRemoved(const std::string& name) = 0;
virtual void LocalPrinterCacheFlushed() = 0;
};
PrivetLocalPrinterLister(ServiceDiscoveryClient* service_discovery_client,
net::URLRequestContextGetter* request_context,
Delegate* delegate);
virtual ~PrivetLocalPrinterLister();
void Start();
// PrivetDeviceLister::Delegate implementation.
virtual void DeviceChanged(bool added,
const std::string& name,
const DeviceDescription& description) OVERRIDE;
virtual void DeviceRemoved(const std::string& name) OVERRIDE;
virtual void DeviceCacheFlushed() OVERRIDE;
// PrivetInfoOperation::Delegate implementation.
virtual void OnPrivetInfoDone(
PrivetInfoOperation* operation,
int http_code,
const base::DictionaryValue* json_value) OVERRIDE;
private:
struct DeviceContext;
void OnPrivetResolved(scoped_ptr<PrivetHTTPClient> http_client);
typedef std::map<std::string, linked_ptr<DeviceContext> > DeviceContextMap;
scoped_ptr<PrivetHTTPAsynchronousFactory> privet_http_factory_;
DeviceContextMap device_contexts_;
Delegate* delegate_;
scoped_ptr<PrivetDeviceLister> privet_lister_;
};
} // namespace local_discovery
#endif // CHROME_BROWSER_LOCAL_DISCOVERY_PRIVET_LOCAL_PRINTER_LISTER_H_
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_loop/message_loop.h"
#include "chrome/browser/local_discovery/privet_local_printer_lister.h"
#include "chrome/browser/local_discovery/test_service_discovery_client.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::StrictMock;
using testing::_;
namespace local_discovery {
namespace {
const uint8 kAnnouncePacket[] = {
// Header
0x00, 0x00, // ID is zeroed out
0x80, 0x00, // Standard query response, no error
0x00, 0x00, // No questions (for simplicity)
0x00, 0x05, // 5 RR (answers)
0x00, 0x00, // 0 authority RRs
0x00, 0x00, // 0 additional RRs
0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r',
0x04, '_', 's', 'u', 'b',
0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
0x04, '_', 't', 'c', 'p',
0x05, 'l', 'o', 'c', 'a', 'l',
0x00,
0x00, 0x0c, // TYPE is PTR.
0x00, 0x01, // CLASS is IN.
0x00, 0x00, // TTL (4 bytes) is 32768 second.
0x10, 0x00,
0x00, 0x0c, // RDLENGTH is 12 bytes.
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
0xc0, 0x0c,
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
0xc0, 0x0c,
0x00, 0x10, // TYPE is TXT.
0x00, 0x01, // CLASS is IN.
0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
0x01, 0x00,
0x00, 0x37, // RDLENGTH is 55 bytes.
0x06, 'i', 'd', '=', 'r', 'e', 'g',
0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
'd', 'e', 'v', 'i', 'c', 'e',
0x1e, 'n', 'o', 't', 'e', '=',
'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
0xc0, 0x0c,
0x00, 0x21, // Type is SRV
0x00, 0x01, // CLASS is IN
0x00, 0x00, // TTL (4 bytes) is 32768 second.
0x10, 0x00,
0x00, 0x17, // RDLENGTH is 23
0x00, 0x00,
0x00, 0x00,
0x22, 0xb8, // port 8888
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
0x05, 'l', 'o', 'c', 'a', 'l',
0x00,
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
0x05, 'l', 'o', 'c', 'a', 'l',
0x00,
0x00, 0x01, // Type is A
0x00, 0x01, // CLASS is IN
0x00, 0x00, // TTL (4 bytes) is 32768 second.
0x10, 0x00,
0x00, 0x04, // RDLENGTH is 4
0x01, 0x02, 0x03, 0x04, // 1.2.3.4
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
0x05, 'l', 'o', 'c', 'a', 'l',
0x00,
0x00, 0x1C, // Type is AAAA
0x00, 0x01, // CLASS is IN
0x00, 0x00, // TTL (4 bytes) is 32768 second.
0x10, 0x00,
0x00, 0x10, // RDLENGTH is 16
0x01, 0x02, 0x03, 0x04, // 1.2.3.4
0x01, 0x02, 0x03, 0x04,
0x01, 0x02, 0x03, 0x04,
0x01, 0x02, 0x03, 0x04,
};
const char kInfoIsLocalPrinter[] = "{"
"\"api\" : [ \"/privet/printer/submitdoc\" ],"
"\"x-privet-token\" : \"sample\""
"}";
const char kInfoIsNotLocalPrinter[] = "{"
"\"api\" : [ \"/privet/register\" ],"
"\"x-privet-token\" : \"sample\""
"}";
class MockLocalPrinterListerDelegate
: public PrivetLocalPrinterLister::Delegate {
public:
MockLocalPrinterListerDelegate() {
}
virtual ~MockLocalPrinterListerDelegate() {
}
MOCK_METHOD3(LocalPrinterChanged, void(bool added,
const std::string& name,
const DeviceDescription& description));
MOCK_METHOD1(LocalPrinterRemoved, void(const std::string& name));
MOCK_METHOD0(LocalPrinterCacheFlushed, void());
};
class PrivetLocalPrinterListerTest : public testing::Test {
public:
PrivetLocalPrinterListerTest() {
test_service_discovery_client_ = new TestServiceDiscoveryClient();
test_service_discovery_client_->Start();
url_request_context_ = new net::TestURLRequestContextGetter(
base::MessageLoopProxy::current());
local_printer_lister_.reset(new PrivetLocalPrinterLister(
test_service_discovery_client_.get(),
url_request_context_.get(),
&delegate_));
}
~PrivetLocalPrinterListerTest() {
}
bool SuccessfulResponseToURL(const GURL& url,
const std::string& response) {
net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
EXPECT_TRUE(fetcher);
EXPECT_EQ(url, fetcher->GetOriginalURL());
if (!fetcher || url != fetcher->GetOriginalURL())
return false;
fetcher->SetResponseString(response);
fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS,
net::OK));
fetcher->set_response_code(200);
fetcher->delegate()->OnURLFetchComplete(fetcher);
return true;
}
void SimulateReceive(const uint8* packet, size_t size) {
test_service_discovery_client_->SimulateReceive(packet, size);
message_loop_.RunUntilIdle();
}
void ExpectAnyPacket() {
EXPECT_CALL(*test_service_discovery_client_, OnSendTo(_))
.Times(2);
}
protected:
base::MessageLoop message_loop_;
scoped_refptr<TestServiceDiscoveryClient> test_service_discovery_client_;
scoped_refptr<net::TestURLRequestContextGetter> url_request_context_;
scoped_ptr<PrivetLocalPrinterLister> local_printer_lister_;
net::TestURLFetcherFactory fetcher_factory_;
StrictMock<MockLocalPrinterListerDelegate> delegate_;
};
TEST_F(PrivetLocalPrinterListerTest, PrinterAddedTest) {
ExpectAnyPacket();
local_printer_lister_->Start();
SimulateReceive(kAnnouncePacket, sizeof(kAnnouncePacket));
EXPECT_CALL(delegate_, LocalPrinterChanged(
true,
"myService._printer._sub._privet._tcp.local",
_));
EXPECT_TRUE(SuccessfulResponseToURL(
GURL("http://1.2.3.4:8888/privet/info"),
std::string(kInfoIsLocalPrinter)));
};
TEST_F(PrivetLocalPrinterListerTest, NonPrinterAddedTest) {
ExpectAnyPacket();
local_printer_lister_->Start();
SimulateReceive(kAnnouncePacket, sizeof(kAnnouncePacket));
EXPECT_TRUE(SuccessfulResponseToURL(
GURL("http://1.2.3.4:8888/privet/info"),
std::string(kInfoIsNotLocalPrinter)));
};
} // namespace
} // namespace local_discovery
...@@ -963,6 +963,8 @@ ...@@ -963,6 +963,8 @@
'browser/local_discovery/privet_http.h', 'browser/local_discovery/privet_http.h',
'browser/local_discovery/privet_http_impl.cc', 'browser/local_discovery/privet_http_impl.cc',
'browser/local_discovery/privet_http_impl.h', 'browser/local_discovery/privet_http_impl.h',
'browser/local_discovery/privet_local_printer_lister.h',
'browser/local_discovery/privet_local_printer_lister.cc',
'browser/local_discovery/privet_url_fetcher.cc', 'browser/local_discovery/privet_url_fetcher.cc',
'browser/local_discovery/privet_url_fetcher.h', 'browser/local_discovery/privet_url_fetcher.h',
'browser/local_discovery/service_discovery_client_mac.h', 'browser/local_discovery/service_discovery_client_mac.h',
...@@ -3534,6 +3536,8 @@ ...@@ -3534,6 +3536,8 @@
'browser/local_discovery/service_discovery_client_mdns.h', 'browser/local_discovery/service_discovery_client_mdns.h',
'browser/local_discovery/service_discovery_host_client.cc', 'browser/local_discovery/service_discovery_host_client.cc',
'browser/local_discovery/service_discovery_host_client.h', 'browser/local_discovery/service_discovery_host_client.h',
'browser/local_discovery/privet_local_printer_lister.h',
'browser/local_discovery/privet_local_printer_lister.cc'
] ]
}] }]
], ],
......
...@@ -2015,8 +2015,6 @@ ...@@ -2015,8 +2015,6 @@
}], }],
['enable_mdns==1', { ['enable_mdns==1', {
'sources' : [ 'sources' : [
'browser/local_discovery/test_service_discovery_client.cc',
'browser/local_discovery/test_service_discovery_client.h',
'browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc', 'browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc',
] ]
}], }],
......
...@@ -411,6 +411,12 @@ ...@@ -411,6 +411,12 @@
'../components/components.gyp:breakpad_stubs', '../components/components.gyp:breakpad_stubs',
], ],
}], }],
['enable_mdns == 1', {
'sources': [
'browser/local_discovery/test_service_discovery_client.cc',
'browser/local_discovery/test_service_discovery_client.h',
]
}],
], ],
}, },
{ {
...@@ -2216,7 +2222,8 @@ ...@@ -2216,7 +2222,8 @@
'utility/local_discovery/local_domain_resolver_unittest.cc', 'utility/local_discovery/local_domain_resolver_unittest.cc',
'utility/local_discovery/service_discovery_client_unittest.cc', 'utility/local_discovery/service_discovery_client_unittest.cc',
'browser/local_discovery/privet_device_lister_unittest.cc', 'browser/local_discovery/privet_device_lister_unittest.cc',
'browser/local_discovery/privet_notifications_unittest.cc' 'browser/local_discovery/privet_notifications_unittest.cc',
'browser/local_discovery/privet_local_printer_lister_unittest.cc',
] ]
}], }],
['configuration_policy==0', { ['configuration_policy==0', {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment