{"action":"created","comment":{"url":"https://api.github.com/repos/vespa-engine/vespa/pulls/comments/220212376","pull_request_review_id":158567362,"id":220212376,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyMDIxMjM3Ng==","diff_hunk":"@@ -0,0 +1,128 @@\n+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.\n+#include \"protocol_snooping.h\"\n+#include <iostream>\n+#include <cstdlib>\n+#include <stdint.h>\n+\n+namespace vespalib::net::tls {\n+\n+namespace {\n+\n+// Precondition for all helper functions: buffer is at least `min_header_bytes_to_observe()` bytes long\n+\n+// From RFC 5246:\n+// 0x16 - Handshake content type byte of TLSCiphertext record\n+// 0x03 - First byte of 2-byte ProtocolVersion, always 3 on TLSv1.2 and v1.3\n+inline bool is_tls_handshake_packet(const char* buf) {\n+    return ((buf[0] == 0x16) && (buf[1] == 0x03));\n+}\n+\n+// Next is the TLS minor version, either 1 or 3 depending on version (though the\n+// RFCs say it _should_ be 1 for backwards compatibility reasons).\n+// Yes, the TLS spec says that you should technically ignore the protocol version\n+// field here, but we want all the signals we can get.\n+inline bool is_expected_tls_protocol_version(const char* buf) {\n+    return ((buf[2] == 0x01) || (buf[2] == 0x03));\n+}\n+\n+// Length is big endian u16 in bytes 3, 4\n+inline uint16_t tls_record_length(const char* buf) {\n+    return (uint16_t(static_cast<unsigned char>(buf[3]) << 8)\n+            + static_cast<unsigned char>(buf[4]));\n+}\n+\n+// First byte of Handshake record in byte 5, which shall be ClientHello (0x01)\n+inline bool is_client_hello_handshake_record(const char* buf) {\n+    return (buf[5] == 0x01);\n+}\n+\n+// Last 2 bytes are the 2 first big-endian bytes of a 3-byte Handshake\n+// record length field. No support for records that are large enough that\n+// the MSB should ever be non-zero.\n+inline bool client_hello_record_size_within_expected_bounds(const char* buf) {\n+    return (buf[6] == 0x00);\n+}\n+\n+// The byte after the MSB of the 24-bit handshake record size should be equal\n+// to the most significant byte of the record length value, minus the Handshake\n+// record header size.\n+// Again, we make the assumption that ClientHello messages are not fragmented,\n+// so their max size must be <= 16KiB. This also just happens to be a lower\n+// number than the minimum FS4/FRT packet type byte at the same location.\n+// Oooh yeah, leaky abstractions to the rescue!\n+inline bool handshake_record_size_matches_length(const char* buf, uint16_t length) {\n+    return (static_cast<unsigned char>(buf[7]) == ((length - 4) >> 8));\n+}\n+\n+} // anon ns\n+\n+TlsSnoopingResult snoop_client_hello_header(const char* buf) noexcept {\n+    if (!is_tls_handshake_packet(buf)) {\n+        return TlsSnoopingResult::HandshakeMismatch;\n+    }\n+    if (!is_expected_tls_protocol_version(buf)) {\n+        return TlsSnoopingResult::ProtocolVersionMismatch;\n+    }\n+    // Length of TLS record follows. Must be <= 16KiB + 2048 (16KiB + 256 on v1.3).\n+    // We expect that the first record contains _only_ a ClientHello with no coalescing\n+    // and no fragmentation. This is technically a violation of the TLS spec, but this\n+    // particular detection logic is only intended to be used against other Vespa nodes\n+    // where we control frame sizes and where such fragmentation should not take place.\n+    // We also do not support TLSv1.3 0-RTT which may trigger early data.\n+    uint16_t length = tls_record_length(buf);\n+    if (length > (16384 + 2048)) {\n+        return TlsSnoopingResult::RecordSizeRfcViolation;\n+    }\n+    if (!is_client_hello_handshake_record(buf)) {\n+        return TlsSnoopingResult::RecordNotClientHello;\n+    }\n+    if (!client_hello_record_size_within_expected_bounds(buf)) {\n+        return TlsSnoopingResult::ClientHelloRecordTooBig;\n+    }\n+    if (!handshake_record_size_matches_length(buf, length)) {\n+        return TlsSnoopingResult::ExpectedRecordSizeMismatch;\n+    }\n+    // Hooray! It very probably most likely is a TLS connection! :D\n+    return TlsSnoopingResult::ProbablyTls;\n+}\n+\n+const char* to_string(TlsSnoopingResult result) noexcept {\n+    switch (result) {\n+    case TlsSnoopingResult::ProbablyTls:                return \"ProbablyTls\";\n+    case TlsSnoopingResult::HandshakeMismatch:          return \"HandshakeMismatch\";\n+    case TlsSnoopingResult::ProtocolVersionMismatch:    return \"ProtocolVersionMismatch\";\n+    case TlsSnoopingResult::RecordSizeRfcViolation:     return \"RecordSizeRfcViolation\";\n+    case TlsSnoopingResult::RecordNotClientHello:       return \"RecordNotClientHello\";\n+    case TlsSnoopingResult::ClientHelloRecordTooBig:    return \"ClientHelloRecordTooBig\";\n+    case TlsSnoopingResult::ExpectedRecordSizeMismatch: return \"ExpectedRecordSizeMismatch\";\n+    default: return \"Unknown TlsSnoopingResult\";","path":"vespalib/src/vespa/vespalib/net/tls/protocol_snooping.cpp","position":null,"original_position":98,"commit_id":"6306d72742feba81038761b9ce8aa6bd76fd088c","original_commit_id":"bcfae9e52bb65ca165fb659971a0c6e4c0a0a3cc","user":{"login":"vekterli","id":16582643,"node_id":"MDQ6VXNlcjE2NTgyNjQz","avatar_url":"https://avatars1.githubusercontent.com/u/16582643?v=4","gravatar_id":"","url":"https://api.github.com/users/vekterli","html_url":"https://github.com/vekterli","followers_url":"https://api.github.com/users/vekterli/followers","following_url":"https://api.github.com/users/vekterli/following{/other_user}","gists_url":"https://api.github.com/users/vekterli/gists{/gist_id}","starred_url":"https://api.github.com/users/vekterli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vekterli/subscriptions","organizations_url":"https://api.github.com/users/vekterli/orgs","repos_url":"https://api.github.com/users/vekterli/repos","events_url":"https://api.github.com/users/vekterli/events{/privacy}","received_events_url":"https://api.github.com/users/vekterli/received_events","type":"User","site_admin":false},"body":"Done, replaced with `abort()`","created_at":"2018-09-25T14:20:02Z","updated_at":"2018-09-25T14:20:03Z","html_url":"https://github.com/vespa-engine/vespa/pull/7081#discussion_r220212376","pull_request_url":"https://api.github.com/repos/vespa-engine/vespa/pulls/7081","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/vespa-engine/vespa/pulls/comments/220212376"},"html":{"href":"https://github.com/vespa-engine/vespa/pull/7081#discussion_r220212376"},"pull_request":{"href":"https://api.github.com/repos/vespa-engine/vespa/pulls/7081"}},"in_reply_to_id":220180989},"pull_request":{"url":"https://api.github.com/repos/vespa-engine/vespa/pulls/7081","id":217942212,"node_id":"MDExOlB1bGxSZXF1ZXN0MjE3OTQyMjEy","html_url":"https://github.com/vespa-engine/vespa/pull/7081","diff_url":"https://github.com/vespa-engine/vespa/pull/7081.diff","patch_url":"https://github.com/vespa-engine/vespa/pull/7081.patch","issue_url":"https://api.github.com/repos/vespa-engine/vespa/issues/7081","number":7081,"state":"open","locked":false,"title":"Add functionality for detecting whether a client is using TLS or not","user":{"login":"vekterli","id":16582643,"node_id":"MDQ6VXNlcjE2NTgyNjQz","avatar_url":"https://avatars1.githubusercontent.com/u/16582643?v=4","gravatar_id":"","url":"https://api.github.com/users/vekterli","html_url":"https://github.com/vekterli","followers_url":"https://api.github.com/users/vekterli/followers","following_url":"https://api.github.com/users/vekterli/following{/other_user}","gists_url":"https://api.github.com/users/vekterli/gists{/gist_id}","starred_url":"https://api.github.com/users/vekterli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vekterli/subscriptions","organizations_url":"https://api.github.com/users/vekterli/orgs","repos_url":"https://api.github.com/users/vekterli/repos","events_url":"https://api.github.com/users/vekterli/events{/privacy}","received_events_url":"https://api.github.com/users/vekterli/received_events","type":"User","site_admin":false},"body":"@havardpe please review\r\n@bjorncs FYI\r\n\r\nInspects first 8 bytes of a client's initial data stream to determine\r\nif it's (with very high confidence) a TLS ClientHello message.","created_at":"2018-09-25T11:16:11Z","updated_at":"2018-09-25T14:20:03Z","closed_at":null,"merged_at":null,"merge_commit_sha":"539e228f425b9e95a8ebb7439e9494e7009935ce","assignee":{"login":"vekterli","id":16582643,"node_id":"MDQ6VXNlcjE2NTgyNjQz","avatar_url":"https://avatars1.githubusercontent.com/u/16582643?v=4","gravatar_id":"","url":"https://api.github.com/users/vekterli","html_url":"https://github.com/vekterli","followers_url":"https://api.github.com/users/vekterli/followers","following_url":"https://api.github.com/users/vekterli/following{/other_user}","gists_url":"https://api.github.com/users/vekterli/gists{/gist_id}","starred_url":"https://api.github.com/users/vekterli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vekterli/subscriptions","organizations_url":"https://api.github.com/users/vekterli/orgs","repos_url":"https://api.github.com/users/vekterli/repos","events_url":"https://api.github.com/users/vekterli/events{/privacy}","received_events_url":"https://api.github.com/users/vekterli/received_events","type":"User","site_admin":false},"assignees":[{"login":"vekterli","id":16582643,"node_id":"MDQ6VXNlcjE2NTgyNjQz","avatar_url":"https://avatars1.githubusercontent.com/u/16582643?v=4","gravatar_id":"","url":"https://api.github.com/users/vekterli","html_url":"https://github.com/vekterli","followers_url":"https://api.github.com/users/vekterli/followers","following_url":"https://api.github.com/users/vekterli/following{/other_user}","gists_url":"https://api.github.com/users/vekterli/gists{/gist_id}","starred_url":"https://api.github.com/users/vekterli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vekterli/subscriptions","organizations_url":"https://api.github.com/users/vekterli/orgs","repos_url":"https://api.github.com/users/vekterli/repos","events_url":"https://api.github.com/users/vekterli/events{/privacy}","received_events_url":"https://api.github.com/users/vekterli/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/vespa-engine/vespa/pulls/7081/commits","review_comments_url":"https://api.github.com/repos/vespa-engine/vespa/pulls/7081/comments","review_comment_url":"https://api.github.com/repos/vespa-engine/vespa/pulls/comments{/number}","comments_url":"https://api.github.com/repos/vespa-engine/vespa/issues/7081/comments","statuses_url":"https://api.github.com/repos/vespa-engine/vespa/statuses/6306d72742feba81038761b9ce8aa6bd76fd088c","head":{"label":"vespa-engine:vekterli/add-tls-protocol-snooping-utilities","ref":"vekterli/add-tls-protocol-snooping-utilities","sha":"6306d72742feba81038761b9ce8aa6bd76fd088c","user":{"login":"vespa-engine","id":29299694,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI5Mjk5Njk0","avatar_url":"https://avatars1.githubusercontent.com/u/29299694?v=4","gravatar_id":"","url":"https://api.github.com/users/vespa-engine","html_url":"https://github.com/vespa-engine","followers_url":"https://api.github.com/users/vespa-engine/followers","following_url":"https://api.github.com/users/vespa-engine/following{/other_user}","gists_url":"https://api.github.com/users/vespa-engine/gists{/gist_id}","starred_url":"https://api.github.com/users/vespa-engine/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vespa-engine/subscriptions","organizations_url":"https://api.github.com/users/vespa-engine/orgs","repos_url":"https://api.github.com/users/vespa-engine/repos","events_url":"https://api.github.com/users/vespa-engine/events{/privacy}","received_events_url":"https://api.github.com/users/vespa-engine/received_events","type":"Organization","site_admin":false},"repo":{"id":60377070,"node_id":"MDEwOlJlcG9zaXRvcnk2MDM3NzA3MA==","name":"vespa","full_name":"vespa-engine/vespa","private":false,"owner":{"login":"vespa-engine","id":29299694,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI5Mjk5Njk0","avatar_url":"https://avatars1.githubusercontent.com/u/29299694?v=4","gravatar_id":"","url":"https://api.github.com/users/vespa-engine","html_url":"https://github.com/vespa-engine","followers_url":"https://api.github.com/users/vespa-engine/followers","following_url":"https://api.github.com/users/vespa-engine/following{/other_user}","gists_url":"https://api.github.com/users/vespa-engine/gists{/gist_id}","starred_url":"https://api.github.com/users/vespa-engine/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vespa-engine/subscriptions","organizations_url":"https://api.github.com/users/vespa-engine/orgs","repos_url":"https://api.github.com/users/vespa-engine/repos","events_url":"https://api.github.com/users/vespa-engine/events{/privacy}","received_events_url":"https://api.github.com/users/vespa-engine/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/vespa-engine/vespa","description":"Vespa - the open big data serving engine","fork":false,"url":"https://api.github.com/repos/vespa-engine/vespa","forks_url":"https://api.github.com/repos/vespa-engine/vespa/forks","keys_url":"https://api.github.com/repos/vespa-engine/vespa/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vespa-engine/vespa/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vespa-engine/vespa/teams","hooks_url":"https://api.github.com/repos/vespa-engine/vespa/hooks","issue_events_url":"https://api.github.com/repos/vespa-engine/vespa/issues/events{/number}","events_url":"https://api.github.com/repos/vespa-engine/vespa/events","assignees_url":"https://api.github.com/repos/vespa-engine/vespa/assignees{/user}","branches_url":"https://api.github.com/repos/vespa-engine/vespa/branches{/branch}","tags_url":"https://api.github.com/repos/vespa-engine/vespa/tags","blobs_url":"https://api.github.com/repos/vespa-engine/vespa/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vespa-engine/vespa/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vespa-engine/vespa/git/refs{/sha}","trees_url":"https://api.github.com/repos/vespa-engine/vespa/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vespa-engine/vespa/statuses/{sha}","languages_url":"https://api.github.com/repos/vespa-engine/vespa/languages","stargazers_url":"https://api.github.com/repos/vespa-engine/vespa/stargazers","contributors_url":"https://api.github.com/repos/vespa-engine/vespa/contributors","subscribers_url":"https://api.github.com/repos/vespa-engine/vespa/subscribers","subscription_url":"https://api.github.com/repos/vespa-engine/vespa/subscription","commits_url":"https://api.github.com/repos/vespa-engine/vespa/commits{/sha}","git_commits_url":"https://api.github.com/repos/vespa-engine/vespa/git/commits{/sha}","comments_url":"https://api.github.com/repos/vespa-engine/vespa/comments{/number}","issue_comment_url":"https://api.github.com/repos/vespa-engine/vespa/issues/comments{/number}","contents_url":"https://api.github.com/repos/vespa-engine/vespa/contents/{+path}","compare_url":"https://api.github.com/repos/vespa-engine/vespa/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vespa-engine/vespa/merges","archive_url":"https://api.github.com/repos/vespa-engine/vespa/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vespa-engine/vespa/downloads","issues_url":"https://api.github.com/repos/vespa-engine/vespa/issues{/number}","pulls_url":"https://api.github.com/repos/vespa-engine/vespa/pulls{/number}","milestones_url":"https://api.github.com/repos/vespa-engine/vespa/milestones{/number}","notifications_url":"https://api.github.com/repos/vespa-engine/vespa/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vespa-engine/vespa/labels{/name}","releases_url":"https://api.github.com/repos/vespa-engine/vespa/releases{/id}","deployments_url":"https://api.github.com/repos/vespa-engine/vespa/deployments","created_at":"2016-06-03T20:54:20Z","updated_at":"2018-09-25T13:38:23Z","pushed_at":"2018-09-25T14:17:42Z","git_url":"git://github.com/vespa-engine/vespa.git","ssh_url":"git@github.com:vespa-engine/vespa.git","clone_url":"https://github.com/vespa-engine/vespa.git","svn_url":"https://github.com/vespa-engine/vespa","homepage":"http://vespa.ai/","size":77413,"stargazers_count":2607,"watchers_count":2607,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":307,"mirror_url":null,"archived":false,"open_issues_count":51,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":307,"open_issues":51,"watchers":2607,"default_branch":"master"}},"base":{"label":"vespa-engine:master","ref":"master","sha":"5cddfbae790d23b5a98506a31bd43f197658486b","user":{"login":"vespa-engine","id":29299694,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI5Mjk5Njk0","avatar_url":"https://avatars1.githubusercontent.com/u/29299694?v=4","gravatar_id":"","url":"https://api.github.com/users/vespa-engine","html_url":"https://github.com/vespa-engine","followers_url":"https://api.github.com/users/vespa-engine/followers","following_url":"https://api.github.com/users/vespa-engine/following{/other_user}","gists_url":"https://api.github.com/users/vespa-engine/gists{/gist_id}","starred_url":"https://api.github.com/users/vespa-engine/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vespa-engine/subscriptions","organizations_url":"https://api.github.com/users/vespa-engine/orgs","repos_url":"https://api.github.com/users/vespa-engine/repos","events_url":"https://api.github.com/users/vespa-engine/events{/privacy}","received_events_url":"https://api.github.com/users/vespa-engine/received_events","type":"Organization","site_admin":false},"repo":{"id":60377070,"node_id":"MDEwOlJlcG9zaXRvcnk2MDM3NzA3MA==","name":"vespa","full_name":"vespa-engine/vespa","private":false,"owner":{"login":"vespa-engine","id":29299694,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI5Mjk5Njk0","avatar_url":"https://avatars1.githubusercontent.com/u/29299694?v=4","gravatar_id":"","url":"https://api.github.com/users/vespa-engine","html_url":"https://github.com/vespa-engine","followers_url":"https://api.github.com/users/vespa-engine/followers","following_url":"https://api.github.com/users/vespa-engine/following{/other_user}","gists_url":"https://api.github.com/users/vespa-engine/gists{/gist_id}","starred_url":"https://api.github.com/users/vespa-engine/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vespa-engine/subscriptions","organizations_url":"https://api.github.com/users/vespa-engine/orgs","repos_url":"https://api.github.com/users/vespa-engine/repos","events_url":"https://api.github.com/users/vespa-engine/events{/privacy}","received_events_url":"https://api.github.com/users/vespa-engine/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/vespa-engine/vespa","description":"Vespa - the open big data serving engine","fork":false,"url":"https://api.github.com/repos/vespa-engine/vespa","forks_url":"https://api.github.com/repos/vespa-engine/vespa/forks","keys_url":"https://api.github.com/repos/vespa-engine/vespa/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vespa-engine/vespa/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vespa-engine/vespa/teams","hooks_url":"https://api.github.com/repos/vespa-engine/vespa/hooks","issue_events_url":"https://api.github.com/repos/vespa-engine/vespa/issues/events{/number}","events_url":"https://api.github.com/repos/vespa-engine/vespa/events","assignees_url":"https://api.github.com/repos/vespa-engine/vespa/assignees{/user}","branches_url":"https://api.github.com/repos/vespa-engine/vespa/branches{/branch}","tags_url":"https://api.github.com/repos/vespa-engine/vespa/tags","blobs_url":"https://api.github.com/repos/vespa-engine/vespa/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vespa-engine/vespa/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vespa-engine/vespa/git/refs{/sha}","trees_url":"https://api.github.com/repos/vespa-engine/vespa/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vespa-engine/vespa/statuses/{sha}","languages_url":"https://api.github.com/repos/vespa-engine/vespa/languages","stargazers_url":"https://api.github.com/repos/vespa-engine/vespa/stargazers","contributors_url":"https://api.github.com/repos/vespa-engine/vespa/contributors","subscribers_url":"https://api.github.com/repos/vespa-engine/vespa/subscribers","subscription_url":"https://api.github.com/repos/vespa-engine/vespa/subscription","commits_url":"https://api.github.com/repos/vespa-engine/vespa/commits{/sha}","git_commits_url":"https://api.github.com/repos/vespa-engine/vespa/git/commits{/sha}","comments_url":"https://api.github.com/repos/vespa-engine/vespa/comments{/number}","issue_comment_url":"https://api.github.com/repos/vespa-engine/vespa/issues/comments{/number}","contents_url":"https://api.github.com/repos/vespa-engine/vespa/contents/{+path}","compare_url":"https://api.github.com/repos/vespa-engine/vespa/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vespa-engine/vespa/merges","archive_url":"https://api.github.com/repos/vespa-engine/vespa/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vespa-engine/vespa/downloads","issues_url":"https://api.github.com/repos/vespa-engine/vespa/issues{/number}","pulls_url":"https://api.github.com/repos/vespa-engine/vespa/pulls{/number}","milestones_url":"https://api.github.com/repos/vespa-engine/vespa/milestones{/number}","notifications_url":"https://api.github.com/repos/vespa-engine/vespa/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vespa-engine/vespa/labels{/name}","releases_url":"https://api.github.com/repos/vespa-engine/vespa/releases{/id}","deployments_url":"https://api.github.com/repos/vespa-engine/vespa/deployments","created_at":"2016-06-03T20:54:20Z","updated_at":"2018-09-25T13:38:23Z","pushed_at":"2018-09-25T14:17:42Z","git_url":"git://github.com/vespa-engine/vespa.git","ssh_url":"git@github.com:vespa-engine/vespa.git","clone_url":"https://github.com/vespa-engine/vespa.git","svn_url":"https://github.com/vespa-engine/vespa","homepage":"http://vespa.ai/","size":77413,"stargazers_count":2607,"watchers_count":2607,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":307,"mirror_url":null,"archived":false,"open_issues_count":51,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":307,"open_issues":51,"watchers":2607,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/vespa-engine/vespa/pulls/7081"},"html":{"href":"https://github.com/vespa-engine/vespa/pull/7081"},"issue":{"href":"https://api.github.com/repos/vespa-engine/vespa/issues/7081"},"comments":{"href":"https://api.github.com/repos/vespa-engine/vespa/issues/7081/comments"},"review_comments":{"href":"https://api.github.com/repos/vespa-engine/vespa/pulls/7081/comments"},"review_comment":{"href":"https://api.github.com/repos/vespa-engine/vespa/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/vespa-engine/vespa/pulls/7081/commits"},"statuses":{"href":"https://api.github.com/repos/vespa-engine/vespa/statuses/6306d72742feba81038761b9ce8aa6bd76fd088c"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"vekterli"}}